From 3bea234ba7722e5d0edf1aa0e5a191364d0922e6 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 17 Apr 2024 21:48:58 +0200 Subject: [PATCH 01/29] Create scafolding for "Predefined Abstain DRep" test --- cardano-testnet/cardano-testnet.cabal | 2 + .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 1 + .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 78 +++++++++++++++++++ .../cardano-testnet-test.hs | 5 +- 4 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index 31762be8e27..2d9ab117aa9 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -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 diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index 63181e1a470..daef447dc07 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -56,6 +56,7 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP work <- H.createDirectoryIfMissing $ tempAbsPath' "work" + -- Create default testnet with 3 DReps and 3 stake holders delegated, one to each DRep. let ceo = ConwayEraOnwardsConway sbe = conwayEraOnwardsToShelleyBasedEra ceo era = toCardanoEra sbe diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs new file mode 100644 index 00000000000..63827ed8cba --- /dev/null +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -0,0 +1,78 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} + +module Cardano.Testnet.Test.Gov.PredefinedAbstainDRep + ( hprop_check_predefined_abstain_drep + ) where + +import Cardano.Api as Api + +import Cardano.Testnet + +import Prelude + +import System.FilePath (()) + +import Testnet.Components.Query (getEpochStateView) +import qualified Testnet.Process.Run as H +import qualified Testnet.Property.Util as H +import Testnet.Runtime +import Testnet.Types (PoolNode (..), TestnetRuntime (..), nodeSocketPath) + +import Hedgehog +import qualified Hedgehog.Extras as H + +-- | Execute me with: +-- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Predefined Abstain DRep/"'@ +hprop_check_predefined_abstain_drep :: Property +hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \tempAbsBasePath' -> do + -- Start a local test net + conf@Conf { tempAbsPath } <- mkConf tempAbsBasePath' + let tempAbsPath' = unTmpAbsPath tempAbsPath + tempBaseAbsPath = makeTmpBaseAbsPath tempAbsPath + work <- H.createDirectoryIfMissing $ tempAbsPath' "work" + + let ceo = ConwayEraOnwardsConway + sbe = conwayEraOnwardsToShelleyBasedEra ceo + era = toCardanoEra sbe + cEra = AnyCardanoEra era + fastTestnetOptions = cardanoDefaultTestnetOptions + { cardanoEpochLength = 100 + , cardanoNodeEra = cEra + , cardanoNumDReps = 1 + } + + testnetRuntime@TestnetRuntime + { testnetMagic + , poolNodes + , wallets=_wallet0:_wallet1:_wallet2:_ + , configurationFile + } + <- cardanoTestnetDefault fastTestnetOptions conf + + PoolNode{poolRuntime} <- H.headM poolNodes + poolSprocket1 <- H.noteShow $ nodeSprocket poolRuntime + _execConfig <- H.mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic + let socketPath = nodeSocketPath poolRuntime + + _epochStateView <- getEpochStateView configurationFile socketPath + + startLedgerNewEpochStateLogging testnetRuntime tempAbsPath' + + H.note_ $ "Sprocket: " <> show poolSprocket1 + H.note_ $ "Abs path: " <> tempAbsBasePath' + H.note_ $ "Socketpath: " <> unFile socketPath + H.note_ $ "Foldblocks config file: " <> unFile configurationFile + + _gov <- H.createDirectoryIfMissing $ work "governance" + + -- ToDo: Do some proposal and vote yes with the first DRep only. + -- ToDo: ASSERT: Check that proposal does NOT pass. + -- ToDo: Take the last two stake delegators and delegate them to "Abstain". + -- ToDo: This can be done using cardano-cli conway stake-address vote-delegation-certificate --always-abstain + -- ToDo: Do some other proposal and vote yes with first DRep only. + -- ToDo: ASSERT: Check the new proposal passes now. + + success diff --git a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs index 859875dadd3..0c8bc4b38ff 100644 --- a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs +++ b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs @@ -17,6 +17,7 @@ import qualified Cardano.Testnet.Test.Gov.CommitteeAddNew as Gov import qualified Cardano.Testnet.Test.Gov.DRepDeposit as Gov import qualified Cardano.Testnet.Test.Gov.DRepRetirement as Gov import qualified Cardano.Testnet.Test.Gov.NoConfidence as Gov +import qualified Cardano.Testnet.Test.Gov.PredefinedAbstainDRep as Gov import qualified Cardano.Testnet.Test.Gov.ProposeNewConstitution as Gov import qualified Cardano.Testnet.Test.Gov.ProposeNewConstitutionSPO as Gov import qualified Cardano.Testnet.Test.Gov.TreasuryGrowth as Gov @@ -50,8 +51,8 @@ tests = do -- TODO: Replace foldBlocks with checkLedgerStateCondition , T.testGroup "Governance" [ ignoreOnMacAndWindows "Committee Add New" Gov.hprop_constitutional_committee_add_new - -- FIXME: This test is broken - drepActivity is not updated within the expeted period - -- , ignoreOnWindows "DRep Activity" Gov.hprop_check_drep_activity + , ignoreOnWindows "Predefined Abstain DRep" Gov.hprop_check_predefined_abstain_drep + , ignoreOnWindows "DRep Activity" Gov.hprop_check_drep_activity , ignoreOnMacAndWindows "Committee Motion Of No Confidence" Gov.hprop_gov_no_confidence , ignoreOnWindows "DRep Deposits" Gov.hprop_ledger_events_drep_deposits -- FIXME Those tests are flaky From 1c6e476e26509e1e776689200ebbf5a478426a4f Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 18 Apr 2024 01:53:10 +0200 Subject: [PATCH 02/29] Test predefined always abstain DRep --- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 286 +++++++++++++++++- 1 file changed, 271 insertions(+), 15 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 63827ed8cba..93d00e0f808 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -1,25 +1,47 @@ +{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} module Cardano.Testnet.Test.Gov.PredefinedAbstainDRep ( hprop_check_predefined_abstain_drep ) where import Cardano.Api as Api +import Cardano.Api.Error (displayError) +import Cardano.Api.IO.Base (Socket) import Cardano.Testnet import Prelude +import Control.Monad (void) +import Control.Monad.Catch (MonadCatch) +import qualified Data.Aeson as Aeson +import qualified Data.Aeson.Lens as AL +import Data.ByteString.Lazy.Char8 (pack) +import Data.String (fromString) +import qualified Data.Text as Text +import Data.Word (Word32) +import GHC.Stack (callStack) +import Lens.Micro ((^?)) import System.FilePath (()) -import Testnet.Components.Query (getEpochStateView) +import Testnet.Components.Query (EpochStateView, findLargestUtxoForPaymentKey, + getCurrentEpochNo, getEpochStateView, getMinDRepDeposit) +import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) +import Testnet.Process.Cli.DRep (createCertificatePublicationTxBody, createVotingTxBody, + generateVoteFiles) +import qualified Testnet.Process.Cli.Keys as P +import Testnet.Process.Cli.Transaction (retrieveTransactionId, signTx, submitTx) import qualified Testnet.Process.Run as H import qualified Testnet.Property.Util as H import Testnet.Runtime -import Testnet.Types (PoolNode (..), TestnetRuntime (..), nodeSocketPath) +import Testnet.Types (KeyPair (..), + PaymentKeyInfo (paymentKeyInfoAddr, paymentKeyInfoPair), PoolNode (..), + SomeKeyPair (SomeKeyPair), StakingKey, TestnetRuntime (..), nodeSocketPath) import Hedgehog import qualified Hedgehog.Extras as H @@ -28,12 +50,13 @@ import qualified Hedgehog.Extras as H -- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Predefined Abstain DRep/"'@ hprop_check_predefined_abstain_drep :: Property hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \tempAbsBasePath' -> do - -- Start a local test net + -- Start a local test net conf@Conf { tempAbsPath } <- mkConf tempAbsBasePath' let tempAbsPath' = unTmpAbsPath tempAbsPath tempBaseAbsPath = makeTmpBaseAbsPath tempAbsPath work <- H.createDirectoryIfMissing $ tempAbsPath' "work" + -- Create default testnet with 3 DReps and 3 stake holders delegated, one to each DRep. let ceo = ConwayEraOnwardsConway sbe = conwayEraOnwardsToShelleyBasedEra ceo era = toCardanoEra sbe @@ -41,23 +64,23 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ fastTestnetOptions = cardanoDefaultTestnetOptions { cardanoEpochLength = 100 , cardanoNodeEra = cEra - , cardanoNumDReps = 1 + , cardanoNumDReps = 3 } testnetRuntime@TestnetRuntime { testnetMagic , poolNodes - , wallets=_wallet0:_wallet1:_wallet2:_ + , wallets=wallet0:wallet1:wallet2:_ , configurationFile } <- cardanoTestnetDefault fastTestnetOptions conf PoolNode{poolRuntime} <- H.headM poolNodes poolSprocket1 <- H.noteShow $ nodeSprocket poolRuntime - _execConfig <- H.mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic + execConfig <- H.mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic let socketPath = nodeSocketPath poolRuntime - _epochStateView <- getEpochStateView configurationFile socketPath + epochStateView <- getEpochStateView configurationFile socketPath startLedgerNewEpochStateLogging testnetRuntime tempAbsPath' @@ -66,13 +89,246 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ H.note_ $ "Socketpath: " <> unFile socketPath H.note_ $ "Foldblocks config file: " <> unFile configurationFile - _gov <- H.createDirectoryIfMissing $ work "governance" + gov <- H.createDirectoryIfMissing $ work "governance" - -- ToDo: Do some proposal and vote yes with the first DRep only. - -- ToDo: ASSERT: Check that proposal does NOT pass. - -- ToDo: Take the last two stake delegators and delegate them to "Abstain". - -- ToDo: This can be done using cardano-cli conway stake-address vote-delegation-certificate --always-abstain - -- ToDo: Do some other proposal and vote yes with first DRep only. - -- ToDo: ASSERT: Check the new proposal passes now. + initialDesiredNumberOfPools <- getDesiredPoolNumberValue execConfig - success + let newNumberOfDesiredPools = fromIntegral (initialDesiredNumberOfPools + 1) + + -- Do some proposal and vote yes with the first DRep only + -- and assert that proposal does NOT pass. + void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "firstProposal" + wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools initialDesiredNumberOfPools 2 + + -- Take the last two stake delegators and delegate them to "Abstain". + delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe gov "delegateToAbstain1" + wallet1 (defaultDelegatorStakeKeyPair 2) + delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe gov "delegateToAbstain2" + wallet2 (defaultDelegatorStakeKeyPair 3) + + -- Do some other proposal and vote yes with first DRep only + -- and assert the new proposal passes now. + let newNumberOfDesiredPools2 = fromIntegral (newNumberOfDesiredPools + 1) + void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "secondProposal" + wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 newNumberOfDesiredPools2 2 + +delegateToAlwaysAbstain + :: (MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m) + => H.ExecConfig + -> EpochStateView + -> NodeConfigFile 'In + -> File Socket 'InOut + -> ShelleyBasedEra ConwayEra + -> FilePath + -> String + -> PaymentKeyInfo + -> KeyPair StakingKey + -> m () +delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe work prefix + payingWallet skeyPair@(KeyPair vKeyFile _sKeyFile) = do + + let era = toCardanoEra sbe + cEra = AnyCardanoEra era + + baseDir <- H.createDirectoryIfMissing $ work prefix + + -- Create vote delegation certificate + let voteDelegationCertificatePath = baseDir "delegation-certificate.delegcert" + void $ H.execCli' execConfig + [ "conway", "stake-address", "vote-delegation-certificate" + , "--always-abstain" + , "--stake-verification-key-file", unFile vKeyFile + , "--out-file", voteDelegationCertificatePath + ] + + -- Compose transaction to publish delegation certificate + repRegTxBody1 <- createCertificatePublicationTxBody execConfig epochStateView sbe baseDir "del-cert-txbody" + (File voteDelegationCertificatePath) payingWallet + + -- Sign transaction + repRegSignedRegTx1 <- signTx execConfig cEra baseDir "signed-reg-tx" + repRegTxBody1 [ SomeKeyPair (paymentKeyInfoPair payingWallet) + , SomeKeyPair skeyPair] + + -- Submit transaction + submitTx execConfig cEra repRegSignedRegTx1 + + -- Wait two epochs + (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView + void $ waitUntilEpoch configurationFile socketPath (EpochNo (epochAfterProp + 2)) + +desiredPoolNumberProposalTest + :: (MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t) + => H.ExecConfig + -> EpochStateView + -> NodeConfigFile 'In + -> File Socket 'InOut + -> ConwayEraOnwards ConwayEra + -> FilePath + -> FilePath + -> PaymentKeyInfo + -> Maybe (String, Word32) + -> t (Int, String) + -> Integer + -> Integer + -> Integer + -> m (String, Word32) +desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo work prefix + wallet previousProposalInfo votes change expected epochsToWait = do + let sbe = conwayEraOnwardsToShelleyBasedEra ceo + + baseDir <- H.createDirectoryIfMissing $ work prefix + + let propVotes :: [(String, Int)] + propVotes = zip (concatMap (uncurry replicate) votes) [1..] + annotateShow propVotes + + thisProposal@(governanceActionTxId, governanceActionIndex) <- + makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile socketPath + ceo baseDir "proposal" previousProposalInfo (fromIntegral change) wallet + + voteChangeProposal execConfig epochStateView sbe baseDir "vote" + governanceActionTxId governanceActionIndex propVotes wallet + + (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView + H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp + + void $ waitUntilEpoch configurationFile socketPath (EpochNo (epochAfterProp + fromIntegral epochsToWait)) + desiredPoolNumberAfterProp <- getDesiredPoolNumberValue execConfig + + desiredPoolNumberAfterProp === expected + + return thisProposal + +makeDesiredPoolNumberChangeProposal + :: (H.MonadAssertion m, MonadTest m, MonadCatch m, MonadIO m) + => H.ExecConfig + -> EpochStateView + -> NodeConfigFile 'In + -> SocketPath + -> ConwayEraOnwards ConwayEra + -> FilePath + -> String + -> Maybe (String, Word32) + -> Word32 + -> PaymentKeyInfo + -> m (String, Word32) +makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile socketPath + ceo work prefix prevGovActionInfo desiredPoolNumber wallet = do + + let sbe = conwayEraOnwardsToShelleyBasedEra ceo + era = toCardanoEra sbe + cEra = AnyCardanoEra era + + baseDir <- H.createDirectoryIfMissing $ work prefix + + let stakeVkeyFp = baseDir "stake.vkey" + stakeSKeyFp = baseDir "stake.skey" + + P.cliStakeAddressKeyGen + $ KeyPair { verificationKey = File stakeVkeyFp + , signingKey = File stakeSKeyFp + } + + proposalAnchorFile <- H.note $ baseDir "sample-proposal-anchor" + H.writeFile proposalAnchorFile "dummy anchor data" + + proposalAnchorDataHash <- H.execCli' execConfig + [ "conway", "governance" + , "hash", "anchor-data", "--file-text", proposalAnchorFile + ] + + minDRepDeposit <- getMinDRepDeposit epochStateView ceo + + proposalFile <- H.note $ baseDir "sample-proposal-file" + + void $ H.execCli' execConfig $ + [ "conway", "governance", "action", "create-protocol-parameters-update" + , "--testnet" + , "--governance-action-deposit", show @Integer minDRepDeposit + , "--deposit-return-stake-verification-key-file", stakeVkeyFp + ] ++ concatMap (\(prevGovernanceActionTxId, prevGovernanceActionIndex) -> + [ "--prev-governance-action-tx-id", prevGovernanceActionTxId + , "--prev-governance-action-index", show prevGovernanceActionIndex + ]) prevGovActionInfo ++ + [ "--number-of-pools", show desiredPoolNumber + , "--anchor-url", "https://tinyurl.com/3wrwb2as" + , "--anchor-data-hash", proposalAnchorDataHash + , "--out-file", proposalFile + ] + + proposalBody <- H.note $ baseDir "tx.body" + txIn <- findLargestUtxoForPaymentKey epochStateView sbe wallet + + void $ H.execCli' execConfig + [ "conway", "transaction", "build" + , "--change-address", Text.unpack $ paymentKeyInfoAddr wallet + , "--tx-in", Text.unpack $ renderTxIn txIn + , "--proposal-file", proposalFile + , "--out-file", proposalBody + ] + + signedProposalTx <- signTx execConfig cEra baseDir "signed-proposal" + (File proposalBody) [SomeKeyPair $ paymentKeyInfoPair wallet] + + submitTx execConfig cEra signedProposalTx + + governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx + + !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) + configurationFile + socketPath + (EpochNo 30) + + governanceActionIndex <- case propSubmittedResult of + Left e -> + H.failMessage callStack + $ "findCondition failed with: " <> displayError e + Right Nothing -> + H.failMessage callStack "Couldn't find proposal." + Right (Just a) -> return a + + return (governanceActionTxId, governanceActionIndex) + +voteChangeProposal :: (MonadTest m, MonadIO m, MonadCatch m, H.MonadAssertion m) + => H.ExecConfig + -> EpochStateView + -> ShelleyBasedEra ConwayEra + -> FilePath + -> FilePath + -> String + -> Word32 + -> [([Char], Int)] + -> PaymentKeyInfo + -> m () +voteChangeProposal execConfig epochStateView sbe work prefix governanceActionTxId governanceActionIndex votes wallet = do + baseDir <- H.createDirectoryIfMissing $ work prefix + + let era = toCardanoEra sbe + cEra = AnyCardanoEra era + + voteFiles <- generateVoteFiles execConfig baseDir "vote-files" + governanceActionTxId governanceActionIndex + [(defaultDRepKeyPair idx, vote) | (vote, idx) <- votes] + + voteTxBodyFp <- createVotingTxBody execConfig epochStateView sbe baseDir "vote-tx-body" + voteFiles wallet + + voteTxFp <- signTx execConfig cEra baseDir "signed-vote-tx" voteTxBodyFp + (SomeKeyPair (paymentKeyInfoPair wallet):[SomeKeyPair $ defaultDRepKeyPair n | (_, n) <- votes]) + submitTx execConfig cEra voteTxFp + +getDesiredPoolNumberValue :: (MonadTest m, MonadCatch m, MonadIO m) => H.ExecConfig -> m Integer +getDesiredPoolNumberValue execConfig = do + govStateString <- H.execCli' execConfig + [ "conway", "query", "gov-state" + , "--volatile-tip" + ] + + govStateJSON <- H.nothingFail (Aeson.decode (pack govStateString) :: Maybe Aeson.Value) + let mTargetPoolNum :: Maybe Integer + mTargetPoolNum = govStateJSON + ^? AL.key "currentPParams" + . AL.key "stakePoolTargetNum" + . AL._Integer + evalMaybe mTargetPoolNum From a855a0556a36b08cbfe8949304011e57187c835a Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 30 Apr 2024 20:36:10 +0200 Subject: [PATCH 03/29] Add comment to `hprop_check_predefined_abstain_drep` explaining what it tests --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 11 ++++++++++- .../test/cardano-testnet-test/cardano-testnet-test.hs | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 93d00e0f808..e763258c8cc 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -46,7 +46,16 @@ import Testnet.Types (KeyPair (..), import Hedgehog import qualified Hedgehog.Extras as H --- | Execute me with: +-- | This test creates a default testnet with three DReps and one stake holder delegated to each. +-- We then do a proposal for an arbitrary parameter change (in this case to the +-- @desiredNumberOfPools@ parameter) to check that it fails, when the first DRep votes "yes" and the +-- last two vote "no". Later we chack that if we change the stake holders under the DReps that vote +-- "no" to delegate to the automate "always abstain" DRep, the same kind of proposal passes. +-- +-- This test is meant to ensure that delegating to "always abstain" has the desired effect of +-- counting as abstaining for the stake delegated. +-- +-- Execute me with: -- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Predefined Abstain DRep/"'@ hprop_check_predefined_abstain_drep :: Property hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \tempAbsBasePath' -> do diff --git a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs index 0c8bc4b38ff..40343be1afa 100644 --- a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs +++ b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs @@ -14,6 +14,7 @@ import qualified Cardano.Testnet.Test.Cli.Query import qualified Cardano.Testnet.Test.Cli.QuerySlotNumber import qualified Cardano.Testnet.Test.FoldEpochState import qualified Cardano.Testnet.Test.Gov.CommitteeAddNew as Gov +import qualified Cardano.Testnet.Test.Gov.DRepActivity as Gov import qualified Cardano.Testnet.Test.Gov.DRepDeposit as Gov import qualified Cardano.Testnet.Test.Gov.DRepRetirement as Gov import qualified Cardano.Testnet.Test.Gov.NoConfidence as Gov From f222b9e0aef4e8f1423dffb079da3e08fb9b5848 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 30 Apr 2024 20:48:22 +0200 Subject: [PATCH 04/29] Add comment to `getDesiredPoolNumberValue` explaining what the `desiredPoolNumber` is --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index e763258c8cc..5ff2ee0bb10 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -327,6 +327,12 @@ voteChangeProposal execConfig epochStateView sbe work prefix governanceActionTxI (SomeKeyPair (paymentKeyInfoPair wallet):[SomeKeyPair $ defaultDRepKeyPair n | (_, n) <- votes]) submitTx execConfig cEra voteTxFp +-- | Obtains the @desiredPoolNumberValue@ from the protocol parameters. +-- The @desiredPoolNumberValue@ or (@k@ in the spec) is the protocol parameter +-- that defines what is the optimal number of SPOs. It is a tradeoff between +-- decentralization and efficiency and the spec suggest it should be between 100 an 1000. +-- Changing this parameter will inderectly affect how easy it is to saturate a pool in order to +-- incentivize that the number of SPOs states close to the parameter value. getDesiredPoolNumberValue :: (MonadTest m, MonadCatch m, MonadIO m) => H.ExecConfig -> m Integer getDesiredPoolNumberValue execConfig = do govStateString <- H.execCli' execConfig From 05bc5ec1a806022b6d00ca767af48e241d659ea1 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 30 Apr 2024 21:21:52 +0200 Subject: [PATCH 05/29] Used a type synonym for `votes` and added comments to `voteChangeProposal` --- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 5ff2ee0bb10..8e1e1025ca5 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -188,7 +188,7 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket baseDir <- H.createDirectoryIfMissing $ work prefix - let propVotes :: [(String, Int)] + let propVotes :: [DefaultDRepVote] propVotes = zip (concatMap (uncurry replicate) votes) [1..] annotateShow propVotes @@ -299,18 +299,26 @@ makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile return (governanceActionTxId, governanceActionIndex) +-- A pair of a vote string (i.e: "yes", "no", or "abstain") and the number of +-- a default DRep (from the ones created by 'cardanoTestnetDefault') +type DefaultDRepVote = (String, Int) + +-- | Create and issue votes for (or against) a government proposal with default +-- Delegate Representative (DReps created by 'cardanoTestnetDefault') using @cardano-cli@. voteChangeProposal :: (MonadTest m, MonadIO m, MonadCatch m, H.MonadAssertion m) - => H.ExecConfig - -> EpochStateView - -> ShelleyBasedEra ConwayEra - -> FilePath - -> FilePath - -> String - -> Word32 - -> [([Char], Int)] - -> PaymentKeyInfo + => H.ExecConfig -- ^ Specifies the CLI execution configuration. + -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained + -- using the 'getEpochStateView' function. + -> ShelleyBasedEra ConwayEra -- ^ The Shelley-based witness for ConwayEra (i.e: ShelleyBasedEraConway). + -> FilePath -- ^ Base directory path where the subdirectory with the intermediate files will be created. + -> String -- ^ Name for the subdirectory that will be created for storing the intermediate files. + -> String -- ^ Transaction id of the governance action to vote. + -> Word32 -- ^ Index of the governance action to vote in the transaction. + -> [DefaultDRepVote] -- ^ List of votes to issue as pairs of the vote and the number of DRep that votes it. + -> PaymentKeyInfo -- ^ Wallet that will pay for the transactions -> m () -voteChangeProposal execConfig epochStateView sbe work prefix governanceActionTxId governanceActionIndex votes wallet = do +voteChangeProposal execConfig epochStateView sbe work prefix + governanceActionTxId governanceActionIndex votes wallet = do baseDir <- H.createDirectoryIfMissing $ work prefix let era = toCardanoEra sbe From 93af4165536f1d080aa4013cd832c7295964909a Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 1 May 2024 20:17:58 +0200 Subject: [PATCH 06/29] Add `HasCallStack` to several functions Co-authored-by: Mateusz Galazyn <228866+carbolymer@users.noreply.github.com> --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 8e1e1025ca5..b21e3dfeffc 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -25,7 +25,7 @@ import Data.ByteString.Lazy.Char8 (pack) import Data.String (fromString) import qualified Data.Text as Text import Data.Word (Word32) -import GHC.Stack (callStack) +import GHC.Stack (HasCallStack, callStack) import Lens.Micro ((^?)) import System.FilePath (()) @@ -51,7 +51,7 @@ import qualified Hedgehog.Extras as H -- @desiredNumberOfPools@ parameter) to check that it fails, when the first DRep votes "yes" and the -- last two vote "no". Later we chack that if we change the stake holders under the DReps that vote -- "no" to delegate to the automate "always abstain" DRep, the same kind of proposal passes. --- +-- -- This test is meant to ensure that delegating to "always abstain" has the desired effect of -- counting as abstaining for the stake delegated. -- @@ -122,7 +122,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 newNumberOfDesiredPools2 2 delegateToAlwaysAbstain - :: (MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m) + :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m) => H.ExecConfig -> EpochStateView -> NodeConfigFile 'In @@ -167,7 +167,7 @@ delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath s void $ waitUntilEpoch configurationFile socketPath (EpochNo (epochAfterProp + 2)) desiredPoolNumberProposalTest - :: (MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t) + :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t) => H.ExecConfig -> EpochStateView -> NodeConfigFile 'In @@ -210,7 +210,7 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket return thisProposal makeDesiredPoolNumberChangeProposal - :: (H.MonadAssertion m, MonadTest m, MonadCatch m, MonadIO m) + :: (HasCallStack, H.MonadAssertion m, MonadTest m, MonadCatch m, MonadIO m) => H.ExecConfig -> EpochStateView -> NodeConfigFile 'In From 2cb5a18e7d18462973acbb1a459d654c9ce519db Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 1 May 2024 20:39:25 +0200 Subject: [PATCH 07/29] Remove unneccessary `fromIntegral` usages --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index b21e3dfeffc..f1f28536113 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -102,7 +102,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ initialDesiredNumberOfPools <- getDesiredPoolNumberValue execConfig - let newNumberOfDesiredPools = fromIntegral (initialDesiredNumberOfPools + 1) + let newNumberOfDesiredPools = initialDesiredNumberOfPools + 1 -- Do some proposal and vote yes with the first DRep only -- and assert that proposal does NOT pass. @@ -117,7 +117,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ -- Do some other proposal and vote yes with first DRep only -- and assert the new proposal passes now. - let newNumberOfDesiredPools2 = fromIntegral (newNumberOfDesiredPools + 1) + let newNumberOfDesiredPools2 = newNumberOfDesiredPools + 1 void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "secondProposal" wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 newNumberOfDesiredPools2 2 From 73af6e90829202b5c25687288ddd6e98a102bc98 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 1 May 2024 21:08:33 +0200 Subject: [PATCH 08/29] Added Haddocks to arguments for several functions --- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index f1f28536113..21e557452cf 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -123,15 +123,16 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ delegateToAlwaysAbstain :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m) - => H.ExecConfig - -> EpochStateView - -> NodeConfigFile 'In - -> File Socket 'InOut - -> ShelleyBasedEra ConwayEra - -> FilePath - -> String - -> PaymentKeyInfo - -> KeyPair StakingKey + => H.ExecConfig -- ^ Specifies the CLI execution configuration. + -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained + -- using the 'getEpochStateView' function. + -> NodeConfigFile 'In -- ^ Path to the node configuration file as returned by 'cardanoTestnetDefault'. + -> File Socket 'InOut -- ^ Path to the cardano-node unix socket file. + -> 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. -> m () delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe work prefix payingWallet skeyPair@(KeyPair vKeyFile _sKeyFile) = do @@ -168,19 +169,21 @@ delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath s desiredPoolNumberProposalTest :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t) - => H.ExecConfig - -> EpochStateView - -> NodeConfigFile 'In - -> File Socket 'InOut - -> ConwayEraOnwards ConwayEra - -> FilePath - -> FilePath - -> PaymentKeyInfo - -> Maybe (String, Word32) - -> t (Int, String) - -> Integer - -> Integer - -> Integer + => 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'. + -> File Socket 'InOut -- ^ Path to the cardano-node unix socket file. + -> ConwayEraOnwards ConwayEra -- ^ The ConwaysEraOnwards witness for the Conway era + -> 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. + -> Maybe (String, Word32) -- ^ The transaction identifier and index of the previous passed + -- governance action if any. + -> t (Int, String) -- ^ Model of votes to issue as a list of pairs of amount of each vote + -- together with the vote (i.e: "yes", "no", "abstain") + -> Integer -- ^ What to change the @desiredPoolNumber@ to + -> Integer -- ^ What the expected result is of the change + -> Integer -- ^ How many epochs to wait before checking the result -> m (String, Word32) desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo work prefix wallet previousProposalInfo votes change expected epochsToWait = do @@ -211,16 +214,18 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket makeDesiredPoolNumberChangeProposal :: (HasCallStack, H.MonadAssertion m, MonadTest m, MonadCatch m, MonadIO m) - => H.ExecConfig - -> EpochStateView - -> NodeConfigFile 'In - -> SocketPath - -> ConwayEraOnwards ConwayEra - -> FilePath - -> String - -> Maybe (String, Word32) - -> Word32 - -> PaymentKeyInfo + => H.ExecConfig -- ^ Specifies the CLI execution configuration. + -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained + -> NodeConfigFile 'In -- ^ Absolute path to the "configuration.yaml" file for the testnet + -- as returned by the 'cardanoTestnetDefault' function. + -> SocketPath -- ^ Path to the cardano-node unix socket file. + -> ConwayEraOnwards ConwayEra -- ^ The conway era onwards witness for the era 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. + -> Maybe (String, Word32) -- ^ The transaction identifier and index of the previous passed + -- governance action if any. + -> Word32 -- ^ What to change the @desiredPoolNumber@ to + -> PaymentKeyInfo -- ^ Wallet that will pay for the transaction. -> m (String, Word32) makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile socketPath ceo work prefix prevGovActionInfo desiredPoolNumber wallet = do From 7f19ce72dd990765251216dcd6bd237be0094aba Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 1 May 2024 21:50:51 +0200 Subject: [PATCH 09/29] =?UTF-8?q?Used=20`getGovState`=20instead=20of=20cal?= =?UTF-8?q?ling=20`cardano-cl=C3=AC`?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 21e557452cf..bfa69b219ee 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -1,5 +1,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -10,27 +11,28 @@ module Cardano.Testnet.Test.Gov.PredefinedAbstainDRep ) where import Cardano.Api as Api +import Cardano.Api.Eon.ShelleyBasedEra (ShelleyLedgerEra) import Cardano.Api.Error (displayError) import Cardano.Api.IO.Base (Socket) +import Cardano.Ledger.Conway.Core (ppNOptL) +import Cardano.Ledger.Conway.Governance (ConwayGovState, cgsCurPParamsL) +import Cardano.Ledger.Core (EraPParams) import Cardano.Testnet import Prelude import Control.Monad (void) import Control.Monad.Catch (MonadCatch) -import qualified Data.Aeson as Aeson -import qualified Data.Aeson.Lens as AL -import Data.ByteString.Lazy.Char8 (pack) import Data.String (fromString) import qualified Data.Text as Text import Data.Word (Word32) import GHC.Stack (HasCallStack, callStack) -import Lens.Micro ((^?)) +import Lens.Micro ((^.)) import System.FilePath (()) import Testnet.Components.Query (EpochStateView, findLargestUtxoForPaymentKey, - getCurrentEpochNo, getEpochStateView, getMinDRepDeposit) + getCurrentEpochNo, getEpochStateView, getGovState, getMinDRepDeposit) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep (createCertificatePublicationTxBody, createVotingTxBody, generateVoteFiles) @@ -100,7 +102,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ gov <- H.createDirectoryIfMissing $ work "governance" - initialDesiredNumberOfPools <- getDesiredPoolNumberValue execConfig + initialDesiredNumberOfPools <- getDesiredPoolNumberValue epochStateView ceo let newNumberOfDesiredPools = initialDesiredNumberOfPools + 1 @@ -206,7 +208,7 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp void $ waitUntilEpoch configurationFile socketPath (EpochNo (epochAfterProp + fromIntegral epochsToWait)) - desiredPoolNumberAfterProp <- getDesiredPoolNumberValue execConfig + desiredPoolNumberAfterProp <- getDesiredPoolNumberValue epochStateView ceo desiredPoolNumberAfterProp === expected @@ -346,17 +348,11 @@ voteChangeProposal execConfig epochStateView sbe work prefix -- decentralization and efficiency and the spec suggest it should be between 100 an 1000. -- Changing this parameter will inderectly affect how easy it is to saturate a pool in order to -- incentivize that the number of SPOs states close to the parameter value. -getDesiredPoolNumberValue :: (MonadTest m, MonadCatch m, MonadIO m) => H.ExecConfig -> m Integer -getDesiredPoolNumberValue execConfig = do - govStateString <- H.execCli' execConfig - [ "conway", "query", "gov-state" - , "--volatile-tip" - ] - - govStateJSON <- H.nothingFail (Aeson.decode (pack govStateString) :: Maybe Aeson.Value) - let mTargetPoolNum :: Maybe Integer - mTargetPoolNum = govStateJSON - ^? AL.key "currentPParams" - . AL.key "stakePoolTargetNum" - . AL._Integer - evalMaybe mTargetPoolNum +getDesiredPoolNumberValue :: (EraPParams (ShelleyLedgerEra era), H.MonadAssertion m, MonadTest m, MonadIO m) + => EpochStateView + -> ConwayEraOnwards era + -> m Integer +getDesiredPoolNumberValue epochStateView ceo = do + govState :: ConwayGovState era <- getGovState epochStateView ceo + return $ toInteger $ govState ^. cgsCurPParamsL + . ppNOptL From 232baa70e27a50fabb2fa292b576351a24f182e6 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 02:44:43 +0200 Subject: [PATCH 10/29] Refactor out and generalize `waitAndCheck` function --- .../src/Testnet/Components/Query.hs | 50 +++++++++++++++++-- .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 30 +++-------- 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index 6ac751f0f4a..2933b713c33 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -2,6 +2,7 @@ {-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} module Testnet.Components.Query @@ -20,14 +21,16 @@ module Testnet.Components.Query , findUtxosWithAddress , findLargestUtxoWithAddress , findLargestUtxoForPaymentKey + , waitAndCheckNewEpochState ) 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 Cardano.Ledger.BaseTypes (addEpochInterval) import qualified Cardano.Ledger.Coin as L import qualified Cardano.Ledger.Conway.Governance as L import qualified Cardano.Ledger.Conway.PParams as L @@ -35,8 +38,9 @@ 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 Control.Monad.Trans.State.Strict (StateT, put) import Data.Bifunctor (bimap) import Data.IORef import Data.List (sortOn) @@ -48,9 +52,10 @@ import Data.Ord (Down (..)) import Data.Text (Text) import qualified Data.Text as T import Data.Type.Equality +import Data.Word (Word64) 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) @@ -353,3 +358,40 @@ getCurrentEpochNo getCurrentEpochNo epochStateView = withFrozenCallStack $ do AnyNewEpochState _ newEpochState <- getEpochState epochStateView pure $ newEpochState ^. L.nesELL + +waitAndCheckNewEpochState :: forall m era value. (MonadAssertion m, MonadTest m, MonadIO m, Eq value) + => EpochStateView -> NodeConfigFile In -> SocketPath -> ShelleyBasedEra era -> EpochInterval -> Maybe value -> EpochInterval + -> Lens' (L.NewEpochState (ShelleyLedgerEra era)) value -> m () +waitAndCheckNewEpochState epochStateView configurationFile socketPath sbe (EpochInterval minWait) mExpected (EpochInterval maxWait) lens = do + (EpochNo curEpoch) <- getCurrentEpochNo epochStateView + eProposalResult + <- H.evalIO . runExceptT $ foldEpochState + configurationFile + socketPath + FullValidation + (EpochNo (curEpoch + fromIntegral maxWait)) + () + (\epochState _ _ -> filterEpochState (isSuccess curEpoch) epochState) + void $ H.evalEither eProposalResult + where + filterEpochState :: (EpochNo -> value -> Bool) -> AnyNewEpochState -> StateT () IO LedgerStateCondition + filterEpochState f (AnyNewEpochState actualEra newEpochState) = + caseShelleyToBabbageOrConwayEraOnwards + (const $ error "waitAndCheck: Only conway era onwards supported") + (const $ do + Refl <- either error pure $ assertErasEqual sbe actualEra + let val = newEpochState ^. lens + currEpoch = L.nesEL newEpochState + return (if f currEpoch val + then ConditionMet + else ConditionNotMet) + ) + sbe + + isSuccess :: Word64 -> EpochNo -> value -> Bool + isSuccess epochAfterProp (EpochNo epochNo) value = + (epochAfterProp + fromIntegral minWait <= epochNo) && + (case mExpected of + Nothing -> True + Just expected -> value == expected) && + (epochNo <= epochAfterProp + fromIntegral maxWait) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index daef447dc07..7e7c7c6607b 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -1,5 +1,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE ScopedTypeVariables #-} @@ -10,11 +11,12 @@ module Cardano.Testnet.Test.Gov.DRepActivity ) where import Cardano.Api as Api +import Cardano.Api.Eon.ShelleyBasedEra (ShelleyLedgerEra) import Cardano.Api.Error (displayError) import Cardano.Api.Ledger (EpochInterval (EpochInterval, unEpochInterval), drepExpiry) -import Cardano.Ledger.Conway.Core (curPParamsGovStateL) -import Cardano.Ledger.Conway.PParams (ppDRepActivityL) +import Cardano.Ledger.Conway.Core (EraGov, curPParamsGovStateL) +import Cardano.Ledger.Conway.PParams (ConwayEraPParams, ppDRepActivityL) import Cardano.Ledger.Shelley.LedgerState (epochStateGovStateL, nesEpochStateL) import Cardano.Testnet @@ -28,13 +30,11 @@ import Data.String import qualified Data.Text as Text import Data.Word (Word32, Word64) import GHC.Stack -import Lens.Micro ((^.)) import System.FilePath (()) import Testnet.Components.Query import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) -import Testnet.EpochStateProcessing (watchEpochStateView) import Testnet.Process.Cli.DRep import Testnet.Process.Cli.Keys import Testnet.Process.Cli.Transaction @@ -155,7 +155,8 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP -- and issues the specified votes using default DReps. Optionally, it also -- waits checks the expected effect of the proposal. activityChangeProposalTest - :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t, Typeable era) + :: forall m t era . (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t, Typeable era, + EraGov (ShelleyLedgerEra era), ConwayEraPParams (ShelleyLedgerEra era)) => H.ExecConfig -- ^ Specifies the CLI execution configuration. -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained -- using the 'getEpochStateView' function. @@ -199,26 +200,11 @@ activityChangeProposalTest execConfig epochStateView configurationFile socketPat (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp - void $ waitForEpochs epochStateView minWait - forM_ mExpected $ \expected -> - H.nothingFailM $ watchEpochStateView epochStateView (isDRepActivityUpdated expected) maxWait + waitAndCheckNewEpochState epochStateView configurationFile socketPath sbe minWait mExpected maxWait + (nesEpochStateL . epochStateGovStateL . curPParamsGovStateL . ppDRepActivityL) return thisProposal - where - isDRepActivityUpdated :: (HasCallStack, MonadTest m) - => EpochInterval -> AnyNewEpochState -> m (Maybe ()) - isDRepActivityUpdated (EpochInterval expected) (AnyNewEpochState sbe newEpochState) = - caseShelleyToBabbageOrConwayEraOnwards - (const $ error "activityChangeProposalTest: Only conway era onwards supported") - (const $ do - let (EpochInterval epochInterval) = newEpochState ^. nesEpochStateL . epochStateGovStateL . curPParamsGovStateL . ppDRepActivityL - return (if epochInterval == expected then Just () else Nothing) - ) - sbe - --- | Create a proposal to change the DRep activity interval. --- Return the transaction id and the index of the governance action. makeActivityChangeProposal :: (HasCallStack, H.MonadAssertion m, MonadTest m, MonadCatch m, MonadIO m, Typeable era) => H.ExecConfig -- ^ Specifies the CLI execution configuration. From 4e2eed91b07b538bd6f693ed64880a1271ef1fe0 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 03:03:12 +0200 Subject: [PATCH 11/29] Change witness to be `ConwayEraOnwards` --- cardano-testnet/src/Testnet/Components/Query.hs | 11 ++++++----- .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index 2933b713c33..30c7ad2e35e 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -360,9 +360,10 @@ getCurrentEpochNo epochStateView = withFrozenCallStack $ do pure $ newEpochState ^. L.nesELL waitAndCheckNewEpochState :: forall m era value. (MonadAssertion m, MonadTest m, MonadIO m, Eq value) - => EpochStateView -> NodeConfigFile In -> SocketPath -> ShelleyBasedEra era -> EpochInterval -> Maybe value -> EpochInterval + => EpochStateView -> NodeConfigFile In -> SocketPath -> ConwayEraOnwards era -> EpochInterval -> Maybe value -> EpochInterval -> Lens' (L.NewEpochState (ShelleyLedgerEra era)) value -> m () -waitAndCheckNewEpochState epochStateView configurationFile socketPath sbe (EpochInterval minWait) mExpected (EpochInterval maxWait) lens = do +waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo (EpochInterval minWait) mExpected (EpochInterval maxWait) lens = do + let sbe = conwayEraOnwardsToShelleyBasedEra ceo (EpochNo curEpoch) <- getCurrentEpochNo epochStateView eProposalResult <- H.evalIO . runExceptT $ foldEpochState @@ -371,11 +372,11 @@ waitAndCheckNewEpochState epochStateView configurationFile socketPath sbe (Epoch FullValidation (EpochNo (curEpoch + fromIntegral maxWait)) () - (\epochState _ _ -> filterEpochState (isSuccess curEpoch) epochState) + (\epochState _ _ -> filterEpochState (isSuccess curEpoch) epochState sbe) void $ H.evalEither eProposalResult where - filterEpochState :: (EpochNo -> value -> Bool) -> AnyNewEpochState -> StateT () IO LedgerStateCondition - filterEpochState f (AnyNewEpochState actualEra newEpochState) = + filterEpochState :: (EpochNo -> value -> Bool) -> AnyNewEpochState -> ShelleyBasedEra era -> StateT () IO LedgerStateCondition + filterEpochState f (AnyNewEpochState actualEra newEpochState) sbe = caseShelleyToBabbageOrConwayEraOnwards (const $ error "waitAndCheck: Only conway era onwards supported") (const $ do diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index 7e7c7c6607b..c5fdc224b95 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -200,7 +200,7 @@ activityChangeProposalTest execConfig epochStateView configurationFile socketPat (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp - waitAndCheckNewEpochState epochStateView configurationFile socketPath sbe minWait mExpected maxWait + waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo minWait mExpected maxWait (nesEpochStateL . epochStateGovStateL . curPParamsGovStateL . ppDRepActivityL) return thisProposal From 55c368d42dd0f319bc073b9451872d87aceb73d3 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 03:12:57 +0200 Subject: [PATCH 12/29] Added comments to the new `waitAndCheckNewEpochState` function --- .../src/Testnet/Components/Query.hs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index 30c7ad2e35e..423a94cc9b7 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -359,9 +359,22 @@ getCurrentEpochNo epochStateView = withFrozenCallStack $ do AnyNewEpochState _ newEpochState <- getEpochState epochStateView pure $ newEpochState ^. L.nesELL -waitAndCheckNewEpochState :: forall m era value. (MonadAssertion m, MonadTest m, MonadIO m, Eq value) - => EpochStateView -> NodeConfigFile In -> SocketPath -> ConwayEraOnwards era -> EpochInterval -> Maybe value -> EpochInterval - -> Lens' (L.NewEpochState (ShelleyLedgerEra era)) value -> m () +-- | Waits for a minimum of @minWait@ epochs and a maximum of @maxWait@ epochs for +-- the value pointed by the @lens@ to become the same as the @mExpected@ (if it is not 'Nothing'). +-- If the value is not reached within the time frame, the test fails. If @mExpected@ is 'Nothing', +-- the value is not checked, but the function will sitll wait for @minWait@ epochs. +waitAndCheckNewEpochState + :: forall m era value. + (MonadAssertion m, MonadTest m, MonadIO m, Eq value) + => EpochStateView -- ^ Current epoch state view. It can be obtained using the 'getEpochStateView' function. + -> NodeConfigFile In -- ^ The file path to the configuration file. + -> SocketPath -- ^ The file path to the unix socket file to connect to the @cardano-node@. + -> ConwayEraOnwards era -- ^ Witness for the current era that shows it is Conway or onwards. + -> EpochInterval -- ^ The minimum wait time in epochs. + -> Maybe 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 () waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo (EpochInterval minWait) mExpected (EpochInterval maxWait) lens = do let sbe = conwayEraOnwardsToShelleyBasedEra ceo (EpochNo curEpoch) <- getCurrentEpochNo epochStateView From e63437cb232949ac779fcc341c57ea20f489dca3 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 03:31:48 +0200 Subject: [PATCH 13/29] Modify `desiredPoolNumberProposalTest` to use `waitAndCheckNewEpochState` --- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index bfa69b219ee..514d9eac3b5 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -14,10 +14,12 @@ import Cardano.Api as Api import Cardano.Api.Eon.ShelleyBasedEra (ShelleyLedgerEra) import Cardano.Api.Error (displayError) import Cardano.Api.IO.Base (Socket) +import Cardano.Api.Ledger (EpochInterval (EpochInterval)) import Cardano.Ledger.Conway.Core (ppNOptL) import Cardano.Ledger.Conway.Governance (ConwayGovState, cgsCurPParamsL) import Cardano.Ledger.Core (EraPParams) +import Cardano.Ledger.Shelley.LedgerState (epochStateGovStateL, nesEpochStateL) import Cardano.Testnet import Prelude @@ -32,7 +34,8 @@ import Lens.Micro ((^.)) import System.FilePath (()) import Testnet.Components.Query (EpochStateView, findLargestUtxoForPaymentKey, - getCurrentEpochNo, getEpochStateView, getGovState, getMinDRepDeposit) + getCurrentEpochNo, getEpochStateView, getGovState, getMinDRepDeposit, + waitAndCheckNewEpochState) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep (createCertificatePublicationTxBody, createVotingTxBody, generateVoteFiles) @@ -109,7 +112,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ -- Do some proposal and vote yes with the first DRep only -- and assert that proposal does NOT pass. void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "firstProposal" - wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools initialDesiredNumberOfPools 2 + wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools 3 (Just initialDesiredNumberOfPools) 10 -- Take the last two stake delegators and delegate them to "Abstain". delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe gov "delegateToAbstain1" @@ -121,7 +124,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ -- and assert the new proposal passes now. let newNumberOfDesiredPools2 = newNumberOfDesiredPools + 1 void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "secondProposal" - wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 newNumberOfDesiredPools2 2 + wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 0 (Just newNumberOfDesiredPools2) 10 delegateToAlwaysAbstain :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m) @@ -184,11 +187,12 @@ desiredPoolNumberProposalTest -> t (Int, String) -- ^ Model of votes to issue as a list of pairs of amount of each vote -- together with the vote (i.e: "yes", "no", "abstain") -> Integer -- ^ What to change the @desiredPoolNumber@ to - -> Integer -- ^ What the expected result is of the change - -> Integer -- ^ How many epochs to wait before checking the result + -> Integer -- ^ Minimum number of epochs to wait before checking the result + -> Maybe Integer -- ^ What the expected result is of the change (if anything) + -> Integer -- ^ Maximum number of epochs to wait while waiting for the result -> m (String, Word32) desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo work prefix - wallet previousProposalInfo votes change expected epochsToWait = do + wallet previousProposalInfo votes change minWait mExpected maxWait = do let sbe = conwayEraOnwardsToShelleyBasedEra ceo baseDir <- H.createDirectoryIfMissing $ work prefix @@ -207,10 +211,10 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp - void $ waitUntilEpoch configurationFile socketPath (EpochNo (epochAfterProp + fromIntegral epochsToWait)) - desiredPoolNumberAfterProp <- getDesiredPoolNumberValue epochStateView ceo - - desiredPoolNumberAfterProp === expected + waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo + (EpochInterval (fromIntegral minWait)) (fromIntegral <$> mExpected) + (EpochInterval (fromIntegral maxWait)) + (nesEpochStateL . epochStateGovStateL . cgsCurPParamsL . ppNOptL) return thisProposal @@ -291,10 +295,12 @@ makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx + (EpochNo curEpoch) <- getCurrentEpochNo epochStateView + !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) configurationFile socketPath - (EpochNo 30) + (EpochNo $ curEpoch + 10) governanceActionIndex <- case propSubmittedResult of Left e -> From 4fc452320081d47a2fff96aa9025e12677c266d1 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 20:17:34 +0200 Subject: [PATCH 14/29] Apply watchdog Co-authored-by: Mateusz Galazyn <228866+carbolymer@users.noreply.github.com> --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 514d9eac3b5..df7b6004dfc 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -50,6 +50,7 @@ import Testnet.Types (KeyPair (..), import Hedgehog import qualified Hedgehog.Extras as H +import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) -- | This test creates a default testnet with three DReps and one stake holder delegated to each. -- We then do a proposal for an arbitrary parameter change (in this case to the @@ -63,7 +64,7 @@ import qualified Hedgehog.Extras as H -- Execute me with: -- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Predefined Abstain DRep/"'@ hprop_check_predefined_abstain_drep :: Property -hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \tempAbsBasePath' -> do +hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \tempAbsBasePath' -> runWithDefaultWatchdog_ $ do -- Start a local test net conf@Conf { tempAbsPath } <- mkConf tempAbsBasePath' let tempAbsPath' = unTmpAbsPath tempAbsPath From 935c809be3633b80282c1e02d757521f53cf504b Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 20:25:00 +0200 Subject: [PATCH 15/29] Remove `startLedgerNewEpochStateLogging` --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index df7b6004dfc..39ed34bbddd 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -43,7 +43,6 @@ import qualified Testnet.Process.Cli.Keys as P import Testnet.Process.Cli.Transaction (retrieveTransactionId, signTx, submitTx) import qualified Testnet.Process.Run as H import qualified Testnet.Property.Util as H -import Testnet.Runtime import Testnet.Types (KeyPair (..), PaymentKeyInfo (paymentKeyInfoAddr, paymentKeyInfoPair), PoolNode (..), SomeKeyPair (SomeKeyPair), StakingKey, TestnetRuntime (..), nodeSocketPath) @@ -82,7 +81,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ , cardanoNumDReps = 3 } - testnetRuntime@TestnetRuntime + TestnetRuntime { testnetMagic , poolNodes , wallets=wallet0:wallet1:wallet2:_ @@ -97,8 +96,6 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ epochStateView <- getEpochStateView configurationFile socketPath - startLedgerNewEpochStateLogging testnetRuntime tempAbsPath' - H.note_ $ "Sprocket: " <> show poolSprocket1 H.note_ $ "Abs path: " <> tempAbsBasePath' H.note_ $ "Socketpath: " <> unFile socketPath From 0bd7d6cbb7b0a81a17e9e7c05dfe56f342446c1c Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 20:45:05 +0200 Subject: [PATCH 16/29] Improve comment in `hprop_check_predefined_abstain_drep` --- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 39ed34bbddd..68da2cc2fa6 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -51,14 +51,19 @@ import Hedgehog import qualified Hedgehog.Extras as H import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) --- | This test creates a default testnet with three DReps and one stake holder delegated to each. --- We then do a proposal for an arbitrary parameter change (in this case to the --- @desiredNumberOfPools@ parameter) to check that it fails, when the first DRep votes "yes" and the --- last two vote "no". Later we chack that if we change the stake holders under the DReps that vote --- "no" to delegate to the automate "always abstain" DRep, the same kind of proposal passes. +-- | This test creates a default testnet with three DReps delegated to by three +-- separate stake holders (one per DRep). We then do a proposal for an arbitrary +-- parameter change (in this case to the @desiredNumberOfPools@ parameter) to check +-- that it fails, when the first DRep votes "yes" and the last two vote "no". Later +-- we chack that if we change the stake holders under the DReps that vote "no" to +-- delegate to the automate "always abstain" DRep, the same kind of proposal passes. +-- If the proposal passes, it means that the stake was counted as abstaining, +-- because the threshold of minimum participation is 50%, if the stake was not counted as +-- abstaining, the "yes" votes would not have been enough, since they only account +-- for the 33% of the total active stake. -- --- This test is meant to ensure that delegating to "always abstain" has the desired effect of --- counting as abstaining for the stake delegated. +-- This test is meant to ensure that delegating to "always abstain" has the desired +-- effect of counting as abstaining for the stake delegated. -- -- Execute me with: -- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Predefined Abstain DRep/"'@ From 23b9a305b4a4dc84592ffcdc968de1468d88c15b Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 20:49:20 +0200 Subject: [PATCH 17/29] Fix typos in `getDesiredPoolNumberValue` --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 68da2cc2fa6..f85873d610e 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -355,8 +355,8 @@ voteChangeProposal execConfig epochStateView sbe work prefix -- The @desiredPoolNumberValue@ or (@k@ in the spec) is the protocol parameter -- that defines what is the optimal number of SPOs. It is a tradeoff between -- decentralization and efficiency and the spec suggest it should be between 100 an 1000. --- Changing this parameter will inderectly affect how easy it is to saturate a pool in order to --- incentivize that the number of SPOs states close to the parameter value. +-- Changing this parameter will indirectly affect how easy it is to saturate a pool in order to +-- incentivize that the number of SPOs stays close to the parameter value. getDesiredPoolNumberValue :: (EraPParams (ShelleyLedgerEra era), H.MonadAssertion m, MonadTest m, MonadIO m) => EpochStateView -> ConwayEraOnwards era From b7107f4139bb4326e006c18915bae3bd1eab9663 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 3 May 2024 20:54:37 +0200 Subject: [PATCH 18/29] Abstract out era in `delegateToAlwaysAbstain` --- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index f85873d610e..7afdf665ba3 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -50,6 +50,8 @@ import Testnet.Types (KeyPair (..), import Hedgehog import qualified Hedgehog.Extras as H import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) +import Testnet.Components.Configuration (anyEraToString) +import Data.Data (Typeable) -- | This test creates a default testnet with three DReps delegated to by three -- separate stake holders (one per DRep). We then do a proposal for an arbitrary @@ -130,13 +132,13 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 0 (Just newNumberOfDesiredPools2) 10 delegateToAlwaysAbstain - :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m) + :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Typeable era) => H.ExecConfig -- ^ Specifies the CLI execution configuration. -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained -- using the 'getEpochStateView' function. -> NodeConfigFile 'In -- ^ Path to the node configuration file as returned by 'cardanoTestnetDefault'. -> File Socket 'InOut -- ^ Path to the cardano-node unix socket file. - -> ShelleyBasedEra ConwayEra -- ^ The Shelley-based era (e.g., 'ConwayEra') in which the transaction will be constructed. + -> ShelleyBasedEra era -- ^ 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. @@ -153,7 +155,7 @@ delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath s -- Create vote delegation certificate let voteDelegationCertificatePath = baseDir "delegation-certificate.delegcert" void $ H.execCli' execConfig - [ "conway", "stake-address", "vote-delegation-certificate" + [ anyEraToString cEra, "stake-address", "vote-delegation-certificate" , "--always-abstain" , "--stake-verification-key-file", unFile vKeyFile , "--out-file", voteDelegationCertificatePath From ed5006586d3e58cf1e0a0f146ad0b87beac58884 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Tue, 14 May 2024 22:38:31 +0200 Subject: [PATCH 19/29] Remove waiting bit from `waitAndCheckNewEpochState` and use `watchEpochStateView` --- .../src/Testnet/Components/Query.hs | 108 ++++++++++-------- .../src/Testnet/EpochStateProcessing.hs | 36 +----- .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 12 +- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 23 ++-- 4 files changed, 85 insertions(+), 94 deletions(-) diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index 423a94cc9b7..84ba1b592d5 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -4,6 +4,8 @@ {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE FlexibleContexts #-} module Testnet.Components.Query ( EpochStateView @@ -21,7 +23,7 @@ module Testnet.Components.Query , findUtxosWithAddress , findLargestUtxoWithAddress , findLargestUtxoForPaymentKey - , waitAndCheckNewEpochState + , assertNewEpochState ) where import Cardano.Api as Api @@ -38,9 +40,8 @@ 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 (StateT, put) +import Control.Monad.Trans.State.Strict (put) import Data.Bifunctor (bimap) import Data.IORef import Data.List (sortOn) @@ -52,7 +53,6 @@ import Data.Ord (Down (..)) import Data.Text (Text) import qualified Data.Text as T import Data.Type.Equality -import Data.Word (Word64) import GHC.Exts (IsList (..)) import GHC.Stack import Lens.Micro (Lens', to, (^.)) @@ -359,53 +359,69 @@ getCurrentEpochNo epochStateView = withFrozenCallStack $ do AnyNewEpochState _ newEpochState <- getEpochState epochStateView pure $ newEpochState ^. L.nesELL --- | Waits for a minimum of @minWait@ epochs and a maximum of @maxWait@ epochs for --- the value pointed by the @lens@ to become the same as the @mExpected@ (if it is not 'Nothing'). --- If the value is not reached within the time frame, the test fails. If @mExpected@ is 'Nothing', --- the value is not checked, but the function will sitll wait for @minWait@ epochs. -waitAndCheckNewEpochState +-- | 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. - (MonadAssertion m, MonadTest m, MonadIO m, Eq 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. - -> NodeConfigFile In -- ^ The file path to the configuration file. - -> SocketPath -- ^ The file path to the unix socket file to connect to the @cardano-node@. - -> ConwayEraOnwards era -- ^ Witness for the current era that shows it is Conway or onwards. - -> EpochInterval -- ^ The minimum wait time in epochs. - -> Maybe value -- ^ The expected value to check in the epoch state. + -> 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 () -waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo (EpochInterval minWait) mExpected (EpochInterval maxWait) lens = do +assertNewEpochState epochStateView ceo expected maxWait lens = withFrozenCallStack $ do let sbe = conwayEraOnwardsToShelleyBasedEra ceo - (EpochNo curEpoch) <- getCurrentEpochNo epochStateView - eProposalResult - <- H.evalIO . runExceptT $ foldEpochState - configurationFile - socketPath - FullValidation - (EpochNo (curEpoch + fromIntegral maxWait)) - () - (\epochState _ _ -> filterEpochState (isSuccess curEpoch) epochState sbe) - void $ H.evalEither eProposalResult + 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 - filterEpochState :: (EpochNo -> value -> Bool) -> AnyNewEpochState -> ShelleyBasedEra era -> StateT () IO LedgerStateCondition - filterEpochState f (AnyNewEpochState actualEra newEpochState) sbe = - caseShelleyToBabbageOrConwayEraOnwards - (const $ error "waitAndCheck: Only conway era onwards supported") - (const $ do - Refl <- either error pure $ assertErasEqual sbe actualEra - let val = newEpochState ^. lens - currEpoch = L.nesEL newEpochState - return (if f currEpoch val - then ConditionMet - else ConditionNotMet) - ) - sbe + checkEpochState :: HasCallStack + => ShelleyBasedEra era -> AnyNewEpochState -> m (Maybe ()) + checkEpochState sbe newEpochState = do + val <- getFromEpochState sbe newEpochState + return $ if val == expected then Just () else Nothing - isSuccess :: Word64 -> EpochNo -> value -> Bool - isSuccess epochAfterProp (EpochNo epochNo) value = - (epochAfterProp + fromIntegral minWait <= epochNo) && - (case mExpected of - Nothing -> True - Just expected -> value == expected) && - (epochNo <= epochAfterProp + fromIntegral maxWait) + 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 100_000 + go (EpochNo timeout) diff --git a/cardano-testnet/src/Testnet/EpochStateProcessing.hs b/cardano-testnet/src/Testnet/EpochStateProcessing.hs index 4068fd55bf0..b12a9f489ac 100644 --- a/cardano-testnet/src/Testnet/EpochStateProcessing.hs +++ b/cardano-testnet/src/Testnet/EpochStateProcessing.hs @@ -1,16 +1,14 @@ {-# LANGUAGE DataKinds #-} -{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} module Testnet.EpochStateProcessing ( maybeExtractGovernanceActionIndex , findCondition - , watchEpochStateView ) where import Cardano.Api -import Cardano.Api.Ledger (EpochInterval (..), GovActionId (..)) +import Cardano.Api.Ledger (GovActionId (..)) import qualified Cardano.Api.Ledger as L import qualified Cardano.Ledger.Conway.Governance as L @@ -25,11 +23,7 @@ import Data.Word (Word32) import GHC.Stack import Lens.Micro ((^.)) -import Testnet.Components.Query (EpochStateView, getEpochState) - import Hedgehog -import Hedgehog.Extras (MonadAssertion) -import qualified Hedgehog.Extras as H findCondition :: HasCallStack @@ -78,31 +72,3 @@ 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) - diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index c5fdc224b95..c33897baf22 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -32,7 +32,9 @@ import Data.Word (Word32, Word64) import GHC.Stack import System.FilePath (()) -import Testnet.Components.Query +import Testnet.Components.Query (EpochStateView, assertNewEpochState, checkDRepState, + findLargestUtxoForPaymentKey, getCurrentEpochNo, getEpochStateView, + getMinDRepDeposit) import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep @@ -200,8 +202,12 @@ activityChangeProposalTest execConfig epochStateView configurationFile socketPat (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp - waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo minWait mExpected maxWait - (nesEpochStateL . epochStateGovStateL . curPParamsGovStateL . ppDRepActivityL) + void $ waitForEpochs epochStateView minWait + + case mExpected of + Nothing -> return () + Just expected -> assertNewEpochState epochStateView ceo expected maxWait + (nesEpochStateL . epochStateGovStateL . curPParamsGovStateL . ppDRepActivityL) return thisProposal diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 7afdf665ba3..f82fd3cdf41 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -26,6 +26,7 @@ import Prelude import Control.Monad (void) import Control.Monad.Catch (MonadCatch) +import Data.Data (Typeable) import Data.String (fromString) import qualified Data.Text as Text import Data.Word (Word32) @@ -33,9 +34,11 @@ import GHC.Stack (HasCallStack, callStack) import Lens.Micro ((^.)) import System.FilePath (()) -import Testnet.Components.Query (EpochStateView, findLargestUtxoForPaymentKey, - getCurrentEpochNo, getEpochStateView, getGovState, getMinDRepDeposit, - waitAndCheckNewEpochState) +import Testnet.Components.Configuration (anyEraToString) +import Testnet.Components.Query (EpochStateView, assertNewEpochState, + findLargestUtxoForPaymentKey, getCurrentEpochNo, getEpochStateView, getGovState, + getMinDRepDeposit) +import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep (createCertificatePublicationTxBody, createVotingTxBody, generateVoteFiles) @@ -49,9 +52,6 @@ import Testnet.Types (KeyPair (..), import Hedgehog import qualified Hedgehog.Extras as H -import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) -import Testnet.Components.Configuration (anyEraToString) -import Data.Data (Typeable) -- | This test creates a default testnet with three DReps delegated to by three -- separate stake holders (one per DRep). We then do a proposal for an arbitrary @@ -216,10 +216,13 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView H.note_ $ "Epoch after \"" <> prefix <> "\" prop: " <> show epochAfterProp - waitAndCheckNewEpochState epochStateView configurationFile socketPath ceo - (EpochInterval (fromIntegral minWait)) (fromIntegral <$> mExpected) - (EpochInterval (fromIntegral maxWait)) - (nesEpochStateL . epochStateGovStateL . cgsCurPParamsL . ppNOptL) + void $ waitForEpochs epochStateView (EpochInterval $ fromIntegral minWait) + + case mExpected of + Nothing -> return () + Just expected -> assertNewEpochState epochStateView ceo (fromIntegral expected) + (EpochInterval $ fromIntegral maxWait) + (nesEpochStateL . epochStateGovStateL . cgsCurPParamsL . ppNOptL) return thisProposal From b17f09a42e06d2a132a2145d84b64b047eb7c28f Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 15 May 2024 16:43:56 +0200 Subject: [PATCH 20/29] Modify `waitForEpochs` to use `watchEpochStateView` --- cardano-testnet/src/Testnet/Components/Query.hs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index 84ba1b592d5..02bb43a8055 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -1,11 +1,11 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GADTs #-} {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} -{-# LANGUAGE NumericUnderscores #-} -{-# LANGUAGE FlexibleContexts #-} module Testnet.Components.Query ( EpochStateView @@ -32,7 +32,6 @@ import Cardano.Api.Ledger (Credential, DRepState, EpochInterval (..), import Cardano.Api.Shelley (ShelleyLedgerEra, fromShelleyTxIn, fromShelleyTxOut) import qualified Cardano.Ledger.Api as L -import Cardano.Ledger.BaseTypes (addEpochInterval) import qualified Cardano.Ledger.Coin as L import qualified Cardano.Ledger.Conway.Governance as L import qualified Cardano.Ledger.Conway.PParams as L @@ -40,6 +39,7 @@ 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) @@ -99,9 +99,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 From 76866df37624102ef79644fa3af24dedd3f8d160 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 15 May 2024 17:04:27 +0200 Subject: [PATCH 21/29] Replace some usages of `waitUntilEpoch` with `waitForEpochs` --- cardano-testnet/src/Testnet/Process/Cli/DRep.hs | 9 ++++----- .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 4 ++-- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 11 ++++------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/cardano-testnet/src/Testnet/Process/Cli/DRep.hs b/cardano-testnet/src/Testnet/Process/Cli/DRep.hs index 4ffde9b535e..a663c3b311f 100644 --- a/cardano-testnet/src/Testnet/Process/Cli/DRep.hs +++ b/cardano-testnet/src/Testnet/Process/Cli/DRep.hs @@ -16,6 +16,7 @@ module Testnet.Process.Cli.DRep ) where import Cardano.Api hiding (Certificate, TxBody) +import Cardano.Api.Ledger (EpochInterval (EpochInterval)) import Prelude @@ -248,8 +249,7 @@ 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. @@ -257,7 +257,7 @@ delegateToDRep -> 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 @@ -288,8 +288,7 @@ delegateToDRep execConfig epochStateView configurationFile' socketPath sbe work submitTx execConfig cEra repRegSignedRegTx1 -- Wait two epochs - (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView - void $ waitUntilEpoch configurationFile' socketPath (EpochNo (epochAfterProp + 2)) + void $ waitForEpochs epochStateView (EpochInterval 2) -- | This function obtains the identifier for the last enacted parameter update proposal -- if any. diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index c33897baf22..c171ff1a063 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -109,11 +109,11 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP -- Now we register two new DReps drep2 <- registerDRep execConfig epochStateView ceo work "drep2" wallet1 - delegateToDRep execConfig epochStateView configurationFile socketPath sbe work "drep2-delegation" + delegateToDRep execConfig epochStateView sbe work "drep2-delegation" wallet2 (defaultDelegatorStakeKeyPair 2) drep2 drep3 <- registerDRep execConfig epochStateView ceo work "drep3" wallet0 - delegateToDRep execConfig epochStateView configurationFile socketPath sbe work "drep3-delegation" + delegateToDRep execConfig epochStateView sbe work "drep3-delegation" wallet1 (defaultDelegatorStakeKeyPair 3) drep3 expirationDates <- checkDRepState epochStateView sbe $ \m -> diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index f82fd3cdf41..457dc9d303c 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -120,9 +120,9 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools 3 (Just initialDesiredNumberOfPools) 10 -- Take the last two stake delegators and delegate them to "Abstain". - delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe gov "delegateToAbstain1" + delegateToAlwaysAbstain execConfig epochStateView sbe gov "delegateToAbstain1" wallet1 (defaultDelegatorStakeKeyPair 2) - delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe gov "delegateToAbstain2" + delegateToAlwaysAbstain execConfig epochStateView sbe gov "delegateToAbstain2" wallet2 (defaultDelegatorStakeKeyPair 3) -- Do some other proposal and vote yes with first DRep only @@ -136,15 +136,13 @@ delegateToAlwaysAbstain => H.ExecConfig -- ^ Specifies the CLI execution configuration. -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained -- using the 'getEpochStateView' function. - -> NodeConfigFile 'In -- ^ Path to the node configuration file as returned by 'cardanoTestnetDefault'. - -> File Socket 'InOut -- ^ Path to the cardano-node unix socket file. -> ShelleyBasedEra era -- ^ 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. -> m () -delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath sbe work prefix +delegateToAlwaysAbstain execConfig epochStateView sbe work prefix payingWallet skeyPair@(KeyPair vKeyFile _sKeyFile) = do let era = toCardanoEra sbe @@ -174,8 +172,7 @@ delegateToAlwaysAbstain execConfig epochStateView configurationFile socketPath s submitTx execConfig cEra repRegSignedRegTx1 -- Wait two epochs - (EpochNo epochAfterProp) <- getCurrentEpochNo epochStateView - void $ waitUntilEpoch configurationFile socketPath (EpochNo (epochAfterProp + 2)) + void $ waitForEpochs epochStateView (EpochInterval 2) desiredPoolNumberProposalTest :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t) From c53a5a1437c5077a60799de5db46fe3580c1b74f Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 15 May 2024 17:43:50 +0200 Subject: [PATCH 22/29] Kick watchdog in middle of `DRep Activity` test --- .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index c171ff1a063..73b544174da 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -35,7 +35,7 @@ import System.FilePath (()) import Testnet.Components.Query (EpochStateView, assertNewEpochState, checkDRepState, findLargestUtxoForPaymentKey, getCurrentEpochNo, getEpochStateView, getMinDRepDeposit) -import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) +import Testnet.Components.TestWatchdog (kickWatchdog, runWithDefaultWatchdog) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep import Testnet.Process.Cli.Keys @@ -50,7 +50,8 @@ import qualified Hedgehog.Extras as H -- | Execute me with: -- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/DRep Activity/"'@ hprop_check_drep_activity :: Property -hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBasePath' -> runWithDefaultWatchdog_ $ do +hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBasePath' -> + runWithDefaultWatchdog $ \watchdog -> do -- Start a local test net conf@Conf { tempAbsPath } <- mkConf tempAbsBasePath' let tempAbsPath' = unTmpAbsPath tempAbsPath @@ -130,6 +131,8 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP minEpochsToWaitIfNotChanging (Just firstTargetDRepActivity) maxEpochsToWaitAfterProposal + kickWatchdog watchdog + -- We now send a bunch of proposals to make sure that the 2 new DReps expire. -- because DReps won't expire if there is not enough activity (opportunites to participate). -- This is accounted for by the dormant epoch count From aa05e61577e5caf287668291ecd504843512ac35 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 15 May 2024 19:27:00 +0200 Subject: [PATCH 23/29] Remove `findCondition` function --- cardano-testnet/src/Cardano/Testnet.hs | 1 - .../src/Testnet/Components/Query.hs | 1 + .../src/Testnet/EpochStateProcessing.hs | 31 ------------ .../src/Testnet/Process/Cli/DRep.hs | 4 +- .../Testnet/Test/Gov/CommitteeAddNew.hs | 13 +---- .../Cardano/Testnet/Test/Gov/DRepActivity.hs | 47 ++++++------------- .../Cardano/Testnet/Test/Gov/InfoAction.hs | 14 +----- .../Cardano/Testnet/Test/Gov/NoConfidence.hs | 28 +++-------- .../Testnet/Test/Gov/PredefinedAbstainDRep.hs | 45 +++++------------- .../Test/Gov/ProposeNewConstitution.hs | 16 +------ 10 files changed, 41 insertions(+), 159 deletions(-) diff --git a/cardano-testnet/src/Cardano/Testnet.hs b/cardano-testnet/src/Cardano/Testnet.hs index 052943262ea..cfc727f607f 100644 --- a/cardano-testnet/src/Cardano/Testnet.hs +++ b/cardano-testnet/src/Cardano/Testnet.hs @@ -27,7 +27,6 @@ module Cardano.Testnet ( -- * EpochState processsing helper functions maybeExtractGovernanceActionIndex, - findCondition, -- * Processes procChairman, diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index 02bb43a8055..d077fcfe001 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -24,6 +24,7 @@ module Testnet.Components.Query , findLargestUtxoWithAddress , findLargestUtxoForPaymentKey , assertNewEpochState + , watchEpochStateView ) where import Cardano.Api as Api diff --git a/cardano-testnet/src/Testnet/EpochStateProcessing.hs b/cardano-testnet/src/Testnet/EpochStateProcessing.hs index b12a9f489ac..4ce83236576 100644 --- a/cardano-testnet/src/Testnet/EpochStateProcessing.hs +++ b/cardano-testnet/src/Testnet/EpochStateProcessing.hs @@ -4,7 +4,6 @@ module Testnet.EpochStateProcessing ( maybeExtractGovernanceActionIndex - , findCondition ) where import Cardano.Api @@ -17,42 +16,12 @@ import qualified Cardano.Ledger.Shelley.LedgerState as L import Prelude -import Control.Monad.State.Strict (MonadState (put), StateT) import qualified Data.Map as Map import Data.Word (Word32) import GHC.Stack import Lens.Micro ((^.)) -import Hedgehog -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 diff --git a/cardano-testnet/src/Testnet/Process/Cli/DRep.hs b/cardano-testnet/src/Testnet/Process/Cli/DRep.hs index a663c3b311f..4406b8408cb 100644 --- a/cardano-testnet/src/Testnet/Process/Cli/DRep.hs +++ b/cardano-testnet/src/Testnet/Process/Cli/DRep.hs @@ -287,8 +287,8 @@ delegateToDRep execConfig epochStateView sbe work prefix -- Submit transaction submitTx execConfig cEra repRegSignedRegTx1 - -- Wait two epochs - void $ waitForEpochs epochStateView (EpochInterval 2) + -- Wait one epoch + void $ waitForEpochs epochStateView (EpochInterval 1) -- | This function obtains the identifier for the last enacted parameter update proposal -- if any. diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs index adcbd61baa5..7f01535d27e 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs @@ -139,7 +139,6 @@ hprop_constitutional_committee_add_new = integrationWorkspace "constitutional-co EpochNo epochNo <- H.noteShowM $ getCurrentEpochNo epochStateView let ccExpiryEpoch = epochNo + 200 - deadlineEpoch = EpochNo $ epochNo + 10 _ <- execCli' execConfig $ [ eraName, "governance", "action" , "update-committee" @@ -176,14 +175,7 @@ hprop_constitutional_committee_add_new = integrationWorkspace "constitutional-co governanceActionTxId <- H.noteM $ retrieveTransactionId execConfig signedProposalTx - governanceActionIx <- - H.nothingFailM . - H.leftFailM $ - findCondition - (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) - configurationFile - socketPath - deadlineEpoch + governanceActionIx <- H.nothingFailM $ watchEpochStateView epochStateView (return . maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) (L.EpochInterval 1) dRepVoteFiles <- DRep.generateVoteFiles @@ -227,8 +219,7 @@ hprop_constitutional_committee_add_new = integrationWorkspace "constitutional-co length (filter ((== L.VoteYes) . snd) gaSpoVotes) === 1 length spoVotes === length gaSpoVotes - H.nothingFailM . H.leftFailM $ - findCondition committeeIsPresent configurationFile socketPath deadlineEpoch + H.nothingFailM $ watchEpochStateView epochStateView (return . committeeIsPresent) (L.EpochInterval 1) -- show proposed committe meembers H.noteShow_ ccCredentials diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs index 73b544174da..07c9d552b45 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/DRepActivity.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} @@ -12,7 +11,6 @@ module Cardano.Testnet.Test.Gov.DRepActivity import Cardano.Api as Api import Cardano.Api.Eon.ShelleyBasedEra (ShelleyLedgerEra) -import Cardano.Api.Error (displayError) import Cardano.Api.Ledger (EpochInterval (EpochInterval, unEpochInterval), drepExpiry) import Cardano.Ledger.Conway.Core (EraGov, curPParamsGovStateL) @@ -28,13 +26,13 @@ import Data.Data (Typeable) import qualified Data.Map as Map import Data.String import qualified Data.Text as Text -import Data.Word (Word32, Word64) -import GHC.Stack +import Data.Word (Word32) +import GHC.Stack (HasCallStack, withFrozenCallStack) import System.FilePath (()) import Testnet.Components.Query (EpochStateView, assertNewEpochState, checkDRepState, findLargestUtxoForPaymentKey, getCurrentEpochNo, getEpochStateView, - getMinDRepDeposit) + getMinDRepDeposit, watchEpochStateView) import Testnet.Components.TestWatchdog (kickWatchdog, runWithDefaultWatchdog) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep @@ -103,7 +101,7 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP -- make sure it doesn't change. maxEpochsToWaitAfterProposal = EpochInterval 2 -- If it takes more than 2 epochs we give up in any case. firstTargetDRepActivity = EpochInterval 3 - void $ activityChangeProposalTest execConfig epochStateView configurationFile socketPath ceo gov + void $ activityChangeProposalTest execConfig epochStateView ceo gov "firstProposal" wallet0 [(1, "yes")] firstTargetDRepActivity minEpochsToWaitIfChanging (Just firstTargetDRepActivity) maxEpochsToWaitAfterProposal @@ -126,7 +124,7 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP -- This proposal should fail because there is 2 DReps that don't vote (out of 3) -- and we have the stake distributed evenly let secondTargetDRepActivity = EpochInterval (unEpochInterval firstTargetDRepActivity + 1) - void $ activityChangeProposalTest execConfig epochStateView configurationFile socketPath ceo gov + void $ activityChangeProposalTest execConfig epochStateView ceo gov "failingProposal" wallet2 [(1, "yes")] secondTargetDRepActivity minEpochsToWaitIfNotChanging (Just firstTargetDRepActivity) maxEpochsToWaitAfterProposal @@ -138,7 +136,7 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP -- This is accounted for by the dormant epoch count let numOfFillerProposals = 4 :: Int sequence_ - [activityChangeProposalTest execConfig epochStateView configurationFile socketPath ceo gov + [activityChangeProposalTest execConfig epochStateView ceo gov ("fillerProposalNum" ++ show proposalNum) wallet [(1, "yes")] (EpochInterval (unEpochInterval secondTargetDRepActivity + fromIntegral proposalNum)) minEpochsToWaitIfNotChanging Nothing @@ -151,7 +149,7 @@ hprop_check_drep_activity = integrationWorkspace "test-activity" $ \tempAbsBaseP -- Last proposal (set activity to something else again and it should pass, because of inactivity) -- Because 2 out of 3 DReps were inactive, prop should pass let lastTargetDRepActivity = EpochInterval (unEpochInterval secondTargetDRepActivity + fromIntegral numOfFillerProposals + 1) - void $ activityChangeProposalTest execConfig epochStateView configurationFile socketPath ceo gov + void $ activityChangeProposalTest execConfig epochStateView ceo gov "lastProposal" wallet0 [(1, "yes")] lastTargetDRepActivity minEpochsToWaitIfChanging (Just lastTargetDRepActivity) maxEpochsToWaitAfterProposal @@ -165,8 +163,6 @@ activityChangeProposalTest => H.ExecConfig -- ^ Specifies the CLI execution configuration. -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained -- using the 'getEpochStateView' function. - -> NodeConfigFile In -- ^ Path to the node configuration file as returned by 'cardanoTestnetDefault'. - -> SocketPath -- ^ Path to the cardano-node unix socket file. -> ConwayEraOnwards era -- ^ The ConwayEraOnwards witness for current era. -> FilePath -- ^ Base directory path where generated files will be stored. -> String -- ^ Name for the subfolder that will be created under 'work' folder. @@ -181,8 +177,8 @@ activityChangeProposalTest -> EpochInterval -- ^ The maximum number of epochs to wait for the DRep activity interval to -- become expected value. -> m (String, Word32) -- ^ The transaction id and the index of the governance action. -activityChangeProposalTest execConfig epochStateView configurationFile socketPath ceo work prefix - wallet votes change minWait mExpected maxWait@(EpochInterval maxWaitNum) = do +activityChangeProposalTest execConfig epochStateView ceo work prefix + wallet votes change minWait mExpected maxWait = do let sbe = conwayEraOnwardsToShelleyBasedEra ceo mPreviousProposalInfo <- getLastPParamUpdateActionId execConfig @@ -196,8 +192,8 @@ activityChangeProposalTest execConfig epochStateView configurationFile socketPat H.note_ $ "Epoch before \"" <> prefix <> "\" prop: " <> show epochBeforeProp thisProposal@(governanceActionTxId, governanceActionIndex) <- - makeActivityChangeProposal execConfig epochStateView configurationFile socketPath - ceo baseDir "proposal" mPreviousProposalInfo change wallet (epochBeforeProp + fromIntegral maxWaitNum) + makeActivityChangeProposal execConfig epochStateView ceo baseDir "proposal" + mPreviousProposalInfo change wallet maxWait voteChangeProposal execConfig epochStateView sbe baseDir "vote" governanceActionTxId governanceActionIndex propVotes wallet @@ -219,18 +215,16 @@ makeActivityChangeProposal => H.ExecConfig -- ^ Specifies the CLI execution configuration. -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained -- using the 'getEpochStateView' function. - -> NodeConfigFile In -- ^ Path to the node configuration file as returned by 'cardanoTestnetDefault'. - -> SocketPath -- ^ Path to the cardano-node unix socket file. -> ConwayEraOnwards era -- ^ The 'ConwayEraOnwards' witness for current era. -> FilePath -- ^ Base directory path where generated files will be stored. -> String -- ^ Name for the subfolder that will be created under 'work' folder. -> Maybe (String, Word32) -- ^ The transaction id and the index of the previosu governance action if any. -> EpochInterval -- ^ The target DRep activity interval to be set by the proposal. -> PaymentKeyInfo -- ^ Wallet that will pay for the transaction. - -> Word64 -- ^ The latest epoch until which to wait for the proposal to be registered by the chain. + -> EpochInterval -- ^ Number of epochs to wait for the proposal to be registered by the chain. -> m (String, Word32) -- ^ The transaction id and the index of the governance action. -makeActivityChangeProposal execConfig epochStateView configurationFile socketPath - ceo work prefix prevGovActionInfo drepActivity wallet timeout = do +makeActivityChangeProposal execConfig epochStateView ceo work prefix + prevGovActionInfo drepActivity wallet timeout = do let sbe = conwayEraOnwardsToShelleyBasedEra ceo era = toCardanoEra sbe @@ -291,18 +285,7 @@ makeActivityChangeProposal execConfig epochStateView configurationFile socketPat governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx - !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) - configurationFile - socketPath - (EpochNo timeout) - - governanceActionIndex <- case propSubmittedResult of - Left e -> - H.failMessage callStack - $ "makeActivityChangeProposal failed waiting for gov action with: " <> displayError e - Right Nothing -> - H.failMessage callStack "Couldn't find proposal." - Right (Just a) -> return a + governanceActionIndex <- H.nothingFailM $ watchEpochStateView epochStateView (return . maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) timeout return (governanceActionTxId, governanceActionIndex) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs index eb2178c0fdd..ea2484dd511 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs @@ -13,6 +13,7 @@ module Cardano.Testnet.Test.Gov.InfoAction import Cardano.Api as Api import Cardano.Api.Error (displayError) +import Cardano.Api.Ledger (EpochInterval (EpochInterval)) import Cardano.Api.Shelley import Cardano.Ledger.Conway.Governance (RatifyState (..)) @@ -144,18 +145,7 @@ hprop_ledger_events_info_action = integrationRetryWorkspace 0 "info-hash" $ \tem , "--tx-file", txbodySignedFp ] - !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString txidString)) - configurationFile - socketPath - (EpochNo 10) - - governanceActionIndex <- case propSubmittedResult of - Left e -> - H.failMessage callStack - $ "findCondition failed with: " <> displayError e - Right Nothing -> - H.failMessage callStack "Couldn't find proposal." - Right (Just a) -> return a + governanceActionIndex <- H.nothingFailM $ watchEpochStateView epochStateView (return . maybeExtractGovernanceActionIndex (fromString txidString)) (EpochInterval 1) let voteFp :: Int -> FilePath voteFp n = work gov "vote-" <> show n diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs index b1c268283f0..cf90051bf7c 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE BangPatterns #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} @@ -10,7 +9,6 @@ module Cardano.Testnet.Test.Gov.NoConfidence ) where import Cardano.Api as Api -import Cardano.Api.Error import Cardano.Api.Ledger import Cardano.Api.Shelley @@ -29,7 +27,6 @@ import qualified Data.Map.Strict as Map import Data.Maybe.Strict import Data.String import qualified Data.Text as Text -import GHC.Stack import Lens.Micro import System.FilePath (()) @@ -132,9 +129,10 @@ hprop_gov_no_confidence = integrationWorkspace "no-confidence" $ \tempAbsBasePat H.note_ $ "Abs path: " <> tempAbsBasePath' H.note_ $ "Socketpath: " <> socketPath - mCommitteePresent - <- H.leftFailM $ findCondition (committeeIsPresent True) configurationFile (File socketPath) (EpochNo 3) - H.nothingFail mCommitteePresent + epochStateView <- getEpochStateView configurationFile (File socketPath) + + H.nothingFailM $ watchEpochStateView epochStateView (return . committeeIsPresent True) (EpochInterval 3) + -- Step 2. Propose motion of no confidence. DRep and SPO voting thresholds must be met. @@ -156,7 +154,6 @@ hprop_gov_no_confidence = integrationWorkspace "no-confidence" $ \tempAbsBasePat cliStakeAddressKeyGen $ KeyPair (File stakeVkeyFp) (File stakeSKeyFp) - epochStateView <- getEpochStateView configurationFile (File socketPath) minActDeposit <- getMinGovActionDeposit epochStateView ceo void $ H.execCli' execConfig $ @@ -193,18 +190,7 @@ hprop_gov_no_confidence = integrationWorkspace "no-confidence" $ \tempAbsBasePat governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx - !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) - configurationFile - (File socketPath) - (EpochNo 10) - - governanceActionIndex <- case propSubmittedResult of - Left e -> - H.failMessage callStack - $ "findCondition failed with: " <> displayError e - Right Nothing -> - H.failMessage callStack "Couldn't find proposal." - Right (Just a) -> return a + governanceActionIndex <- H.nothingFailM $ watchEpochStateView epochStateView (return . maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) (EpochInterval 10) let spoVotes :: [(String, Int)] spoVotes = [("yes", 1), ("yes", 2), ("yes", 3)] @@ -236,9 +222,7 @@ hprop_gov_no_confidence = integrationWorkspace "no-confidence" $ \tempAbsBasePat -- Step 4. We confirm the no confidence motion has been ratified by checking -- for an empty constitutional committee. - mCommitteeEmpty - <- H.leftFailM $ findCondition (committeeIsPresent False) configurationFile (File socketPath) (EpochNo 5) - H.nothingFail mCommitteeEmpty + H.nothingFailM $ watchEpochStateView epochStateView (return . committeeIsPresent False) (EpochInterval 10) -- | Checks if the committee is empty or not. committeeIsPresent :: Bool -> AnyNewEpochState -> Maybe () diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index 457dc9d303c..c853828b0a6 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE BangPatterns #-} {-# LANGUAGE DataKinds #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NamedFieldPuns #-} @@ -12,8 +11,6 @@ module Cardano.Testnet.Test.Gov.PredefinedAbstainDRep import Cardano.Api as Api import Cardano.Api.Eon.ShelleyBasedEra (ShelleyLedgerEra) -import Cardano.Api.Error (displayError) -import Cardano.Api.IO.Base (Socket) import Cardano.Api.Ledger (EpochInterval (EpochInterval)) import Cardano.Ledger.Conway.Core (ppNOptL) @@ -30,14 +27,14 @@ import Data.Data (Typeable) import Data.String (fromString) import qualified Data.Text as Text import Data.Word (Word32) -import GHC.Stack (HasCallStack, callStack) +import GHC.Stack (HasCallStack) import Lens.Micro ((^.)) import System.FilePath (()) import Testnet.Components.Configuration (anyEraToString) import Testnet.Components.Query (EpochStateView, assertNewEpochState, findLargestUtxoForPaymentKey, getCurrentEpochNo, getEpochStateView, getGovState, - getMinDRepDeposit) + getMinDRepDeposit, watchEpochStateView) import Testnet.Components.TestWatchdog (runWithDefaultWatchdog_) import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair) import Testnet.Process.Cli.DRep (createCertificatePublicationTxBody, createVotingTxBody, @@ -116,7 +113,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ -- Do some proposal and vote yes with the first DRep only -- and assert that proposal does NOT pass. - void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "firstProposal" + void $ desiredPoolNumberProposalTest execConfig epochStateView ceo gov "firstProposal" wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools 3 (Just initialDesiredNumberOfPools) 10 -- Take the last two stake delegators and delegate them to "Abstain". @@ -128,7 +125,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ -- Do some other proposal and vote yes with first DRep only -- and assert the new proposal passes now. let newNumberOfDesiredPools2 = newNumberOfDesiredPools + 1 - void $ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo gov "secondProposal" + void $ desiredPoolNumberProposalTest execConfig epochStateView ceo gov "secondProposal" wallet0 Nothing [(1, "yes")] newNumberOfDesiredPools2 0 (Just newNumberOfDesiredPools2) 10 delegateToAlwaysAbstain @@ -172,14 +169,12 @@ delegateToAlwaysAbstain execConfig epochStateView sbe work prefix submitTx execConfig cEra repRegSignedRegTx1 -- Wait two epochs - void $ waitForEpochs epochStateView (EpochInterval 2) + void $ waitForEpochs epochStateView (EpochInterval 1) desiredPoolNumberProposalTest :: (HasCallStack, MonadTest m, MonadIO m, H.MonadAssertion m, MonadCatch m, Foldable t) => 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'. - -> File Socket 'InOut -- ^ Path to the cardano-node unix socket file. -> ConwayEraOnwards ConwayEra -- ^ The ConwaysEraOnwards witness for the Conway era -> FilePath -- ^ Base directory path where generated files will be stored. -> String -- ^ Name for the subfolder that will be created under 'work' folder. @@ -193,8 +188,8 @@ desiredPoolNumberProposalTest -> Maybe Integer -- ^ What the expected result is of the change (if anything) -> Integer -- ^ Maximum number of epochs to wait while waiting for the result -> m (String, Word32) -desiredPoolNumberProposalTest execConfig epochStateView configurationFile socketPath ceo work prefix - wallet previousProposalInfo votes change minWait mExpected maxWait = do +desiredPoolNumberProposalTest execConfig epochStateView ceo work prefix wallet + previousProposalInfo votes change minWait mExpected maxWait = do let sbe = conwayEraOnwardsToShelleyBasedEra ceo baseDir <- H.createDirectoryIfMissing $ work prefix @@ -204,8 +199,8 @@ desiredPoolNumberProposalTest execConfig epochStateView configurationFile socket annotateShow propVotes thisProposal@(governanceActionTxId, governanceActionIndex) <- - makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile socketPath - ceo baseDir "proposal" previousProposalInfo (fromIntegral change) wallet + makeDesiredPoolNumberChangeProposal execConfig epochStateView ceo baseDir "proposal" + previousProposalInfo (fromIntegral change) wallet voteChangeProposal execConfig epochStateView sbe baseDir "vote" governanceActionTxId governanceActionIndex propVotes wallet @@ -227,9 +222,6 @@ makeDesiredPoolNumberChangeProposal :: (HasCallStack, H.MonadAssertion m, MonadTest m, MonadCatch m, MonadIO m) => H.ExecConfig -- ^ Specifies the CLI execution configuration. -> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained - -> NodeConfigFile 'In -- ^ Absolute path to the "configuration.yaml" file for the testnet - -- as returned by the 'cardanoTestnetDefault' function. - -> SocketPath -- ^ Path to the cardano-node unix socket file. -> ConwayEraOnwards ConwayEra -- ^ The conway era onwards witness for the era 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. @@ -238,8 +230,8 @@ makeDesiredPoolNumberChangeProposal -> Word32 -- ^ What to change the @desiredPoolNumber@ to -> PaymentKeyInfo -- ^ Wallet that will pay for the transaction. -> m (String, Word32) -makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile socketPath - ceo work prefix prevGovActionInfo desiredPoolNumber wallet = do +makeDesiredPoolNumberChangeProposal execConfig epochStateView ceo work prefix + prevGovActionInfo desiredPoolNumber wallet = do let sbe = conwayEraOnwardsToShelleyBasedEra ceo era = toCardanoEra sbe @@ -300,20 +292,7 @@ makeDesiredPoolNumberChangeProposal execConfig epochStateView configurationFile governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx - (EpochNo curEpoch) <- getCurrentEpochNo epochStateView - - !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) - configurationFile - socketPath - (EpochNo $ curEpoch + 10) - - governanceActionIndex <- case propSubmittedResult of - Left e -> - H.failMessage callStack - $ "findCondition failed with: " <> displayError e - Right Nothing -> - H.failMessage callStack "Couldn't find proposal." - Right (Just a) -> return a + governanceActionIndex <- H.nothingFailM $ watchEpochStateView epochStateView (return . maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) (EpochInterval 1) return (governanceActionTxId, governanceActionIndex) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs index a3bb6650a54..d9b5f38248b 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs @@ -1,4 +1,3 @@ -{-# LANGUAGE BangPatterns #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE NumericUnderscores #-} {-# LANGUAGE OverloadedStrings #-} @@ -10,7 +9,6 @@ module Cardano.Testnet.Test.Gov.ProposeNewConstitution ) where import Cardano.Api as Api -import Cardano.Api.Error (displayError) import Cardano.Api.Ledger (EpochInterval (..)) import qualified Cardano.Crypto.Hash as L @@ -29,7 +27,6 @@ import Data.Maybe.Strict import Data.String import qualified Data.Text as Text import GHC.Exts (IsList (..)) -import GHC.Stack (callStack) import Lens.Micro import System.FilePath (()) @@ -169,18 +166,7 @@ hprop_ledger_events_propose_new_constitution = integrationWorkspace "propose-new governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx - !propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) - configurationFile - socketPath - (EpochNo 10) - - governanceActionIndex <- case propSubmittedResult of - Left e -> - H.failMessage callStack - $ "findCondition failed with: " <> displayError e - Right Nothing -> - H.failMessage callStack "Couldn't find proposal." - Right (Just a) -> return a + governanceActionIndex <- H.nothingFailM $ watchEpochStateView epochStateView (return . maybeExtractGovernanceActionIndex (fromString governanceActionTxId)) (EpochInterval 1) -- Proposal was successfully submitted, now we vote on the proposal and confirm it was ratified voteFiles <- generateVoteFiles execConfig work "vote-files" From 7e326945250a57ca633a1bbac4e6493004b23c4e Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Wed, 15 May 2024 21:38:32 +0200 Subject: [PATCH 24/29] Fix issue with vote counting --- .../src/Testnet/EpochStateProcessing.hs | 46 ++++++++++++++++++- .../Testnet/Test/Gov/CommitteeAddNew.hs | 3 +- .../Test/Gov/ProposeNewConstitution.hs | 3 +- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/cardano-testnet/src/Testnet/EpochStateProcessing.hs b/cardano-testnet/src/Testnet/EpochStateProcessing.hs index 4ce83236576..568c624207e 100644 --- a/cardano-testnet/src/Testnet/EpochStateProcessing.hs +++ b/cardano-testnet/src/Testnet/EpochStateProcessing.hs @@ -1,27 +1,39 @@ {-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeFamilies #-} module Testnet.EpochStateProcessing ( maybeExtractGovernanceActionIndex + , waitForGovActionVotes ) where import Cardano.Api -import Cardano.Api.Ledger (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 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, watchEpochStateView) +import Testnet.Property.Assert (assertErasEqual) +import Hedgehog (MonadTest) +import Hedgehog.Extras (MonadAssertion) +import qualified Hedgehog.Extras as H maybeExtractGovernanceActionIndex :: HasCallStack @@ -41,3 +53,33 @@ maybeExtractGovernanceActionIndex txid (AnyNewEpochState sbe newEpochState) = | ti1 == L.extractHash ti2 = Just gai compareWithTxId _ x _ _ = x +-- | 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 () diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs index 7f01535d27e..b6a3d683da9 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs @@ -37,6 +37,7 @@ import Testnet.Components.Configuration import Testnet.Components.Query import Testnet.Components.TestWatchdog import Testnet.Defaults +import Testnet.EpochStateProcessing (waitForGovActionVotes) import qualified Testnet.Process.Cli.DRep as DRep import Testnet.Process.Cli.Keys import qualified Testnet.Process.Cli.SPO as SPO @@ -205,7 +206,7 @@ hprop_constitutional_committee_add_new = integrationWorkspace "constitutional-co submitTx execConfig cEra voteTxFp - _ <- waitForEpochs epochStateView (L.EpochInterval 1) + waitForGovActionVotes epochStateView ceo (L.EpochInterval 1) govState <- getGovState epochStateView ceo govActionState <- H.headM $ govState ^. L.cgsProposalsL . L.pPropsL . to toList diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs index d9b5f38248b..be831abf329 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs @@ -34,6 +34,7 @@ import Testnet.Components.Configuration import Testnet.Components.Query import Testnet.Components.TestWatchdog import Testnet.Defaults +import Testnet.EpochStateProcessing (waitForGovActionVotes) import Testnet.Process.Cli.DRep import Testnet.Process.Cli.Keys import Testnet.Process.Cli.Transaction @@ -182,7 +183,7 @@ hprop_ledger_events_propose_new_constitution = integrationWorkspace "propose-new submitTx execConfig cEra voteTxFp - _ <- waitForEpochs epochStateView (EpochInterval 1) + waitForGovActionVotes epochStateView ceo (EpochInterval 1) -- Count votes before checking for ratification. It may happen that the proposal gets removed after -- ratification because of a long waiting time, so we won't be able to access votes. From d401f369c117d032e99efc295973dfcfeac1c592 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 16 May 2024 21:33:46 +0200 Subject: [PATCH 25/29] Adjust `cardanoEpochLength` to avoid flakiness --- .../Cardano/Testnet/Test/Gov/CommitteeAddNew.hs | 2 +- .../cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs | 2 +- .../Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs | 2 +- .../Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs | 2 +- .../Cardano/Testnet/Test/Gov/TreasuryWithdrawal.hs | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs index b6a3d683da9..4900e710565 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/CommitteeAddNew.hs @@ -73,7 +73,7 @@ hprop_constitutional_committee_add_new = integrationWorkspace "constitutional-co cEra = AnyCardanoEra era eraName = eraToString era fastTestnetOptions = cardanoDefaultTestnetOptions - { cardanoEpochLength = 100 + { cardanoEpochLength = 200 , cardanoNodeEra = cEra , cardanoNumDReps = nDrepVotes } diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs index ea2484dd511..4799aef74a9 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/InfoAction.hs @@ -59,7 +59,7 @@ hprop_ledger_events_info_action = integrationRetryWorkspace 0 "info-hash" $ \tem era = toCardanoEra sbe sbe = conwayEraOnwardsToShelleyBasedEra ceo fastTestnetOptions = cardanoDefaultTestnetOptions - { cardanoEpochLength = 100 + { cardanoEpochLength = 200 , cardanoNodeEra = AnyCardanoEra era } diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs index c853828b0a6..9f65bde583b 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/PredefinedAbstainDRep.hs @@ -80,7 +80,7 @@ hprop_check_predefined_abstain_drep = H.integrationWorkspace "test-activity" $ \ era = toCardanoEra sbe cEra = AnyCardanoEra era fastTestnetOptions = cardanoDefaultTestnetOptions - { cardanoEpochLength = 100 + { cardanoEpochLength = 200 , cardanoNodeEra = cEra , cardanoNumDReps = 3 } diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs index be831abf329..bd6bd73624c 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/ProposeNewConstitution.hs @@ -70,7 +70,7 @@ hprop_ledger_events_propose_new_constitution = integrationWorkspace "propose-new era = toCardanoEra sbe cEra = AnyCardanoEra era fastTestnetOptions = cardanoDefaultTestnetOptions - { cardanoEpochLength = 100 + { cardanoEpochLength = 200 , cardanoNodeEra = cEra , cardanoNumDReps = numVotes } diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/TreasuryWithdrawal.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/TreasuryWithdrawal.hs index b36f0110b81..ccf06d80166 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/TreasuryWithdrawal.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/TreasuryWithdrawal.hs @@ -63,7 +63,7 @@ hprop_ledger_events_treasury_withdrawal = integrationRetryWorkspace 1 "treasury cEra = AnyCardanoEra era fastTestnetOptions = cardanoDefaultTestnetOptions - { cardanoEpochLength = 100 + { cardanoEpochLength = 200 , cardanoNodeEra = cEra , cardanoActiveSlotsCoeff = 0.3 } From 5b62ae6503d0bad8614d2efe3df22a4793a55c4a Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Thu, 16 May 2024 23:44:36 +0200 Subject: [PATCH 26/29] Disable tests that don't work --- .../test/cardano-testnet-test/cardano-testnet-test.hs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs index 40343be1afa..aa47ff9755e 100644 --- a/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs +++ b/cardano-testnet/test/cardano-testnet-test/cardano-testnet-test.hs @@ -14,11 +14,9 @@ import qualified Cardano.Testnet.Test.Cli.Query import qualified Cardano.Testnet.Test.Cli.QuerySlotNumber import qualified Cardano.Testnet.Test.FoldEpochState import qualified Cardano.Testnet.Test.Gov.CommitteeAddNew as Gov -import qualified Cardano.Testnet.Test.Gov.DRepActivity as Gov import qualified Cardano.Testnet.Test.Gov.DRepDeposit as Gov import qualified Cardano.Testnet.Test.Gov.DRepRetirement as Gov import qualified Cardano.Testnet.Test.Gov.NoConfidence as Gov -import qualified Cardano.Testnet.Test.Gov.PredefinedAbstainDRep as Gov import qualified Cardano.Testnet.Test.Gov.ProposeNewConstitution as Gov import qualified Cardano.Testnet.Test.Gov.ProposeNewConstitutionSPO as Gov import qualified Cardano.Testnet.Test.Gov.TreasuryGrowth as Gov @@ -52,8 +50,9 @@ tests = do -- TODO: Replace foldBlocks with checkLedgerStateCondition , T.testGroup "Governance" [ ignoreOnMacAndWindows "Committee Add New" Gov.hprop_constitutional_committee_add_new - , ignoreOnWindows "Predefined Abstain DRep" Gov.hprop_check_predefined_abstain_drep - , ignoreOnWindows "DRep Activity" Gov.hprop_check_drep_activity + -- TODO: Disabled because proposals for parameter changes are not working + -- , ignoreOnWindows "DRep Activity" Gov.hprop_check_drep_activity + -- , ignoreOnWindows "Predefined Abstain DRep" Gov.hprop_check_predefined_abstain_drep , ignoreOnMacAndWindows "Committee Motion Of No Confidence" Gov.hprop_gov_no_confidence , ignoreOnWindows "DRep Deposits" Gov.hprop_ledger_events_drep_deposits -- FIXME Those tests are flaky From 342c40572c0f6a95c94bbc601557c59cd54a38c9 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 17 May 2024 00:43:43 +0200 Subject: [PATCH 27/29] Fix delay in PlutusV3 test --- .../Cardano/Testnet/Test/Cli/Conway/Plutus.hs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Conway/Plutus.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Conway/Plutus.hs index 8a60974f369..f96de449fe3 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Conway/Plutus.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Cli/Conway/Plutus.hs @@ -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 @@ -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" @@ -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 +waitForJustM src = do m <- src + case m of + Just a -> pure a + Nothing -> do H.threadDelay 100_000 + waitForJustM src + From 1d715dc9bf792ada27bd28716274f431152d9583 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 17 May 2024 00:50:07 +0200 Subject: [PATCH 28/29] Remove unnecessary "$" --- .../Cardano/Testnet/Test/Gov/NoConfidence.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs index cf90051bf7c..93996d8c082 100644 --- a/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs +++ b/cardano-testnet/test/cardano-testnet-test/Cardano/Testnet/Test/Gov/NoConfidence.hs @@ -156,7 +156,7 @@ hprop_gov_no_confidence = integrationWorkspace "no-confidence" $ \tempAbsBasePat minActDeposit <- getMinGovActionDeposit epochStateView ceo - void $ H.execCli' execConfig $ + void $ H.execCli' execConfig [ eraToString era, "governance", "action", "create-no-confidence" , "--testnet" , "--governance-action-deposit", show @Integer minActDeposit From 5b16cd1225e58474c69834b41ec6330b9435c467 Mon Sep 17 00:00:00 2001 From: Pablo Lamela Date: Fri, 17 May 2024 01:37:45 +0200 Subject: [PATCH 29/29] Increase polling frequency --- cardano-testnet/src/Testnet/Components/Query.hs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cardano-testnet/src/Testnet/Components/Query.hs b/cardano-testnet/src/Testnet/Components/Query.hs index d077fcfe001..5787a62bd5e 100644 --- a/cardano-testnet/src/Testnet/Components/Query.hs +++ b/cardano-testnet/src/Testnet/Components/Query.hs @@ -424,5 +424,5 @@ watchEpochStateView epochStateView f (EpochInterval maxWait) = withFrozenCallSta if currentEpoch > timeout then pure Nothing else do - H.threadDelay 100_000 + H.threadDelay 10_000 go (EpochNo timeout)