Skip to content

Commit

Permalink
db: Add an OrphanedReward table
Browse files Browse the repository at this point in the history
This table contains rewards that could not be distributed to the relevant
stake address because the stake address had been de-registered after the
epoch started but before the rewards is distributed. Rewards listed in this
table go back to the reserves.

We need to keep both a 'Reward' and an 'OrphanedReward' table so that pool
profitiabilty can be correctly calculated.

Closes: #415
Closes: #467
  • Loading branch information
erikd committed Jan 7, 2021
1 parent eea3c8b commit 37b0a65
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 6 deletions.
34 changes: 30 additions & 4 deletions cardano-db-sync/src/Cardano/DbSync/Era/Shelley/Insert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,11 @@ insertShelleyBlock tracer env blk lStateSnap details = do
]

whenJust (lssEpochUpdate lStateSnap) $ \ esum -> do
whenJust (Generic.euRewards esum) $ \ rewards ->
whenJust (Generic.euRewards esum) $ \ rewards -> do
-- Subtract 2 from the epoch to calculate when the epoch in which the reward was earned.
insertRewards tracer blkId (sdEpochNo details - 2) rewards
insertRewards tracer blkId (sdEpochNo details - 2) (Generic.rewards rewards)
insertOrphanedRewards tracer blkId (sdEpochNo details - 2) (Generic.orphaned rewards)

insertEpochParam tracer blkId (sdEpochNo details) (Generic.euProtoParams esum) (Generic.euNonce esum)
insertEpochStake tracer blkId (sdEpochNo details) (Generic.euStakeDistribution esum)

Expand Down Expand Up @@ -609,10 +611,10 @@ containsUnicodeNul = Text.isInfixOf "\\u000"

insertRewards
:: (MonadBaseControl IO m, MonadIO m)
=> Trace IO Text -> DB.BlockId -> EpochNo -> Generic.Rewards
=> Trace IO Text -> DB.BlockId -> EpochNo -> Map Generic.StakeCred Coin
-> ExceptT DbSyncNodeError (ReaderT SqlBackend m) ()
insertRewards _tracer blkId epoch rewards =
mapM_ insertOneReward $ Map.toList (Generic.rewards rewards)
mapM_ insertOneReward $ Map.toList rewards
where
insertOneReward
:: (MonadBaseControl IO m, MonadIO m)
Expand All @@ -631,6 +633,30 @@ insertRewards _tracer blkId epoch rewards =
, DB.rewardBlockId = blkId
}

insertOrphanedRewards
:: (MonadBaseControl IO m, MonadIO m)
=> Trace IO Text -> DB.BlockId -> EpochNo -> Map Generic.StakeCred Coin
-> ExceptT DbSyncNodeError (ReaderT SqlBackend m) ()
insertOrphanedRewards _tracer blkId epoch orphanedRewards =
mapM_ insertOneOrphanedReward $ Map.toList orphanedRewards
where
insertOneOrphanedReward
:: (MonadBaseControl IO m, MonadIO m)
=> (Generic.StakeCred, Shelley.Coin)
-> ExceptT DbSyncNodeError (ReaderT SqlBackend m) ()
insertOneOrphanedReward (saddr, coin) = do
(saId, poolId) <- firstExceptT (NELookup "insertOrphanedReward")
. newExceptT
$ queryStakeAddressAndPool (unEpochNo epoch) (Generic.unStakeCred saddr)
void . lift . DB.insertOrphanedReward $
DB.OrphanedReward
{ DB.orphanedRewardAddrId = saId
, DB.orphanedRewardAmount = Generic.coinToDbLovelace coin
, DB.orphanedRewardEpochNo = unEpochNo epoch
, DB.orphanedRewardPoolId = poolId
, DB.orphanedRewardBlockId = blkId
}

insertEpochParam
:: (MonadBaseControl IO m, MonadIO m)
=> Trace IO Text -> DB.BlockId -> EpochNo -> Generic.ProtoParams -> Shelley.Nonce
Expand Down
8 changes: 6 additions & 2 deletions cardano-db/src/Cardano/Db/Insert.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ module Cardano.Db.Insert
, insertEpoch
, insertEpochParam
, insertEpochStake
, insertMeta
, insertMaTxMint
, insertMaTxOut
, insertMeta
, insertOrphanedReward
, insertParamProposal
, insertPoolHash
, insertPoolMetaData
Expand Down Expand Up @@ -40,7 +41,7 @@ import Control.Monad.IO.Class (MonadIO, liftIO)
import Control.Monad.Trans.Control (MonadBaseControl)
import Control.Monad.Trans.Reader (ReaderT)

import Database.Persist.Class (AtLeastOneUniqueKey, Key, PersistEntityBackend, getByValue,
import Database.Persist.Class (AtLeastOneUniqueKey, PersistEntityBackend, getByValue,
insert)
import Database.Persist.Sql (SqlBackend)
import Database.Persist.Types (entityKey)
Expand Down Expand Up @@ -73,6 +74,9 @@ insertMaTxOut = insertByReturnKey "insertMaTxOut"
insertMeta :: (MonadBaseControl IO m, MonadIO m) => Meta -> ReaderT SqlBackend m MetaId
insertMeta = insertByReturnKey "Meta"

insertOrphanedReward :: (MonadBaseControl IO m, MonadIO m) => OrphanedReward -> ReaderT SqlBackend m OrphanedRewardId
insertOrphanedReward = insertByReturnKey "OrphanedReward"

insertParamProposal :: (MonadBaseControl IO m, MonadIO m) => ParamProposal -> ReaderT SqlBackend m ParamProposalId
insertParamProposal = insertByReturnKey "ParamProposal"

Expand Down
10 changes: 10 additions & 0 deletions cardano-db/src/Cardano/Db/Schema.hs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,16 @@ share
blockId BlockId OnDeleteCascade
UniqueReward addrId blockId

-- Orphaned rewards happen when a stake address earns rewards, but the stake address is
-- deregistered before the rewards are distributed.
OrphanedReward
addrId StakeAddressId
amount DbLovelace sqltype=lovelace
epochNo Word64
poolId PoolHashId OnDeleteCascade
blockId BlockId OnDeleteCascade
UniqueOrphaned addrId blockId

EpochStake
addrId StakeAddressId OnDeleteCascade
poolId PoolHashId OnDeleteCascade
Expand Down
23 changes: 23 additions & 0 deletions schema/migration-2-0006-20210107.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- Persistent generated migration.

CREATE FUNCTION migrate() RETURNS void AS $$
DECLARE
next_version int ;
BEGIN
SELECT stage_two + 1 INTO next_version FROM schema_version ;
IF next_version = 6 THEN
EXECUTE 'CREATe TABLE "orphaned_reward"("id" SERIAL8 PRIMARY KEY UNIQUE,"addr_id" INT8 NOT NULL,"amount" lovelace NOT NULL,"epoch_no" INT8 NOT NULL,"pool_id" INT8 NOT NULL,"block_id" INT8 NOT NULL)' ;
EXECUTE 'ALTER TABLE "orphaned_reward" ADD CONSTRAINT "unique_orphaned" UNIQUE("addr_id","block_id")' ;
EXECUTE 'ALTER TABLE "orphaned_reward" ADD CONSTRAINT "orphaned_reward_addr_id_fkey" FOREIGN KEY("addr_id") REFERENCES "stake_address"("id") ON DELETE RESTRICT ON UPDATE RESTRICT' ;
EXECUTE 'ALTER TABLE "orphaned_reward" ADD CONSTRAINT "orphaned_reward_pool_id_fkey" FOREIGN KEY("pool_id") REFERENCES "pool_hash"("id") ON DELETE CASCADE ON UPDATE RESTRICT' ;
EXECUTE 'ALTER TABLE "orphaned_reward" ADD CONSTRAINT "orphaned_reward_block_id_fkey" FOREIGN KEY("block_id") REFERENCES "block"("id") ON DELETE CASCADE ON UPDATE RESTRICT' ;
-- Hand written SQL statements can be added here.
UPDATE schema_version SET stage_two = next_version ;
RAISE NOTICE 'DB has been migrated to stage_two version %', next_version ;
END IF ;
END ;
$$ LANGUAGE plpgsql ;

SELECT migrate() ;

DROP FUNCTION migrate() ;

0 comments on commit 37b0a65

Please sign in to comment.