Skip to content
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
24 changes: 20 additions & 4 deletions .github/workflows/code_check_backend.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ jobs:
- name: Use Haskell
uses: haskell-actions/setup@v2
with:
ghc-version: '9.2.7'
cabal-version: '3.6.0.0'
ghc-version: '9.2.8'
cabal-version: '3.8.1.0'
cabal-update: false

- name: Use Python
uses: actions/setup-python@v2
Expand All @@ -26,6 +27,13 @@ jobs:

- name: Install HLint
run: |
rm -rf ~/.cabal ~/.ghcup
mkdir -p ~/.cabal
cat <<EOF > ~/.cabal/config
repository hackage.haskell.org
url: http://hackage.haskell.org/
secure: False
EOF
cabal update
cabal install hlint

Expand All @@ -41,8 +49,9 @@ jobs:
- name: Use Haskell
uses: haskell-actions/setup@v2
with:
ghc-version: '9.2.7'
cabal-version: '3.6.0.0'
ghc-version: '9.2.8'
cabal-version: '3.8.1.0'
cabal-update: false

- name: Use Python
uses: actions/setup-python@v2
Expand All @@ -54,6 +63,13 @@ jobs:

- name: Install HLint
run: |
rm -rf ~/.cabal ~/.ghcup
mkdir -p ~/.cabal
cat <<EOF > ~/.cabal/config
repository hackage.haskell.org
url: http://hackage.haskell.org/
secure: False
EOF
cabal update
cabal install stylish-haskell

Expand Down
71 changes: 35 additions & 36 deletions govtool/backend/src/VVA/API.hs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE DataKinds #-}

module VVA.API where

Expand All @@ -13,48 +13,47 @@ import Control.Exception (throw, throwIO)
import Control.Monad.Except (runExceptT, throwError)
import Control.Monad.Reader

import Data.Aeson (Value(..), Array, decode, ToJSON, toJSON)
import Data.Aeson (Array, ToJSON, Value (..), decode, toJSON)
import Data.Bool (Bool)
import Data.List (sortOn, sort)
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BSL
import Data.List (sort, sortOn)
import qualified Data.Map as Map
import Data.Maybe (Maybe (Nothing), catMaybes, fromMaybe, mapMaybe)
import Data.Ord (Down (..))
import Data.Text hiding (any, drop, elem, filter, length, map, null, take)
import qualified Data.Text as Text
import qualified Data.Text.Lazy as TL
import qualified Data.Text.Lazy.Encoding as TL
import qualified Data.Vector as V
import Data.Time (TimeZone, localTimeToUTC)
import Data.Time.LocalTime (TimeZone, getCurrentTimeZone)

import qualified Data.Vector as V

import Numeric.Natural (Natural)

import Servant.API
import Servant.Server
import Servant.Exception (Throws)
import Servant.Server

import System.Random (randomRIO)

import Text.Read (readMaybe)

import VVA.Account as Account
import qualified VVA.AdaHolder as AdaHolder
import VVA.API.Types
import VVA.Cache (cacheRequest)
import VVA.Config
import qualified VVA.DRep as DRep
import qualified VVA.Epoch as Epoch
import qualified VVA.Ipfs as Ipfs
import VVA.Network as Network
import VVA.Account as Account
import qualified VVA.Proposal as Proposal
import qualified VVA.Transaction as Transaction
import qualified VVA.Types as Types
import VVA.Types (App, AppEnv (..),
AppError (CriticalError, InternalError, ValidationError, AppIpfsError),
AppError (AppIpfsError, CriticalError, InternalError, ValidationError),
CacheEnv (..))
import Data.Time (TimeZone, localTimeToUTC)
import qualified VVA.Ipfs as Ipfs
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as BSL
import Servant.Exception (Throws)

type VVAApi =
"ipfs"
Expand Down Expand Up @@ -127,7 +126,7 @@ upload mFileName fileContentText = do
throwError $ ValidationError "The uploaded file is larger than 500Kb"
eIpfsHash <- liftIO $ Ipfs.ipfsUpload vvaPinataJwt fileName fileContent
case eIpfsHash of
Left err -> throwError $ AppIpfsError err
Left err -> throwError $ AppIpfsError err
Right ipfsHash -> return $ UploadResponse ipfsHash

mapDRepType :: Types.DRepType -> DRepType
Expand Down Expand Up @@ -188,9 +187,9 @@ drepList mSearchQuery statuses mSortMode mPage mPageSize = do
viewLower = Text.toLower dRepRegistrationView
hashLower = Text.toLower dRepRegistrationDRepHash
in case dRepRegistrationType of
Types.SoleVoter ->
Types.SoleVoter ->
searchLower == viewLower || searchLower == hashLower
Types.DRep ->
Types.DRep ->
True


Expand Down Expand Up @@ -318,13 +317,13 @@ getVotes :: App m => HexText -> [GovernanceActionType] -> Maybe GovernanceAction
getVotes (unHexText -> dRepId) selectedTypes sortMode mSearch = do
CacheEnv {dRepGetVotesCache} <- asks vvaCache
(votes, proposals) <- cacheRequest dRepGetVotesCache dRepId $ DRep.getVotes dRepId []

let voteMapById = Map.fromList $
map (\vote -> (Types.voteGovActionId vote, vote)) votes
processedProposals <- filter (isProposalSearchedFor mSearch) <$>

processedProposals <- filter (isProposalSearchedFor mSearch) <$>
mapSortAndFilterProposals selectedTypes sortMode proposals

return
[ VoteResponse
{ voteResponseVote = voteToResponse vote
Expand All @@ -334,7 +333,7 @@ getVotes (unHexText -> dRepId) selectedTypes sortMode mSearch = do
, let govActionId = unHexText (proposalResponseTxHash proposalResponse) <> "#" <> pack (show $ proposalResponseIndex proposalResponse)
, Just vote <- [Map.lookup govActionId voteMapById]
]

drepInfo :: App m => HexText -> m DRepInfoResponse
drepInfo (unHexText -> dRepId) = do
CacheEnv {dRepInfoCache} <- asks vvaCache
Expand Down Expand Up @@ -365,15 +364,15 @@ drepInfo (unHexText -> dRepId) = do
drepVotingPowerList :: App m => [Text] -> m [DRepVotingPowerListResponse]
drepVotingPowerList identifiers = do
CacheEnv {dRepVotingPowerListCache} <- asks vvaCache

let cacheKey = Text.intercalate "," (sort identifiers)
results <- cacheRequest dRepVotingPowerListCache cacheKey $

results <- cacheRequest dRepVotingPowerListCache cacheKey $
DRep.getDRepsVotingPowerList identifiers

return $ map toDRepVotingPowerListResponse results
where
toDRepVotingPowerListResponse Types.DRepVotingPowerList{..} =
toDRepVotingPowerListResponse Types.DRepVotingPowerList{..} =
DRepVotingPowerListResponse
{ drepVotingPowerListResponseView = drepView
, drepVotingPowerListResponseHashRaw = HexText drepHashRaw
Expand Down Expand Up @@ -456,9 +455,9 @@ getProposal g@(GovActionId govActionTxHash govActionIndex) mDrepId' = do
let mDrepId = unHexText <$> mDrepId'
CacheEnv {getProposalCache} <- asks vvaCache
proposal@Types.Proposal {proposalUrl, proposalDocHash} <- cacheRequest getProposalCache (unHexText govActionTxHash, govActionIndex) (Proposal.getProposal (unHexText govActionTxHash) govActionIndex)

timeZone <- liftIO getCurrentTimeZone

let proposalResponse = proposalToResponse timeZone proposal
voteResponse <- case mDrepId of
Nothing -> return Nothing
Expand All @@ -478,20 +477,20 @@ getProposal g@(GovActionId govActionTxHash govActionIndex) mDrepId' = do
getEnactedProposalDetails :: App m => Maybe GovernanceActionType -> m (Maybe EnactedProposalDetailsResponse)
getEnactedProposalDetails maybeType = do
let proposalType = maybe "HardForkInitiation" governanceActionTypeToText maybeType

mDetails <- Proposal.getPreviousEnactedProposal proposalType

let response = enactedProposalDetailsToResponse <$> mDetails

return response
where
governanceActionTypeToText :: GovernanceActionType -> Text
governanceActionTypeToText actionType =
governanceActionTypeToText actionType =
case actionType of
HardForkInitiation -> "HardForkInitiation"
ParameterChange -> "ParameterChange"
_ -> "HardForkInitiation"
ParameterChange -> "ParameterChange"
_ -> "HardForkInitiation"

enactedProposalDetailsToResponse :: Types.EnactedProposalDetails -> EnactedProposalDetailsResponse
enactedProposalDetailsToResponse Types.EnactedProposalDetails{..} =
EnactedProposalDetailsResponse
Expand All @@ -513,7 +512,7 @@ getTransactionStatus (unHexText -> transactionId) = do
return $ GetTransactionStatusResponse $ case status of
Just value -> Just $ toJSON value
Nothing -> Nothing

throw500 :: App m => m ()
throw500 = throwError $ CriticalError "intentional system break for testing purposes"

Expand Down
42 changes: 24 additions & 18 deletions govtool/backend/src/VVA/API/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,14 @@ instance ToParamSchema GovernanceActionType where
& enum_ ?~ map toJSON (enumFromTo minBound maxBound :: [GovernanceActionType])


data DRepSortMode = Random | VotingPower | RegistrationDate | Status deriving (Bounded, Enum, Eq, Generic, Read, Show)
data DRepSortMode = Random | VotingPower | RegistrationDate | Status deriving
( Bounded
, Enum
, Eq
, Generic
, Read
, Show
)

instance FromJSON DRepSortMode where
parseJSON (Aeson.String dRepSortMode) = pure $ fromJust $ readMaybe (Text.unpack dRepSortMode)
Expand Down Expand Up @@ -406,7 +413,8 @@ data ProposalResponse
}
deriving (Generic, Show)

newtype ProposalAuthors = ProposalAuthors { getProposalAuthors :: Value }
newtype ProposalAuthors
= ProposalAuthors { getProposalAuthors :: Value }
deriving newtype (Show)

instance FromJSON ProposalAuthors where
Expand Down Expand Up @@ -659,7 +667,7 @@ data DRepInfoResponse
, dRepInfoResponseGivenName :: Maybe Text
, dRepInfoResponseObjectives :: Maybe Text
, dRepInfoResponseMotivations :: Maybe Text
, dRepInfoResponseQualifications :: Maybe Text
, dRepInfoResponseQualifications :: Maybe Text
, dRepInfoResponseImageUrl :: Maybe Text
, dRepInfoResponseImageHash :: Maybe HexText
}
Expand Down Expand Up @@ -906,7 +914,7 @@ data DRep
, dRepGivenName :: Maybe Text
, dRepObjectives :: Maybe Text
, dRepMotivations :: Maybe Text
, dRepQualifications :: Maybe Text
, dRepQualifications :: Maybe Text
, dRepImageUrl :: Maybe Text
, dRepImageHash :: Maybe HexText
, dRepIdentityReferences :: Maybe DRepReferences
Expand Down Expand Up @@ -1011,11 +1019,11 @@ instance ToSchema DelegationResponse where

data GetNetworkInfoResponse
= GetNetworkInfoResponse
{ getNetworkInfoResponseCurrentTime :: UTCTime
, getNetworkInfoResponseEpochNo :: Integer
, getNetworkInfoResponseBlockNo :: Integer
, getNetworkInfoResponseNetworkName :: Text
}
{ getNetworkInfoResponseCurrentTime :: UTCTime
, getNetworkInfoResponseEpochNo :: Integer
, getNetworkInfoResponseBlockNo :: Integer
, getNetworkInfoResponseNetworkName :: Text
}

deriveJSON (jsonOptions "getNetworkInfoResponse") ''GetNetworkInfoResponse

Expand All @@ -1035,11 +1043,11 @@ instance ToSchema GetNetworkInfoResponse where

data GetNetworkTotalStakeResponse
= GetNetworkTotalStakeResponse
{ getNetworkTotalStakeResponseTotalStakeControlledByDReps :: Integer
, getNetworkTotalStakeResponseTotalStakeControlledBySPOs :: Integer
, getNetworkTotalStakeResponseAlwaysAbstainVotingPower :: Integer
, getNetworkTotalStakeResponseAlwaysNoConfidenceVotingPower :: Integer
}
{ getNetworkTotalStakeResponseTotalStakeControlledByDReps :: Integer
, getNetworkTotalStakeResponseTotalStakeControlledBySPOs :: Integer
, getNetworkTotalStakeResponseAlwaysAbstainVotingPower :: Integer
, getNetworkTotalStakeResponseAlwaysNoConfidenceVotingPower :: Integer
}

deriveJSON (jsonOptions "getNetworkTotalStakeResponse") ''GetNetworkTotalStakeResponse

Expand Down Expand Up @@ -1113,10 +1121,8 @@ data GetAccountInfoResponse
deriving (Generic, Show)
deriveJSON (jsonOptions "getAccountInfoResponse") ''GetAccountInfoResponse

data UploadResponse
= UploadResponse
{ uploadResponseIpfsCid :: Text
}
newtype UploadResponse
= UploadResponse { uploadResponseIpfsCid :: Text }
deriving (Generic, Show)
deriveJSON (jsonOptions "uploadResponse") ''UploadResponse

Expand Down
13 changes: 8 additions & 5 deletions govtool/backend/src/VVA/Account.hs
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TemplateHaskell #-}

module VVA.Account where

import Control.Monad.Except (MonadError, throwError)
import Control.Monad.Reader (MonadIO, MonadReader, liftIO)

import Data.ByteString (ByteString)
import Data.FileEmbed (embedFile)
import Data.Has (Has)
import Data.String (fromString)
import qualified Database.PostgreSQL.Simple as SQL
import VVA.Types (AppError(..), AccountInfo(..))
import Data.Text (Text, unpack)
import qualified Data.Text.Encoding as Text
import qualified Data.Text.IO as Text
import Data.Has (Has)

import qualified Database.PostgreSQL.Simple as SQL

import VVA.Pool (ConnectionPool, withPool)
import VVA.Types (AccountInfo (..), AppError (..))

sqlFrom :: ByteString -> SQL.Query
sqlFrom = fromString . unpack . Text.decodeUtf8
Expand Down
6 changes: 3 additions & 3 deletions govtool/backend/src/VVA/AdaHolder.hs
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}

module VVA.AdaHolder where

import Control.Exception (try, SomeException)
import Control.Exception (SomeException, try)
import Control.Monad.Except
import Control.Monad.Reader

Expand Down Expand Up @@ -65,4 +65,4 @@ getStakeKeyVotingPower stakeKey = withPool $ \conn -> do
return 0
Right _ -> do
Text.putStrLn ("Unexpected result for stake key: " <> stakeKey)
return 0
return 0
Loading