Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Change ApplyBlock interface #4889

Merged
merged 1 commit into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions eras/shelley/impl/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## 1.16.0.0

* Remove redundant supercalss constraints for `ApplyBlock`
* Add `applyBlockEither`, `applyBlockEitherNoEvents`, `applyBlockNoValidaton`, `applyTickNoEvents`.
* Add `applyBlock` and `applyTick` to `ApplyBlock` type class.
* Remove `applyBlockOpts` (in favor of `applyBlockEither`), `reapplyBlock` (in favor of
`applyBlockNoValidation`) and `applyTickOpts` (in favor `applyTick`).
* Disable validation level for `applyTick`
* Add `DecCBOR` instances for:
* `ShelleyTxWits`
* `ShelleyTxAuxData`
Expand Down
2 changes: 1 addition & 1 deletion eras/shelley/impl/cardano-ledger-shelley.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ library
nothunks,
quiet,
set-algebra >=1.0,
small-steps >=1.1,
small-steps >=1.1.1,
text,
time,
transformers,
Expand Down
225 changes: 114 additions & 111 deletions eras/shelley/impl/src/Cardano/Ledger/Shelley/API/Validation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE UndecidableSuperClasses #-}

-- | Interface to the block validation and chain extension logic in the Shelley
-- API.
module Cardano.Ledger.Shelley.API.Validation (
ApplyBlock (..),
applyBlock,
applyTick,
applyBlockEither,
applyBlockEitherNoEvents,
applyBlockNoValidaton,
applyTickNoEvents,
TickTransitionError (..),
BlockTransitionError (..),
chainChecks,
Expand All @@ -26,7 +29,6 @@ where

import Cardano.Ledger.BHeaderView (BHeaderView)
import Cardano.Ledger.BaseTypes (Globals (..), ShelleyBase, Version)
import Cardano.Ledger.Binary (EncCBORGroup)
import Cardano.Ledger.Block (Block)
import qualified Cardano.Ledger.Chain as STS
import Cardano.Ledger.Core
Expand All @@ -38,11 +40,10 @@ import Cardano.Ledger.Shelley.PParams ()
import Cardano.Ledger.Shelley.Rules ()
import qualified Cardano.Ledger.Shelley.Rules as STS
import Cardano.Ledger.Slot (SlotNo)
import Control.Arrow (left, right)
import Control.Monad.Except
import Control.Monad.Trans.Reader (runReader)
import Control.State.Transition.Extended
import Data.List.NonEmpty (NonEmpty)
import Data.List.NonEmpty (NonEmpty (..))
import GHC.Generics (Generic)
import Lens.Micro ((^.))
import NoThunks.Class (NoThunks (..))
Expand All @@ -51,133 +52,135 @@ import NoThunks.Class (NoThunks (..))
Block validation API
-------------------------------------------------------------------------------}

class
( STS (EraRule "TICK" era)
, BaseM (EraRule "TICK" era) ~ ShelleyBase
, Environment (EraRule "TICK" era) ~ ()
, State (EraRule "TICK" era) ~ NewEpochState era
, Signal (EraRule "TICK" era) ~ SlotNo
, STS (EraRule "BBODY" era)
, BaseM (EraRule "BBODY" era) ~ ShelleyBase
, Environment (EraRule "BBODY" era) ~ STS.BbodyEnv era
, State (EraRule "BBODY" era) ~ STS.ShelleyBbodyState era
, Signal (EraRule "BBODY" era) ~ Block BHeaderView era
, EncCBORGroup (TxSeq era)
, State (EraRule "LEDGERS" era) ~ LedgerState era
) =>
ApplyBlock era
where
-- | Apply the header level ledger transition.
--
-- This handles checks and updates that happen on a slot tick, as well as a
-- few header level checks, such as size constraints.
applyTickOpts ::
ApplySTSOpts ep ->
Globals ->
NewEpochState era ->
SlotNo ->
EventReturnType ep (EraRule "TICK" era) (NewEpochState era)
applyTickOpts opts globals state hdr =
either err id
. flip runReader globals
. applySTSOptsEither @(EraRule "TICK" era) opts
$ TRC ((), state, hdr)
where
err :: Show a => a -> b
err msg = error $ "Panic! applyTick failed: " <> show msg

-- | Apply the block level ledger transition.
applyBlockOpts ::
forall ep m.
(EventReturnTypeRep ep, MonadError (BlockTransitionError era) m) =>
ApplySTSOpts ep ->
class (EraGov era, EraSegWits era) => ApplyBlock era where
-- | Run the `BBODY` rule with `globalAssertionPolicy`. This function always succeeds, but
-- whenever validation is turned on it is necessary to check for presence of predicate failures
-- before a call can be marked successful. Therefore it is recommended to call `applyBlockEither`
-- instead.
applyBlock ::
SingEP ep ->
ValidationPolicy ->
Globals ->
NewEpochState era ->
Block BHeaderView era ->
m (EventReturnType ep (EraRule "BBODY" era) (NewEpochState era))
default applyBlockOpts ::
forall ep m.
(EventReturnTypeRep ep, MonadError (BlockTransitionError era) m, EraGov era) =>
ApplySTSOpts ep ->
(NewEpochState era, [PredicateFailure (EraRule "BBODY" era)], [Event (EraRule "BBODY" era)])
default applyBlock ::
( STS (EraRule "BBODY" era)
, BaseM (EraRule "BBODY" era) ~ ShelleyBase
, Environment (EraRule "BBODY" era) ~ STS.BbodyEnv era
, State (EraRule "BBODY" era) ~ STS.ShelleyBbodyState era
, Signal (EraRule "BBODY" era) ~ Block BHeaderView era
, State (EraRule "LEDGERS" era) ~ LedgerState era
) =>
SingEP ep ->
ValidationPolicy ->
Globals ->
NewEpochState era ->
Block BHeaderView era ->
m (EventReturnType ep (EraRule "BBODY" era) (NewEpochState era))
applyBlockOpts opts globals state blk =
liftEither
. left BlockTransitionError
. right
( mapEventReturn @ep @(EraRule "BBODY" era) $
updateNewEpochState state
)
$ res
(NewEpochState era, [PredicateFailure (EraRule "BBODY" era)], [Event (EraRule "BBODY" era)])
applyBlock eventsPolicy validationPolicy globals newEpochState block =
(updateNewEpochState newEpochState stsResultState, stsResultFailures, stsResultEvents)
where
res =
flip runReader globals
. applySTSOptsEither @(EraRule "BBODY" era)
opts
$ TRC (mkBbodyEnv state, bbs, blk)
bbs =
opts =
ApplySTSOpts
{ asoAssertions = globalAssertionPolicy
, asoValidation = validationPolicy
, asoEvents = eventsPolicy
}
STSResult {stsResultState, stsResultFailures, stsResultEvents} =
flip runReader globals $
applySTSOptsResult @(EraRule "BBODY" era) opts $
TRC (mkBbodyEnv newEpochState, bBodyState, block)
bBodyState =
STS.BbodyState
(LedgerState.esLState $ LedgerState.nesEs state)
(LedgerState.nesBcur state)

-- | Re-apply a ledger block to the same state it has been applied to before.
--
-- This function does no validation of whether the block applies successfully;
-- the caller implicitly guarantees that they have previously called
-- 'applyBlockTransition' on the same block and that this was successful.
reapplyBlock ::
(LedgerState.esLState $ LedgerState.nesEs newEpochState)
(LedgerState.nesBcur newEpochState)

-- | Run the `TICK` rule with `globalAssertionPolicy` and without any validation, since it can't
-- fail anyways.
applyTick ::
SingEP ep ->
Globals ->
NewEpochState era ->
Block BHeaderView era ->
NewEpochState era
default reapplyBlock ::
EraGov era =>
SlotNo ->
(NewEpochState era, [Event (EraRule "TICK" era)])
default applyTick ::
( STS (EraRule "TICK" era)
, BaseM (EraRule "TICK" era) ~ ShelleyBase
, Environment (EraRule "TICK" era) ~ ()
, State (EraRule "TICK" era) ~ NewEpochState era
, Signal (EraRule "TICK" era) ~ SlotNo
) =>
SingEP ep ->
Globals ->
NewEpochState era ->
Block BHeaderView era ->
NewEpochState era
reapplyBlock globals state blk =
updateNewEpochState state res
SlotNo ->
(NewEpochState era, [Event (EraRule "TICK" era)])
applyTick eventsPolicy globals newEpochState slotNo = (stsResultState, stsResultEvents)
where
res =
flip runReader globals . reapplySTS @(EraRule "BBODY" era) $
TRC (mkBbodyEnv state, bbs, blk)
bbs =
STS.BbodyState
(LedgerState.esLState $ LedgerState.nesEs state)
(LedgerState.nesBcur state)
opts =
ApplySTSOpts
{ asoAssertions = globalAssertionPolicy
, asoValidation = ValidateNone
, asoEvents = eventsPolicy
}
STSResult {stsResultState, stsResultEvents} =
flip runReader globals . applySTSOptsResult @(EraRule "TICK" era) opts $
TRC ((), newEpochState, slotNo)

applyTick ::
-- | Same as `applyBlock`, except it produces a Left when there are failures present and `Right`
-- with result otherwise.
applyBlockEither ::
ApplyBlock era =>
SingEP ep ->
ValidationPolicy ->
Globals ->
NewEpochState era ->
SlotNo ->
NewEpochState era
applyTick =
applyTickOpts $
ApplySTSOpts
{ asoAssertions = globalAssertionPolicy
, asoValidation = ValidateAll
, asoEvents = EPDiscard
}
Block BHeaderView era ->
Either (BlockTransitionError era) (NewEpochState era, [Event (EraRule "BBODY" era)])
applyBlockEither eventsPolicy validationPolicy globals newEpochState block =
case failure of
[] -> Right (newEpochStateResult, events)
f : fs -> Left $ BlockTransitionError $ f :| fs
where
(newEpochStateResult, failure, events) =
applyBlock eventsPolicy validationPolicy globals newEpochState block

applyBlock ::
( ApplyBlock era
, MonadError (BlockTransitionError era) m
) =>
applyBlockEitherNoEvents ::
ApplyBlock era =>
ValidationPolicy ->
Globals ->
NewEpochState era ->
Block BHeaderView era ->
m (NewEpochState era)
applyBlock =
applyBlockOpts $
ApplySTSOpts
{ asoAssertions = globalAssertionPolicy
, asoValidation = ValidateAll
, asoEvents = EPDiscard
}
Either (BlockTransitionError era) (NewEpochState era)
applyBlockEitherNoEvents validationPolicy globals newEpochState block =
fst <$> applyBlockEither EPDiscard validationPolicy globals newEpochState block

-- | Re-apply a ledger block to the same state it has been applied to before.
--
-- This function does no validation of whether the block applies successfully;
-- the caller implicitly guarantees that they have previously called
-- 'applyBlockTransition' on the same block and that this was successful.
applyBlockNoValidaton ::
ApplyBlock era =>
Globals ->
NewEpochState era ->
Block BHeaderView era ->
NewEpochState era
applyBlockNoValidaton globals newEpochState block = newEpochStateResult
where
(newEpochStateResult, _failure, _events) =
applyBlock EPDiscard ValidateNone globals newEpochState block

-- | Same as `applyTick`, but do not retain any ledger events
applyTickNoEvents ::
ApplyBlock era =>
Globals ->
NewEpochState era ->
SlotNo ->
NewEpochState era
applyTickNoEvents globals newEpochState slotNo =
fst $ applyTick EPDiscard globals newEpochState slotNo

instance ApplyBlock ShelleyEra

Expand Down
31 changes: 14 additions & 17 deletions eras/shelley/test-suite/bench/BenchValidation.hs
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wno-orphans #-}
Expand Down Expand Up @@ -55,6 +50,7 @@ import Cardano.Protocol.TPraos.Rules.Tickn (TicknState (..))
import Cardano.Slotting.Slot (withOriginToMaybe)
import Control.DeepSeq (NFData (rnf))
import Control.Monad.Except ()
import Control.State.Transition
import qualified Data.Map.Strict as Map
import Data.Proxy
import Test.Cardano.Ledger.Shelley.ConcreteCryptoTypes (MockCrypto)
Expand Down Expand Up @@ -104,37 +100,38 @@ genValidateInput n = do
pure (ValidateInput testGlobals (chainNes chainstate) block)

benchValidate ::
forall era.
(API.ApplyBlock era, Era era) =>
(API.ApplyBlock era, Show (PredicateFailure (EraRule "BBODY" era))) =>
ValidateInput era ->
IO (NewEpochState era)
benchValidate (ValidateInput globals state (Block bh txs)) =
case API.applyBlock @era globals state (UnsafeUnserialisedBlock (makeHeaderView bh) txs) of
Right x -> pure x
Left x -> error (show x)
let block = UnsafeUnserialisedBlock (makeHeaderView bh) txs
in case API.applyBlockEitherNoEvents ValidateAll globals state block of
Right x -> pure x
Left x -> error (show x)

applyBlock ::
forall era.
( EraTxOut era
, API.ApplyBlock era
( API.ApplyBlock era
, NFData (StashedAVVMAddresses era)
, GovState era ~ ShelleyGovState era
, EraCertState era
, Show (PredicateFailure (EraRule "BBODY" era))
) =>
ValidateInput era ->
Int ->
Int
applyBlock (ValidateInput globals state (Block bh txs)) n =
case API.applyBlock @era globals state (UnsafeUnserialisedBlock (makeHeaderView bh) txs) of
Right x -> seq (rnf x) (n + 1)
Left x -> error (show x)
let block = UnsafeUnserialisedBlock (makeHeaderView bh) txs
in case API.applyBlockEitherNoEvents ValidateAll globals state block of
Right x -> seq (rnf x) (n + 1)
Left x -> error (show x)

benchreValidate ::
(API.ApplyBlock era, Era era) =>
API.ApplyBlock era =>
ValidateInput era ->
NewEpochState era
benchreValidate (ValidateInput globals state (Block bh txs)) =
API.reapplyBlock globals state (UnsafeUnserialisedBlock (makeHeaderView bh) txs)
API.applyBlockNoValidaton globals state (UnsafeUnserialisedBlock (makeHeaderView bh) txs)

-- ==============================================================

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ tickChainState
ChainDepState {csProtocol, csTickn} =
tickChainDepState testGlobals lv isNewEpoch cds
PrtclState ocertIssue evNonce candNonce = csProtocol
nes' = applyTick testGlobals chainNes slotNo
nes' = applyTickNoEvents testGlobals chainNes slotNo
in ChainState
{ chainNes = nes'
, chainOCertIssue = ocertIssue
Expand Down
Loading