{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
module Test.Cardano.Ledger.Shelley.Rules.IncrementalStake (
incrStakeComputationTest,
incrStakeComparisonTest,
stakeDistr,
aggregateUtxoCoinByCredential,
) where
import Test.Cardano.Ledger.Shelley.Rules.TestChain (
TestingLedger,
forAllChainTrace,
ledgerTraceFromBlock,
longTraceLen,
traceLen,
)
import Cardano.Ledger.Address (Addr (..))
import Cardano.Ledger.Coin
import Cardano.Ledger.Compactible (fromCompact)
import Cardano.Ledger.Core
import Cardano.Ledger.Credential (Credential (..), Ptr, StakeReference (StakeRefBase, StakeRefPtr))
import Cardano.Ledger.EpochBoundary (SnapShot (..), Stake (..))
import Cardano.Ledger.Shelley.Core
import Cardano.Ledger.Shelley.LedgerState (
CertState (..),
DState (..),
EpochState (..),
IncrementalStake (..),
LedgerState (..),
NewEpochState (..),
PState (..),
UTxOState (..),
credMap,
curPParamsEpochStateL,
incrementalStakeDistr,
ptrsMap,
)
import qualified Cardano.Ledger.UMap as UM
import Cardano.Ledger.UTxO (UTxO (..), coinBalance)
import Control.SetAlgebra (dom, eval, (▷), (◁))
import Data.Foldable (fold)
import Data.Map (Map)
import qualified Data.Map.Strict as Map
import Data.Proxy
import qualified Data.VMap as VMap
import Lens.Micro hiding (ix)
import Test.Cardano.Ledger.Shelley.Constants (defaultConstants)
import Test.Cardano.Ledger.Shelley.Generator.Core (GenEnv)
import Test.Cardano.Ledger.Shelley.Generator.EraGen (EraGen (..))
import Test.Cardano.Ledger.Shelley.Generator.ShelleyEraGen ()
import Test.Cardano.Ledger.Shelley.Rules.Chain (CHAIN, ChainState (..))
import Test.Cardano.Ledger.Shelley.Utils (
ChainProperty,
)
import Test.Cardano.Ledger.TerseTools (tersemapdiffs)
import Test.Control.State.Transition.Trace (
SourceSignalTarget (..),
sourceSignalTargets,
)
import qualified Test.Control.State.Transition.Trace.Generator.QuickCheck as QC
import Test.QuickCheck (
Property,
conjoin,
counterexample,
(===),
)
import Test.Tasty (TestTree)
import Test.Tasty.QuickCheck (testProperty)
incrStakeComputationTest ::
forall era ledger.
( EraGen era
, TestingLedger era ledger
, ChainProperty era
, QC.HasTrace (CHAIN era) (GenEnv era)
) =>
TestTree
incrStakeComputationTest :: forall era ledger.
(EraGen era, TestingLedger era ledger, ChainProperty era,
HasTrace (CHAIN era) (GenEnv era)) =>
TestTree
incrStakeComputationTest =
forall a. Testable a => TestName -> a -> TestTree
testProperty TestName
"incremental stake calc" forall a b. (a -> b) -> a -> b
$
forall era prop.
(Testable prop, EraGen era, HasTrace (CHAIN era) (GenEnv era),
EraGov era) =>
Word64 -> Constants -> (Trace (CHAIN era) -> prop) -> Property
forAllChainTrace @era Word64
longTraceLen Constants
defaultConstants forall a b. (a -> b) -> a -> b
$ \Trace (CHAIN era)
tr -> do
let ssts :: [SourceSignalTarget (CHAIN era)]
ssts = forall a. Trace a -> [SourceSignalTarget a]
sourceSignalTargets Trace (CHAIN era)
tr
forall prop. Testable prop => [prop] -> Property
conjoin forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat forall a b. (a -> b) -> a -> b
$
[
forall a b. (a -> b) -> [a] -> [b]
map (forall era ledger.
(EraSegWits era, ChainProperty era, TestingLedger era ledger) =>
SourceSignalTarget (CHAIN era) -> Property
incrStakeComp @era @ledger) [SourceSignalTarget (CHAIN era)]
ssts
]
incrStakeComp ::
forall era ledger.
(EraSegWits era, ChainProperty era, TestingLedger era ledger) =>
SourceSignalTarget (CHAIN era) ->
Property
incrStakeComp :: forall era ledger.
(EraSegWits era, ChainProperty era, TestingLedger era ledger) =>
SourceSignalTarget (CHAIN era) -> Property
incrStakeComp SourceSignalTarget {source :: forall a. SourceSignalTarget a -> State a
source = State (CHAIN era)
chainSt, signal :: forall a. SourceSignalTarget a -> Signal a
signal = Signal (CHAIN era)
block} =
forall prop. Testable prop => [prop] -> Property
conjoin forall a b. (a -> b) -> a -> b
$
forall a b. (a -> b) -> [a] -> [b]
map SourceSignalTarget ledger -> Property
checkIncrStakeComp forall a b. (a -> b) -> a -> b
$
forall a. Trace a -> [SourceSignalTarget a]
sourceSignalTargets Trace ledger
ledgerTr
where
(ChainState era
_, Trace ledger
ledgerTr) = forall era ledger.
(ChainProperty era, EraSegWits era, TestingLedger era ledger) =>
ChainState era
-> Block (BHeader MockCrypto) era -> (ChainState era, Trace ledger)
ledgerTraceFromBlock @era @ledger State (CHAIN era)
chainSt Signal (CHAIN era)
block
checkIncrStakeComp :: SourceSignalTarget ledger -> Property
checkIncrStakeComp :: SourceSignalTarget ledger -> Property
checkIncrStakeComp
SourceSignalTarget
{ source :: forall a. SourceSignalTarget a -> State a
source = LedgerState UTxOState {utxosUtxo :: forall era. UTxOState era -> UTxO era
utxosUtxo = UTxO era
u, utxosStakeDistr :: forall era. UTxOState era -> IncrementalStake
utxosStakeDistr = IncrementalStake
sd} CertState era
dp
, signal :: forall a. SourceSignalTarget a -> Signal a
signal = Signal ledger
tx
, target :: forall a. SourceSignalTarget a -> State a
target = LedgerState UTxOState {utxosUtxo :: forall era. UTxOState era -> UTxO era
utxosUtxo = UTxO era
u', utxosStakeDistr :: forall era. UTxOState era -> IncrementalStake
utxosStakeDistr = IncrementalStake
sd'} CertState era
dp'
} =
forall prop. Testable prop => TestName -> prop -> Property
counterexample
( forall a. Monoid a => [a] -> a
mconcat
( [ TestName
"\nDetails:\n"
, TestName
"\ntx\n"
, forall a. Show a => a -> TestName
show Signal ledger
tx
, TestName
"\nsize original utxo\n"
, forall a. Show a => a -> TestName
show (forall k a. Map k a -> Int
Map.size forall a b. (a -> b) -> a -> b
$ forall era. UTxO era -> Map TxIn (TxOut era)
unUTxO UTxO era
u)
, TestName
"\noriginal utxo\n"
, forall a. Show a => a -> TestName
show UTxO era
u
, TestName
"\noriginal sd\n"
, forall a. Show a => a -> TestName
show IncrementalStake
sd
, TestName
"\nfinal utxo\n"
, forall a. Show a => a -> TestName
show UTxO era
u'
, TestName
"\nfinal sd\n"
, forall a. Show a => a -> TestName
show IncrementalStake
sd'
, TestName
"\noriginal ptrs\n"
, forall a. Show a => a -> TestName
show Map Ptr (Credential 'Staking)
ptrs
, TestName
"\nfinal ptrs\n"
, forall a. Show a => a -> TestName
show Map Ptr (Credential 'Staking)
ptrs'
]
)
)
forall a b. (a -> b) -> a -> b
$ Coin
utxoBal forall a. (Eq a, Show a) => a -> a -> Property
=== forall a. Compactible a => CompactForm a -> a
fromCompact CompactForm Coin
incrStakeBal
where
utxoBal :: Coin
utxoBal = forall era. EraTxOut era => UTxO era -> Coin
coinBalance UTxO era
u'
incrStakeBal :: CompactForm Coin
incrStakeBal = forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold (IncrementalStake -> Map (Credential 'Staking) (CompactForm Coin)
credMap IncrementalStake
sd') forall a. Semigroup a => a -> a -> a
<> forall (t :: * -> *) m. (Foldable t, Monoid m) => t m -> m
fold (IncrementalStake -> Map Ptr (CompactForm Coin)
ptrMap IncrementalStake
sd')
ptrs :: Map Ptr (Credential 'Staking)
ptrs = forall era. DState era -> Map Ptr (Credential 'Staking)
ptrsMap forall a b. (a -> b) -> a -> b
$ forall era. CertState era -> DState era
certDState CertState era
dp
ptrs' :: Map Ptr (Credential 'Staking)
ptrs' = forall era. DState era -> Map Ptr (Credential 'Staking)
ptrsMap forall a b. (a -> b) -> a -> b
$ forall era. CertState era -> DState era
certDState CertState era
dp'
incrStakeComparisonTest ::
forall era.
( EraGen era
, QC.HasTrace (CHAIN era) (GenEnv era)
, EraGov era
) =>
Proxy era ->
TestTree
incrStakeComparisonTest :: forall era.
(EraGen era, HasTrace (CHAIN era) (GenEnv era), EraGov era) =>
Proxy era -> TestTree
incrStakeComparisonTest Proxy era
Proxy =
forall a. Testable a => TestName -> a -> TestTree
testProperty TestName
"Incremental stake distribution at epoch boundaries agrees" forall a b. (a -> b) -> a -> b
$
forall era prop.
(Testable prop, EraGen era, HasTrace (CHAIN era) (GenEnv era),
EraGov era) =>
Word64 -> Constants -> (Trace (CHAIN era) -> prop) -> Property
forAllChainTrace Word64
traceLen Constants
defaultConstants forall a b. (a -> b) -> a -> b
$ \Trace (CHAIN era)
tr ->
forall prop. Testable prop => [prop] -> Property
conjoin forall a b. (a -> b) -> a -> b
$
forall a b. (a -> b) -> [a] -> [b]
map (\(SourceSignalTarget State (CHAIN era)
_ State (CHAIN era)
target Signal (CHAIN era)
_) -> forall era.
(EraTxOut era, EraGov era) =>
EpochState era -> Property
checkIncrementalStake @era ((forall era. NewEpochState era -> EpochState era
nesEs forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall era. ChainState era -> NewEpochState era
chainNes) State (CHAIN era)
target)) forall a b. (a -> b) -> a -> b
$
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {a} {era}.
(State a ~ ChainState era) =>
SourceSignalTarget a -> Bool
sameEpoch) (forall a. Trace a -> [SourceSignalTarget a]
sourceSignalTargets Trace (CHAIN era)
tr)
where
sameEpoch :: SourceSignalTarget a -> Bool
sameEpoch SourceSignalTarget {State a
source :: State a
source :: forall a. SourceSignalTarget a -> State a
source, State a
target :: State a
target :: forall a. SourceSignalTarget a -> State a
target} = forall {era}. ChainState era -> EpochNo
epoch State a
source forall a. Eq a => a -> a -> Bool
== forall {era}. ChainState era -> EpochNo
epoch State a
target
epoch :: ChainState era -> EpochNo
epoch = forall era. NewEpochState era -> EpochNo
nesEL forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall era. ChainState era -> NewEpochState era
chainNes
checkIncrementalStake ::
forall era.
(EraTxOut era, EraGov era) =>
EpochState era ->
Property
checkIncrementalStake :: forall era.
(EraTxOut era, EraGov era) =>
EpochState era -> Property
checkIncrementalStake EpochState era
es =
let
(LedgerState (UTxOState UTxO era
utxo Coin
_ Coin
_ GovState era
_ IncrementalStake
incStake Coin
_) (CertState VState era
_vstate PState era
pstate DState era
dstate)) = forall era. EpochState era -> LedgerState era
esLState EpochState era
es
stake :: SnapShot
stake = forall era.
EraTxOut era =>
UTxO era -> DState era -> PState era -> SnapShot
stakeDistr @era UTxO era
utxo DState era
dstate PState era
pstate
istake :: SnapShot
istake = forall era.
EraPParams era =>
PParams era
-> IncrementalStake -> DState era -> PState era -> SnapShot
incrementalStakeDistr (EpochState era
es forall s a. s -> Getting a s a -> a
^. forall era. EraGov era => Lens' (EpochState era) (PParams era)
curPParamsEpochStateL) IncrementalStake
incStake DState era
dstate PState era
pstate
in
forall prop. Testable prop => TestName -> prop -> Property
counterexample
( TestName
"\nIncremental stake distribution does not match old style stake distribution"
forall a. [a] -> [a] -> [a]
++ TestName -> Stake -> Stake -> TestName
tersediffincremental TestName
"differences: Old vs Incremental" (SnapShot -> Stake
ssStake SnapShot
stake) (SnapShot -> Stake
ssStake SnapShot
istake)
)
(SnapShot
stake forall a. (Eq a, Show a) => a -> a -> Property
=== SnapShot
istake)
tersediffincremental :: String -> Stake -> Stake -> String
tersediffincremental :: TestName -> Stake -> Stake -> TestName
tersediffincremental TestName
message (Stake VMap VB VP (Credential 'Staking) (CompactForm Coin)
a) (Stake VMap VB VP (Credential 'Staking) (CompactForm Coin)
c) =
forall a b.
(Terse a, Terse b, Ord a, Eq b) =>
TestName -> Map a b -> Map a b -> TestName
tersemapdiffs (TestName
message forall a. [a] -> [a] -> [a]
++ TestName
" " forall a. [a] -> [a] -> [a]
++ TestName
"hashes") (VMap VB VP (Credential 'Staking) (CompactForm Coin)
-> Map (Credential 'Staking) Coin
mp VMap VB VP (Credential 'Staking) (CompactForm Coin)
a) (VMap VB VP (Credential 'Staking) (CompactForm Coin)
-> Map (Credential 'Staking) Coin
mp VMap VB VP (Credential 'Staking) (CompactForm Coin)
c)
where
mp :: VMap VB VP (Credential 'Staking) (CompactForm Coin)
-> Map (Credential 'Staking) Coin
mp = forall a b k. (a -> b) -> Map k a -> Map k b
Map.map forall a. Compactible a => CompactForm a -> a
fromCompact forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (kv :: * -> *) k (vv :: * -> *) v.
(Vector kv k, Vector vv v) =>
VMap kv vv k v -> Map k v
VMap.toMap
stakeDistr ::
forall era.
EraTxOut era =>
UTxO era ->
DState era ->
PState era ->
SnapShot
stakeDistr :: forall era.
EraTxOut era =>
UTxO era -> DState era -> PState era -> SnapShot
stakeDistr UTxO era
u DState era
ds PState era
ps =
Stake
-> VMap VB VB (Credential 'Staking) (KeyHash 'StakePool)
-> VMap VB VB (KeyHash 'StakePool) PoolParams
-> SnapShot
SnapShot
(VMap VB VP (Credential 'Staking) (CompactForm Coin) -> Stake
Stake forall a b. (a -> b) -> a -> b
$ forall (kv :: * -> *) k (vv :: * -> *) v.
(Vector kv k, Vector vv v) =>
Map k v -> VMap kv vv k v
VMap.fromMap (HasCallStack => Coin -> CompactForm Coin
UM.compactCoinOrError forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall s t. Embed s t => Exp t -> s
eval (forall k s (f :: * -> * -> *) v.
(Ord k, HasExp s (f k v)) =>
s -> Exp (Sett k ())
dom Map (Credential 'Staking) (KeyHash 'StakePool)
activeDelegs forall k s1 s2 (f :: * -> * -> *) v.
(Ord k, HasExp s1 (Sett k ()), HasExp s2 (f k v)) =>
s1 -> s2 -> Exp (f k v)
◁ Map (Credential 'Staking) Coin
stakeRelation)))
(forall (kv :: * -> *) k (vv :: * -> *) v.
(Vector kv k, Vector vv v) =>
Map k v -> VMap kv vv k v
VMap.fromMap Map (Credential 'Staking) (KeyHash 'StakePool)
delegs)
(forall (kv :: * -> *) k (vv :: * -> *) v.
(Vector kv k, Vector vv v) =>
Map k v -> VMap kv vv k v
VMap.fromMap Map (KeyHash 'StakePool) PoolParams
poolParams)
where
rewards' :: Map.Map (Credential 'Staking) Coin
rewards' :: Map (Credential 'Staking) Coin
rewards' = UMap -> Map (Credential 'Staking) Coin
UM.rewardMap (forall era. DState era -> UMap
dsUnified DState era
ds)
delegs :: Map.Map (Credential 'Staking) (KeyHash 'StakePool)
delegs :: Map (Credential 'Staking) (KeyHash 'StakePool)
delegs = UMap -> Map (Credential 'Staking) (KeyHash 'StakePool)
UM.sPoolMap (forall era. DState era -> UMap
dsUnified DState era
ds)
ptrs' :: Map Ptr (Credential 'Staking)
ptrs' = forall era. DState era -> Map Ptr (Credential 'Staking)
ptrsMap DState era
ds
PState {psStakePoolParams :: forall era. PState era -> Map (KeyHash 'StakePool) PoolParams
psStakePoolParams = Map (KeyHash 'StakePool) PoolParams
poolParams} = PState era
ps
stakeRelation :: Map (Credential 'Staking) Coin
stakeRelation :: Map (Credential 'Staking) Coin
stakeRelation = forall era.
EraTxOut era =>
Map Ptr (Credential 'Staking)
-> UTxO era
-> Map (Credential 'Staking) Coin
-> Map (Credential 'Staking) Coin
aggregateUtxoCoinByCredential Map Ptr (Credential 'Staking)
ptrs' UTxO era
u Map (Credential 'Staking) Coin
rewards'
activeDelegs :: Map.Map (Credential 'Staking) (KeyHash 'StakePool)
activeDelegs :: Map (Credential 'Staking) (KeyHash 'StakePool)
activeDelegs = forall s t. Embed s t => Exp t -> s
eval ((forall k s (f :: * -> * -> *) v.
(Ord k, HasExp s (f k v)) =>
s -> Exp (Sett k ())
dom Map (Credential 'Staking) Coin
rewards' forall k s1 s2 (f :: * -> * -> *) v.
(Ord k, HasExp s1 (Sett k ()), HasExp s2 (f k v)) =>
s1 -> s2 -> Exp (f k v)
◁ Map (Credential 'Staking) (KeyHash 'StakePool)
delegs) forall k (g :: * -> * -> *) v s1 (f :: * -> * -> *) s2.
(Ord k, Iter g, Ord v, HasExp s1 (f k v), HasExp s2 (g v ())) =>
s1 -> s2 -> Exp (f k v)
▷ forall k s (f :: * -> * -> *) v.
(Ord k, HasExp s (f k v)) =>
s -> Exp (Sett k ())
dom Map (KeyHash 'StakePool) PoolParams
poolParams)
aggregateUtxoCoinByCredential ::
forall era.
EraTxOut era =>
Map Ptr (Credential 'Staking) ->
UTxO era ->
Map (Credential 'Staking) Coin ->
Map (Credential 'Staking) Coin
aggregateUtxoCoinByCredential :: forall era.
EraTxOut era =>
Map Ptr (Credential 'Staking)
-> UTxO era
-> Map (Credential 'Staking) Coin
-> Map (Credential 'Staking) Coin
aggregateUtxoCoinByCredential Map Ptr (Credential 'Staking)
ptrs (UTxO Map TxIn (TxOut era)
u) Map (Credential 'Staking) Coin
initial =
forall a b k. (a -> b -> a) -> a -> Map k b -> a
Map.foldl' Map (Credential 'Staking) Coin
-> TxOut era -> Map (Credential 'Staking) Coin
accum Map (Credential 'Staking) Coin
initial Map TxIn (TxOut era)
u
where
accum :: Map (Credential 'Staking) Coin
-> TxOut era -> Map (Credential 'Staking) Coin
accum Map (Credential 'Staking) Coin
ans TxOut era
out =
let c :: Coin
c = TxOut era
out forall s a. s -> Getting a s a -> a
^. forall era. (HasCallStack, EraTxOut era) => Lens' (TxOut era) Coin
coinTxOutL
in case TxOut era
out forall s a. s -> Getting a s a -> a
^. forall era. EraTxOut era => Lens' (TxOut era) Addr
addrTxOutL of
Addr Network
_ PaymentCredential
_ (StakeRefPtr Ptr
p)
| Just Credential 'Staking
cred <- forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Ptr
p Map Ptr (Credential 'Staking)
ptrs -> forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith forall a. Semigroup a => a -> a -> a
(<>) Credential 'Staking
cred Coin
c Map (Credential 'Staking) Coin
ans
Addr Network
_ PaymentCredential
_ (StakeRefBase Credential 'Staking
hk) -> forall k a. Ord k => (a -> a -> a) -> k -> a -> Map k a -> Map k a
Map.insertWith forall a. Semigroup a => a -> a -> a
(<>) Credential 'Staking
hk Coin
c Map (Credential 'Staking) Coin
ans
Addr
_other -> Map (Credential 'Staking) Coin
ans