diff --git a/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/Imp.hs b/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/Imp.hs index 7a0806c6432..98e2f9581bd 100644 --- a/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/Imp.hs +++ b/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/Imp.hs @@ -20,4 +20,5 @@ spec = do describe "AllegraImpSpec" . withEachEraVersion @era $ UtxowSpec.spec -instance EraSpecificSpec AllegraEra +instance EraSpecificSpec AllegraEra where + eraSpecificSpec = ShelleyImp.shelleyEraSpecificSpec diff --git a/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/ImpTest.hs b/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/ImpTest.hs index a45d9d1fdf8..6bfc2eb6d86 100644 --- a/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/ImpTest.hs +++ b/eras/allegra/impl/testlib/Test/Cardano/Ledger/Allegra/ImpTest.hs @@ -43,6 +43,7 @@ instance ShelleyEraImp AllegraEra where modifyImpInitProtVer = shelleyModifyImpInitProtVer genRegTxCert = shelleyGenRegTxCert genUnRegTxCert = shelleyGenUnRegTxCert + delegStakeTxCert = shelleyDelegStakeTxCert impAllegraSatisfyNativeScript :: ( ShelleyEraImp era diff --git a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Imp.hs b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Imp.hs index eff94dd389d..dee9216e2b4 100644 --- a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Imp.hs +++ b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/Imp.hs @@ -16,6 +16,7 @@ import qualified Test.Cardano.Ledger.Alonzo.Imp.UtxowSpec as Utxow import Test.Cardano.Ledger.Alonzo.ImpTest import Test.Cardano.Ledger.Imp.Common import qualified Test.Cardano.Ledger.Mary.Imp as MaryImp +import qualified Test.Cardano.Ledger.Shelley.Imp as ShelleyImp spec :: forall era. @@ -40,4 +41,5 @@ alonzoEraSpecificSpec = do Utxow.alonzoEraSpecificSpec instance EraSpecificSpec AlonzoEra where - eraSpecificSpec = alonzoEraSpecificSpec + eraSpecificSpec = + ShelleyImp.shelleyEraSpecificSpec >> alonzoEraSpecificSpec diff --git a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/ImpTest.hs b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/ImpTest.hs index 3288cbebe58..139f7a0b18e 100644 --- a/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/ImpTest.hs +++ b/eras/alonzo/impl/testlib/Test/Cardano/Ledger/Alonzo/ImpTest.hs @@ -439,6 +439,7 @@ instance ShelleyEraImp AlonzoEra where modifyImpInitProtVer = shelleyModifyImpInitProtVer genRegTxCert = shelleyGenRegTxCert genUnRegTxCert = shelleyGenUnRegTxCert + delegStakeTxCert = shelleyDelegStakeTxCert instance MaryEraImp AlonzoEra diff --git a/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/Imp.hs b/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/Imp.hs index a5722064c56..d02a3372932 100644 --- a/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/Imp.hs +++ b/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/Imp.hs @@ -16,6 +16,7 @@ import qualified Test.Cardano.Ledger.Babbage.Imp.UtxosSpec as Utxos import qualified Test.Cardano.Ledger.Babbage.Imp.UtxowSpec as Utxow import Test.Cardano.Ledger.Babbage.ImpTest (BabbageEraImp) import Test.Cardano.Ledger.Imp.Common +import qualified Test.Cardano.Ledger.Shelley.Imp as ShelleyImp spec :: forall era. (BabbageEraImp era, EraSpecificSpec era) => Spec spec = do @@ -28,4 +29,4 @@ spec = do instance EraSpecificSpec BabbageEra where eraSpecificSpec = - AlonzoImp.alonzoEraSpecificSpec + ShelleyImp.shelleyEraSpecificSpec >> AlonzoImp.alonzoEraSpecificSpec diff --git a/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/ImpTest.hs b/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/ImpTest.hs index 2d451d4409a..b573cd318df 100644 --- a/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/ImpTest.hs +++ b/eras/babbage/impl/testlib/Test/Cardano/Ledger/Babbage/ImpTest.hs @@ -62,6 +62,7 @@ instance ShelleyEraImp BabbageEra where modifyImpInitProtVer = shelleyModifyImpInitProtVer genRegTxCert = shelleyGenRegTxCert genUnRegTxCert = shelleyGenUnRegTxCert + delegStakeTxCert = shelleyDelegStakeTxCert babbageFixupTx :: ( HasCallStack diff --git a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp.hs b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp.hs index aa688a97034..f3ffa9461bb 100644 --- a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp.hs +++ b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp.hs @@ -73,10 +73,9 @@ conwayEraGenericSpec = do conwayEraSpecificSpec :: SpecWith (ImpInit (LedgerSpec ConwayEra)) conwayEraSpecificSpec = do - describe "Conway era specific Imp spec" $ - describe "Certificates without deposits" $ do - describe "DELEG" Deleg.conwayEraSpecificSpec - describe "UTXO" Utxo.conwayEraSpecificSpec + describe "Conway era specific Imp spec" $ do + describe "DELEG" Deleg.conwayEraSpecificSpec + describe "UTXO" Utxo.conwayEraSpecificSpec instance EraSpecificSpec ConwayEra where eraSpecificSpec = diff --git a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/DelegSpec.hs b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/DelegSpec.hs index d0456003b57..d7e7b4c482e 100644 --- a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/DelegSpec.hs +++ b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/DelegSpec.hs @@ -22,7 +22,7 @@ import Cardano.Ledger.BaseTypes ( addEpochInterval, natVersion, ) -import Cardano.Ledger.Coin (Coin (..), compactCoinOrError) +import Cardano.Ledger.Coin (Coin (..)) import Cardano.Ledger.Conway import Cardano.Ledger.Conway.Core import Cardano.Ledger.Conway.Governance @@ -52,51 +52,24 @@ spec :: SpecWith (ImpInit (LedgerSpec era)) spec = do describe "Register stake credential" $ do - it "With correct deposit or without any deposit" $ do + it "With correct deposit" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - cred <- KeyHashObj <$> freshKeyHash - -- NOTE: This will always generate certs with deposits post-Conway - regTxCert <- genRegTxCert cred - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL .~ [regTxCert] - expectRegistered cred - freshKeyHash >>= \kh -> do submitTx_ $ mkBasicTx mkBasicTxBody & bodyTxL . certsTxBodyL .~ [RegDepositTxCert (KeyHashObj kh) expectedDeposit] - expectRegistered (KeyHashObj kh) + expectStakeCredRegistered (KeyHashObj kh) it "Twice the same certificate in the same transaction" $ do -- This is expected behavior because `certsTxBodyL` removes duplicates - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL freshKeyHash >>= \kh -> do + regTxCert <- genRegTxCert (KeyHashObj kh) submitTx_ $ mkBasicTx mkBasicTxBody & bodyTxL . certsTxBodyL - .~ [ RegDepositTxCert (KeyHashObj kh) expectedDeposit - , RegDepositTxCert (KeyHashObj kh) expectedDeposit - ] - expectRegistered (KeyHashObj kh) - - it "When already already registered" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - let sh = hashPlutusScript $ evenRedeemerNoDatum SPlutusV3 - let tx = - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [RegDepositTxCert (ScriptHashObj sh) expectedDeposit] - submitTx_ tx - - submitFailingTx - tx - [ injectFailure $ StakeKeyRegisteredDELEG (ScriptHashObj sh) - ] - expectRegistered (ScriptHashObj sh) - + .~ [regTxCert, regTxCert] + expectStakeCredRegistered (KeyHashObj kh) it "With incorrect deposit" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL pv <- getsNES $ nesEsL . curPParamsEpochStateL . ppProtocolVersionL @@ -120,39 +93,14 @@ spec = do } else IncorrectDepositDELEG wrongDeposit ] - expectNotRegistered (KeyHashObj kh) + expectStakeCredNotRegistered (KeyHashObj kh) describe "Unregister stake credentials" $ do - it "When registered" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - let sh = ScriptHashObj $ hashPlutusScript (evenRedeemerNoDatum SPlutusV3) - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [RegDepositTxCert sh expectedDeposit] - - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [UnRegDepositTxCert sh expectedDeposit] - expectNotRegistered sh - - it "When not registered" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - freshKeyHash >>= \kh -> - submitFailingTx - ( mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [UnRegDepositTxCert (KeyHashObj kh) expectedDeposit] - ) - [ injectFailure $ StakeKeyNotRegisteredDELEG (KeyHashObj kh) - ] - it "With incorrect refund" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL pv <- getsNES $ nesEsL . curPParamsEpochStateL . ppProtocolVersionL - cred <- KeyHashObj <$> freshKeyHash + let cred = ScriptHashObj $ hashPlutusScript $ evenRedeemerNoDatum SPlutusV3 submitTx_ $ mkBasicTx mkBasicTxBody @@ -177,40 +125,9 @@ spec = do else IncorrectDepositDELEG wrongDeposit ] - expectRegistered cred - - it "With non-zero reward balance" $ do - modifyPParams $ ppGovActionLifetimeL .~ EpochInterval 2 - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - cred <- KeyHashObj <$> freshKeyHash + expectStakeCredRegistered cred - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL .~ [RegDepositTxCert cred expectedDeposit] - - submitAndExpireProposalToMakeReward cred - - balance <- getBalance cred - submitFailingTx - ( mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL .~ [UnRegDepositTxCert cred expectedDeposit] - ) - [injectFailure $ StakeKeyHasNonZeroRewardAccountBalanceDELEG balance] - expectRegistered cred - - it "Register and unregister in the same transaction" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - freshKeyHash >>= \kh -> do - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [ RegDepositTxCert (KeyHashObj kh) expectedDeposit - , UnRegDepositTxCert (KeyHashObj kh) expectedDeposit - ] - expectNotRegistered (KeyHashObj kh) - - it "deregistering returns the deposit" $ do + it "Deregistering returns the deposit" $ do let keyDeposit = Coin 2 -- This is paid out as the reward @@ -251,50 +168,6 @@ spec = do expectNotRegisteredRewardAddress rewardAccount describe "Delegate stake" $ do - it "Delegate registered stake credentials to registered pool" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - cred <- KeyHashObj <$> freshKeyHash - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [RegDepositTxCert cred expectedDeposit] - - poolKh <- freshKeyHash - registerPool poolKh - - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [DelegTxCert cred (DelegStake poolKh)] - - expectDelegatedToPool cred poolKh - - it "Register and delegate in the same transaction" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - poolKh <- freshKeyHash - registerPool poolKh - freshKeyHash >>= \kh -> do - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [RegDepositDelegTxCert (KeyHashObj kh) (DelegStake poolKh) expectedDeposit] - expectDelegatedToPool (KeyHashObj kh) poolKh - - it "Delegate unregistered stake credentials" $ do - cred <- KeyHashObj <$> freshKeyHash - poolKh <- freshKeyHash - registerPool poolKh - submitFailingTx - ( mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [DelegTxCert cred (DelegStake poolKh)] - ) - [injectFailure $ StakeKeyNotRegisteredDELEG cred] - - expectNotRegistered cred - it "Delegate to unregistered pool" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL @@ -311,64 +184,8 @@ spec = do .~ [DelegTxCert cred (DelegStake poolKh)] ) [injectFailure $ DelegateeStakePoolNotRegisteredDELEG poolKh] - expectNotDelegatedToPool cred - it "Delegate already delegated credentials" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - cred <- KeyHashObj <$> freshKeyHash - poolKh <- freshKeyHash - registerPool poolKh - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [ RegDepositTxCert cred expectedDeposit - , DelegTxCert cred (DelegStake poolKh) - ] - expectDelegatedToPool cred poolKh - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [DelegTxCert cred (DelegStake poolKh)] - expectDelegatedToPool cred poolKh - - poolKh1 <- freshKeyHash - registerPool poolKh1 - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [DelegTxCert cred (DelegStake poolKh1)] - expectDelegatedToPool cred poolKh1 - - poolKh2 <- freshKeyHash - registerPool poolKh2 - poolKh3 <- freshKeyHash - registerPool poolKh3 - - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [ DelegTxCert cred (DelegStake poolKh2) - , DelegTxCert cred (DelegStake poolKh3) - ] - - expectDelegatedToPool cred poolKh3 - - it "Delegate and unregister" $ do - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - cred <- KeyHashObj <$> freshKeyHash - poolKh <- freshKeyHash - registerPool poolKh - submitTx_ $ - mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ [ RegDepositDelegTxCert cred (DelegStake poolKh) expectedDeposit - , UnRegDepositTxCert cred expectedDeposit - ] - expectNotRegistered cred - describe "Delegate vote" $ do it "Delegate vote of registered stake credentials to registered drep" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL @@ -442,7 +259,7 @@ spec = do ) [injectFailure $ StakeKeyNotRegisteredDELEG cred] - expectNotRegistered cred + expectStakeCredNotRegistered cred it "Redelegate vote" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL @@ -481,7 +298,7 @@ spec = do mkBasicTx mkBasicTxBody & bodyTxL . certsTxBodyL .~ [UnRegDepositTxCert cred expectedDeposit] - expectNotRegistered cred + expectStakeCredNotRegistered cred expectNotDelegatedVote cred -- https://github.com/IntersectMBO/formal-ledger-specifications/issues/917 -- TODO: Re-enable after issue is resolved, by removing this override @@ -511,7 +328,7 @@ spec = do & bodyTxL . certsTxBodyL .~ [RegDepositDelegTxCert cred (DelegVote DRepAlwaysAbstain) expectedDeposit] registerAndRetirePoolToMakeReward cred - expectRegistered cred + expectStakeCredRegistered cred expectDelegatedVote cred DRepAlwaysAbstain impAnn "Version should be unchanged" $ getProtVer `shouldReturn` initialProtVer @@ -532,7 +349,7 @@ spec = do & bodyTxL . certsTxBodyL .~ [UnRegDepositTxCert cred expectedDeposit] & bodyTxL . withdrawalsTxBodyL .~ Withdrawals (Map.singleton rewardAccount withdrawalAmount) - expectNotRegistered cred + expectStakeCredNotRegistered cred expectNotDelegatedVote cred -- https://github.com/IntersectMBO/formal-ledger-specifications/issues/916 -- TODO: Re-enable after issue is resolved, by removing this override @@ -545,7 +362,7 @@ spec = do & bodyTxL . certsTxBodyL .~ [RegDepositDelegTxCert cred (DelegVote DRepAlwaysAbstain) expectedDeposit] registerAndRetirePoolToMakeReward cred - expectRegistered cred + expectStakeCredRegistered cred expectDelegatedVote cred DRepAlwaysAbstain forM_ @[] [1 .. 3 :: Int] $ \_ -> do submitTx_ $ @@ -561,7 +378,7 @@ spec = do .~ [UnRegDepositTxCert cred expectedDeposit] & bodyTxL . withdrawalsTxBodyL .~ Withdrawals (Map.singleton rewardAccount withdrawalAmount) - expectNotRegistered cred + expectStakeCredNotRegistered cred expectNotDelegatedVote cred -- https://github.com/IntersectMBO/formal-ledger-specifications/issues/640 @@ -657,7 +474,7 @@ spec = do mkBasicTx mkBasicTxBody & bodyTxL . certsTxBodyL .~ [UnRegDepositTxCert cred expectedDeposit] - expectNotRegistered cred + expectStakeCredNotRegistered cred it "Delegate to DRep and SPO and change delegation to a different SPO" $ do expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL @@ -687,19 +504,6 @@ spec = do expectDelegatedToPool cred poolKh' expectDelegatedVote cred (DRepCredential drepCred) where - expectNotRegistered :: Credential 'Staking -> ImpTestM era () - expectNotRegistered cred = do - accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL - impAnn (show cred <> " expected to not be in Accounts") $ do - expectNothingExpr $ lookupAccountState cred accounts - - expectNotDelegatedToPool :: Credential 'Staking -> ImpTestM era () - expectNotDelegatedToPool cred = do - accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL - impAnn (show cred <> " expected to not have delegated to a stake pool") $ do - accountState <- expectJust $ lookupAccountState cred accounts - expectNothingExpr (accountState ^. stakePoolDelegationAccountStateL) - expectDelegatedVote :: HasCallStack => Credential 'Staking -> DRep -> ImpTestM era () expectDelegatedVote cred drep = do accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL @@ -751,20 +555,3 @@ conwayEraSpecificSpec = do , DelegStakeTxCert cred2 poolKh -- using the pattern from Shelley ] expectDelegatedToPool cred2 poolKh - -expectRegistered :: (HasCallStack, ConwayEraImp era) => Credential 'Staking -> ImpTestM era () -expectRegistered cred = do - accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL - expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL - - accountState <- expectJust $ lookupAccountState cred accounts - impAnn (show cred <> " expected to be in Accounts with the correct deposit") $ do - accountState ^. depositAccountStateL `shouldBe` compactCoinOrError expectedDeposit - -expectDelegatedToPool :: - (HasCallStack, ConwayEraImp era) => Credential 'Staking -> KeyHash 'StakePool -> ImpTestM era () -expectDelegatedToPool cred poolKh = do - accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL - impAnn (show cred <> " expected to have delegated to " <> show poolKh) $ do - accountState <- expectJust $ lookupAccountState cred accounts - accountState ^. stakePoolDelegationAccountStateL `shouldBe` Just poolKh diff --git a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/ImpTest.hs b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/ImpTest.hs index 4c38d0bb8b2..315d6f50e1d 100644 --- a/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/ImpTest.hs +++ b/eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/ImpTest.hs @@ -133,6 +133,7 @@ module Test.Cardano.Ledger.Conway.ImpTest ( FailBoth (..), delegateSPORewardAddressToDRep_, getCommittee, + conwayDelegStakeTxCert, ) where import Cardano.Ledger.Address (RewardAccount (..)) @@ -313,6 +314,7 @@ instance ShelleyEraImp ConwayEra where modifyImpInitProtVer = conwayModifyImpInitProtVer genRegTxCert = conwayGenRegTxCert genUnRegTxCert = conwayGenUnRegTxCert + delegStakeTxCert = conwayDelegStakeTxCert conwayModifyImpInitProtVer :: forall era. @@ -417,7 +419,6 @@ unRegisterDRep drep = do .~ SSeq.singleton (UnRegDRepTxCert drep refund) conwayGenUnRegTxCert :: - forall era. ( ShelleyEraImp era , ConwayEraTxCert era , ShelleyEraTxCert era @@ -435,7 +436,6 @@ conwayGenUnRegTxCert stakingCredential = do ] conwayGenRegTxCert :: - forall era. ( ShelleyEraImp era , ConwayEraTxCert era , ShelleyEraTxCert era @@ -449,6 +449,13 @@ conwayGenRegTxCert stakingCredential = <$> getsNES (nesEsL . curPParamsEpochStateL . ppKeyDepositL) ] +conwayDelegStakeTxCert :: + ConwayEraTxCert era => + Credential 'Staking -> + KeyHash 'StakePool -> + TxCert era +conwayDelegStakeTxCert cred pool = DelegTxCert cred (DelegStake pool) + -- | Submit a transaction that updates a given DRep updateDRep :: forall era. diff --git a/eras/dijkstra/impl/testlib/Test/Cardano/Ledger/Dijkstra/ImpTest.hs b/eras/dijkstra/impl/testlib/Test/Cardano/Ledger/Dijkstra/ImpTest.hs index def972d33f5..9177ace4048 100644 --- a/eras/dijkstra/impl/testlib/Test/Cardano/Ledger/Dijkstra/ImpTest.hs +++ b/eras/dijkstra/impl/testlib/Test/Cardano/Ledger/Dijkstra/ImpTest.hs @@ -80,6 +80,7 @@ instance ShelleyEraImp DijkstraEra where modifyImpInitProtVer = conwayModifyImpInitProtVer genRegTxCert = dijkstraGenRegTxCert genUnRegTxCert = dijkstraGenUnRegTxCert + delegStakeTxCert = conwayDelegStakeTxCert instance MaryEraImp DijkstraEra diff --git a/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/Imp.hs b/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/Imp.hs index a76c61bc419..bc2884debf7 100644 --- a/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/Imp.hs +++ b/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/Imp.hs @@ -13,6 +13,7 @@ import qualified Test.Cardano.Ledger.Allegra.Imp as AllegraImp import Test.Cardano.Ledger.Imp.Common import qualified Test.Cardano.Ledger.Mary.Imp.UtxoSpec as Utxo import Test.Cardano.Ledger.Mary.ImpTest +import qualified Test.Cardano.Ledger.Shelley.Imp as ShelleyImp spec :: forall era. (MaryEraImp era, EraSpecificSpec era) => Spec spec = do @@ -21,4 +22,5 @@ spec = do withEachEraVersion @era $ Utxo.spec -instance EraSpecificSpec MaryEra +instance EraSpecificSpec MaryEra where + eraSpecificSpec = ShelleyImp.shelleyEraSpecificSpec diff --git a/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/ImpTest.hs b/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/ImpTest.hs index 7b17187b6fa..49a9150a0c2 100644 --- a/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/ImpTest.hs +++ b/eras/mary/impl/testlib/Test/Cardano/Ledger/Mary/ImpTest.hs @@ -29,6 +29,7 @@ instance ShelleyEraImp MaryEra where modifyImpInitProtVer = shelleyModifyImpInitProtVer genRegTxCert = shelleyGenRegTxCert genUnRegTxCert = shelleyGenUnRegTxCert + delegStakeTxCert = shelleyDelegStakeTxCert class ( ShelleyEraImp era diff --git a/eras/shelley/impl/CHANGELOG.md b/eras/shelley/impl/CHANGELOG.md index 57af3c6ad39..ef372e552fc 100644 --- a/eras/shelley/impl/CHANGELOG.md +++ b/eras/shelley/impl/CHANGELOG.md @@ -11,6 +11,8 @@ * Added `genUnRegTxCert` to `ShelleyEraImp` * Added `shelleyGenRegTxCert` * Added `genRegTxCert` to `ShelleyEraImp` +* Added `delegStakeTxCert` to `ShelleyEraImp` +* Added `expectStakeCredRegistered`, `expectStakeCredNotRegistered`, `expectDelegatedToPool`, `expectNotDelegatedToPool` to `ImpTest` ## 1.17.0.0 diff --git a/eras/shelley/impl/cardano-ledger-shelley.cabal b/eras/shelley/impl/cardano-ledger-shelley.cabal index eb4f3e3f425..640ee4d34be 100644 --- a/eras/shelley/impl/cardano-ledger-shelley.cabal +++ b/eras/shelley/impl/cardano-ledger-shelley.cabal @@ -146,6 +146,7 @@ library testlib Test.Cardano.Ledger.Shelley.Era Test.Cardano.Ledger.Shelley.Examples Test.Cardano.Ledger.Shelley.Imp + Test.Cardano.Ledger.Shelley.Imp.DelegSpec Test.Cardano.Ledger.Shelley.Imp.EpochSpec Test.Cardano.Ledger.Shelley.Imp.LedgerSpec Test.Cardano.Ledger.Shelley.Imp.PoolSpec diff --git a/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp.hs b/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp.hs index 839c97393a4..7d4f84bff4b 100644 --- a/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp.hs +++ b/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp.hs @@ -5,10 +5,13 @@ {-# LANGUAGE TypeApplications #-} {-# OPTIONS_GHC -Wno-orphans #-} -module Test.Cardano.Ledger.Shelley.Imp (spec) where +module Test.Cardano.Ledger.Shelley.Imp (spec, shelleyEraSpecificSpec) where import Cardano.Ledger.Shelley (ShelleyEra) +import Cardano.Ledger.Shelley.Core +import Cardano.Ledger.Shelley.Rules import Test.Cardano.Ledger.Imp.Common +import qualified Test.Cardano.Ledger.Shelley.Imp.DelegSpec as Deleg import qualified Test.Cardano.Ledger.Shelley.Imp.EpochSpec as Epoch import qualified Test.Cardano.Ledger.Shelley.Imp.LedgerSpec as Ledger import qualified Test.Cardano.Ledger.Shelley.Imp.PoolSpec as Pool @@ -26,6 +29,7 @@ spec :: spec = do describe "Era specific tests" . withEachEraVersion @era $ eraSpecificSpec describe "ShelleyImpSpec" $ withEachEraVersion @era $ do + describe "DELEG" Deleg.spec Epoch.spec Ledger.spec Pool.spec @@ -34,4 +38,17 @@ spec = do describe "ShelleyPureTests" $ do Instant.spec @era -instance EraSpecificSpec ShelleyEra +shelleyEraSpecificSpec :: + forall era. + ( ShelleyEraImp era + , InjectRuleFailure "LEDGER" ShelleyDelegsPredFailure era + ) => + SpecWith (ImpInit (LedgerSpec era)) +shelleyEraSpecificSpec = do + describe "Shelley era specific Imp spec" $ + describe "DELEG" $ + Deleg.shelleyEraSpecificSpec + +instance EraSpecificSpec ShelleyEra where + eraSpecificSpec = + describe "DELEG" Deleg.shelleyEraSpecificSpec diff --git a/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp/DelegSpec.hs b/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp/DelegSpec.hs new file mode 100644 index 00000000000..62c359f087e --- /dev/null +++ b/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/Imp/DelegSpec.hs @@ -0,0 +1,279 @@ +{-# LANGUAGE DataKinds #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE OverloadedLists #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} +{-# LANGUAGE TypeFamilies #-} + +module Test.Cardano.Ledger.Shelley.Imp.DelegSpec ( + shelleyEraSpecificSpec, + spec, +) where + +import Cardano.Ledger.BaseTypes +import Cardano.Ledger.Coin (Coin (Coin)) +import Cardano.Ledger.Credential (Credential (..)) +import Cardano.Ledger.Shelley.Core +import Cardano.Ledger.Shelley.Rules +import Cardano.Ledger.Shelley.Scripts +import qualified Data.Map.Strict as Map +import Lens.Micro +import Test.Cardano.Ledger.Imp.Common +import Test.Cardano.Ledger.Shelley.Arbitrary () +import Test.Cardano.Ledger.Shelley.ImpTest + +shelleyEraSpecificSpec :: + ( ShelleyEraImp era + , InjectRuleFailure "LEDGER" ShelleyDelegsPredFailure era + ) => + SpecWith (ImpInit (LedgerSpec era)) +shelleyEraSpecificSpec = do + it "Twice the same certificate in the same transaction" $ do + freshKeyHash >>= \kh -> do + regTxCert <- genRegTxCert (KeyHashObj kh) + submitFailingTx + ( mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [regTxCert, regTxCert] + ) + [injectFailure $ StakeKeyAlreadyRegisteredDELEG (KeyHashObj kh)] + expectStakeCredNotRegistered (KeyHashObj kh) + + it "Delegate to unregistered pool" $ do + cred <- KeyHashObj <$> freshKeyHash + regTxCert <- genRegTxCert cred + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert] + + poolKh <- freshKeyHash + submitFailingTx + ( mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [delegStakeTxCert cred poolKh] + ) + [injectFailure $ DelegateeNotRegisteredDELEG poolKh] + expectNotDelegatedToPool cred + + it "Deregistering returns the deposit" $ do + let keyDeposit = Coin 2 + -- This is paid out as the reward + let poolDeposit = Coin 3 + modifyPParams $ \pp -> + pp + & ppKeyDepositL .~ keyDeposit + & ppPoolDepositL .~ poolDeposit + stakeCred <- KeyHashObj <$> freshKeyHash + rewardAccount <- getRewardAccountFor stakeCred + otherStakeCred <- KeyHashObj <$> freshKeyHash + otherRewardAccount <- getRewardAccountFor otherStakeCred + khStakePool <- freshKeyHash + registerPool khStakePool + stakeCredRegTxCert <- genRegTxCert stakeCred + otherStakeCredRegTxCert <- genRegTxCert otherStakeCred + submitTx_ . mkBasicTx $ + mkBasicTxBody + & certsTxBodyL + .~ [ stakeCredRegTxCert + , delegStakeTxCert stakeCred khStakePool + , otherStakeCredRegTxCert + , delegStakeTxCert otherStakeCred khStakePool + ] + expectRegisteredRewardAddress rewardAccount + expectRegisteredRewardAddress otherRewardAccount + registerAndRetirePoolToMakeReward otherStakeCred + + getBalance otherStakeCred `shouldReturn` poolDeposit + unRegTxCert <- genUnRegTxCert stakeCred + + submitTx_ . mkBasicTx $ + mkBasicTxBody + & certsTxBodyL .~ [unRegTxCert] + & withdrawalsTxBodyL + .~ Withdrawals + ( Map.fromList + [ (rewardAccount, Coin 0) + , (otherRewardAccount, poolDeposit) + ] + ) + getBalance otherStakeCred `shouldReturn` Coin 0 + expectNotRegisteredRewardAddress rewardAccount + +spec :: + ShelleyEraImp era => + SpecWith (ImpInit (LedgerSpec era)) +spec = do + describe "Register stake credential" $ do + it "With correct deposit or without any deposit" $ do + cred <- KeyHashObj <$> freshKeyHash + -- NOTE: This will always generate certs with deposits post-Conway + regTxCert <- genRegTxCert cred + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert] + expectStakeCredRegistered cred + + it "When already already registered" $ do + cred <- ScriptHashObj <$> impAddNativeScript (RequireAllOf []) + regTxCert <- genRegTxCert cred + let tx = + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [regTxCert] + submitTx_ tx + submitFailingTx + tx + [ injectFailure $ StakeKeyAlreadyRegisteredDELEG cred + ] + expectStakeCredRegistered cred + + describe "Unregister stake credentials" $ do + it "When registered" $ do + cred <- ScriptHashObj <$> impAddNativeScript (RequireAllOf []) + regTxCert <- genRegTxCert cred + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert] + expectStakeCredRegistered cred + + unRegTxCert <- genUnRegTxCert cred + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [unRegTxCert] + expectStakeCredNotRegistered cred + + it "When not registered" $ do + freshKeyHash >>= \kh -> do + unRegTxCert <- genUnRegTxCert (KeyHashObj kh) + submitFailingTx + ( mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [unRegTxCert] + ) + [injectFailure $ StakeKeyNotRegisteredDELEG (KeyHashObj kh)] + + -- https://github.com/IntersectMBO/formal-ledger-specifications/issues/917 + -- impacts `registerAndRetirePoolToMakeReward` + -- TODO: Re-enable after issue is resolved, by removing this override + disableInConformanceIt "With non-zero reward balance" $ do + cred <- KeyHashObj <$> freshKeyHash + regTxCert <- genRegTxCert cred + + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert] + + registerAndRetirePoolToMakeReward cred + + balance <- getBalance cred + unRegTxCert <- genUnRegTxCert cred + submitFailingTx + ( mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [unRegTxCert] + ) + [injectFailure $ StakeKeyNonZeroAccountBalanceDELEG balance] + expectStakeCredRegistered cred + + it "Register and unregister in the same transaction" $ do + freshKeyHash >>= \kh -> do + regTxCert <- genRegTxCert (KeyHashObj kh) + unRegTxCert <- genUnRegTxCert (KeyHashObj kh) + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert, unRegTxCert] + expectStakeCredNotRegistered (KeyHashObj kh) + + describe "Delegate stake" $ do + it "Delegate registered stake credentials to registered pool" $ do + cred <- KeyHashObj <$> freshKeyHash + regTxCert <- genRegTxCert cred + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert] + + poolKh <- freshKeyHash + registerPool poolKh + + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [delegStakeTxCert cred poolKh] + expectDelegatedToPool cred poolKh + + it "Register and delegate in the same transaction" $ do + poolKh <- freshKeyHash + registerPool poolKh + freshKeyHash >>= \kh -> do + regTxCert <- genRegTxCert (KeyHashObj kh) + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [regTxCert, delegStakeTxCert (KeyHashObj kh) poolKh] + expectDelegatedToPool (KeyHashObj kh) poolKh + + it "Delegate unregistered stake credentials" $ do + cred <- KeyHashObj <$> freshKeyHash + poolKh <- freshKeyHash + registerPool poolKh + pv <- getProtVer + submitFailingTx + ( mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [delegStakeTxCert cred poolKh] + ) + [ injectFailure $ + if pvMajor pv < natVersion @9 + then StakeDelegationImpossibleDELEG cred + else StakeKeyNotRegisteredDELEG cred + ] + expectStakeCredNotRegistered cred + + it "Delegate already delegated credentials" $ do + cred <- KeyHashObj <$> freshKeyHash + poolKh <- freshKeyHash + registerPool poolKh + regTxCert <- genRegTxCert cred + let delegTxCert = delegStakeTxCert cred poolKh + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [regTxCert, delegTxCert] + expectDelegatedToPool cred poolKh + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL .~ [delegTxCert] + expectDelegatedToPool cred poolKh + + poolKh1 <- freshKeyHash + registerPool poolKh1 + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [delegStakeTxCert cred poolKh1] + expectDelegatedToPool cred poolKh1 + + poolKh2 <- freshKeyHash + registerPool poolKh2 + poolKh3 <- freshKeyHash + registerPool poolKh3 + + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [ delegStakeTxCert cred poolKh2 + , delegStakeTxCert cred poolKh3 + ] + + expectDelegatedToPool cred poolKh3 + + it "Delegate and unregister" $ do + cred <- KeyHashObj <$> freshKeyHash + poolKh <- freshKeyHash + registerPool poolKh + regTxCert <- genRegTxCert cred + unRegTxCert <- genUnRegTxCert cred + submitTx_ $ + mkBasicTx mkBasicTxBody + & bodyTxL . certsTxBodyL + .~ [regTxCert, delegStakeTxCert cred poolKh, unRegTxCert] + expectStakeCredNotRegistered cred diff --git a/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/ImpTest.hs b/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/ImpTest.hs index 01eee27901b..f4492431511 100644 --- a/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/ImpTest.hs +++ b/eras/shelley/impl/testlib/Test/Cardano/Ledger/Shelley/ImpTest.hs @@ -71,6 +71,10 @@ module Test.Cardano.Ledger.Shelley.ImpTest ( delegateStake, registerRewardAccount, registerStakeCredential, + expectNotDelegatedToPool, + expectStakeCredRegistered, + expectStakeCredNotRegistered, + expectDelegatedToPool, getRewardAccountFor, getReward, lookupReward, @@ -125,6 +129,7 @@ module Test.Cardano.Ledger.Shelley.ImpTest ( impSatisfySignature, shelleyGenRegTxCert, shelleyGenUnRegTxCert, + shelleyDelegStakeTxCert, -- * Logging Doc, @@ -524,6 +529,8 @@ class genUnRegTxCert :: Credential 'Staking -> ImpTestM era (TxCert era) + delegStakeTxCert :: Credential 'Staking -> KeyHash 'StakePool -> TxCert era + impSatisfySignature :: KeyHash 'Witness -> Set.Set (KeyHash 'Witness) -> @@ -819,6 +826,7 @@ instance modifyImpInitProtVer = shelleyModifyImpInitProtVer genRegTxCert = shelleyGenRegTxCert genUnRegTxCert = shelleyGenUnRegTxCert + delegStakeTxCert = shelleyDelegStakeTxCert -- | Figure out all the Byron Addresses that need witnesses as well as all of the -- KeyHashes for Shelley Key witnesses that are required. @@ -1594,16 +1602,55 @@ registerStakeCredential cred = do pure $ RewardAccount networkId cred delegateStake :: - (ShelleyEraImp era, ShelleyEraTxCert era) => + ShelleyEraImp era => Credential 'Staking -> KeyHash 'StakePool -> ImpTestM era () delegateStake cred poolKH = do submitTxAnn_ ("Delegate Staking Credential: " <> T.unpack (credToText cred)) $ mkBasicTx mkBasicTxBody - & bodyTxL . certsTxBodyL - .~ SSeq.fromList - [DelegStakeTxCert cred poolKH] + & bodyTxL . certsTxBodyL .~ [delegStakeTxCert cred poolKH] + +expectStakeCredRegistered :: + (HasCallStack, ShelleyEraImp era) => + Credential 'Staking -> + ImpTestM era () +expectStakeCredRegistered cred = do + accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL + expectedDeposit <- getsNES $ nesEsL . curPParamsEpochStateL . ppKeyDepositL + accountState <- expectJust $ lookupAccountState cred accounts + impAnn (show cred <> " expected to be in Accounts with the correct deposit") $ do + accountState ^. depositAccountStateL `shouldBe` compactCoinOrError expectedDeposit + +expectStakeCredNotRegistered :: + (HasCallStack, ShelleyEraImp era) => + Credential 'Staking -> + ImpTestM era () +expectStakeCredNotRegistered cred = do + accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL + impAnn (show cred <> " expected to not be in Accounts") $ do + expectNothingExpr $ lookupAccountState cred accounts + +expectDelegatedToPool :: + (HasCallStack, ShelleyEraImp era) => + Credential 'Staking -> + KeyHash 'StakePool -> + ImpTestM era () +expectDelegatedToPool cred poolKh = do + accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL + impAnn (show cred <> " expected to have delegated to " <> show poolKh) $ do + accountState <- expectJust $ lookupAccountState cred accounts + accountState ^. stakePoolDelegationAccountStateL `shouldBe` Just poolKh + +expectNotDelegatedToPool :: + (HasCallStack, ShelleyEraImp era) => + Credential 'Staking -> + ImpTestM era () +expectNotDelegatedToPool cred = do + accounts <- getsNES $ nesEsL . esLStateL . lsCertStateL . certDStateL . accountsL + impAnn (show cred <> " expected to not have delegated to a stake pool") $ do + accountState <- expectJust $ lookupAccountState cred accounts + expectNothingExpr (accountState ^. stakePoolDelegationAccountStateL) registerRewardAccount :: forall era. @@ -1865,15 +1912,20 @@ simulateThenRestore sim = do pure result shelleyGenRegTxCert :: - forall era. ShelleyEraTxCert era => Credential 'Staking -> ImpTestM era (TxCert era) shelleyGenRegTxCert = pure . RegTxCert shelleyGenUnRegTxCert :: - forall era. ShelleyEraTxCert era => Credential 'Staking -> ImpTestM era (TxCert era) shelleyGenUnRegTxCert = pure . UnRegTxCert + +shelleyDelegStakeTxCert :: + ShelleyEraTxCert era => + Credential 'Staking -> + KeyHash 'StakePool -> + TxCert era +shelleyDelegStakeTxCert cred pool = DelegStakeTxCert cred pool