Skip to content

Commit

Permalink
[#446] extend drep/list endpoint
Browse files Browse the repository at this point in the history
drep/list endpoint now returns 2 additional fields (status and type), it returns retired dreps and it allows user to search for given drep by drep name
  • Loading branch information
jankun4 committed Mar 11, 2024
1 parent 2c6e008 commit e925eab
Show file tree
Hide file tree
Showing 7 changed files with 173 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ changes.
- Fixed CSP settings to allow error reports with Sentry [Issue 291](https://github.com/IntersectMBO/govtool/issues/291).

### Changed
- `drep/list` now return also `status` and `type` fields. Also it now returns the retired dreps, and you can search for given drep by name using optional query parameter. If the drep name is passed exactly, then you can even find a drep that's sole voter. [Issue 446](https://github.com/IntersectMBO/govtool/issues/446)
- `drep/list` and `drep/info` endpoints now return additional data such as metadata url and hash, and voting power [Issue 223](https://github.com/IntersectMBO/govtool/issues/223)
- `drep/info` now does not return sole voters (dreps without metadata) [Issue 317](https://github.com/IntersectMBO/govtool/issues/317)
- `isRegistered` and `wasRegistered` fields in the drep/info endpoint changed to `isRegisteredAsDRep` and `wasRegisteredAsDRep` respectively [Issue 212](https://github.com/IntersectMBO/govtool/issues/212)
Expand Down
45 changes: 40 additions & 5 deletions govtool/backend/sql/list-dreps.sql
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,64 @@ WITH DRepDistr AS (
FROM drep_distr
JOIN drep_hash
on drep_hash.id = drep_distr.hash_id
), DRepActivity AS (
select
drep_activity as drep_activity,
epoch_no as epoch_no
from epoch_param
where epoch_no is not null
order by epoch_no desc
limit 1
)

SELECT
encode(dh.raw, 'hex'),
dh.view,
va.url,
encode(va.data_hash, 'hex'),
dr_deposit.deposit,
DRepDistr.amount
DRepDistr.amount,
(DRepActivity.epoch_no - Max(coalesce(block.epoch_no,block_first_register.epoch_no))) <= DRepActivity.drep_activity as active,
second_to_newest_drep_registration.voting_anchor_id is not null as has_voting_anchor
FROM drep_hash dh
JOIN (
SELECT dr.id, dr.drep_hash_id, dr.deposit,
ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.id DESC) AS rn
ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn
FROM drep_registration dr
where dr.deposit > 0
where dr.deposit is not null
) as dr_deposit
on dr_deposit.drep_hash_id = dh.id and dr_deposit.rn = 1
LEFT JOIN (
SELECT dr.id, dr.drep_hash_id, dr.voting_anchor_id,
ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.id DESC) AS rn
ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn
FROM drep_registration dr
) as dr_voting_anchor
on dr_voting_anchor.drep_hash_id = dh.id and dr_voting_anchor.rn = 1
LEFT JOIN (
SELECT dr.id, dr.drep_hash_id, dr.voting_anchor_id,
ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id DESC) AS rn
FROM drep_registration dr
) as second_to_newest_drep_registration
on second_to_newest_drep_registration.drep_hash_id = dh.id and second_to_newest_drep_registration.rn = 2
LEFT JOIN DRepDistr
on DRepDistr.hash_id = dh.id and DRepDistr.rn = 1
JOIN voting_anchor va ON va.id = dr_voting_anchor.voting_anchor_id
LEFT JOIN voting_anchor va ON va.id = dr_voting_anchor.voting_anchor_id
CROSS JOIN DRepActivity
LEFT JOIN voting_procedure as voting_procedure
on voting_procedure.drep_voter = dh.id
LEFT JOIN tx as tx
on tx.id = voting_procedure.tx_id
LEFT JOIN block as block
on block.id = tx.block_id
JOIN (
SELECT dr.tx_id, dr.drep_hash_id,
ROW_NUMBER() OVER(PARTITION BY dr.drep_hash_id ORDER BY dr.tx_id ASC) AS rn
FROM drep_registration dr
) as dr_first_register
on dr_first_register.drep_hash_id = dh.id and dr_first_register.rn = 1
JOIN tx as tx_first_register
on tx_first_register.id = dr_first_register.tx_id
JOIN block as block_first_register
ON block_first_register.id = tx_first_register.block_id

GROUP BY dh.raw, second_to_newest_drep_registration.voting_anchor_id, dh.view, va.url, va.data_hash, dr_deposit.deposit, DRepDistr.amount, DRepActivity.epoch_no, DRepActivity.drep_activity
39 changes: 34 additions & 5 deletions govtool/backend/src/VVA/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import VVA.Network as Network
import Numeric.Natural (Natural)

type VVAApi =
"drep" :> "list" :> Get '[JSON] [DRep]
"drep" :> "list" :> QueryParam "drepView" Text :> Get '[JSON] [DRep]
:<|> "drep" :> "get-voting-power" :> Capture "drepId" HexText :> Get '[JSON] Integer
:<|> "drep" :> "getVotes" :> Capture "drepId" HexText :> QueryParams "type" GovernanceActionType :> QueryParam "sort" GovernanceActionSortMode :> Get '[JSON] [VoteResponse]
:<|> "drep" :> "info" :> Capture "drepId" HexText :> Get '[JSON] DRepInfoResponse
Expand Down Expand Up @@ -68,11 +68,40 @@ server = drepList
:<|> throw500
:<|> getNetworkMetrics

drepList :: App m => m [DRep]
drepList = do

mapDRepType :: Types.DRepType -> DRepType
mapDRepType Types.DRep = NormalDRep
mapDRepType Types.SoleVoter = SoleVoter

mapDRepStatus :: Types.DRepStatus -> DRepStatus
mapDRepStatus Types.Retired = Retired
mapDRepStatus Types.Active = Active
mapDRepStatus Types.Inactive = Inactive

drepRegistrationToDrep :: Types.DRepRegistration -> DRep
drepRegistrationToDrep Types.DRepRegistration {..} =
DRep
{ dRepDrepId = DRepHash dRepRegistrationDRepHash,
dRepView = dRepRegistrationView,
dRepUrl = dRepRegistrationUrl,
dRepMetadataHash = dRepRegistrationDataHash,
dRepDeposit = dRepRegistrationDeposit,
dRepVotingPower = dRepRegistrationVotingPower,
dRepStatus = mapDRepStatus dRepRegistrationStatus,
dRepType = mapDRepType dRepRegistrationType
}

drepList :: App m => Maybe Text -> m [DRep]
drepList mDRepView = do
CacheEnv {dRepListCache} <- asks vvaCache
map (\(Types.DRepRegistration drep_hash url data_hash deposit votingPower) -> DRep (DRepHash drep_hash) url data_hash deposit votingPower)
<$> cacheRequest dRepListCache () DRep.listDReps
dreps <- cacheRequest dRepListCache () DRep.listDReps
let filtered = flip filter dreps $ \Types.DRepRegistration {..} ->
case (dRepRegistrationType, mDRepView) of
(Types.SoleVoter, Just x) -> x == dRepRegistrationView
(Types.DRep, Just x) -> isInfixOf x dRepRegistrationView
(Types.DRep, Nothing) -> True
_ -> False
return $ map drepRegistrationToDrep filtered

getVotingPower :: App m => HexText -> m Integer
getVotingPower (unHexText -> dRepId) = do
Expand Down
77 changes: 71 additions & 6 deletions govtool/backend/src/VVA/API/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -511,14 +511,66 @@ instance ToSchema DRepHash where
?~ toJSON exampleDrepHash


data DRepStatus = Retired | Active | Inactive
deriving (Generic, Show)

-- ToJSON instance for DRepStatus
instance ToJSON DRepStatus where
toJSON Retired = "Retired"
toJSON Active = "Active"
toJSON Inactive = "Inactive"

-- FromJSON instance for DRepStatus
instance FromJSON DRepStatus where
parseJSON = withText "DRepStatus" $ \case
"Retired" -> pure Retired
"Active" -> pure Active
"Inactive" -> pure Inactive
_ -> fail "Invalid DRepStatus"

-- ToSchema instance for DRepStatus
instance ToSchema DRepStatus where
declareNamedSchema _ = pure $ NamedSchema (Just "DRepStatus") $ mempty
& type_ ?~ OpenApiString
& description ?~ "DRep Status"
& enum_ ?~ map toJSON [Retired, Active, Inactive]



data DRepType = NormalDRep | SoleVoter

instance Show DRepType where
show NormalDRep = "DRep"
show SoleVoter = "SoleVoter"

-- ToJSON instance for DRepType
instance ToJSON DRepType where
toJSON NormalDRep = "DRep"
toJSON SoleVoter = "SoleVoter"

-- FromJSON instance for DRepType
instance FromJSON DRepType where
parseJSON = withText "DRepType" $ \case
"DRep" -> pure NormalDRep
"SoleVoter" -> pure SoleVoter
_ -> fail "Invalid DRepType"

-- ToSchema instance for DRepType
instance ToSchema DRepType where
declareNamedSchema _ = pure $ NamedSchema (Just "DRepType") $ mempty
& type_ ?~ OpenApiString
& description ?~ "DRep Type"
& enum_ ?~ map toJSON [NormalDRep, SoleVoter]

data DRep = DRep
{ dRepDrepId :: DRepHash
, dRepView :: Text
, dRepUrl :: Maybe Text
, dRepMetadataHash :: Maybe Text
, dRepDeposit :: Integer
, dRepVotingPower :: Maybe Integer
, dRepStatus :: DRepStatus
, dRepType :: DRepType
} deriving (Generic, Show)


Expand All @@ -527,17 +579,30 @@ deriveJSON (jsonOptions "dRep") ''DRep
exampleDrep :: Text
exampleDrep =
"{\"drepId\": \"d3a62ffe9c214e1a6a9809f7ab2a104c117f85e1f171f8f839d94be5\","
<> "\"view\": \"drep1l8uyy66sm8u82h82gc8hkcy2xu24dl8ffsh58aa0v7d37yp48u8\","
<> "\"url\": \"https://proposal.metadata.xyz\","
<> "\"metadataHash\": \"9af10e89979e51b8cdc827c963124a1ef4920d1253eef34a1d5cfe76438e3f11\","
<> "\"deposit\": 0,"
<> "\"votingPower\": 0}"
<> "\"votingPower\": 0,"
<> "\"status\": \"Active\","
<> "\"type\": \"DRep\"}"

-- ToSchema instance for DRep
instance ToSchema DRep where
declareNamedSchema _ = pure $ NamedSchema (Just "DRep") $ mempty
& type_ ?~ OpenApiObject
& description ?~ "DRep"
& example
?~ toJSON exampleDrep
declareNamedSchema proxy = do
NamedSchema name_ schema_ <-
genericDeclareNamedSchema
( fromAesonOptions $ jsonOptions "dRep" )
proxy
return $
NamedSchema name_ $
schema_
& description ?~ "DRep"
& example
?~ toJSON exampleDrep




data GetNetworkMetricsResponse = GetNetworkMetricsResponse
{ getNetworkMetricsResponseCurrentTime :: UTCTime
Expand Down
14 changes: 13 additions & 1 deletion govtool/backend/src/VVA/DRep.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ import VVA.Types
, Proposal(..)
, Vote(..)
, DRepInfo(..)
, DRepType(..)
, DRepStatus(..)
)


Expand Down Expand Up @@ -60,7 +62,17 @@ listDReps ::
m [DRepRegistration]
listDReps = withPool $ \conn -> do
results <- liftIO $ SQL.query_ conn listDRepsSql
return [DRepRegistration drepHash url dataHash (floor @Scientific deposit) votingPower | (drepHash, url, dataHash, deposit, votingPower) <- results]
return
[ DRepRegistration drepHash drepView url dataHash (floor @Scientific deposit) votingPower status drepType
| (drepHash, drepView, url, dataHash, deposit, votingPower, isActive, wasDRep) <- results
, let status = case (isActive, deposit) of
(_, d) | d < 0 -> Retired
(isActive, d) | d >= 0 && isActive -> Active
| d >= 0 && not isActive -> Inactive
, let drepType | url == Nothing && wasDRep = DRep
| url == Nothing && not wasDRep = SoleVoter
| url /= Nothing = DRep
]

getVotesSql :: SQL.Query
getVotesSql = sqlFrom $(embedFile "sql/get-votes.sql")
Expand Down
14 changes: 13 additions & 1 deletion govtool/backend/src/VVA/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,24 @@ data DRepInfo = DRepInfo
, dRepInfoVotingPower :: Maybe Integer
}

data DRepStatus
= Retired
| Active
| Inactive

data DRepType
= DRep
| SoleVoter

data DRepRegistration = DRepRegistration
{ dRepRegistrationDrepHash :: Text
{ dRepRegistrationDRepHash :: Text
, dRepRegistrationView :: Text
, dRepRegistrationUrl :: Maybe Text
, dRepRegistrationDataHash :: Maybe Text
, dRepRegistrationDeposit :: Integer
, dRepRegistrationVotingPower :: Maybe Integer
, dRepRegistrationStatus :: DRepStatus
, dRepRegistrationType :: DRepType
}

data Proposal = Proposal
Expand Down
2 changes: 1 addition & 1 deletion govtool/status-service/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
GRAFANA_PASSWORD = environ['GRAFANA_PASSWORD']

alert_health_mapping = {
'inactive': 'healthy',
'Inactive': 'healthy',
'pending': 'warning',
'firing': 'not healthy'
}
Expand Down

0 comments on commit e925eab

Please sign in to comment.