Skip to content

Commit

Permalink
Add motion of no confidence test
Browse files Browse the repository at this point in the history
  • Loading branch information
Jimbo4350 committed May 8, 2024
1 parent 6c36c4d commit 738281b
Show file tree
Hide file tree
Showing 6 changed files with 305 additions and 2 deletions.
1 change: 1 addition & 0 deletions cardano-testnet/cardano-testnet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ test-suite cardano-testnet-test
Cardano.Testnet.Test.Gov.DRepDeposit
Cardano.Testnet.Test.Gov.DRepRetirement
Cardano.Testnet.Test.Gov.InfoAction
Cardano.Testnet.Test.Gov.NoConfidence
Cardano.Testnet.Test.Gov.ProposeNewConstitution
Cardano.Testnet.Test.Gov.ProposeNewConstitutionSPO
Cardano.Testnet.Test.Gov.TreasuryGrowth
Expand Down
1 change: 1 addition & 0 deletions cardano-testnet/src/Testnet/Components/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,7 @@ signTx execConfig cEra work prefix txBody signatoryKeyPairs = do
]
return signedTx

-- TODO: Move me to Testnet.Process.Cli
-- | Submits a signed transaction using @cardano-cli@.
submitTx
:: (MonadTest m, MonadCatch m, MonadIO m)
Expand Down
20 changes: 20 additions & 0 deletions cardano-testnet/src/Testnet/Defaults.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,15 @@ module Testnet.Defaults
, defaultByronProtocolParamsJsonValue
, defaultYamlConfig
, defaultConwayGenesis
, defaultCommitteeKeyPair
, defaultCommitteeVkeyFp
, defaultCommitteeSkeyFp
, defaultDRepVkeyFp
, defaultDRepSkeyFp
, defaultDRepKeyPair
, defaultDelegatorStakeKeyPair
, defaultSPOKeys
, defaultSPOKeyPair
, defaultSPOColdKeyPair
, defaultSPOColdVKeyFp
, defaultSPOColdSKeyFp
Expand Down Expand Up @@ -504,6 +508,16 @@ defaultGenesisFilepath era =
-- This path is actually generated by create-testnet-data. Don't change it.
eraToString era <> "-genesis.json"

defaultCommitteeVkeyFp
:: Int -- ^ The Committee's index (starts at 1)
-> FilePath
defaultCommitteeVkeyFp n = "committee-keys" </> "committee" <> show n <> ".vkey"

defaultCommitteeSkeyFp
:: Int -- ^ The Committee's index (starts at 1)
-> FilePath
defaultCommitteeSkeyFp n = "committee-keys" </> "committee" <> show n <> ".skey"

-- | The relative path to DRep keys in directories created by cardano-testnet
defaultDRepVkeyFp
:: Int -- ^ The DRep's index (starts at 1)
Expand All @@ -516,10 +530,16 @@ defaultDRepSkeyFp
-> FilePath
defaultDRepSkeyFp n = "drep-keys" </> ("drep" <> show n) </> "drep.skey"

defaultCommitteeKeyPair :: Int -> PaymentKeyPair
defaultCommitteeKeyPair n = PaymentKeyPair (defaultCommitteeVkeyFp n) (defaultCommitteeSkeyFp n)

-- | The relative path to DRep key pairs in directories created by cardano-testnet
defaultDRepKeyPair :: Int -> PaymentKeyPair
defaultDRepKeyPair n = PaymentKeyPair (defaultDRepVkeyFp n) (defaultDRepSkeyFp n)

defaultSPOKeyPair :: Int -> PaymentKeyPair
defaultSPOKeyPair n = PaymentKeyPair (defaultSPOColdVKeyFp n) (defaultSPOColdSKeyFp n)

-- | The relative path to SPO cold verification key in directories created by cardano-testnet
defaultSPOColdVKeyFp :: Int -> FilePath
defaultSPOColdVKeyFp n = "pools-keys" </> "pool" <> show n </> "cold.vkey"
Expand Down
19 changes: 19 additions & 0 deletions cardano-testnet/src/Testnet/Process/Run.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ module Testnet.Process.Run
, procSubmitApi
, procChairman
, mkExecConfig
, mkExecConfigOffline
, ProcessError(..)
, ExecutableError(..)
) where
Expand Down Expand Up @@ -174,6 +175,24 @@ mkExecConfig tempBaseAbsPath sprocket networkId = do
, H.execConfigCwd = Last $ Just tempBaseAbsPath
}

-- | Creates an 'ExecConfig' that can be used to run a process offline.
-- e.g cardano-cli without a node running.
mkExecConfigOffline :: ()
=> MonadTest m
=> MonadIO m
=> FilePath
-> m ExecConfig
mkExecConfigOffline tempBaseAbsPath = do
env' <- H.evalIO IO.getEnvironment

return H.ExecConfig
{ H.execConfigEnv = Last $ Just
-- The environment must be passed onto child process on Windows in order to
-- successfully start that process.
env'
, H.execConfigCwd = Last $ Just tempBaseAbsPath
}


data ProcessError
= ProcessIOException IOException
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE NumericUnderscores #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}

module Cardano.Testnet.Test.Gov.NoConfidence
( hprop_gov_no_confidence
) where

import Cardano.Api as Api
import Cardano.Api.Error
import Cardano.Api.Ledger
import Cardano.Api.Shelley

import qualified Cardano.Ledger.Conway.Genesis as L
import qualified Cardano.Ledger.Conway.Governance as L
import qualified Cardano.Ledger.Credential as L
import qualified Cardano.Ledger.Shelley.LedgerState as L
import Cardano.Testnet

import Prelude

import Control.Monad
import Data.Bifunctor
import qualified Data.ByteString.Char8 as BSC
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 ((</>))

import Testnet.Components.Configuration
import Testnet.Components.DRep as DRep
import Testnet.Components.Query
import Testnet.Components.SPO as SPO
import Testnet.Components.TestWatchdog
import Testnet.Defaults
import qualified Testnet.Process.Cli as P
import qualified Testnet.Process.Run as H
import Testnet.Property.Util (integrationWorkspace)
import Testnet.Runtime

import Hedgehog
import qualified Hedgehog as H
import qualified Hedgehog.Extras as H
import qualified Hedgehog.Extras.Stock.IO.Network.Sprocket as IO

-- | Execute me with:
-- @DISABLE_RETRIES=1 cabal test cardano-testnet-test --test-options '-p "/Committee Motion Of No Confidence/"'@
-- Generate a testnet with a committee defined in the Conway genesis. Submit a motion of no confidence
-- and have the required threshold of SPOs and DReps vote yes on it.
hprop_gov_no_confidence :: Property
hprop_gov_no_confidence = integrationWorkspace "no-confidence" $ \tempAbsBasePath' -> runWithDefaultWatchdog_ $ 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
}
execConfigOffline <- H.mkExecConfigOffline tempBaseAbsPath

-- Step 1. Define generate and define a committee in the genesis file

-- Create committee cold key
H.createDirectoryIfMissing_ $ tempAbsPath' </> work </> "committee-keys"
H.forConcurrently_ [1] $ \n -> do
H.execCli' execConfigOffline
[ anyEraToString cEra, "governance", "committee"
, "key-gen-cold"
, "--cold-verification-key-file", work </> defaultCommitteeVkeyFp n
, "--cold-signing-key-file", work </> defaultCommitteeSkeyFp n
]

committeeVkey1Fp <- H.noteShow $ work </> defaultCommitteeVkeyFp 1

-- Read committee cold keys from disk to put into conway genesis

comKeyHash1Str <- filter (/= '\n') <$> H.execCli' execConfigOffline
[ anyEraToString cEra, "governance", "committee"
, "key-hash"
, "--verification-key-file", committeeVkey1Fp
]

CommitteeColdKeyHash comKeyHash1 <-
H.evalEither
$ deserialiseFromRawBytesHex (AsHash AsCommitteeColdKey)
$ BSC.pack comKeyHash1Str

let comKeyCred1 = L.KeyHashObj comKeyHash1
committeeThreshold = unsafeBoundedRational 0.5
committee = L.Committee (Map.fromList [(comKeyCred1, EpochNo 100)]) committeeThreshold

alonzoGenesis <- evalEither $ first prettyError defaultAlonzoGenesis
(startTime, shelleyGenesis') <- getDefaultShelleyGenesis fastTestnetOptions
let conwayGenesisWithCommittee =
defaultConwayGenesis { L.cgCommittee = committee }

TestnetRuntime
{ testnetMagic
, poolNodes
, wallets=wallet0:_wallet1:_
, configurationFile
} <- cardanoTestnet
fastTestnetOptions
conf startTime shelleyGenesis'
alonzoGenesis conwayGenesisWithCommittee

poolNode1 <- H.headM poolNodes
poolSprocket1 <- H.noteShow $ nodeSprocket $ poolRuntime poolNode1
execConfig <- H.mkExecConfig tempBaseAbsPath poolSprocket1 testnetMagic

let socketName' = IO.sprocketName poolSprocket1
socketBase = IO.sprocketBase poolSprocket1 -- /tmp
socketPath = socketBase </> socketName'

H.note_ $ "Sprocket: " <> show poolSprocket1
H.note_ $ "Abs path: " <> tempAbsBasePath'
H.note_ $ "Socketpath: " <> socketPath

mCommitteePresent
<- H.leftFailM $ findCondition (committeeIsPresent True) configurationFile socketPath (EpochNo 3)
H.nothingFail mCommitteePresent

-- Step 2. Propose motion of no confidence. DRep and SPO voting thresholds must be met.

-- Create proposal to add a new member to the committee

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

proposalAnchorDataHash <- H.execCli' execConfig
[ eraToString era, "governance"
, "hash", "anchor-data", "--file-text", proposalAnchorFile
]


proposalFile <- H.note $ work </> "sample-proposal-anchor"
stakeVkeyFp <- H.note $ work </> "stake.vkey"
stakeSKeyFp <- H.note $ work </> "stake.skey"

_ <- P.cliStakeAddressKeyGen tempAbsPath'
$ P.KeyNames { P.verificationKeyFile = stakeVkeyFp
, P.signingKeyFile = stakeSKeyFp
}
void $ H.execCli' execConfig $
[ eraToString era, "governance", "action", "create-no-confidence"
, "--testnet"
, "--governance-action-deposit", show @Integer 1_000_000 --- TODO: Get from protocol parameters
, "--deposit-return-stake-verification-key-file", stakeVkeyFp
, "--anchor-url", "https://tinyurl.com/3wrwb2as"
, "--anchor-data-hash", proposalAnchorDataHash
, "--out-file", proposalFile
]

txbodyFp <- H.note $ work </> "tx.body"
epochStateView <- getEpochStateView (File configurationFile) (File socketPath)

txin1 <- findLargestUtxoForPaymentKey epochStateView sbe wallet0

void $ H.execCli' execConfig
[ eraToString era, "transaction", "build"
, "--change-address", Text.unpack $ paymentKeyInfoAddr wallet0
, "--tx-in", Text.unpack $ renderTxIn txin1
, "--tx-out", Text.unpack (paymentKeyInfoAddr wallet0) <> "+" <> show @Int 5_000_000
, "--proposal-file", proposalFile
, "--out-file", txbodyFp
]

signedProposalTx <- signTx execConfig cEra work "signed-proposal"
(File txbodyFp) [paymentKeyInfoPair wallet0]

submitTx execConfig cEra signedProposalTx

-- Step 3. Create and submit votes on motion of no confidence proposal.
-- Proposal was successfully submitted, now we vote on the proposal
-- and confirm it was ratified.

governanceActionTxId <- retrieveTransactionId execConfig signedProposalTx

!propSubmittedResult <- findCondition (maybeExtractGovernanceActionIndex sbe (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

let spoVotes :: [(String, Int)]
spoVotes = [("yes", 1), ("yes", 2), ("yes", 3)]
drepVotes :: [(String, Int)]
drepVotes = [("yes", 1), ("yes", 2), ("yes", 3)]

spoVoteFiles <- SPO.generateVoteFiles ceo execConfig work "spo-vote-files"
governanceActionTxId governanceActionIndex
[(defaultSPOKeys idx, vote) | (vote, idx) <- spoVotes]
drepVoteFiles <- DRep.generateVoteFiles execConfig work "drep-vote-files"
governanceActionTxId governanceActionIndex
[(defaultDRepKeyPair idx, vote) | (vote, idx) <- drepVotes]

let allVoteFiles = spoVoteFiles ++ drepVoteFiles
annotateShow allVoteFiles

-- Submit votes
voteTxBodyFp <- createVotingTxBody execConfig epochStateView sbe work "vote-tx-body"
allVoteFiles wallet0
let spoSigningKeys = [defaultSPOKeyPair n | (_, n) <- spoVotes]
drepSigningKeys = [defaultDRepKeyPair n | (_, n) <- drepVotes]
allVoteSigningKeys = spoSigningKeys ++ drepSigningKeys

voteTxFp <- signTx execConfig cEra work "signed-vote-tx" voteTxBodyFp
(paymentKeyInfoPair wallet0 : allVoteSigningKeys)

submitTx execConfig cEra voteTxFp

-- 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 socketPath (EpochNo 5)
H.nothingFail mCommitteeEmpty

-- | Checks if the committee is empty or not.
committeeIsPresent :: Bool -> AnyNewEpochState -> Maybe ()
committeeIsPresent committeeExists (AnyNewEpochState sbe newEpochState) =
caseShelleyToBabbageOrConwayEraOnwards
(const $ error "Constitutional committee does not exist pre-Conway era")
(const $ let mCommittee = newEpochState
^. L.nesEsL
. L.esLStateL
. L.lsUTxOStateL
. L.utxosGovStateL
. L.cgsCommitteeL
in if committeeExists
then if isSJust mCommittee
then Just () -- The committee is non empty and we terminate.
else Nothing
else if mCommittee == SNothing
then Just () -- The committee is empty and we terminate.
else Nothing
)
sbe

Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import qualified Cardano.Testnet.Test.Cli.QuerySlotNumber
import qualified Cardano.Testnet.Test.FoldBlocks
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.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 @@ -47,10 +48,10 @@ tests = do
, H.ignoreOnWindows "Treasury Growth" Gov.prop_check_if_treasury_is_growing
-- TODO: Replace foldBlocks with checkLedgerStateCondition
, T.testGroup "Governance"
[
[ H.ignoreOnMacAndWindows "Committee Motion Of No Confidence" Gov.hprop_gov_no_confidence
-- TODO: "DRep Activity" is too flaky at the moment. Disabling until we can fix it.
-- , H.ignoreOnWindows "DRep Activity" Cardano.Testnet.Test.LedgerEvents.Gov.DRepActivity.hprop_check_drep_activity
H.ignoreOnWindows "DRep Deposits" Gov.hprop_ledger_events_drep_deposits
, H.ignoreOnWindows "DRep Deposits" Gov.hprop_ledger_events_drep_deposits
-- FIXME Those tests are flaky
-- , H.ignoreOnWindows "InfoAction" LedgerEvents.hprop_ledger_events_info_action
, H.ignoreOnWindows "DRep Retirement" Gov.hprop_drep_retirement
Expand Down

0 comments on commit 738281b

Please sign in to comment.