Skip to content

Commit

Permalink
Merge pull request #5871 from IntersectMBO/test-pparam-change-spo-fails
Browse files Browse the repository at this point in the history
Test SPOs cannot vote protocol parameter change proposals
  • Loading branch information
palas authored Jun 6, 2024
2 parents ce9ea33 + 81b4fe8 commit 0fba660
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 89 deletions.
1 change: 1 addition & 0 deletions cardano-testnet/cardano-testnet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ test-suite cardano-testnet-test
Cardano.Testnet.Test.Gov.DRepRetirement
Cardano.Testnet.Test.Gov.InfoAction
Cardano.Testnet.Test.Gov.NoConfidence
Cardano.Testnet.Test.Gov.PParamChangeFailsSPO
Cardano.Testnet.Test.Gov.ProposeNewConstitution
Cardano.Testnet.Test.Gov.ProposeNewConstitutionSPO
Cardano.Testnet.Test.Gov.TreasuryGrowth
Expand Down
91 changes: 90 additions & 1 deletion cardano-testnet/src/Testnet/Process/Cli/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,13 @@ module Testnet.Process.Cli.DRep
, registerDRep
, delegateToDRep
, getLastPParamUpdateActionId
, makeActivityChangeProposal
) where

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

import Cardano.Testnet (maybeExtractGovernanceActionIndex)

import Prelude

Expand All @@ -26,12 +29,15 @@ import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Lens as AL
import Data.Text (Text)
import qualified Data.Text as Text
import Data.Typeable (Typeable)
import Data.Word (Word32)
import GHC.Exts (fromString)
import GHC.Stack
import Lens.Micro ((^?))
import System.FilePath ((</>))

import Testnet.Components.Query
import Testnet.Process.Cli.Keys (cliStakeAddressKeyGen)
import Testnet.Process.Cli.Transaction
import Testnet.Process.Run (execCli', execCliStdoutToJson)
import Testnet.Types
Expand Down Expand Up @@ -329,3 +335,86 @@ getLastPParamUpdateActionId execConfig = do
actionIx <- evalMaybe mActionIx
txId <- evalMaybe mTxId
return (Just (Text.unpack txId, fromIntegral actionIx))

-- | 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.
-> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained
-- using the 'getEpochStateView' function.
-> 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.
-> 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 ceo work prefix
prevGovActionInfo drepActivity wallet timeout = 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"

cliStakeAddressKeyGen
$ KeyPair { verificationKey = File stakeVkeyFp
, signingKey = File stakeSKeyFp
}

proposalAnchorFile <- H.note $ baseDir </> "sample-proposal-anchor"
H.writeFile proposalAnchorFile "dummy anchor data"

proposalAnchorDataHash <- execCli' execConfig
[ "conway", "governance"
, "hash", "anchor-data", "--file-text", proposalAnchorFile
]

minDRepDeposit <- getMinDRepDeposit epochStateView ceo

proposalFile <- H.note $ baseDir </> "sample-proposal-anchor"

void $ 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 ++
[ "--drep-activity", show (unEpochInterval drepActivity)
, "--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 $ 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

governanceActionIndex <-
H.nothingFailM $ watchEpochStateUpdate epochStateView timeout $ \(anyNewEpochState, _, _) ->
return $ maybeExtractGovernanceActionIndex (fromString governanceActionTxId) anyNewEpochState

return (governanceActionTxId, governanceActionIndex)
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Cardano.Testnet.Test.Gov.DRepActivity
( hprop_check_drep_activity
Expand All @@ -24,18 +23,15 @@ import Control.Monad
import Control.Monad.Catch (MonadCatch)
import Data.Data (Typeable)
import qualified Data.Map as Map
import Data.String
import qualified Data.Text as Text
import Data.Word (Word32)
import GHC.Stack (HasCallStack, withFrozenCallStack)
import System.FilePath ((</>))

import Testnet.Components.Query
import Testnet.Defaults (defaultDRepKeyPair, defaultDelegatorStakeKeyPair)
import Testnet.Process.Cli.DRep
import Testnet.Process.Cli.Keys
import Testnet.Process.Cli.Transaction
import Testnet.Process.Run (execCli', mkExecConfig)
import Testnet.Process.Run (mkExecConfig)
import Testnet.Property.Util (integrationWorkspace)
import Testnet.Types

Expand Down Expand Up @@ -208,89 +204,6 @@ activityChangeProposalTest execConfig epochStateView ceo work prefix

pure thisProposal

-- | 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.
-> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained
-- using the 'getEpochStateView' function.
-> 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.
-> 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 ceo work prefix
prevGovActionInfo drepActivity wallet timeout = 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"

cliStakeAddressKeyGen
$ KeyPair { verificationKey = File stakeVkeyFp
, signingKey = File stakeSKeyFp
}

proposalAnchorFile <- H.note $ baseDir </> "sample-proposal-anchor"
H.writeFile proposalAnchorFile "dummy anchor data"

proposalAnchorDataHash <- execCli' execConfig
[ "conway", "governance"
, "hash", "anchor-data", "--file-text", proposalAnchorFile
]

minDRepDeposit <- getMinDRepDeposit epochStateView ceo

proposalFile <- H.note $ baseDir </> "sample-proposal-anchor"

void $ 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 ++
[ "--drep-activity", show (unEpochInterval drepActivity)
, "--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 $ 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

governanceActionIndex <-
H.nothingFailM $ watchEpochStateUpdate epochStateView timeout $ \(anyNewEpochState, _, _) ->
return $ maybeExtractGovernanceActionIndex (fromString governanceActionTxId) anyNewEpochState

return (governanceActionTxId, governanceActionIndex)

-- | Cast votes for a governance action.
voteChangeProposal
:: (HasCallStack, MonadTest m, MonadIO m, MonadCatch m, H.MonadAssertion m, Typeable era)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Cardano.Testnet.Test.Gov.PParamChangeFailsSPO
( hprop_check_pparam_fails_spo
) where

import Cardano.Api as Api
import Cardano.Api.Ledger (EpochInterval (EpochInterval))

import Cardano.Testnet

import Prelude

import Control.Monad.Catch (MonadCatch)
import Data.Typeable (Typeable)
import Data.Word (Word32)
import System.FilePath ((</>))

import Testnet.Components.Query
import Testnet.Defaults (defaultSpoColdKeyPair, defaultSpoKeys)
import Testnet.Process.Cli.DRep
import qualified Testnet.Process.Cli.SPO as SPO
import Testnet.Process.Cli.Transaction (failToSubmitTx, signTx)
import Testnet.Process.Run (mkExecConfig)
import Testnet.Property.Util (integrationWorkspace)
import Testnet.Types

import Hedgehog (Property, annotateShow)
import qualified Hedgehog.Extras as H
import Hedgehog.Internal.Property (MonadTest)
import Hedgehog.Internal.Source (HasCallStack, withFrozenCallStack)

-- | Test that SPOs cannot vote on a Protocol Parameter change
-- | Execute me with:
-- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/PParam change fails for SPO/"'@
hprop_check_pparam_fails_spo :: Property
hprop_check_pparam_fails_spo = integrationWorkspace "test-pparam-spo" $ \tempAbsBasePath' ->
H.runWithDefaultWatchdog_ $ do
-- 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
let ceo = ConwayEraOnwardsConway
sbe = conwayEraOnwardsToShelleyBasedEra ceo
era = toCardanoEra sbe
cEra = AnyCardanoEra era
fastTestnetOptions = cardanoDefaultTestnetOptions
{ cardanoEpochLength = 200
, cardanoNodeEra = cEra
}

TestnetRuntime
{ testnetMagic
, poolNodes
, wallets=wallet0:wallet1:_wallet2:_
, configurationFile
}
<- cardanoTestnetDefault fastTestnetOptions conf

PoolNode{poolRuntime} <- H.headM poolNodes
poolSprocket1 <- H.noteShow $ nodeSprocket poolRuntime
execConfig <- mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic
let socketPath = nodeSocketPath poolRuntime

epochStateView <- getEpochStateView configurationFile socketPath

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"

baseDir <- H.createDirectoryIfMissing $ gov </> "output"


let propVotes :: [(String, Int)]
propVotes = zip (concatMap (uncurry replicate) [(1, "yes")]) [1..]
annotateShow propVotes

(governanceActionTxId, governanceActionIndex) <-
makeActivityChangeProposal execConfig epochStateView ceo baseDir "proposal"
Nothing (EpochInterval 3) wallet0 (EpochInterval 2)

failToVoteChangeProposalWithSPOs ceo execConfig epochStateView baseDir "vote"
governanceActionTxId governanceActionIndex propVotes wallet1

-- | Cast votes for a governance action with SPO keys.
failToVoteChangeProposalWithSPOs
:: (HasCallStack, MonadTest m, MonadIO m, MonadCatch m, H.MonadAssertion m, Typeable era)
=> ConwayEraOnwards era -- ^ The conway era onwards witness for the era in which the
-- transaction will be constructed.
-> H.ExecConfig -- ^ Specifies the CLI execution configuration.v
-> EpochStateView -- ^ Current epoch state view for transaction building. It can be obtained
-- using the 'getEpochStateView' function.
-> FilePath -- ^ Base directory path where generated files will be stored.
-> String -- ^ Name for the subfolder that will be created under 'work' folder.
-> String -- ^ The transaction id of the governance action to vote.
-> Word32 -- ^ The index of the governance action to vote.
-> [([Char], Int)] -- ^ Votes to be casted for the proposal. Each tuple contains the index
-- of the default SPO that will make the vote and the type of the vote
-- (i.e: "yes", "no", "abstain").
-> PaymentKeyInfo -- ^ Wallet that will pay for the transaction.
-> m ()
failToVoteChangeProposalWithSPOs ceo execConfig epochStateView work prefix
governanceActionTxId governanceActionIndex votes wallet = withFrozenCallStack $ do
baseDir <- H.createDirectoryIfMissing $ work </> prefix

let sbe = conwayEraOnwardsToShelleyBasedEra ceo
era = toCardanoEra sbe
cEra = AnyCardanoEra era

voteFiles <- SPO.generateVoteFiles ceo execConfig baseDir "vote-files"
governanceActionTxId governanceActionIndex
[(defaultSpoKeys idx, vote) | (vote, idx) <- votes]

voteTxBodyFp <- createVotingTxBody execConfig epochStateView sbe baseDir "vote-tx-body"
voteFiles wallet

let signingKeys = SomeKeyPair (paymentKeyInfoPair wallet):(SomeKeyPair . defaultSpoColdKeyPair . snd <$> votes)
voteTxFp <- signTx execConfig cEra baseDir "signed-vote-tx" voteTxBodyFp signingKeys

failToSubmitTx execConfig cEra voteTxFp "DisallowedVoters"
Original file line number Diff line number Diff line change
Expand Up @@ -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.PParamChangeFailsSPO 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
Expand Down Expand Up @@ -59,6 +60,7 @@ tests = do
, ignoreOnMacAndWindows "Propose And Ratify New Constitution" Gov.hprop_ledger_events_propose_new_constitution
, ignoreOnWindows "Propose New Constitution SPO" Gov.hprop_ledger_events_propose_new_constitution_spo
, ignoreOnWindows "Treasury Withdrawal" Gov.hprop_ledger_events_treasury_withdrawal
, ignoreOnWindows "PParam change fails for SPO" Gov.hprop_check_pparam_fails_spo
-- FIXME Those tests are flaky
-- , ignoreOnWindows "InfoAction" LedgerEvents.hprop_ledger_events_info_action
]
Expand Down

0 comments on commit 0fba660

Please sign in to comment.