Skip to content

Commit db5d390

Browse files
committed
db: Populate the Stake table
For each epoch, store '(stake-address, amount, pool-id)' where pool id is the pool to which the stake address delegated. Closes: #319
1 parent 1c51743 commit db5d390

File tree

5 files changed

+85
-38
lines changed

5 files changed

+85
-38
lines changed

cardano-db-sync/src/Cardano/DbSync/Era/Shelley/Insert.hs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import Shelley.Spec.Ledger.BaseTypes (StrictMaybe, strictMaybeToMaybe)
6060
import qualified Shelley.Spec.Ledger.BaseTypes as Shelley
6161
import qualified Shelley.Spec.Ledger.Coin as Shelley
6262
import qualified Shelley.Spec.Ledger.Credential as Shelley
63+
import qualified Shelley.Spec.Ledger.EpochBoundary as Shelley
6364
import qualified Shelley.Spec.Ledger.Keys as Shelley
6465
import qualified Shelley.Spec.Ledger.LedgerState as Shelley
6566
import qualified Shelley.Spec.Ledger.MetaData as Shelley
@@ -118,12 +119,11 @@ insertShelleyBlock tracer env blk lStateSnap details = do
118119
, ", hash ", renderByteArray (Shelley.blockHash blk)
119120
]
120121

121-
whenJust (lssRewardUpdate lStateSnap) $ \ rewards ->
122+
whenJust (lssEpochUpdate lStateSnap) $ \ esum -> do
122123
-- Subtract 2 from the epoch to calculate when the epoch in which the reward was earned.
123-
insertRewards tracer env blkId (sdEpochNo details - 2) rewards
124-
125-
whenJust (lssParamUpdate lStateSnap) $ \ params ->
126-
insertEpochParam tracer blkId (sdEpochNo details) params
124+
insertRewards tracer env blkId (sdEpochNo details - 2) (esRewardUpdate esum)
125+
insertEpochParam tracer blkId (sdEpochNo details) (esParamUpdate esum)
126+
insertEpochStake tracer env blkId (sdEpochNo details) (esStakeUpdate esum)
127127

128128
when (getSyncStatus details == SyncFollowing) $
129129
-- Serializiing things during syncing can drastically slow down full sync
@@ -496,7 +496,7 @@ insertParamProposal _tracer txId (Shelley.Update (Shelley.ProposedPPUpdates umap
496496
, DB.paramProposalMonetaryExpandRate = Shelley.unitIntervalToDouble <$> strictMaybeToMaybe (Shelley._rho pmap)
497497
, DB.paramProposalTreasuryGrowthRate = Shelley.unitIntervalToDouble <$> strictMaybeToMaybe (Shelley._tau pmap)
498498
, DB.paramProposalDecentralisation = Shelley.unitIntervalToDouble <$> strictMaybeToMaybe (Shelley._d pmap)
499-
, DB.paramProposalEntropy = join (Shelley.nonceToBytes <$> strictMaybeToMaybe (Shelley._extraEntropy pmap))
499+
, DB.paramProposalEntropy = Shelley.nonceToBytes =<< strictMaybeToMaybe (Shelley._extraEntropy pmap)
500500
, DB.paramProposalProtocolVersion = strictMaybeToMaybe (Shelley._protocolVersion pmap)
501501
, DB.paramProposalMinUtxoValue = fromIntegral . Shelley.unCoin <$> strictMaybeToMaybe (Shelley._minUTxOValue pmap)
502502
, DB.paramProposalMinPoolCost = fromIntegral . Shelley.unCoin <$> strictMaybeToMaybe (Shelley._minPoolCost pmap)
@@ -596,3 +596,28 @@ insertEpochParam _tracer blkId (EpochNo epoch) params =
596596
, DB.epochParamMinPoolCost = fromIntegral $ Shelley.unCoin (Shelley._minPoolCost params)
597597
, DB.epochParamBlockId = blkId
598598
}
599+
600+
insertEpochStake
601+
:: (MonadBaseControl IO m, MonadIO m)
602+
=> Trace IO Text -> DbSyncEnv -> DB.BlockId -> EpochNo -> Shelley.Stake StandardShelley
603+
-> ExceptT DbSyncNodeError (ReaderT SqlBackend m) ()
604+
insertEpochStake _tracer env blkId (EpochNo epoch) smap =
605+
mapM_ insert $ Map.toList (Shelley.unStake smap)
606+
where
607+
insert
608+
:: (MonadBaseControl IO m, MonadIO m)
609+
=> (Shelley.Credential 'Shelley.Staking StandardShelley, Shelley.Coin)
610+
-> ExceptT DbSyncNodeError (ReaderT SqlBackend m) ()
611+
insert (saddr, coin) = do
612+
(saId, poolId) <- firstExceptT (NELookup "insertEpochStake")
613+
. newExceptT
614+
$ queryStakeAddressAndPool epoch
615+
(Shelley.stakingCredHash env saddr)
616+
void . lift . DB.insertEpochStake $
617+
DB.EpochStake
618+
{ DB.epochStakeAddrId = saId
619+
, DB.epochStakePoolId = poolId
620+
, DB.epochStakeAmount = fromIntegral $ Shelley.unCoin coin
621+
, DB.epochStakeEpochNo = epoch + 1 -- The epoch where this delegation becomes valid.
622+
, DB.epochStakeBlockId = blkId
623+
}

cardano-db-sync/src/Cardano/DbSync/LedgerState.hs

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
module Cardano.DbSync.LedgerState
88
( CardanoLedgerState (..)
9+
, EpochUpdate (..)
910
, LedgerStateSnapshot (..)
1011
, LedgerStateVar (..)
1112
, applyBlock
@@ -29,7 +30,7 @@ import Cardano.Prelude
2930
import Cardano.Slotting.EpochInfo (EpochInfo, epochInfoEpoch)
3031
import Cardano.Slotting.Slot (EpochNo (..), SlotNo (..), fromWithOrigin)
3132

32-
import Control.Concurrent.STM.TVar (TVar, newTVarIO, writeTVar, readTVar)
33+
import Control.Concurrent.STM.TVar (TVar, newTVarIO, writeTVar, readTVar, readTVarIO)
3334
import Control.Exception (IOException, handle)
3435
import qualified Control.Exception as Exception
3536
import Control.Monad.Extra (firstJustM)
@@ -54,6 +55,7 @@ import Ouroboros.Consensus.Shelley.Protocol (StandardShelley)
5455
import Ouroboros.Consensus.Storage.Serialisation (DecodeDisk (..), EncodeDisk (..))
5556

5657
import qualified Shelley.Spec.Ledger.BaseTypes as Shelley
58+
import qualified Shelley.Spec.Ledger.EpochBoundary as Shelley
5759
import qualified Shelley.Spec.Ledger.LedgerState as Shelley
5860
import qualified Shelley.Spec.Ledger.PParams as Shelley
5961

@@ -66,6 +68,12 @@ data CardanoLedgerState = CardanoLedgerState
6668
, clsCodec :: !(CodecConfig CardanoBlock)
6769
}
6870

71+
data EpochUpdate = EpochUpdate
72+
{ esParamUpdate :: !(Shelley.PParams StandardShelley)
73+
, esRewardUpdate :: !(Shelley.RewardUpdate StandardShelley)
74+
, esStakeUpdate :: !(Shelley.Stake StandardShelley)
75+
}
76+
6977
newtype LedgerStateVar = LedgerStateVar
7078
{ unLedgerStateVar :: TVar CardanoLedgerState
7179
}
@@ -77,10 +85,10 @@ data LedgerStateFile = LedgerStateFile -- Internal use only.
7785

7886
data LedgerStateSnapshot = LedgerStateSnapshot
7987
{ lssState :: !CardanoLedgerState
80-
, lssRewardUpdate :: !(Maybe (Shelley.RewardUpdate StandardShelley))
81-
, lssParamUpdate :: !(Maybe (Shelley.PParams StandardShelley))
88+
, lssEpochUpdate :: !(Maybe EpochUpdate) -- Only Just for a single block at the epoch boundary
8289
}
8390

91+
8492
initLedgerStateVar :: GenesisConfig -> IO LedgerStateVar
8593
initLedgerStateVar genesisConfig = do
8694
LedgerStateVar <$>
@@ -108,19 +116,12 @@ applyBlock (LedgerStateVar stateVar) blk =
108116
then do
109117
let !newState = oldState { clsState = applyBlk (clsConfig oldState) blk (clsState oldState) }
110118
writeTVar stateVar newState
111-
let mRewards =
112-
case (ledgerRewardUpdate (clsState newState), ledgerRewardUpdate (clsState oldState)) of
113-
(Nothing, Just r) -> Just r
114-
_otherwise -> Nothing
115-
mParams =
116-
if ledgerEpochNo newState == ledgerEpochNo oldState + 1
117-
then ledgerEpochProtocolParams (clsState newState)
118-
else Nothing
119-
120119
pure $ LedgerStateSnapshot
121120
{ lssState = newState
122-
, lssRewardUpdate = mRewards
123-
, lssParamUpdate = mParams
121+
, lssEpochUpdate =
122+
if ledgerEpochNo newState == ledgerEpochNo oldState + 1
123+
then Just $ ledgerEpochUpdate (clsState newState) (ledgerRewardUpdate $ clsState oldState)
124+
else Nothing
124125
}
125126
else panic $ mconcat
126127
[ "applyBlock: Hash mismatch when applying block with slot no ", textShow (blockSlot blk), "\n"
@@ -179,7 +180,7 @@ cleanupLedgerStateFiles stateDir slotNo = do
179180
-- Remove invalid (ie SlotNo >= current) ledger state files (occurs on rollback).
180181
mapM_ safeRemoveFile invalid
181182
-- Remove all but 8 most recent state files.
182-
mapM_ safeRemoveFile $ map lsfFilePath (List.drop 8 valid)
183+
mapM_ (safeRemoveFile . lsfFilePath) (List.drop 8 valid)
183184
where
184185
-- Left files are deleted, Right files are kept.
185186
keepFile :: LedgerStateFile -> Either FilePath LedgerStateFile
@@ -246,8 +247,7 @@ listLedgerStateSlotNos :: LedgerStateDir -> IO [SlotNo]
246247
listLedgerStateSlotNos = fmap3 (SlotNo . lsfSlotNo) listLedgerStateFilesOrdered
247248

248249
readLedgerState :: LedgerStateVar -> IO CardanoLedgerState
249-
readLedgerState (LedgerStateVar stateVar) =
250-
atomically $ readTVar stateVar
250+
readLedgerState (LedgerStateVar stateVar) = readTVarIO stateVar
251251

252252
-- | Remove given file path and ignore any IOEXceptions.
253253
safeRemoveFile :: FilePath -> IO ()
@@ -262,15 +262,30 @@ ledgerEpochNo cls =
262262
epochInfo :: EpochInfo Identity
263263
epochInfo = epochInfoLedger (clsConfig cls) $ hardForkLedgerStatePerEra (clsState cls)
264264

265-
ledgerEpochProtocolParams :: LedgerState CardanoBlock -> Maybe (Shelley.PParams StandardShelley)
266-
ledgerEpochProtocolParams lsc =
267-
case lsc of
268-
LedgerStateByron _ -> Nothing
269-
LedgerStateShelley sls -> Just $ Shelley.esPp (Shelley.nesEs $ Consensus.shelleyLedgerState sls)
270-
265+
-- Create an EpochUpdate from the current epoch state and the rewards from the last epoch.
266+
ledgerEpochUpdate :: LedgerState CardanoBlock -> Maybe (Shelley.RewardUpdate StandardShelley) -> EpochUpdate
267+
ledgerEpochUpdate lcs mRewards =
268+
case lcs of
269+
LedgerStateByron _ -> panic "ledgerEpochUpdate: LedgerStateByron but should be Shelley"
270+
LedgerStateShelley sls ->
271+
EpochUpdate
272+
{ esParamUpdate = Shelley.esPp $ Shelley.nesEs (Consensus.shelleyLedgerState sls)
273+
, esRewardUpdate = fromMaybe Shelley.emptyRewardUpdate mRewards
274+
275+
-- Use '_pstakeSet' here instead of '_pstateMark' because the stake addresses for the
276+
-- later may not have been added to the database yet. That means that whne these values
277+
-- are added to the database, the epoch number where they become active is the current
278+
-- epoch plus one.
279+
, esStakeUpdate = Shelley._stake . Shelley._pstakeSet . Shelley.esSnapshots
280+
$ Shelley.nesEs (Consensus.shelleyLedgerState sls)
281+
}
282+
283+
-- This will return a 'Just' from the time the rewards are updated until the end of the
284+
-- epoch. It is 'Nothing' for the first block of a new epoch (which is slightly inconvenient).
271285
ledgerRewardUpdate :: LedgerState CardanoBlock -> Maybe (Shelley.RewardUpdate StandardShelley)
272286
ledgerRewardUpdate lsc =
273287
case lsc of
274-
LedgerStateByron _ -> Nothing
288+
LedgerStateByron _ -> Nothing -- This actually happens on the Byron/Shelley boundary.
275289
LedgerStateShelley sls -> Shelley.strictMaybeToMaybe . Shelley.nesRu
276290
$ Consensus.shelleyLedgerState sls
291+

cardano-db/src/Cardano/Db/Insert.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Cardano.Db.Insert
66
, insertDelegation
77
, insertEpoch
88
, insertEpochParam
9+
, insertEpochStake
910
, insertMeta
1011
, insertParamProposal
1112
, insertPoolHash
@@ -58,6 +59,9 @@ insertEpoch = insertByReturnKey "Epoch"
5859
insertEpochParam :: (MonadBaseControl IO m, MonadIO m) => EpochParam -> ReaderT SqlBackend m EpochParamId
5960
insertEpochParam = insertByReturnKey "EpochParam"
6061

62+
insertEpochStake :: (MonadBaseControl IO m, MonadIO m) => EpochStake -> ReaderT SqlBackend m EpochStakeId
63+
insertEpochStake = insertByReturnKey "EpochStake"
64+
6165
insertMeta :: (MonadBaseControl IO m, MonadIO m) => Meta -> ReaderT SqlBackend m MetaId
6266
insertMeta = insertByReturnKey "Meta"
6367

cardano-db/src/Cardano/Db/Schema.hs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,11 +263,13 @@ share
263263
blockId BlockId
264264
UniqueReward addrId blockId
265265

266-
Stake
266+
EpochStake
267267
addrId StakeAddressId
268-
txId TxId
269-
stake Word64 sqltype=lovelace
270-
UniqueStake addrId stake
268+
poolId PoolHashId
269+
amount Word64 sqltype=lovelace
270+
epochNo Word64
271+
blockId BlockId -- To make rollbacks work correctly.
272+
UniqueStake addrId epochNo
271273

272274
Treasury
273275
addrId StakeAddressId

schema/migration-2-0003-20201006.sql renamed to schema/migration-2-0003-20201007.sql

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,11 @@ BEGIN
7676
EXECUTE 'ALTER TABLE "reward" ADD CONSTRAINT "reward_addr_id_fkey" FOREIGN KEY("addr_id") REFERENCES "stake_address"("id")' ;
7777
EXECUTE 'ALTER TABLE "reward" ADD CONSTRAINT "reward_pool_id_fkey" FOREIGN KEY("pool_id") REFERENCES "pool_hash"("id")' ;
7878
EXECUTE 'ALTER TABLE "reward" ADD CONSTRAINT "reward_block_id_fkey" FOREIGN KEY("block_id") REFERENCES "block"("id")' ;
79-
EXECUTE 'CREATe TABLE "stake"("id" SERIAL8 PRIMARY KEY UNIQUE,"addr_id" INT8 NOT NULL,"tx_id" INT8 NOT NULL,"stake" lovelace NOT NULL)' ;
80-
EXECUTE 'ALTER TABLE "stake" ADD CONSTRAINT "unique_stake" UNIQUE("addr_id","stake")' ;
81-
EXECUTE 'ALTER TABLE "stake" ADD CONSTRAINT "stake_addr_id_fkey" FOREIGN KEY("addr_id") REFERENCES "stake_address"("id")' ;
82-
EXECUTE 'ALTER TABLE "stake" ADD CONSTRAINT "stake_tx_id_fkey" FOREIGN KEY("tx_id") REFERENCES "tx"("id")' ;
79+
EXECUTE 'CREATe TABLE "epoch_stake"("id" SERIAL8 PRIMARY KEY UNIQUE,"addr_id" INT8 NOT NULL,"pool_id" INT8 NOT NULL,"amount" lovelace NOT NULL,"epoch_no" INT8 NOT NULL,"block_id" INT8 NOT NULL)' ;
80+
EXECUTE 'ALTER TABLE "epoch_stake" ADD CONSTRAINT "unique_stake" UNIQUE("addr_id","epoch_no")' ;
81+
EXECUTE 'ALTER TABLE "epoch_stake" ADD CONSTRAINT "epoch_stake_addr_id_fkey" FOREIGN KEY("addr_id") REFERENCES "stake_address"("id")' ;
82+
EXECUTE 'ALTER TABLE "epoch_stake" ADD CONSTRAINT "epoch_stake_pool_id_fkey" FOREIGN KEY("pool_id") REFERENCES "pool_hash"("id")' ;
83+
EXECUTE 'ALTER TABLE "epoch_stake" ADD CONSTRAINT "epoch_stake_block_id_fkey" FOREIGN KEY("block_id") REFERENCES "block"("id")' ;
8384
EXECUTE 'CREATe TABLE "treasury"("id" SERIAL8 PRIMARY KEY UNIQUE,"addr_id" INT8 NOT NULL,"cert_index" INT4 NOT NULL,"amount" lovelace NOT NULL,"tx_id" INT8 NOT NULL)' ;
8485
EXECUTE 'ALTER TABLE "treasury" ADD CONSTRAINT "unique_treasury" UNIQUE("addr_id","tx_id")' ;
8586
EXECUTE 'ALTER TABLE "treasury" ADD CONSTRAINT "treasury_addr_id_fkey" FOREIGN KEY("addr_id") REFERENCES "stake_address"("id")' ;

0 commit comments

Comments
 (0)