From ab9bd8cf2111e40f132e3396ff1f42f3ad13ed79 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Wed, 13 Nov 2019 09:26:57 -0800 Subject: [PATCH 01/70] Martin/nameservice example (#96) * basic auth types * more types * validate basic * added basic signature verification * better typings for keys / accounts * nameservice and token sketch * Add token module * token with exceptions * Nameservice with exceptions * Start Messages module * Add pending test spec * Add spec stub * Add stubs for negative tests * Add tests for querying names and checking account balances * clean up namespace and token modules * nameservice eff * move messages into nameservice module * better constraints * better query error message * router * remove stylish and hlint from yaml * make burn, mint * sepeficy messages in protobuf file * split module into components * use JSON for to/from events * make stylish * use aeson instances for event types as stopgap * added delete message * move to where clause * blank import * stylish * add router * Store key refactor * Revert "Store key refactor" This reverts commit ae2a8cbcdbf93df33cacb8d626c929e3c70bb851. * all libs compile * tests compile * hlint * Messages + Whois parser (#100) * Start impl message parsers * fix shadowing * buyName WIP * Parse delete name messages * Impl parse MsgBuyName * Use a better name for converter * Use cs * Wrap Int32->Word64 as amount * Hide internal field names * Delete nonNegativeAmount * Add whois stub * Whois ISO f WIP * hlint * Impl iso t * remove unless * compiles --- hs-abci-examples/nameservice/Setup.hs | 4 +- hs-abci-examples/nameservice/package.yaml | 34 ++- .../protos/nameservice/messages.proto | 20 ++ .../protos/nameservice/whois.proto | 8 + .../nameservice/src/Nameservice/Aeson.hs | 7 + .../src/Nameservice/Modules/Nameservice.hs | 29 +++ .../Nameservice/Modules/Nameservice/Keeper.hs | 160 +++++++++++++ .../Modules/Nameservice/Messages.hs | 72 ++++++ .../Nameservice/Modules/Nameservice/Query.hs | 22 ++ .../Nameservice/Modules/Nameservice/Router.hs | 18 ++ .../Nameservice/Modules/Nameservice/Types.hs | 157 +++++++++++++ .../src/Nameservice/Modules/Token.hs | 203 +++++++++++++++++ .../test/Nameservice/Test/NameserviceSpec.hs | 30 +++ hs-abci-examples/nameservice/test/Spec.hs | 4 +- hs-abci-examples/simple-storage/package.yaml | 1 + .../SimpleStorage/Modules/SimpleStorage.hs | 40 +++- hs-abci-sdk/package.yaml | 9 +- hs-abci-sdk/src/Tendermint/SDK/Aeson.hs | 9 + hs-abci-sdk/src/Tendermint/SDK/Auth.hs | 215 ++++++++++++++++++ .../src/Tendermint/SDK/AuthTreeStore.hs | 16 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 2 + hs-abci-sdk/src/Tendermint/SDK/Errors.hs | 81 +++++++ hs-abci-sdk/src/Tendermint/SDK/Events.hs | 36 ++- hs-abci-sdk/src/Tendermint/SDK/Router.hs | 4 +- .../src/Tendermint/SDK/Router/Delayed.hs | 10 +- .../src/Tendermint/SDK/Router/Types.hs | 6 +- hs-abci-sdk/src/Tendermint/SDK/Store.hs | 73 +++--- .../src/Tendermint/SDK/StoreQueries.hs | 55 +++-- .../Tendermint/SDK/Test/AuthTreeStoreSpec.hs | 18 +- stack.yaml | 8 +- stack.yaml.lock | 44 +++- 31 files changed, 1284 insertions(+), 111 deletions(-) create mode 100644 hs-abci-examples/nameservice/protos/nameservice/messages.proto create mode 100644 hs-abci-examples/nameservice/protos/nameservice/whois.proto create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Aeson.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs create mode 100644 hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Aeson.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Auth.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Errors.hs diff --git a/hs-abci-examples/nameservice/Setup.hs b/hs-abci-examples/nameservice/Setup.hs index 44671092..c81784fa 100644 --- a/hs-abci-examples/nameservice/Setup.hs +++ b/hs-abci-examples/nameservice/Setup.hs @@ -1,2 +1,2 @@ -import Distribution.Simple -main = defaultMain +import Data.ProtoLens.Setup +main = defaultMainGeneratingProtos "protos" diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index b1fcac0a..68043cf4 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -6,8 +6,6 @@ author: "Author name here" maintainer: "example@example.com" copyright: "2019 Author name here" -extra-source-files: -- README.md # Metadata used when publishing your package # synopsis: Short description of your package @@ -18,6 +16,16 @@ extra-source-files: # common to point users to the README.md file. description: Please see the README on GitHub at +extra-source-files: +- README.md +- protos/**/*.proto + +custom-setup: + dependencies: + - base + - Cabal + - proto-lens-setup + default-extensions: - DeriveGeneric - NamedFieldPuns @@ -45,20 +53,41 @@ default-extensions: - ConstraintKinds dependencies: +- aeson +- aeson-casing - base >= 4.7 && < 5 +- binary +- bytestring - exceptions - hs-abci-extra - hs-abci-sdk - hs-abci-server - hs-abci-types +- lens - polysemy +- proto-lens +- proto-lens-runtime - katip +- servant +- string-conversions +- text library: source-dirs: src exposed-modules: - Nameservice.Application - Nameservice.Server + - Nameservice.Modules.Nameservice.Messages + - Nameservice.Modules.Nameservice.Types + - Nameservice.Modules.Nameservice.Keeper + - Nameservice.Modules.Nameservice.Query + - Nameservice.Modules.Nameservice.Router + + generated-exposed-modules: + - Proto.Nameservice.Messages + - Proto.Nameservice.Messages_Fields + - Proto.Nameservice.Whois + - Proto.Nameservice.Whois_Fields executables: nameservice-exe: @@ -81,3 +110,4 @@ tests: - -with-rtsopts=-N dependencies: - nameservice + - hspec diff --git a/hs-abci-examples/nameservice/protos/nameservice/messages.proto b/hs-abci-examples/nameservice/protos/nameservice/messages.proto new file mode 100644 index 00000000..30fc9006 --- /dev/null +++ b/hs-abci-examples/nameservice/protos/nameservice/messages.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package nameservice; + +message SetName { + bytes owner = 1; + string name = 2; + string value = 3; +} + +message DeleteName { + bytes owner = 1; + string name = 2; +} + +message BuyName { + bytes buyer = 1; + string name = 2; + string value = 3; + uint64 bid = 4; +} \ No newline at end of file diff --git a/hs-abci-examples/nameservice/protos/nameservice/whois.proto b/hs-abci-examples/nameservice/protos/nameservice/whois.proto new file mode 100644 index 00000000..82e8096e --- /dev/null +++ b/hs-abci-examples/nameservice/protos/nameservice/whois.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package nameservice; + +message Whois { + string value = 1; + bytes owner = 2; + uint64 price = 3; +} \ No newline at end of file diff --git a/hs-abci-examples/nameservice/src/Nameservice/Aeson.hs b/hs-abci-examples/nameservice/src/Nameservice/Aeson.hs new file mode 100644 index 00000000..5b4a80af --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Aeson.hs @@ -0,0 +1,7 @@ +module Nameservice.Aeson where + +import Data.Aeson (Options) +import Data.Aeson.Casing (aesonDrop, snakeCase) + +defaultNameserviceOptions :: String -> Options +defaultNameserviceOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs new file mode 100644 index 00000000..68c81a00 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -0,0 +1,29 @@ +module Nameservice.Modules.Nameservice + + ( + -- * types + Name(..) + , Whois (..) + , NameserviceException(..) + + -- * effects + , NameserviceEffR + , HasNameserviceEff + , getWhois + , buyName + , setName + , deleteName + + -- * interpreter + , eval + + -- * query API + , Api + , server + + ) where + +import Nameservice.Modules.Nameservice.Keeper +import Nameservice.Modules.Nameservice.Messages +import Nameservice.Modules.Nameservice.Query +import Nameservice.Modules.Nameservice.Types diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs new file mode 100644 index 00000000..c4c0ce10 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -0,0 +1,160 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Nameservice.Modules.Nameservice.Keeper where + +import Data.Maybe (isNothing) +import Data.Proxy +import Data.String.Conversions (cs) +import GHC.TypeLits (symbolVal) +import Nameservice.Modules.Nameservice.Messages +import Nameservice.Modules.Nameservice.Types +import Nameservice.Modules.Token (HasTokenEff, burn, + mint, transfer) +import Polysemy (Member, Members, Sem, + interpret, makeSem) +import Polysemy.Error (Error, mapError, + throw) +import Polysemy.Output (Output) +import Tendermint.SDK.BaseApp (HasBaseApp) +import Tendermint.SDK.Errors (AppError (..), + IsAppError (..)) +import Tendermint.SDK.Events (Event, emit) +import qualified Tendermint.SDK.Store as Store + +data Nameservice m a where + PutWhois :: Name -> Whois -> Nameservice m () + GetWhois :: Name -> Nameservice m (Maybe Whois) + DeleteWhois :: Name -> Nameservice m () + +makeSem ''Nameservice + +type NameserviceEffR = '[Nameservice, Error NameserviceException] +type HasNameserviceEff r = (Members NameserviceEffR r, Member (Output Event) r) + +storeKey :: Store.StoreKey NameserviceModule +storeKey = Store.StoreKey . cs . symbolVal $ (Proxy :: Proxy NameserviceModule) + +eval + :: HasBaseApp r + => HasTokenEff r + => Member (Error AppError) r + => Sem (Nameservice ': Error NameserviceException ': r) a + -> Sem r a +eval = mapError makeAppError . evalNameservice + where + evalNameservice + :: HasBaseApp r + => Sem (Nameservice ': r) a + -> Sem r a + evalNameservice = + interpret (\case + GetWhois name -> + Store.get storeKey name + PutWhois name whois -> + Store.put storeKey name whois + DeleteWhois name -> + Store.delete storeKey name + ) + +-------------------------------------------------------------------------------- + +setName + :: HasTokenEff r + => HasNameserviceEff r + => MsgSetName + -> Sem r () +setName MsgSetName{..} = do + mwhois <- getWhois msgSetNameName + case mwhois of + Nothing -> throw $ UnauthorizedSet "Cannot claim name with SetMessage tx." + Just currentWhois@Whois{..} -> + if whoisOwner /= msgSetNameOwner + then throw $ UnauthorizedSet "Setter must be the owner of the Name." + else do + putWhois msgSetNameName currentWhois {whoisValue = msgSetNameValue} + emit NameRemapped + { nameRemappedName = msgSetNameName + , nameRemappedNewValue = msgSetNameValue + , nameRemappedOldValue = whoisValue + } + +deleteName + :: HasTokenEff r + => HasNameserviceEff r + => Member (Output Event) r + => MsgDeleteName + -> Sem r () +deleteName MsgDeleteName{..} = do + mWhois <- getWhois msgDeleteNameName + case mWhois of + Nothing -> throw $ InvalidDelete "Can't remove unassigned name." + Just Whois{..} -> + if whoisOwner /= msgDeleteNameOwner + then throw $ InvalidDelete "Deleter must be the owner." + else do + mint msgDeleteNameOwner whoisPrice + deleteWhois msgDeleteNameName + emit NameDeleted + { nameDeletedName = msgDeleteNameName + } + + +buyName + :: HasTokenEff r + => HasNameserviceEff r + => Member (Output Event) r + => MsgBuyName + -> Sem r () +-- ^ did it succeed +buyName msg = do + let name = msgBuyNameName msg + mWhois <- getWhois name + case mWhois of + -- The name is unclaimed, go ahead and debit the account + -- and create it. + Nothing -> buyUnclaimedName msg + -- The name is currently claimed, we will transfer the + -- funds and ownership + Just whois -> buyClaimedName msg whois + where + buyUnclaimedName + :: HasTokenEff r + => HasNameserviceEff r + => Member (Output Event) r + => MsgBuyName + -> Sem r () + buyUnclaimedName MsgBuyName{..} = do + burn msgBuyNameBuyer msgBuyNameBid + let whois = Whois + { whoisOwner = msgBuyNameBuyer + , whoisValue = msgBuyNameValue + , whoisPrice = msgBuyNameBid + } + putWhois msgBuyNameName whois + emit NameClaimed + { nameClaimedOwner = msgBuyNameBuyer + , nameClaimedName = msgBuyNameName + , nameClaimedValue = msgBuyNameValue + , nameClaimedBid = msgBuyNameBid + } + + buyClaimedName + :: HasNameserviceEff r + => HasTokenEff r + => Member (Output Event) r + => MsgBuyName + -> Whois + -> Sem r () + buyClaimedName MsgBuyName{..} currentWhois = + let Whois{ whoisPrice = forsalePrice, whoisOwner = previousOwner } = currentWhois + in if msgBuyNameBid > forsalePrice + then do + transfer msgBuyNameBuyer msgBuyNameBid previousOwner + putWhois msgBuyNameName currentWhois {whoisOwner = msgBuyNameBuyer} + emit NameClaimed + { nameClaimedOwner = msgBuyNameBuyer + , nameClaimedName = msgBuyNameName + , nameClaimedValue = msgBuyNameValue + , nameClaimedBid = msgBuyNameBid + } + else throw (InsufficientBid "Bid must exceed the price.") diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs new file mode 100644 index 00000000..56b4ebb2 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -0,0 +1,72 @@ +module Nameservice.Modules.Nameservice.Messages where + +import Control.Lens ((^.)) +import Data.Either (Either) +import Data.String.Conversions (cs) +import Data.Text (Text) +import Nameservice.Modules.Nameservice.Types (Name (..)) +import Nameservice.Modules.Token (Address (..), + Amount (..)) +import qualified Proto.Nameservice.Messages as M +import qualified Proto.Nameservice.Messages_Fields as M +import Tendermint.SDK.Auth (Address, + addressFromBytes) + +data NameserviceMessage = + SetName MsgSetName + | BuyName MsgBuyName + | DeleteName MsgDeleteName + + +data MsgSetName = MsgSetName + { msgSetNameName :: Name + , msgSetNameValue :: String + , msgSetNameOwner :: Address + } + +-- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg +fromProtoMsgSetName :: M.SetName -> Either Text MsgSetName +fromProtoMsgSetName msg = do + msgName <- nonEmpty "Name" . cs $ msg ^. M.name + msgValue <- nonEmpty "Value" . cs $ msg ^. M.value + msgOwner <- nonEmpty "Owner" $ msg ^. M.owner + return MsgSetName { msgSetNameName = Name msgName + , msgSetNameValue = msgValue + , msgSetNameOwner = addressFromBytes msgOwner + } + +data MsgDeleteName = MsgDeleteName + { msgDeleteNameName :: Name + , msgDeleteNameOwner :: Address + } + +fromProtoMsgDeleteName :: M.DeleteName -> Either Text MsgDeleteName +fromProtoMsgDeleteName msg = do + msgName <- nonEmpty "Name" . cs $ msg ^. M.name + msgOwner <- nonEmpty "Owner" $ msg ^. M.owner + return MsgDeleteName { msgDeleteNameName = Name msgName + , msgDeleteNameOwner = addressFromBytes msgOwner + } + +data MsgBuyName = MsgBuyName + { msgBuyNameName :: Name + , msgBuyNameValue :: String + , msgBuyNameBuyer :: Address + , msgBuyNameBid :: Amount + } + +fromProtoMsgBuyName :: M.BuyName -> Either Text MsgBuyName +fromProtoMsgBuyName msg = do + msgName <- nonEmpty "Name" . cs $ msg ^. M.name + msgValue <- nonEmpty "Value" . cs $ msg ^. M.value + msgBuyer <- nonEmpty "Buyer" $ msg ^. M.buyer + msgBid <- Right . Amount $ msg ^. M.bid + return MsgBuyName { msgBuyNameName = Name msgName + , msgBuyNameValue = msgValue + , msgBuyNameBuyer = addressFromBytes msgBuyer + , msgBuyNameBid = msgBid + } + +nonEmpty :: (Eq a, Monoid a) => String -> a -> Either Text a +nonEmpty field x | x == mempty = Left . cs $ (show field ++ ": value cannot be empty") + | otherwise = Right x diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs new file mode 100644 index 00000000..2964b9f4 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -0,0 +1,22 @@ +module Nameservice.Modules.Nameservice.Query where + +import Data.Proxy +import Nameservice.Modules.Nameservice.Types (Name, Whois) +import Polysemy (Member, Sem) +import Servant.API ((:>)) +import qualified Tendermint.SDK.Router as R +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.StoreQueries (QueryApi, + storeQueryHandlers) + +-------------------------------------------------------------------------------- +-- | Query API +-------------------------------------------------------------------------------- + +type NameserviceContents = '[(Name, Whois)] + +type Api = "nameservice" :> QueryApi NameserviceContents + +server :: Member Store.RawStore r => R.RouteT Api (Sem r) +server = + storeQueryHandlers (Proxy :: Proxy NameserviceContents) (Proxy :: Proxy "nameservice") (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs new file mode 100644 index 00000000..66fe7188 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -0,0 +1,18 @@ +module Nameservice.Modules.Nameservice.Router where + +import Nameservice.Modules.Nameservice.Keeper (HasNameserviceEff, + buyName, deleteName, + setName) +import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) +import Nameservice.Modules.Token (HasTokenEff) +import Polysemy (Sem) + +router + :: HasTokenEff r + => HasNameserviceEff r + => NameserviceMessage + -> Sem r () +router = \case + SetName msg -> setName msg + BuyName msg -> buyName msg + DeleteName msg -> deleteName msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs new file mode 100644 index 00000000..a17b3302 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -0,0 +1,157 @@ +module Nameservice.Modules.Nameservice.Types where + +import Control.Lens (iso, (&), (.~), (^.)) +import Control.Lens.Wrapped (Wrapped (..)) +import Data.Aeson as A +import qualified Data.Binary as Binary +import Data.ByteString (ByteString) +import Data.ByteString as BS +import Data.Maybe (fromJust) +import Data.ProtoLens.Message (Message (defMessage)) +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) +import Nameservice.Aeson (defaultNameserviceOptions) +import Nameservice.Modules.Token (Address (..), Amount (..)) +import qualified Proto.Nameservice.Whois as W +import qualified Proto.Nameservice.Whois_Fields as W +import Tendermint.SDK.Auth (Address, addressFromBytes, + addressToBytes) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) +import qualified Tendermint.SDK.Router as R +import qualified Tendermint.SDK.Store as Store + +-------------------------------------------------------------------------------- + +type NameserviceModule = "nameservice" + +-------------------------------------------------------------------------------- + +newtype Name = Name String deriving (Eq, Show, Binary.Binary, A.ToJSON, A.FromJSON) + +data Whois = Whois + { whoisValue :: String + , whoisOwner :: Address + , whoisPrice :: Amount + } deriving (Eq, Show, Generic) + +instance Binary.Binary Whois + +instance HasCodec Whois where + encode = cs . Binary.encode + decode = Right . Binary.decode . cs + +instance Store.RawKey Name where + rawKey = iso (\(Name n) -> cs n) (Name . cs) + +instance Store.IsKey Name NameserviceModule where + type Value Name NameserviceModule = Whois + +instance R.Queryable Whois where + type Name Whois = "whois" + +instance Wrapped Whois where + type Unwrapped Whois = W.Whois + + _Wrapped' = iso t f + where + t Whois{..} = + let value = cs whoisValue + owner = addressToBytes whoisOwner + Amount price = whoisPrice + in defMessage + & W.value .~ value + & W.owner .~ owner + & W.price .~ price + f msg = + let msgValue = cs $ msg ^. W.value + msgOwner = addressFromBytes $ msg ^. W.owner + msgPrice = Amount $ msg ^. W.price + in Whois { whoisValue = msgValue + , whoisOwner = msgOwner + , whoisPrice = msgPrice + } + +-------------------------------------------------------------------------------- +-- Exceptions +-------------------------------------------------------------------------------- + +data NameserviceException = + InsufficientBid Text + | UnauthorizedSet Text + | InvalidDelete Text + +instance IsAppError NameserviceException where + makeAppError (InsufficientBid msg) = + AppError + { appErrorCode = 1 + , appErrorCodespace = "nameservice" + , appErrorMessage = msg + } + makeAppError (UnauthorizedSet msg) = + AppError + { appErrorCode = 2 + , appErrorCodespace = "nameservice" + , appErrorMessage = msg + } + makeAppError (InvalidDelete msg) = + AppError + { appErrorCode = 3 + , appErrorCodespace = "nameservice" + , appErrorMessage = msg + } + +-------------------------------------------------------------------------------- +-- Events +-------------------------------------------------------------------------------- + +data NameClaimed = NameClaimed + { nameClaimedOwner :: Address + , nameClaimedName :: Name + , nameClaimedValue :: String + , nameClaimedBid :: Amount + } deriving (Generic) + +nameClaimedAesonOptions :: A.Options +nameClaimedAesonOptions = defaultNameserviceOptions "nameClaimed" + +instance ToJSON NameClaimed where + toJSON = A.genericToJSON nameClaimedAesonOptions +instance FromJSON NameClaimed where + parseJSON = A.genericParseJSON nameClaimedAesonOptions +instance ToEvent NameClaimed where + makeEventType _ = "NameClaimed" + +data NameRemapped = NameRemapped + { nameRemappedName :: Name + , nameRemappedOldValue :: String + , nameRemappedNewValue :: String + } deriving Generic + +nameRemappedAesonOptions :: A.Options +nameRemappedAesonOptions = defaultNameserviceOptions "nameRemapped" + +instance ToJSON NameRemapped where + toJSON = A.genericToJSON nameRemappedAesonOptions +instance FromJSON NameRemapped where + parseJSON = A.genericParseJSON nameRemappedAesonOptions +instance ToEvent NameRemapped where + makeEventType _ = "NameRemapped" +instance FromEvent NameRemapped + +data NameDeleted = NameDeleted + { nameDeletedName :: Name + } deriving Generic + +nameDeletedAesonOptions :: A.Options +nameDeletedAesonOptions = defaultNameserviceOptions "nameDeleted" + +instance ToJSON NameDeleted where + toJSON = A.genericToJSON nameDeletedAesonOptions +instance FromJSON NameDeleted where + parseJSON = A.genericParseJSON nameDeletedAesonOptions +instance ToEvent NameDeleted where + makeEventType _ = "NameDeleted" +instance FromEvent NameDeleted diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs new file mode 100644 index 00000000..42d3b163 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -0,0 +1,203 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Nameservice.Modules.Token + ( + -- * types + Address(..) + , Amount(..) + , TokenException(..) + , Transfer + + -- * effects + , Token + , TokenEffR + , HasTokenEff + , getBalance + , transfer + , mint + , burn + + -- * interpreter + , eval + + -- * Query Api + , Api + , server + + ) where + +import Control.Lens (iso) +import Data.Aeson as A +import qualified Data.Binary as Binary +import Data.ByteString (ByteString) +import qualified Data.ByteString as BS +import Data.Maybe (fromJust, fromMaybe) +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word64) +import GHC.Generics (Generic) +import Nameservice.Aeson (defaultNameserviceOptions) +import Polysemy +import Polysemy.Error (Error, mapError, throw) +import Polysemy.Output (Output) +import Servant.API ((:>)) +import Tendermint.SDK.Auth (Address, addressFromBytes, + addressToBytes) +import Tendermint.SDK.BaseApp (HasBaseApp) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Events (Event, FromEvent, ToEvent (..), + emit) +import Tendermint.SDK.Router (Queryable (..), RouteT) +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.StoreQueries (QueryApi, storeQueryHandlers) + +newtype Amount = Amount Word64 deriving (Eq, Show, Binary.Binary, Num, Generic, Ord, A.ToJSON, A.FromJSON) + +instance Queryable Amount where + type Name Amount = "balance" + +instance HasCodec Amount where + encode (Amount b) = cs $ Binary.encode b + decode = Right . Amount . Binary.decode . cs + +instance Store.IsKey Address "token" where + type Value Address "token" = Amount + +-------------------------------------------------------------------------------- +-- Events +-------------------------------------------------------------------------------- + +data Transfer = Transfer + { transferAmount :: Amount + , transferTo :: Address + , transferFrom :: Address + } deriving Generic + +transferAesonOptions :: A.Options +transferAesonOptions = defaultNameserviceOptions "transfer" + +instance A.ToJSON Transfer where + toJSON = A.genericToJSON transferAesonOptions + +instance A.FromJSON Transfer where + parseJSON = A.genericParseJSON transferAesonOptions + +instance ToEvent Transfer where + makeEventType _ = "Transfer" + +instance FromEvent Transfer + +-------------------------------------------------------------------------------- +-- Exceptions +-------------------------------------------------------------------------------- + +data TokenException = + InsufficientFunds Text + +instance IsAppError TokenException where + makeAppError (InsufficientFunds msg) = + AppError + { appErrorCode = 1 + , appErrorCodespace = "token" + , appErrorMessage = msg + } + +-------------------------------------------------------------------------------- + +data Token m a where + PutBalance :: Address -> Amount -> Token m () + GetBalance' :: Address -> Token m (Maybe Amount) + +makeSem ''Token + +type TokenEffR = '[Token, Error TokenException] +type HasTokenEff r = (Members TokenEffR r, Member (Output Event) r) + +storeKey :: Store.StoreKey "token" +storeKey = Store.StoreKey "token" + +eval + :: HasBaseApp r + => Member (Error AppError) r + => Sem (Token ': Error TokenException ': r) a + -> Sem r a +eval = mapError makeAppError . evalToken + where + evalToken + :: HasBaseApp r + => Sem (Token ': r) a + -> Sem r a + evalToken = + interpret (\case + GetBalance' address -> + Store.get storeKey address + PutBalance address balance -> + Store.put storeKey address balance + ) +-------------------------------------------------------------------------------- +-- | Server +-------------------------------------------------------------------------------- + +type TokenContents = '[(Address, Amount)] + +type Api = "token" :> QueryApi TokenContents + +server :: Member Store.RawStore r => RouteT Api (Sem r) +server = + storeQueryHandlers (Proxy :: Proxy TokenContents) (Proxy :: Proxy "token") (Proxy :: Proxy (Sem r)) + +-------------------------------------------------------------------------------- + +getBalance + :: Member Token r + => Address + -> Sem r Amount +getBalance address = + fromMaybe (Amount 0) <$> getBalance' address + +transfer + :: HasTokenEff r + => Address + -> Amount + -> Address + -> Sem r () +transfer addr1 amount addr2 = do + -- check if addr1 has amt + addr1Bal <- getBalance addr1 + if addr1Bal > amount + then do + addr2Bal <- getBalance addr2 + let newBalance1 = addr1Bal - amount + newBalance2 = addr2Bal + amount + -- update both balances + putBalance addr1 newBalance1 + putBalance addr2 newBalance2 + emit $ Transfer + { transferAmount = amount + , transferTo = addr2 + , transferFrom = addr1 + } + else throw (InsufficientFunds "Insufficient funds for transfer.") + +burn + :: Members '[Token, Error TokenException] r + => Address + -> Amount + -> Sem r () +burn addr amount = do + bal <- getBalance addr + if bal < amount + then throw $ InsufficientFunds "Insuffient funds for burn." + else putBalance addr (bal - amount) + +mint + :: Members '[Token, Error TokenException] r + => Address + -> Amount + -> Sem r () +mint addr amount = do + bal <- getBalance addr + putBalance addr (bal + amount) + diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs new file mode 100644 index 00000000..6ba4f6ff --- /dev/null +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs @@ -0,0 +1,30 @@ +module Nameservice.Test.NameserviceSpec where + +import Test.Hspec + +spec :: Spec +spec = do + describe "Nameservice Spec" $ do + it "Can query account balances" $ do + pending + + it "Can create a name" $ do + pending + + it "Can query for a name that exists" $ do + pending + + it "Can query for a name that doesn't exist" $ do + pending + + it "Can set a name value" $ do + pending + + it "Can buy a name" $ do + pending + + it "Can fail to buy a name" $ do + pending + + it "Can fail a transfer" $ do + pending diff --git a/hs-abci-examples/nameservice/test/Spec.hs b/hs-abci-examples/nameservice/test/Spec.hs index cd4753fc..fcb16768 100644 --- a/hs-abci-examples/nameservice/test/Spec.hs +++ b/hs-abci-examples/nameservice/test/Spec.hs @@ -1,2 +1,2 @@ -main :: IO () -main = putStrLn "Test suite not yet implemented" +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} +{-# OPTIONS_GHC -fno-warn-missing-import-lists #-} diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index 13c61263..8df742ad 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -39,6 +39,7 @@ default-extensions: dependencies: +- aeson - async - avl-auth - base >= 4.7 && < 5 diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index ebabcdb9..6f63e10d 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -26,6 +26,7 @@ module SimpleStorage.Modules.SimpleStorage import Control.Lens (iso) import Crypto.Hash (SHA256 (..), hashWith) +import qualified Data.Aeson as A import qualified Data.Binary as Binary import Data.ByteArray (convert) import Data.ByteString (ByteString) @@ -33,6 +34,7 @@ import Data.Int (Int32) import Data.Maybe (fromJust) import Data.Proxy import Data.String.Conversions (cs) +import GHC.Generics (Generic) import Polysemy (Member, Sem, interpret, makeSem) import Polysemy.Output (Output) import Servant.API ((:>)) @@ -41,15 +43,15 @@ import Tendermint.SDK.Codec (HasCodec (..)) import qualified Tendermint.SDK.Events as Events import Tendermint.SDK.Router (EncodeQueryResult, FromQueryData, Queryable (..), RouteT) -import Tendermint.SDK.Store (HasKey (..), RawStore, Root, get, - put) +import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, + StoreKey (..), get, put) import Tendermint.SDK.StoreQueries (QueryApi, storeQueryHandlers) -------------------------------------------------------------------------------- -- Types -------------------------------------------------------------------------------- -newtype Count = Count Int32 deriving (Eq, Show) +newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON) data CountKey = CountKey @@ -57,13 +59,15 @@ instance HasCodec Count where encode (Count c) = cs . Binary.encode $ c decode = Right . Count . Binary.decode . cs -instance HasKey Count where - type Key Count = CountKey +instance RawKey CountKey where rawKey = iso (\_ -> cs countKey) (const CountKey) where countKey :: ByteString countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) +instance IsKey CountKey "simple_storage" where + type Value CountKey "simple_storage" = Count + instance FromQueryData CountKey instance EncodeQueryResult Count @@ -75,16 +79,27 @@ instance Queryable Count where -- Events -------------------------------------------------------------------------------- -data CountSet = CountSet { newCount :: Count } +data CountSet = CountSet { newCount :: Count } deriving Generic + +countSetOptions :: A.Options +countSetOptions = A.defaultOptions + +instance A.ToJSON CountSet where + toJSON = A.genericToJSON countSetOptions -instance Events.IsEvent CountSet where +instance A.FromJSON CountSet where + parseJSON = A.genericParseJSON countSetOptions + +instance Events.ToEvent CountSet where makeEventType _ = "count_set" - makeEventData CountSet{newCount} = [("new_count", encode newCount)] -------------------------------------------------------------------------------- -- SimpleStorage Module -------------------------------------------------------------------------------- +storeKey :: StoreKey "simple_storage" +storeKey = StoreKey "simple_storage" + data SimpleStorage m a where PutCount :: Count -> SimpleStorage m () GetCount :: SimpleStorage m Count @@ -97,10 +112,10 @@ eval => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) eval = interpret (\case PutCount count -> do - put CountKey count + put storeKey CountKey count Events.emit $ CountSet count - GetCount -> fromJust <$> get (undefined :: Root) CountKey + GetCount -> fromJust <$> get storeKey CountKey ) initialize @@ -114,9 +129,10 @@ initialize = eval $ do -- Query Api -------------------------------------------------------------------------------- -type CountStoreContents = '[Count] +type CountStoreContents = '[(CountKey, Count)] type Api = "simple_storage" :> QueryApi CountStoreContents server :: Member RawStore r => RouteT Api (Sem r) -server = storeQueryHandlers (Proxy :: Proxy CountStoreContents) (Proxy :: Proxy (Sem r)) +server = + storeQueryHandlers (Proxy :: Proxy CountStoreContents) (Proxy :: Proxy "simple_storage") (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index c9ed27fd..95472792 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -36,9 +36,12 @@ default-extensions: dependencies: +- aeson +- aeson-casing - async - avl-auth - base >= 4.7 && < 5 +- bech32 - binary - bytestring - containers @@ -57,6 +60,7 @@ dependencies: - mtl - polysemy - polysemy-plugin +- profunctors - servant - stm - string-conversions @@ -83,8 +87,11 @@ library: - Tendermint.SDK.Logger - Tendermint.SDK.Logger.Katip - Tendermint.SDK.Application + - Tendermint.SDK.Auth + - Tendermint.SDK.Aeson - Tendermint.SDK.Events - + - Tendermint.SDK.Errors + tests: hs-abci-sdk-test: main: Spec.hs diff --git a/hs-abci-sdk/src/Tendermint/SDK/Aeson.hs b/hs-abci-sdk/src/Tendermint/SDK/Aeson.hs new file mode 100644 index 00000000..4b957524 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Aeson.hs @@ -0,0 +1,9 @@ +module Tendermint.SDK.Aeson + ( defaultSDKAesonOptions + ) where + +import Data.Aeson (Options) +import Data.Aeson.Casing (aesonDrop, snakeCase) + +defaultSDKAesonOptions :: String -> Options +defaultSDKAesonOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs new file mode 100644 index 00000000..7767150a --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs @@ -0,0 +1,215 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.Auth where + +import qualified Codec.Binary.Bech32 as Bech32 +import Control.Lens (iso) +import Control.Monad (when) +import Control.Monad.Catch (Exception, MonadCatch, catch) +import Control.Monad.IO.Class (MonadIO (..)) +import Control.Monad.Reader (ReaderT, asks) +import qualified Data.Aeson as A +import qualified Data.Binary as Binary +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) +import Data.Int (Int64) +import Data.IORef +import Data.Monoid (Endo (..)) +import Data.Proxy (Proxy (..)) +import Data.String.Conversions +import Data.Text (Text) +import GHC.Generics (Generic) +import GHC.TypeLits (KnownSymbol, symbolVal) +import Polysemy +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Store (IsKey (..), RawKey (..)) + +newtype Address = Address Hex.HexString deriving (Eq, Show, Ord, A.ToJSON, A.FromJSON) + +instance Binary.Binary Address where + put (Address a) = Binary.put @ByteString . Hex.toBytes $ a + get = Address . Hex.fromBytes <$> Binary.get @ByteString + +addressToBytes :: Address -> ByteString +addressToBytes (Address addrHex) = Hex.toBytes addrHex + +addressFromBytes :: ByteString -> Address +addressFromBytes = Address . Hex.fromBytes + +newtype AccountAddress prefix = AccountAddress Address deriving (Eq, Show, Ord) + +instance IsHumanReadable prefix => A.ToJSON (AccountAddress prefix) where + toJSON = A.toJSON . toBech32 + +instance IsHumanReadable prefix => A.FromJSON (AccountAddress prefix) where + parseJSON = A.withText "AccountAddress" $ \t -> + either (fail . cs) pure $ fromBech32 t + +fromBech32 :: forall prefix. IsHumanReadable prefix => Text -> Either Text (AccountAddress prefix) +fromBech32 a = case Bech32.decode a of + Left e -> Left . cs . show $ e + Right (_hrp, dp) -> + if getPrefix (Proxy :: Proxy prefix) /= _hrp + then Left "MismatchedHumanReadablePartError" + else case Bech32.dataPartToBytes dp of + Nothing -> Left "FailedToParseDataPartAsBytesError" + Just bs -> Right . AccountAddress . Address $ Hex.fromBytes bs + +toBech32 :: IsHumanReadable prefix => AccountAddress prefix -> Text +toBech32 ((AccountAddress (Address a)) :: AccountAddress prefix) = + Bech32.encodeLenient (getPrefix (Proxy :: Proxy prefix)) (Bech32.dataPartFromBytes . Hex.toBytes $ a) + +-- | NOTE: There are rules for valid prefix p, namely +-- | 1. 1 <= length p <= 83 +-- | 2. min (map chr p) >= 33 +-- | 3. max (map chr p) <= 126 +class KnownSymbol prefix => IsHumanReadable prefix where + getPrefix :: Proxy prefix -> Bech32.HumanReadablePart + + default getPrefix :: Proxy prefix -> Bech32.HumanReadablePart + getPrefix p = + case Bech32.humanReadablePartFromText. cs $ symbolVal p of + Left err -> error $ show err + Right hrp -> hrp + +data PubKey = PubKey + { pubKeyAddress :: Address + , pubKeyRaw :: ByteString + } deriving (Eq, Ord, Generic) + +instance Binary.Binary PubKey + +data PrivateKey = PrivateKey + { privateKeyPubKey :: ByteString + , privateKeyRaw :: ByteString + } + +newtype Signature = Signature ByteString deriving Eq + +data Signer = Signer + { signerSign :: PrivateKey -> ByteString -> Either Text Signature + , signerRecover :: Signature -> ByteString -> PubKey + } + +signerVerify + :: Signer + -> PubKey + -> Signature + -> ByteString + -> Bool +signerVerify Signer{signerRecover} pubKey sig msg = + signerRecover sig msg == pubKey + +data Coin = Coin + { coinDenomination :: Text + , coinAmount :: Int64 + } deriving Generic + +instance Binary.Binary Coin + +data Account = Account + { accountPubKey :: PubKey + , accountCoins :: [Coin] + , accountNumber :: Int64 + , accountSequence :: Int64 + } deriving Generic + +instance Binary.Binary Account + +data Accounts m a where + PutAccount :: Account -> Accounts m () + GetAccount :: Address -> Accounts m Account + +makeSem ''Accounts + +instance HasCodec Account where + encode = cs . Binary.encode + decode = Right . Binary.decode . cs + +instance RawKey Address where + rawKey = iso (\(Address a) -> Hex.toBytes a) (Address . Hex.fromBytes) + +instance IsKey Address "auth" where + type Value Address "auth" = Account + +-------------------------------------------------------------------------------- + +data Msg msg = Msg + { msgRoute :: Text + , msgType :: Text + , msgSignBytes :: ByteString + , msgGetSigners :: [Address] + , msgValidate :: Maybe Text + , msgData :: msg + } + +--verifyAllMsgSignatures :: Signer -> Msg msg -> Bool +--verifyAllMsgSignatures signer Msg{msgGetSigners, msgSignBytes} = +-- let isValid (pubKey, sig) = signerVerify signer pubKey sig msgSignBytes +-- in getAll . mconcat . map (All . isValid) $ msgGetSigners + + +data Fee = Fee + { feeAmount :: [Coin] + , feeGas :: Int64 + } + +data Tx tx msg = Tx + { txMsgs :: [Msg msg] + , txFee :: Fee + , txSignatures :: [Signature] + , txMemo :: Text + , txValidate :: Maybe Text + , txData :: tx + } + +data GasMeter = GasMeter + { gasMeterLimit :: Int64 + , gasMeterConsumed :: Int64 + } + +data OutOfGasException = OutOfGasException deriving (Show) + +instance Exception OutOfGasException + +data Context = Context + { contextGasMeter :: GasMeter + } + +data TxEnv tx = TxEnv + { transaction :: tx + , context :: IORef Context + } + +type AnteDecorator m tx msg = ReaderT (TxEnv (Tx tx msg)) m () + +newContextDecorator + :: forall m tx msg. + MonadCatch m + => MonadIO m + => Endo (AnteDecorator m tx msg) +newContextDecorator = Endo $ \next -> do + tx <- asks transaction + contextVar <- asks context + let gasMeter = GasMeter (feeGas . txFee $ tx) 0 + liftIO $ writeIORef contextVar (Context gasMeter) + next `catch` (\(_ :: OutOfGasException) -> + pure () + ) + +validateBasicDecorator + :: forall m tx msg. + Monad m + => Endo (AnteDecorator m tx msg) +validateBasicDecorator = Endo $ \next -> do + tx <- asks transaction + let msgSigners = map msgGetSigners $ txMsgs tx + expectedSignersN = length $ txSignatures tx + when (expectedSignersN == 0) $ + error "TODO: There must be signers" + when (any (\signers -> length signers /= expectedSignersN) msgSigners) $ + error "TODO: fill in error for wrong number of signers" + let feeAmounts = map coinAmount . feeAmount . txFee $ tx + when (any (< 0) feeAmounts) $ + error "TODO: No negative fees" + next diff --git a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs index 903eb350..b60ba552 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs @@ -7,11 +7,10 @@ import qualified Crypto.Data.Auth.Tree as AT import qualified Crypto.Data.Auth.Tree.Class as AT import qualified Crypto.Data.Auth.Tree.Cryptonite as Cryptonite import qualified Crypto.Hash as Cryptonite -import Data.ByteArray (convert) import Data.ByteString (ByteString) import Polysemy (Embed, Member, Sem, interpret) -import Tendermint.SDK.Store (RawStore (..), Root (..)) +import Tendermint.SDK.Store (RawStore (..), StoreKey (..)) -------------------------------------------------------------------------------- -- -------------------------------------------------------------------------------- @@ -38,15 +37,14 @@ interpretAuthTreeStore interpretAuthTreeStore AuthTreeDriver{treeVar} = interpret (\case - RawStorePut k v -> liftIO . atomically $ do + RawStorePut (StoreKey sk) k v -> liftIO . atomically $ do tree <- readTVar treeVar - writeTVar treeVar $ AT.insert k v tree - RawStoreGet _ k -> liftIO . atomically $ do + writeTVar treeVar $ AT.insert (sk <> k) v tree + RawStoreGet (StoreKey sk) k -> liftIO . atomically $ do tree <- readTVar treeVar - pure $ AT.lookup k tree + pure $ AT.lookup (sk <> k) tree RawStoreProve _ _ -> pure Nothing - RawStoreRoot -> liftIO . atomically $ do + RawStoreDelete (StoreKey sk) k -> liftIO . atomically $ do tree <- readTVar treeVar - let AuthTreeHash r = AT.merkleHash tree :: AuthTreeHash - pure $ Root $ convert r + writeTVar treeVar $ AT.delete (sk <> k) tree ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index b395e6dc..9695ef0b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -81,3 +81,5 @@ eval Context{..} action = KL.evalKatip . interpretAuthTreeStore contextAuthTreeDriver . evalWithBuffer $ action + +-------------------------------------------------------------------------------- diff --git a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs new file mode 100644 index 00000000..b790f797 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs @@ -0,0 +1,81 @@ +module Tendermint.SDK.Errors + ( AppError(..) + , IsAppError(..) + , HasAppError(..) + , catchAppError + ) where + +import Control.Lens (Lens', lens, (&), (.~)) +import Data.Default.Class (Default (..)) +import Data.Text (Text) +import Data.Word (Word32) +import qualified Network.ABCI.Types.Messages.Response as Response +import Polysemy +import Polysemy.Error (Error, runError) + +data AppError = AppError + { appErrorCode :: Word32 + , appErrorCodespace :: Text + , appErrorMessage :: Text + } + +class IsAppError e where + makeAppError :: e -> AppError + +class HasAppError resp where + appError :: Lens' resp AppError + +-- NOTE: We're not actually using this right now as Query messages +-- are handled by a special router. +instance HasAppError Response.Query where + appError = lens g s + where + g Response.Query{..} = AppError + { appErrorCode = queryCode + , appErrorCodespace = queryCodespace + , appErrorMessage = queryLog + } + s query AppError{..} = query + { Response.queryCode = appErrorCode + , Response.queryCodespace = appErrorCodespace + , Response.queryLog = appErrorMessage + } + +instance HasAppError Response.CheckTx where + appError = lens g s + where + g Response.CheckTx{..} = AppError + { appErrorCode = checkTxCode + , appErrorCodespace = checkTxCodespace + , appErrorMessage = checkTxLog + } + s checkTx AppError{..} = checkTx + { Response.checkTxCode = appErrorCode + , Response.checkTxCodespace = appErrorCodespace + , Response.checkTxLog = appErrorMessage + } + +instance HasAppError Response.DeliverTx where + appError = lens g s + where + g Response.DeliverTx{..} = AppError + { appErrorCode = deliverTxCode + , appErrorCodespace = deliverTxCodespace + , appErrorMessage = deliverTxLog + } + s deliverTx AppError{..} = deliverTx + { Response.deliverTxCode = appErrorCode + , Response.deliverTxCodespace = appErrorCodespace + , Response.deliverTxLog = appErrorMessage + } + +catchAppError + :: HasAppError resp + => Default resp + => Sem (Error AppError ': r) resp + -> Sem r resp +catchAppError action = do + eRes <- runError action + case eRes of + Right res -> pure res + Left err -> pure $ def & appError .~ err diff --git a/hs-abci-sdk/src/Tendermint/SDK/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/Events.hs index a10d2e17..c2d28de3 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Events.hs @@ -1,6 +1,7 @@ module Tendermint.SDK.Events ( Event(..) - , IsEvent(..) + , ToEvent(..) + , FromEvent(..) , emit , EventBuffer @@ -13,13 +14,18 @@ module Tendermint.SDK.Events ) where import qualified Control.Concurrent.MVar as MVar +import Control.Error (fmapL) import Control.Monad (void) import Control.Monad.IO.Class +import qualified Data.Aeson as A +import Data.Bifunctor (bimap) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteString as BS import qualified Data.List as L import Data.Proxy import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Exts (fromList, toList) import Network.ABCI.Types.Messages.FieldTypes (Event (..), KVPair (..)) import Polysemy (Embed, Member, Sem, @@ -28,9 +34,31 @@ import Polysemy.Output (Output (..), output) import Polysemy.Reader (Reader (..), ask) import Polysemy.Resource (Resource, onException) -class IsEvent e where +{- +TODO : These JSON instances are fragile but convenient. We +should come up with a custom solution. +-} + +class ToEvent e where makeEventType :: Proxy e -> String makeEventData :: e -> [(BS.ByteString, BS.ByteString)] + default makeEventData :: A.ToJSON e => e -> [(BS.ByteString, BS.ByteString)] + makeEventData e = case A.toJSON e of + A.Object obj -> bimap cs (cs . A.encode) <$> toList obj + _ -> mempty + +class FromEvent e where + fromEvent :: Event -> Either Text e + default fromEvent :: A.FromJSON e => Event -> Either Text e + fromEvent Event{eventAttributes} = + let fromKVPair :: KVPair -> Either String (Text, A.Value) + fromKVPair (KVPair k v) = do + value <- A.eitherDecode . cs @BS.ByteString . Base64.toBytes $ v + return (cs @BS.ByteString . Base64.toBytes $ k, value) + in fmapL cs $ do + kvPairs <- traverse fromKVPair eventAttributes + A.eitherDecode . A.encode . A.Object . fromList $ kvPairs + data EventBuffer = EventBuffer (MVar.MVar [Event]) @@ -63,7 +91,7 @@ withEventBuffer action = do onException (action *> flushEventBuffer buffer) (void $ flushEventBuffer buffer) makeEvent - :: IsEvent e + :: ToEvent e => e -> Event makeEvent (e :: e) = Event @@ -72,7 +100,7 @@ makeEvent (e :: e) = Event } emit - :: IsEvent e + :: ToEvent e => Member (Output Event) r => e -> Sem r () diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Router.hs index d39afbc3..29e61f28 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Router.hs @@ -42,6 +42,6 @@ toApplication toApplication ra query = do res <- ra query case res of - Fail e -> pure $ responseQueryError e - FailFatal e -> pure $ responseQueryError e + Fail e -> pure $ responseQueryError query e + FailFatal e -> pure $ responseQueryError query e Route a -> pure a diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs index 1fdd8c89..402698ae 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs @@ -4,6 +4,7 @@ import Control.Error (ExceptT, runExceptT) import Control.Monad.Reader (MonadReader, ReaderT, ask, runReaderT) import Control.Monad.Trans (MonadTrans (..)) +import qualified Data.ByteArray.Base64String as Base64 import Data.Default.Class (def) import Data.String.Conversions (cs) import qualified Network.ABCI.Types.Messages.Request as Request @@ -63,22 +64,23 @@ runAction action env query k = go (Route a) = do e <- runExceptT a case e of - Left err -> pure $ Route (responseQueryError err) + Left err -> pure $ Route (responseQueryError query err) Right a' -> pure $ k a' -- | Fail with the option to recover. delayedFail :: Monad m => QueryError -> DelayedM m a delayedFail err = liftRouteResult $ Fail err -responseQueryError :: QueryError -> Response.Query -responseQueryError e = +responseQueryError :: Request.Query -> QueryError -> Response.Query +responseQueryError Request.Query{..} e = let msg = case e of PathNotFound -> "Path Not Found" - ResourceNotFound -> "Resource Not Found" + ResourceNotFound -> "Resource Not Found: queryData=" <> cs (Base64.format queryData) InvalidQuery m -> "Invalid Query: " <> m InternalError _ -> "Internal Error" in def { Response.queryCode = 1 , Response.queryLog = cs msg + , Response.queryCodespace = queryPath } addQueryArgs diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs index b86a01eb..a2864742 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs @@ -11,7 +11,7 @@ import Network.ABCI.Types.Messages.FieldTypes (Proof, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Store (HasKey (..)) +import Tendermint.SDK.Store (RawKey (..)) data Leaf (a :: *) @@ -48,7 +48,7 @@ data QueryResult a = QueryResult -------------------------------------------------------------------------------- -class HasKey a => Queryable a where +class Queryable a where type Name a :: Symbol class EncodeQueryResult a where @@ -59,7 +59,7 @@ class EncodeQueryResult a where class FromQueryData a where fromQueryData :: Base64String -> Either String a - default fromQueryData :: (HasKey b, Key b ~ a) => Base64String -> Either String a + default fromQueryData :: RawKey a => Base64String -> Either String a fromQueryData bs = Right (toBytes bs ^. from rawKey) -------------------------------------------------------------------------------- diff --git a/hs-abci-sdk/src/Tendermint/SDK/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Store.hs index 07f42f61..5d330742 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Store.hs @@ -2,12 +2,13 @@ module Tendermint.SDK.Store ( RawStore(..) - , HasKey(..) - , Root(..) + , RawKey(..) + , IsKey(..) + , StoreKey(..) , get , put + , delete , prove - , root ) where import Control.Lens (Iso', (^.)) @@ -15,57 +16,65 @@ import qualified Data.ByteString as BS import Polysemy (Member, Sem, makeSem) import Tendermint.SDK.Codec (HasCodec (..)) -newtype Root = Root BS.ByteString +newtype StoreKey n = StoreKey BS.ByteString data RawStore m a where - RawStorePut :: BS.ByteString -> BS.ByteString -> RawStore m () - RawStoreGet :: Root -> BS.ByteString -> RawStore m (Maybe BS.ByteString) - RawStoreProve :: Root -> BS.ByteString -> RawStore m (Maybe BS.ByteString) - RawStoreRoot :: RawStore m Root + RawStorePut :: StoreKey ns -> BS.ByteString -> BS.ByteString -> RawStore m () + RawStoreGet :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) + RawStoreDelete :: StoreKey ns -> BS.ByteString -> RawStore m () + RawStoreProve :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) makeSem ''RawStore -class HasCodec a => HasKey a where - type Key a = k | k -> a - rawKey :: Iso' (Key a) BS.ByteString +class RawKey k where + rawKey :: Iso' k BS.ByteString -root - :: Member RawStore r - => Sem r Root -root = rawStoreRoot +class RawKey k => IsKey k ns where + type Value k ns = a | a -> ns k put - :: forall a r. - HasKey a + :: forall k r ns. + IsKey k ns + => HasCodec (Value k ns) => Member RawStore r - => Key a - -> a + => StoreKey ns + -> k + -> Value k ns -> Sem r () -put k a = +put sk k a = let key = k ^. rawKey val = encode a - in rawStorePut key val + in rawStorePut sk key val get - :: forall a r. - HasKey a + :: forall k r ns. + IsKey k ns + => HasCodec (Value k ns) => Member RawStore r - => Root - -> Key a - -> Sem r (Maybe a) -get index k = do + => StoreKey ns + -> k + -> Sem r (Maybe (Value k ns)) +get sk k = do let key = k ^. rawKey - mRes <- rawStoreGet index key + mRes <- rawStoreGet sk key pure $ case mRes of Nothing -> Nothing Just raw -> case decode raw of Left e -> error $ "Impossible codec error " <> e Right a -> Just a +delete + :: IsKey k ns + => Member RawStore r + => StoreKey ns + -> k + -> Sem r () +delete sk k = rawStoreDelete sk (k ^. rawKey) + prove - :: HasKey a + :: IsKey k ns => Member RawStore r - => Root - -> Key a + => StoreKey ns + -> k -> Sem r (Maybe BS.ByteString) -prove r k = rawStoreProve r (k ^. rawKey) +prove sk k = rawStoreProve sk (k ^. rawKey) diff --git a/hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs b/hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs index 7b32cf26..b89cb701 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs @@ -7,22 +7,26 @@ import Control.Lens (to, (^.)) import Control.Monad.Trans (lift) import Data.ByteArray.Base64String (fromBytes) import Data.Proxy +import GHC.TypeLits (Symbol) import Polysemy (Member, Sem) import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.Codec (HasCodec) import Tendermint.SDK.Router.Class import Tendermint.SDK.Router.Types -import Tendermint.SDK.Store (HasKey (..), RawStore, get) +import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, + get) -class StoreQueryHandler a h where - storeQueryHandler :: Proxy a -> h +class StoreQueryHandler a (ns :: Symbol) h where + storeQueryHandler :: Proxy a -> Proxy ns -> h instance - ( HasKey a - , Key a ~ k + ( IsKey k ns + , a ~ Value k ns + , HasCodec a , Member RawStore r ) - => StoreQueryHandler a (QueryArgs k -> ExceptT QueryError (Sem r) (QueryResult a)) where - storeQueryHandler _ QueryArgs{..} = do + => StoreQueryHandler a ns (QueryArgs k -> ExceptT QueryError (Sem r) (QueryResult a)) where + storeQueryHandler _ _ QueryArgs{..} = do let key = queryArgsData mRes <- lift $ get undefined key case mRes of @@ -36,32 +40,39 @@ instance , queryResultHeight = 0 } -class StoreQueryHandlers (items :: [*]) m where - type QueryApi items :: * - storeQueryHandlers :: Proxy items -> Proxy m -> RouteT (QueryApi items) m +class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) m where + type QueryApi kvs :: * + storeQueryHandlers :: Proxy kvs -> Proxy ns -> Proxy m -> RouteT (QueryApi kvs) m instance ( Queryable a + , IsKey k ns + , a ~ Value k ns + , HasCodec a , Member RawStore r - ) => StoreQueryHandlers (a ': '[]) (Sem r) where - type QueryApi (a ': '[]) = Name a :> QA (Key a) :> Leaf a - storeQueryHandlers _ _ = storeQueryHandler (Proxy :: Proxy a) + ) => StoreQueryHandlers '[(k,a)] ns (Sem r) where + type QueryApi '[(k,a)] = Name a :> QA k :> Leaf a + storeQueryHandlers _ pns _ = storeQueryHandler (Proxy :: Proxy a) pns instance ( Queryable a - , StoreQueryHandlers (a': as) (Sem r) + , IsKey k ns + , a ~ Value k ns + , HasCodec a + , StoreQueryHandlers ((k', a') ': as) ns (Sem r) , Member RawStore r - ) => StoreQueryHandlers (a ': a' : as) (Sem r) where - type (QueryApi (a ': a' : as)) = (Name a :> QA (Key a) :> Leaf a) :<|> QueryApi (a' ': as) - storeQueryHandlers _ pm = - storeQueryHandler (Proxy :: Proxy a) :<|> - storeQueryHandlers (Proxy :: Proxy (a' ': as)) pm + ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns (Sem r) where + type (QueryApi ((k, a) ': (k', a') : as)) = (Name a :> QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) + storeQueryHandlers _ pns pm = + storeQueryHandler (Proxy :: Proxy a) pns :<|> + storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) pns pm allStoreHandlers - :: forall (contents :: [*]) r. - StoreQueryHandlers contents (Sem r) + :: forall (contents :: [*]) ns r. + StoreQueryHandlers contents ns (Sem r) => Member RawStore r => Proxy contents + -> Proxy ns -> Proxy r -> RouteT (QueryApi contents) (Sem r) -allStoreHandlers _ _ = storeQueryHandlers (Proxy :: Proxy contents) (Proxy :: Proxy (Sem r)) +allStoreHandlers pcs pns _ = storeQueryHandlers pcs pns (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs index a023cb87..1019b729 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs @@ -9,19 +9,20 @@ import Tendermint.SDK.AuthTreeStore (AuthTreeDriver, initAuthTreeDriver, interpretAuthTreeStore) import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Store (HasKey (..), Root, get, put) +import Tendermint.SDK.Store (IsKey (..), RawKey (..), + StoreKey (..), get, put) import Test.Hspec spec :: Spec spec = beforeAll beforeAction $ describe "AuthTreeStore" $ do it "can fail to query an empty AuthTreeStore" $ \driver -> do - mv <- runM . interpretAuthTreeStore driver $ get (undefined :: Root) IntStoreKey + mv <- runM . interpretAuthTreeStore driver $ get storeKey IntStoreKey mv `shouldBe` Nothing it "can set a value and query the value" $ \driver -> do mv <- runM . interpretAuthTreeStore driver $ do - put IntStoreKey (IntStore 1) - get (undefined :: Root) IntStoreKey + put storeKey IntStoreKey (IntStore 1) + get storeKey IntStoreKey mv `shouldBe` Just (IntStore 1) @@ -36,9 +37,14 @@ instance HasCodec IntStore where encode (IntStore c) = cs . Binary.encode $ c decode = Right . IntStore . Binary.decode . cs -instance HasKey IntStore where - type Key IntStore = IntStoreKey +instance RawKey IntStoreKey where rawKey = iso (\_ -> cs intStoreKey) (const IntStoreKey) where intStoreKey :: ByteString intStoreKey = "IntStore" + +instance IsKey IntStoreKey "int_store" where + type Value IntStoreKey "int_store" = IntStore + +storeKey :: StoreKey "int_store" +storeKey = StoreKey "int_store" diff --git a/stack.yaml b/stack.yaml index 5eac516f..f0aea6c1 100644 --- a/stack.yaml +++ b/stack.yaml @@ -22,8 +22,12 @@ extra-deps: - proto-lens-protoc-0.5.0.0 - git: https://github.com/oscoin/avl-auth commit: dfc468845a82cdd7d759943b20853999bc026505 - - stylish-haskell-0.9.4.3@sha256:98f60e087ee3e661220cd3e1cf77677d4cecd3b90d110f7ce6f36babb5c2f895,4953 - - hlint-2.1.26@sha256:e0489fd31bc4a531aaf27d7e6b9814b50863b2a78c10e1d77e7b652b00933c9b,3231 + - git: https://github.com/awakesecurity/proto3-suite + commit: 3f6dd6f612cf2eba3c05798926ff924b0d5ab4fa + - git: https://github.com/awakesecurity/proto3-wire + commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 + - bech32-1.0.0@sha256:6bd23abe4a11d61a2d41fdb11db9e1361fc43c215cf2b8602cafaa516c8b6d76,2017 + - polysemy-1.2.0.0 explicit-setup-deps: hs-abci-server: true diff --git a/stack.yaml.lock b/stack.yaml.lock index 1654ffb9..3918934e 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -54,19 +54,47 @@ packages: git: https://github.com/oscoin/avl-auth commit: dfc468845a82cdd7d759943b20853999bc026505 - completed: - hackage: stylish-haskell-0.9.4.3@sha256:98f60e087ee3e661220cd3e1cf77677d4cecd3b90d110f7ce6f36babb5c2f895,4953 + cabal-file: + size: 5720 + sha256: 51545a2592f22d1bf612a2f55ca3af8b0083fb5c620c8996bc6d48f742d1faa1 + name: proto3-suite + version: 0.4.0.0 + git: https://github.com/awakesecurity/proto3-suite + pantry-tree: + size: 4272 + sha256: c8dfec249b4db1d4f0e15332153ed539cb10343ae73345d8cdbf1cf09c714cfc + commit: 3f6dd6f612cf2eba3c05798926ff924b0d5ab4fa + original: + git: https://github.com/awakesecurity/proto3-suite + commit: 3f6dd6f612cf2eba3c05798926ff924b0d5ab4fa +- completed: + cabal-file: + size: 1928 + sha256: 5e750211a8fdae75ffcdd0a70581ae635be5510b321924c5196ac44189fd1432 + name: proto3-wire + version: 1.1.0 + git: https://github.com/awakesecurity/proto3-wire + pantry-tree: + size: 978 + sha256: d3a5d569e005d87e4eb343b150a1c4cbc455cc9833e7713efb937d66b6679b87 + commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 + original: + git: https://github.com/awakesecurity/proto3-wire + commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 +- completed: + hackage: bech32-1.0.0@sha256:6bd23abe4a11d61a2d41fdb11db9e1361fc43c215cf2b8602cafaa516c8b6d76,2017 pantry-tree: - size: 2863 - sha256: 731ad0ffd18ef72bfa4935d717d31598b3286325c9e8d1370ca6406ab104f954 + size: 424 + sha256: 7193ff37729da6c18519a09d88e7e53b5dbd9a1d06399a11fa2676be1bd254f1 original: - hackage: stylish-haskell-0.9.4.3@sha256:98f60e087ee3e661220cd3e1cf77677d4cecd3b90d110f7ce6f36babb5c2f895,4953 + hackage: bech32-1.0.0@sha256:6bd23abe4a11d61a2d41fdb11db9e1361fc43c215cf2b8602cafaa516c8b6d76,2017 - completed: - hackage: hlint-2.1.26@sha256:e0489fd31bc4a531aaf27d7e6b9814b50863b2a78c10e1d77e7b652b00933c9b,3231 + hackage: polysemy-1.2.0.0@sha256:9313315be0746936c65bc02a5b9bc2abd3bd02bcef9329db272a991939e5b037,5776 pantry-tree: - size: 4036 - sha256: b424391b338be3eb5da8c0ba97117e8373848830592445828a3cb9694cbf8988 + size: 3622 + sha256: d7b017cc60394fd87330c577e8c8e48e0fcff3f72cb6c518a20a7fd5cfc8ddab original: - hackage: hlint-2.1.26@sha256:e0489fd31bc4a531aaf27d7e6b9814b50863b2a78c10e1d77e7b652b00933c9b,3231 + hackage: polysemy-1.2.0.0 snapshots: - completed: size: 524786 From 419ae40661e038364f3656fef44638874b5aec7f Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Fri, 15 Nov 2019 13:05:20 -0500 Subject: [PATCH 02/70] Integrate proto3-suite (#104) * WIP testing encode/decode with high level types * Complete test example * Add example to/from different type * Move test derivations to right modules * Use the correct names for proper generation * fix tests? * Remove generated modules list * address some review changes * Make protogen directory * Use proto3 message codecs for Whois * Use less whitespace * Add codec for Amount quite hacky * expand hack comments * use slightly more verbosity for clarity --- hs-abci-examples/nameservice/Setup.hs | 4 +- hs-abci-examples/nameservice/package.yaml | 22 ++-- hs-abci-examples/nameservice/protogen/Main.hs | 4 + .../nameservice/protogen/Protogen.hs | 72 +++++++++++++ .../protos/nameservice/messages.proto | 20 ---- .../protos/nameservice/whois.proto | 8 -- .../Nameservice/Modules/Nameservice/Keeper.hs | 73 +++++++------ .../Modules/Nameservice/Messages.hs | 102 +++++++----------- .../Nameservice/Modules/Nameservice/Router.hs | 6 +- .../Nameservice/Modules/Nameservice/Types.hs | 91 +++++++--------- .../src/Nameservice/Modules/Token.hs | 51 ++++++--- hs-abci-sdk/src/Tendermint/SDK/Auth.hs | 2 +- hs-abci-types/src/Data/ByteArray/HexString.hs | 4 +- 13 files changed, 248 insertions(+), 211 deletions(-) create mode 100644 hs-abci-examples/nameservice/protogen/Main.hs create mode 100644 hs-abci-examples/nameservice/protogen/Protogen.hs delete mode 100644 hs-abci-examples/nameservice/protos/nameservice/messages.proto delete mode 100644 hs-abci-examples/nameservice/protos/nameservice/whois.proto diff --git a/hs-abci-examples/nameservice/Setup.hs b/hs-abci-examples/nameservice/Setup.hs index c81784fa..44671092 100644 --- a/hs-abci-examples/nameservice/Setup.hs +++ b/hs-abci-examples/nameservice/Setup.hs @@ -1,2 +1,2 @@ -import Data.ProtoLens.Setup -main = defaultMainGeneratingProtos "protos" +import Distribution.Simple +main = defaultMain diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 68043cf4..c671eb5c 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -18,7 +18,6 @@ description: Please see the README on GitHub at BL.ByteString -> Either Decode.ParseError a +fromLazyByteString = fromByteString . BL.toStrict + +-------------------------------------------------------------------------------- +-- Requires magic hash extension +-------------------------------------------------------------------------------- + +stripPrefixName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc +stripPrefixName (Single typeName) (Single fieldName) _ = + let prefixLen = length typeName + fieldName' = Single . snakeCase . drop prefixLen $ fieldName + in pPrint fieldName' +-- @NOTE: we don't yet need for other identifiers +stripPrefixName _ _ _ = undefined + +msgStripPrefixOptions :: RenderingOptions +msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } + +messagesProtoFile :: String +messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" + ([ message (proxy# :: Proxy# SetName) + , message (proxy# :: Proxy# BuyName) + , message (proxy# :: Proxy# DeleteName) + ] :: [DotProtoDefinition]) + +whoisProtoFile :: String +whoisProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" + ([ message (proxy# :: Proxy# Whois) ] :: [DotProtoDefinition]) diff --git a/hs-abci-examples/nameservice/protos/nameservice/messages.proto b/hs-abci-examples/nameservice/protos/nameservice/messages.proto deleted file mode 100644 index 30fc9006..00000000 --- a/hs-abci-examples/nameservice/protos/nameservice/messages.proto +++ /dev/null @@ -1,20 +0,0 @@ -syntax = "proto3"; -package nameservice; - -message SetName { - bytes owner = 1; - string name = 2; - string value = 3; -} - -message DeleteName { - bytes owner = 1; - string name = 2; -} - -message BuyName { - bytes buyer = 1; - string name = 2; - string value = 3; - uint64 bid = 4; -} \ No newline at end of file diff --git a/hs-abci-examples/nameservice/protos/nameservice/whois.proto b/hs-abci-examples/nameservice/protos/nameservice/whois.proto deleted file mode 100644 index 82e8096e..00000000 --- a/hs-abci-examples/nameservice/protos/nameservice/whois.proto +++ /dev/null @@ -1,8 +0,0 @@ -syntax = "proto3"; -package nameservice; - -message Whois { - string value = 1; - bytes owner = 2; - uint64 price = 3; -} \ No newline at end of file diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index c4c0ce10..9488ca08 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -2,7 +2,6 @@ module Nameservice.Modules.Nameservice.Keeper where -import Data.Maybe (isNothing) import Data.Proxy import Data.String.Conversions (cs) import GHC.TypeLits (symbolVal) @@ -61,20 +60,20 @@ eval = mapError makeAppError . evalNameservice setName :: HasTokenEff r => HasNameserviceEff r - => MsgSetName + => SetName -> Sem r () -setName MsgSetName{..} = do - mwhois <- getWhois msgSetNameName +setName SetName{..} = do + mwhois <- getWhois setNameName case mwhois of Nothing -> throw $ UnauthorizedSet "Cannot claim name with SetMessage tx." Just currentWhois@Whois{..} -> - if whoisOwner /= msgSetNameOwner + if whoisOwner /= setNameOwner then throw $ UnauthorizedSet "Setter must be the owner of the Name." else do - putWhois msgSetNameName currentWhois {whoisValue = msgSetNameValue} + putWhois setNameName currentWhois {whoisValue = setNameValue} emit NameRemapped - { nameRemappedName = msgSetNameName - , nameRemappedNewValue = msgSetNameValue + { nameRemappedName = setNameName + , nameRemappedNewValue = setNameValue , nameRemappedOldValue = whoisValue } @@ -82,20 +81,20 @@ deleteName :: HasTokenEff r => HasNameserviceEff r => Member (Output Event) r - => MsgDeleteName + => DeleteName -> Sem r () -deleteName MsgDeleteName{..} = do - mWhois <- getWhois msgDeleteNameName +deleteName DeleteName{..} = do + mWhois <- getWhois deleteNameName case mWhois of Nothing -> throw $ InvalidDelete "Can't remove unassigned name." Just Whois{..} -> - if whoisOwner /= msgDeleteNameOwner + if whoisOwner /= deleteNameOwner then throw $ InvalidDelete "Deleter must be the owner." else do - mint msgDeleteNameOwner whoisPrice - deleteWhois msgDeleteNameName + mint deleteNameOwner whoisPrice + deleteWhois deleteNameName emit NameDeleted - { nameDeletedName = msgDeleteNameName + { nameDeletedName = deleteNameName } @@ -103,11 +102,11 @@ buyName :: HasTokenEff r => HasNameserviceEff r => Member (Output Event) r - => MsgBuyName + => BuyName -> Sem r () -- ^ did it succeed buyName msg = do - let name = msgBuyNameName msg + let name = buyNameName msg mWhois <- getWhois name case mWhois of -- The name is unclaimed, go ahead and debit the account @@ -121,40 +120,40 @@ buyName msg = do :: HasTokenEff r => HasNameserviceEff r => Member (Output Event) r - => MsgBuyName + => BuyName -> Sem r () - buyUnclaimedName MsgBuyName{..} = do - burn msgBuyNameBuyer msgBuyNameBid + buyUnclaimedName BuyName{..} = do + burn buyNameBuyer buyNameBid let whois = Whois - { whoisOwner = msgBuyNameBuyer - , whoisValue = msgBuyNameValue - , whoisPrice = msgBuyNameBid + { whoisOwner = buyNameBuyer + , whoisValue = buyNameValue + , whoisPrice = buyNameBid } - putWhois msgBuyNameName whois + putWhois buyNameName whois emit NameClaimed - { nameClaimedOwner = msgBuyNameBuyer - , nameClaimedName = msgBuyNameName - , nameClaimedValue = msgBuyNameValue - , nameClaimedBid = msgBuyNameBid + { nameClaimedOwner = buyNameBuyer + , nameClaimedName = buyNameName + , nameClaimedValue = buyNameValue + , nameClaimedBid = buyNameBid } buyClaimedName :: HasNameserviceEff r => HasTokenEff r => Member (Output Event) r - => MsgBuyName + => BuyName -> Whois -> Sem r () - buyClaimedName MsgBuyName{..} currentWhois = + buyClaimedName BuyName{..} currentWhois = let Whois{ whoisPrice = forsalePrice, whoisOwner = previousOwner } = currentWhois - in if msgBuyNameBid > forsalePrice + in if buyNameBid > forsalePrice then do - transfer msgBuyNameBuyer msgBuyNameBid previousOwner - putWhois msgBuyNameName currentWhois {whoisOwner = msgBuyNameBuyer} + transfer buyNameBuyer buyNameBid previousOwner + putWhois buyNameName currentWhois {whoisOwner = buyNameBuyer} emit NameClaimed - { nameClaimedOwner = msgBuyNameBuyer - , nameClaimedName = msgBuyNameName - , nameClaimedValue = msgBuyNameValue - , nameClaimedBid = msgBuyNameBid + { nameClaimedOwner = buyNameBuyer + , nameClaimedName = buyNameName + , nameClaimedValue = buyNameValue + , nameClaimedBid = buyNameBid } else throw (InsufficientBid "Bid must exceed the price.") diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index 56b4ebb2..f7bac798 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -1,72 +1,42 @@ module Nameservice.Modules.Nameservice.Messages where -import Control.Lens ((^.)) -import Data.Either (Either) -import Data.String.Conversions (cs) import Data.Text (Text) +import GHC.Generics (Generic) import Nameservice.Modules.Nameservice.Types (Name (..)) -import Nameservice.Modules.Token (Address (..), - Amount (..)) -import qualified Proto.Nameservice.Messages as M -import qualified Proto.Nameservice.Messages_Fields as M -import Tendermint.SDK.Auth (Address, - addressFromBytes) +import Nameservice.Modules.Token (Amount (..)) +import Proto3.Suite (Message, Named) +import Tendermint.SDK.Auth (Address) data NameserviceMessage = - SetName MsgSetName - | BuyName MsgBuyName - | DeleteName MsgDeleteName - - -data MsgSetName = MsgSetName - { msgSetNameName :: Name - , msgSetNameValue :: String - , msgSetNameOwner :: Address - } - --- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg -fromProtoMsgSetName :: M.SetName -> Either Text MsgSetName -fromProtoMsgSetName msg = do - msgName <- nonEmpty "Name" . cs $ msg ^. M.name - msgValue <- nonEmpty "Value" . cs $ msg ^. M.value - msgOwner <- nonEmpty "Owner" $ msg ^. M.owner - return MsgSetName { msgSetNameName = Name msgName - , msgSetNameValue = msgValue - , msgSetNameOwner = addressFromBytes msgOwner - } - -data MsgDeleteName = MsgDeleteName - { msgDeleteNameName :: Name - , msgDeleteNameOwner :: Address - } - -fromProtoMsgDeleteName :: M.DeleteName -> Either Text MsgDeleteName -fromProtoMsgDeleteName msg = do - msgName <- nonEmpty "Name" . cs $ msg ^. M.name - msgOwner <- nonEmpty "Owner" $ msg ^. M.owner - return MsgDeleteName { msgDeleteNameName = Name msgName - , msgDeleteNameOwner = addressFromBytes msgOwner - } - -data MsgBuyName = MsgBuyName - { msgBuyNameName :: Name - , msgBuyNameValue :: String - , msgBuyNameBuyer :: Address - , msgBuyNameBid :: Amount - } - -fromProtoMsgBuyName :: M.BuyName -> Either Text MsgBuyName -fromProtoMsgBuyName msg = do - msgName <- nonEmpty "Name" . cs $ msg ^. M.name - msgValue <- nonEmpty "Value" . cs $ msg ^. M.value - msgBuyer <- nonEmpty "Buyer" $ msg ^. M.buyer - msgBid <- Right . Amount $ msg ^. M.bid - return MsgBuyName { msgBuyNameName = Name msgName - , msgBuyNameValue = msgValue - , msgBuyNameBuyer = addressFromBytes msgBuyer - , msgBuyNameBid = msgBid - } - -nonEmpty :: (Eq a, Monoid a) => String -> a -> Either Text a -nonEmpty field x | x == mempty = Left . cs $ (show field ++ ": value cannot be empty") - | otherwise = Right x + NSetName SetName + | NBuyName BuyName + | NDeleteName DeleteName + +-- @NOTE: .proto genration will use these type names as is +-- only field names stripped of prefixes during generation +data SetName = SetName + { setNameName :: Name + , setNameValue :: Text + , setNameOwner :: Address + } deriving (Eq, Show, Generic) + +instance Message SetName +instance Named SetName + +data DeleteName = DeleteName + { deleteNameName :: Name + , deleteNameOwner :: Address + } deriving (Eq, Show, Generic) + +instance Message DeleteName +instance Named DeleteName + +data BuyName = BuyName + { buyNameName :: Name + , buyNameValue :: Text + , buyNameBuyer :: Address + , buyNameBid :: Amount + } deriving (Eq, Show, Generic) + +instance Message BuyName +instance Named BuyName diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 66fe7188..a612fc86 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -13,6 +13,6 @@ router => NameserviceMessage -> Sem r () router = \case - SetName msg -> setName msg - BuyName msg -> buyName msg - DeleteName msg -> deleteName msg + NSetName msg -> setName msg + NBuyName msg -> buyName msg + NDeleteName msg -> deleteName msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index a17b3302..faf59533 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -1,27 +1,28 @@ module Nameservice.Modules.Nameservice.Types where -import Control.Lens (iso, (&), (.~), (^.)) -import Control.Lens.Wrapped (Wrapped (..)) -import Data.Aeson as A -import qualified Data.Binary as Binary -import Data.ByteString (ByteString) -import Data.ByteString as BS -import Data.Maybe (fromJust) -import Data.ProtoLens.Message (Message (defMessage)) -import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.Generics (Generic) -import Nameservice.Aeson (defaultNameserviceOptions) -import Nameservice.Modules.Token (Address (..), Amount (..)) -import qualified Proto.Nameservice.Whois as W -import qualified Proto.Nameservice.Whois_Fields as W -import Tendermint.SDK.Auth (Address, addressFromBytes, - addressToBytes) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) -import qualified Tendermint.SDK.Router as R -import qualified Tendermint.SDK.Store as Store +import Control.Lens (iso) +import Data.Aeson as A +import qualified Data.Binary as Binary +import qualified Data.ByteString.Lazy as BL +import Data.Either.Combinators (mapLeft) +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Data.Text.Lazy as TL +import GHC.Generics (Generic) +import Nameservice.Aeson (defaultNameserviceOptions) +import Nameservice.Modules.Token (Amount (..)) +import Proto3.Suite (HasDefault, Message, MessageField, + Named, Primitive (..), + fromByteString, toLazyByteString) +import qualified Proto3.Suite.DotProto as DotProto +import qualified Proto3.Wire.Decode as Decode +import qualified Proto3.Wire.Encode as Encode +import Tendermint.SDK.Auth (Address) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) +import qualified Tendermint.SDK.Router as R +import qualified Tendermint.SDK.Store as Store -------------------------------------------------------------------------------- @@ -29,19 +30,25 @@ type NameserviceModule = "nameservice" -------------------------------------------------------------------------------- -newtype Name = Name String deriving (Eq, Show, Binary.Binary, A.ToJSON, A.FromJSON) +newtype Name = Name Text deriving (Eq, Show, Generic, Binary.Binary, A.ToJSON, A.FromJSON) +instance Primitive Name where + encodePrimitive n (Name txt) = Encode.text n . TL.fromStrict $ txt + decodePrimitive = Name . TL.toStrict <$> Decode.text + primType _ = DotProto.String +instance HasDefault Name +instance MessageField Name data Whois = Whois - { whoisValue :: String + { whoisValue :: Text , whoisOwner :: Address , whoisPrice :: Amount } deriving (Eq, Show, Generic) - -instance Binary.Binary Whois +instance Message Whois +instance Named Whois instance HasCodec Whois where - encode = cs . Binary.encode - decode = Right . Binary.decode . cs + encode = BL.toStrict . toLazyByteString + decode = mapLeft show . fromByteString instance Store.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) @@ -52,28 +59,6 @@ instance Store.IsKey Name NameserviceModule where instance R.Queryable Whois where type Name Whois = "whois" -instance Wrapped Whois where - type Unwrapped Whois = W.Whois - - _Wrapped' = iso t f - where - t Whois{..} = - let value = cs whoisValue - owner = addressToBytes whoisOwner - Amount price = whoisPrice - in defMessage - & W.value .~ value - & W.owner .~ owner - & W.price .~ price - f msg = - let msgValue = cs $ msg ^. W.value - msgOwner = addressFromBytes $ msg ^. W.owner - msgPrice = Amount $ msg ^. W.price - in Whois { whoisValue = msgValue - , whoisOwner = msgOwner - , whoisPrice = msgPrice - } - -------------------------------------------------------------------------------- -- Exceptions -------------------------------------------------------------------------------- @@ -110,7 +95,7 @@ instance IsAppError NameserviceException where data NameClaimed = NameClaimed { nameClaimedOwner :: Address , nameClaimedName :: Name - , nameClaimedValue :: String + , nameClaimedValue :: Text , nameClaimedBid :: Amount } deriving (Generic) @@ -126,8 +111,8 @@ instance ToEvent NameClaimed where data NameRemapped = NameRemapped { nameRemappedName :: Name - , nameRemappedOldValue :: String - , nameRemappedNewValue :: String + , nameRemappedOldValue :: Text + , nameRemappedNewValue :: Text } deriving Generic nameRemappedAesonOptions :: A.Options diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 42d3b163..8434dede 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -1,4 +1,5 @@ {-# LANGUAGE TemplateHaskell #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} module Nameservice.Modules.Token ( @@ -26,14 +27,12 @@ module Nameservice.Modules.Token ) where -import Control.Lens (iso) import Data.Aeson as A -import qualified Data.Binary as Binary -import Data.ByteString (ByteString) -import qualified Data.ByteString as BS -import Data.Maybe (fromJust, fromMaybe) +import qualified Data.ByteArray.HexString as Hex +import qualified Data.ByteString.Lazy as BL +import Data.Either.Combinators (mapBoth) +import Data.Maybe (fromMaybe) import Data.Proxy -import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word (Word64) import GHC.Generics (Generic) @@ -41,9 +40,14 @@ import Nameservice.Aeson (defaultNameserviceOptions) import Polysemy import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) +import Proto3.Suite (HasDefault (..), MessageField, + Primitive (..)) +import qualified Proto3.Suite.DotProto as DotProto +import qualified Proto3.Wire.Decode as Decode +import qualified Proto3.Wire.Encode as Encode +import Proto3.Wire.Types (fieldNumber) import Servant.API ((:>)) -import Tendermint.SDK.Auth (Address, addressFromBytes, - addressToBytes) +import Tendermint.SDK.Auth (Address (..)) import Tendermint.SDK.BaseApp (HasBaseApp) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) @@ -53,17 +57,40 @@ import Tendermint.SDK.Router (Queryable (..), RouteT) import qualified Tendermint.SDK.Store as Store import Tendermint.SDK.StoreQueries (QueryApi, storeQueryHandlers) -newtype Amount = Amount Word64 deriving (Eq, Show, Binary.Binary, Num, Generic, Ord, A.ToJSON, A.FromJSON) +newtype Amount = Amount Word64 deriving (Eq, Show, Num, Generic, Ord, A.ToJSON, A.FromJSON) +instance Primitive Amount where + encodePrimitive n (Amount amt) = Encode.uint64 n amt + decodePrimitive = Amount <$> Decode.uint64 + primType _ = DotProto.UInt64 +instance HasDefault Amount +instance MessageField Amount instance Queryable Amount where type Name Amount = "balance" +-- @NOTE: hacks instance HasCodec Amount where - encode (Amount b) = cs $ Binary.encode b - decode = Right . Amount . Binary.decode . cs + encode (Amount b) = + -- proto3-wire only exports encoders for message types + let dummyMsgEncoder = Encode.uint64 (fieldNumber 1) + in BL.toStrict . Encode.toLazyByteString . dummyMsgEncoder $ b + decode = mapBoth show Amount . Decode.parse dummyMsgParser + where + -- field is always present; 0 is an arbitrary value + fieldParser = Decode.one Decode.uint64 0 + dummyMsgParser = Decode.at fieldParser (fieldNumber 1) + +-- orphans +instance Primitive Address where + encodePrimitive n (Address hx) = Encode.byteString n (Hex.toBytes hx) + decodePrimitive = Address . Hex.fromBytes <$> Decode.byteString + primType _ = DotProto.Bytes +instance HasDefault Hex.HexString +instance HasDefault Address +instance MessageField Address instance Store.IsKey Address "token" where - type Value Address "token" = Amount + type Value Address "token" = Amount -------------------------------------------------------------------------------- -- Events diff --git a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs index 7767150a..bad34f46 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs @@ -24,7 +24,7 @@ import Polysemy import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Store (IsKey (..), RawKey (..)) -newtype Address = Address Hex.HexString deriving (Eq, Show, Ord, A.ToJSON, A.FromJSON) +newtype Address = Address Hex.HexString deriving (Eq, Show, Generic, Ord, A.ToJSON, A.FromJSON) instance Binary.Binary Address where put (Address a) = Binary.put @ByteString . Hex.toBytes $ a diff --git a/hs-abci-types/src/Data/ByteArray/HexString.hs b/hs-abci-types/src/Data/ByteArray/HexString.hs index 73b30336..fd895060 100644 --- a/hs-abci-types/src/Data/ByteArray/HexString.hs +++ b/hs-abci-types/src/Data/ByteArray/HexString.hs @@ -15,12 +15,12 @@ import Data.Semigroup (Semigroup) import Data.String (IsString (..)) import Data.Text (Text) import Data.Text.Encoding (decodeUtf8, encodeUtf8) - +import GHC.Generics (Generic) -- | Represents a Hex string. Guarantees that all characters it contains -- are valid hex characters. newtype HexString = HexString { unHexString :: ByteString } - deriving (Eq, Ord, Semigroup, Monoid, ByteArrayAccess, ByteArray) + deriving (Eq, Ord, Generic, Semigroup, Monoid, ByteArrayAccess, ByteArray) instance Show HexString where show = ("HexString " ++) . show . format From b28f051ee163053834b6a1b1491e4136f18ddbc0 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 18 Nov 2019 09:24:51 -0800 Subject: [PATCH 03/70] Auth module cleanup (#105) * added MakeMessage class and instances fo r nameservice * added web3 dep for signatures * use serialize * put in validators' * need alternatice instance for Wire.Parser * baby router * baby router * added generic router * added handlers * wip * wip * compile to baseapp * compile to coreeff * stylish, hlint, BaseAppEffR type * fails due to C dep on secp256k1 * remove undefineds * Added basic types modules for messages and address * added transaction parser * Added test for signature recover * refactor general router * whole app compiles * more comments and simplifictions * rename query module * make tests compile * comments for baseapp * added event spec * removed old files * try to fix travis * try to fix travis * try to fix travis * try to fix travis * fix traivs * fix travis * fix travis --- .stylish_haskell.yaml | 1 + .travis.yml | 16 +- hs-abci-examples/nameservice/package.yaml | 12 +- .../nameservice/protogen/Protogen.hs | 6 +- .../src/Nameservice/Application.hs | 55 ++-- .../nameservice/src/Nameservice/Handlers.hs | 129 ++++++++ .../src/Nameservice/Modules/Nameservice.hs | 4 + .../Nameservice/Modules/Nameservice/Keeper.hs | 8 +- .../Modules/Nameservice/Messages.hs | 65 ++++- .../Nameservice/Modules/Nameservice/Query.hs | 19 +- .../Nameservice/Modules/Nameservice/Router.hs | 40 ++- .../Nameservice/Modules/Nameservice/Types.hs | 61 ++-- .../src/Nameservice/Modules/Token.hs | 72 ++--- .../nameservice/src/Nameservice/Server.hs | 59 ++-- hs-abci-examples/simple-storage/package.yaml | 3 +- .../src/SimpleStorage/Application.hs | 2 - .../src/SimpleStorage/Handlers.hs | 2 +- .../SimpleStorage/Modules/SimpleStorage.hs | 57 ++-- .../src/SimpleStorage/Server.hs | 2 +- .../simple-storage/src/SimpleStorage/Types.hs | 8 +- .../test/SimpleStorage/Test/E2ESpec.hs | 7 +- .../test/SimpleStorage/Test/HandlersSpec.hs | 9 +- hs-abci-sdk/package.yaml | 50 ++-- hs-abci-sdk/src/Tendermint/SDK/Auth.hs | 275 ++++++------------ .../src/Tendermint/SDK/AuthTreeStore.hs | 25 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 97 +++--- hs-abci-sdk/src/Tendermint/SDK/Codec.hs | 11 +- hs-abci-sdk/src/Tendermint/SDK/Crypto.hs | 92 ++++++ hs-abci-sdk/src/Tendermint/SDK/Errors.hs | 158 ++++++---- hs-abci-sdk/src/Tendermint/SDK/Events.hs | 38 ++- hs-abci-sdk/src/Tendermint/SDK/Logger.hs | 1 + .../Tendermint/SDK/{Router.hs => Query.hs} | 22 +- .../Tendermint/SDK/{Router => Query}/Class.hs | 27 +- .../SDK/{Router => Query}/Delayed.hs | 5 +- .../SDK/{Router => Query}/Router.hs | 12 +- .../SDK/{StoreQueries.hs => Query/Store.hs} | 30 +- .../Tendermint/SDK/{Router => Query}/Types.hs | 12 +- hs-abci-sdk/src/Tendermint/SDK/Store.hs | 11 +- hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs | 23 ++ .../src/Tendermint/SDK/Types/Address.hs | 29 ++ .../src/Tendermint/SDK/Types/Message.hs | 115 ++++++++ .../src/Tendermint/SDK/Types/Transaction.hs | 84 ++++++ .../Tendermint/SDK/Test/AuthTreeStoreSpec.hs | 21 +- .../test/Tendermint/SDK/Test/CryptoSpec.hs | 35 +++ .../test/Tendermint/SDK/Test/EventSpec.hs | 30 ++ .../test/Tendermint/SDK/Test/ModuleSpec.hs | 109 ------- stack.yaml | 1 - stack.yaml.lock | 7 - 48 files changed, 1240 insertions(+), 717 deletions(-) create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Handlers.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Crypto.hs rename hs-abci-sdk/src/Tendermint/SDK/{Router.hs => Query.hs} (67%) rename hs-abci-sdk/src/Tendermint/SDK/{Router => Query}/Class.hs (62%) rename hs-abci-sdk/src/Tendermint/SDK/{Router => Query}/Delayed.hs (95%) rename hs-abci-sdk/src/Tendermint/SDK/{Router => Query}/Router.hs (92%) rename hs-abci-sdk/src/Tendermint/SDK/{StoreQueries.hs => Query/Store.hs} (71%) rename hs-abci-sdk/src/Tendermint/SDK/{Router => Query}/Types.hs (85%) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs delete mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/ModuleSpec.hs diff --git a/.stylish_haskell.yaml b/.stylish_haskell.yaml index b294d976..8362829d 100644 --- a/.stylish_haskell.yaml +++ b/.stylish_haskell.yaml @@ -205,3 +205,4 @@ language_extensions: - ExistentialQuantification - FunctionalDependencies - ViewPatterns + - PackageImports diff --git a/.travis.yml b/.travis.yml index 3e75c647..e35ed203 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,13 @@ ghc: cabal: "2.4" sudo: required # Cache .stack for build_times-- +addons: + apt: + sources: + - sourceline: 'ppa:tah83/secp256k1' + packages: + - libsecp256k1-dev + cache: directories: - $HOME/.stack @@ -27,7 +34,8 @@ script: - if [ "$TRAVIS_BRANCH" == "master" ]; then make stylish && git diff-index --quiet HEAD; fi - make test-libraries - -- curl https://nixos.org/nix/install | sh -- . $HOME/.nix-profile/etc/profile.d/nix.sh -- stack --nix test hs-abci-types hs-abci-server hs-abci-sdk +# commenting for now as this build is broken with the addition of secp256k1 +# and will uncomment and fix when we are using again. +#- curl https://nixos.org/nix/install | sh +#- . $HOME/.nix-profile/etc/profile.d/nix.sh +#- stack --nix test hs-abci-types hs-abci-server hs-abci-sdk diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index c671eb5c..c55afd8d 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -19,12 +19,6 @@ description: Please see the README on GitHub at = 4.7 && < 5 -- binary - bytestring +- cereal +- data-default-class +- errors - exceptions - either - hs-abci-extra @@ -74,12 +70,14 @@ dependencies: - servant - string-conversions - text +- validation library: source-dirs: src exposed-modules: - Nameservice.Application - Nameservice.Server + - Nameservice.Handlers - Nameservice.Modules.Token - Nameservice.Modules.Nameservice.Messages - Nameservice.Modules.Nameservice.Types diff --git a/hs-abci-examples/nameservice/protogen/Protogen.hs b/hs-abci-examples/nameservice/protogen/Protogen.hs index 1c66231f..69a74249 100644 --- a/hs-abci-examples/nameservice/protogen/Protogen.hs +++ b/hs-abci-examples/nameservice/protogen/Protogen.hs @@ -13,8 +13,7 @@ import Nameservice.Modules.Nameservice.Messages (BuyName (..), SetName (..)) import Nameservice.Modules.Nameservice.Types (Name (..), Whois (..)) -import Nameservice.Modules.Token (Address (..), - Amount (..)) +import Nameservice.Modules.Token (Amount (..)) import Proto3.Suite (DotProtoDefinition, Message, fromByteString, @@ -25,6 +24,7 @@ import Proto3.Suite.DotProto.Rendering (RenderingOptions, defRenderingOptions) import qualified Proto3.Wire.Decode as Decode import Proto3.Wire.Types (FieldNumber (..)) +import Tendermint.SDK.Types.Address (Address (..)) import qualified Text.PrettyPrint as PP -------------------------------------------------------------------------------- @@ -55,7 +55,7 @@ stripPrefixName (Single typeName) (Single fieldName) _ = fieldName' = Single . snakeCase . drop prefixLen $ fieldName in pPrint fieldName' -- @NOTE: we don't yet need for other identifiers -stripPrefixName _ _ _ = undefined +stripPrefixName _ _ _ = error "stripPrefixName unused case" msgStripPrefixOptions :: RenderingOptions msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index e1bc7a0c..92b1a1f6 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -1,19 +1,23 @@ -{-# LANGUAGE UndecidableInstances #-} - module Nameservice.Application - ( AppError(..) - , AppConfig(..) + ( AppConfig(..) , makeAppConfig , Handler + , compileToBaseApp , runHandler + , queryServer ) where -import Control.Exception (Exception) -import Control.Monad.Catch (throwM) -import Polysemy (Sem) -import Polysemy.Error (Error, runError) -import qualified Tendermint.SDK.BaseApp as BaseApp -import qualified Tendermint.SDK.Logger.Katip as KL +import Control.Exception (Exception) +import Data.Proxy +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import Polysemy (Sem) +import Servant.API ((:<|>) (..)) +import qualified Tendermint.SDK.Auth as A +import Tendermint.SDK.BaseApp ((:&)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.Logger.Katip as KL +import Tendermint.SDK.Query (QueryApplication, serve) data AppConfig = AppConfig { baseAppContext :: BaseApp.Context @@ -27,25 +31,30 @@ makeAppConfig logCfg = do -------------------------------------------------------------------------------- -data AppError = AppError String deriving (Show) - -instance Exception AppError - type EffR = - ( Error AppError - ': BaseApp.BaseApp - ) + N.NameserviceEffR :& T.TokenEffR :& A.AuthEffR :& BaseApp.BaseApp type Handler = Sem EffR +compileToBaseApp + :: Sem EffR a + -> Sem BaseApp.BaseApp a +compileToBaseApp = A.eval . T.eval . N.eval + -- NOTE: this should probably go in the library runHandler :: AppConfig -> Handler a -> IO a -runHandler AppConfig{baseAppContext} m = do - eRes <- BaseApp.eval baseAppContext . - runError $ m - case eRes of - Left e -> throwM e - Right a -> pure a +runHandler AppConfig{baseAppContext} = + BaseApp.eval baseAppContext . compileToBaseApp + +-------------------------------------------------------------------------------- + +type QueryApi = T.Api :<|> N.Api + +apiP :: Proxy QueryApi +apiP = Proxy + +queryServer :: QueryApplication (Sem BaseApp.BaseApp) +queryServer = serve (Proxy :: Proxy QueryApi) (T.server :<|> N.server) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs new file mode 100644 index 00000000..150cb2e3 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs @@ -0,0 +1,129 @@ +module Nameservice.Handlers where + +import Control.Lens (to, (&), (.~), (^.)) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Default.Class (def) +import Data.Proxy +import Nameservice.Application (Handler, + compileToBaseApp) +import Nameservice.Modules.Nameservice as N +import Network.ABCI.Server.App (App (..), + MessageType (..), + Request (..), + Response (..)) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import Polysemy (Sem) +import Polysemy.Error (catch) +import Tendermint.SDK.Application (defaultHandler) +import Tendermint.SDK.BaseApp (BaseApp) +import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Errors (AppError, SDKError (..), + deliverTxAppError, + throwSDKError) +import Tendermint.SDK.Events (withEventBuffer) +import Tendermint.SDK.Query (QueryApplication) +import Tendermint.SDK.Types.Transaction (parseRawTransaction, + parseTx) + +echoH + :: Request 'MTEcho + -> Sem BaseApp (Response 'MTEcho) +echoH (RequestEcho echo) = + pure . ResponseEcho $ def & Resp._echoMessage .~ echo ^. Req._echoMessage + +flushH + :: Request 'MTFlush + -> Sem BaseApp (Response 'MTFlush) +flushH = defaultHandler + +infoH + :: Request 'MTInfo + -> Sem BaseApp (Response 'MTInfo) +infoH = defaultHandler + +setOptionH + :: Request 'MTSetOption + -> Sem BaseApp (Response 'MTSetOption) +setOptionH = defaultHandler + +-- TODO: this one might be useful for initializing to 0 +-- instead of doing that manually in code +initChainH + :: Request 'MTInitChain + -> Sem BaseApp (Response 'MTInitChain) +initChainH = defaultHandler + +queryH + :: QueryApplication (Sem BaseApp) + -> Request 'MTQuery + -> Sem BaseApp (Response 'MTQuery) +queryH serveRoutes (RequestQuery query) = do + queryResp <- serveRoutes query + pure $ ResponseQuery queryResp + +beginBlockH + :: Request 'MTBeginBlock + -> Sem BaseApp (Response 'MTBeginBlock) +beginBlockH = defaultHandler + +-- only checks to see if the tx parses +checkTxH + :: Request 'MTCheckTx + -> Sem BaseApp (Response 'MTCheckTx) +checkTxH = defaultHandler--(RequestCheckTx checkTx) = + + -- pure . ResponseCheckTx $ + -- case decodeAppTxMessage $ checkTx ^. Req._checkTxTx . to convert of + -- Left _ -> def & Resp._checkTxCode .~ 1 + -- Right (ATMUpdateCount _) -> def & Resp._checkTxCode .~ 0 + +deliverTxH + :: Request 'MTDeliverTx + -> Sem BaseApp (Response 'MTDeliverTx) -- Sem BaseApp (Response 'MTDeliverTx) +deliverTxH (RequestDeliverTx deliverTx) = + let tryToRespond = do + tx <- either (throwSDKError . ParseError) return $ do + rawTx <- parseRawTransaction $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes + parseTx (Proxy @Secp256k1) rawTx + events <- withEventBuffer . compileToBaseApp $ N.router tx + return $ ResponseDeliverTx $ + def & Resp._deliverTxCode .~ 0 + & Resp._deliverTxEvents .~ events + in tryToRespond `catch` \(err :: AppError) -> + return . ResponseDeliverTx $ def & deliverTxAppError .~ err + + + --case decodeAppTxMessage $ deliverTx ^. Req._deliverTxTx . to convert of + -- Left _ -> return . ResponseDeliverTx $ + -- def & Resp._deliverTxCode .~ 1 + -- Right (ATMUpdateCount updateCountTx) -> do + -- let count = SS.Count $ updateCountTxCount updateCountTx + -- events <- withEventBuffer $ putCount count + -- return $ ResponseDeliverTx $ + -- def & Resp._deliverTxCode .~ 0 + -- & Resp._deliverTxEvents .~ events + +endBlockH + :: Request 'MTEndBlock + -> Sem BaseApp (Response 'MTEndBlock) +endBlockH = defaultHandler + +commitH + :: Request 'MTCommit + -> Sem BaseApp (Response 'MTCommit) +commitH = defaultHandler + +nameserviceApp :: QueryApplication (Sem BaseApp) -> App (Sem BaseApp) +nameserviceApp serveRoutes = App $ \case + msg@(RequestEcho _) -> echoH msg + msg@(RequestFlush _) -> flushH msg + msg@(RequestInfo _) -> infoH msg + msg@(RequestSetOption _) -> setOptionH msg + msg@(RequestInitChain _) -> initChainH msg + msg@(RequestQuery _) -> queryH serveRoutes msg + msg@(RequestBeginBlock _) -> beginBlockH msg + msg@(RequestCheckTx _) -> checkTxH msg + msg@(RequestDeliverTx _) -> deliverTxH msg + msg@(RequestEndBlock _) -> endBlockH msg + msg@(RequestCommit _) -> commitH msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index 68c81a00..a1b83ac0 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -17,6 +17,9 @@ module Nameservice.Modules.Nameservice -- * interpreter , eval + -- * message router + , router + -- * query API , Api , server @@ -26,4 +29,5 @@ module Nameservice.Modules.Nameservice import Nameservice.Modules.Nameservice.Keeper import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query +import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 9488ca08..ac20fd3e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -14,7 +14,7 @@ import Polysemy (Member, Members, Sem, import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) -import Tendermint.SDK.BaseApp (HasBaseApp) +import Tendermint.SDK.BaseApp (HasBaseAppEff) import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) import Tendermint.SDK.Events (Event, emit) @@ -34,15 +34,14 @@ storeKey :: Store.StoreKey NameserviceModule storeKey = Store.StoreKey . cs . symbolVal $ (Proxy :: Proxy NameserviceModule) eval - :: HasBaseApp r + :: HasBaseAppEff r => HasTokenEff r - => Member (Error AppError) r => Sem (Nameservice ': Error NameserviceException ': r) a -> Sem r a eval = mapError makeAppError . evalNameservice where evalNameservice - :: HasBaseApp r + :: HasBaseAppEff r => Sem (Nameservice ': r) a -> Sem r a evalNameservice = @@ -157,3 +156,4 @@ buyName msg = do , nameClaimedBid = buyNameBid } else throw (InsufficientBid "Bid must exceed the price.") + diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index f7bac798..b31b4d56 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -1,16 +1,35 @@ module Nameservice.Modules.Nameservice.Messages where +import Control.Applicative ((<|>)) +import Control.Lens (( # ), (^.)) +import qualified Data.Aeson as A +import Data.Either (Either) +import Data.Foldable (sequenceA_) +import Data.Proxy +import Data.String.Conversions (cs) import Data.Text (Text) +import qualified Data.Validation as V import GHC.Generics (Generic) -import Nameservice.Modules.Nameservice.Types (Name (..)) +import GHC.TypeLits (symbolVal) +import Nameservice.Aeson (defaultNameserviceOptions) +import Nameservice.Modules.Nameservice.Types (Name (..), + NameserviceModule) import Nameservice.Modules.Token (Amount (..)) import Proto3.Suite (Message, Named) -import Tendermint.SDK.Auth (Address) +import Tendermint.SDK.Types.Address (Address, + addressFromBytes) +import Tendermint.SDK.Types.Message (DecodingOption (..), + Msg (..), + ParseMessage (..), + ValidateMessage (..), + isAuthorCheck, + nonEmptyCheck) data NameserviceMessage = NSetName SetName | NBuyName BuyName | NDeleteName DeleteName + deriving (Eq, Show, Generic) -- @NOTE: .proto genration will use these type names as is -- only field names stripped of prefixes during generation @@ -40,3 +59,45 @@ data BuyName = BuyName instance Message BuyName instance Named BuyName + +instance {-# OVERLAPPING #-} ParseMessage 'Proto3Suite NameserviceMessage where + decodeMessage p bs = + fmap NSetName (decodeMessage p bs) <> + fmap NBuyName (decodeMessage p bs) <> + fmap NDeleteName (decodeMessage p bs) + +instance ValidateMessage NameserviceMessage where + validateMessage m@Msg{msgData} = case msgData of + NSetName msg -> validateMessage m {msgData = msg} + NBuyName msg -> validateMessage m {msgData = msg} + NDeleteName msg -> validateMessage m {msgData = msg} + +-- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg +instance ValidateMessage SetName where + validateMessage msg@Msg{..} = + let SetName{setNameName, setNameValue} = msgData + Name name = setNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , nonEmptyCheck "Value" setNameValue + , isAuthorCheck "Owner" msg setNameOwner + ] + +instance ValidateMessage DeleteName where + validateMessage msg@Msg{..} = + let DeleteName{deleteNameName} = msgData + Name name = deleteNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , isAuthorCheck "Owner" msg deleteNameOwner + ] + +instance ValidateMessage BuyName where + validateMessage msg@Msg{..} = + let BuyName{buyNameName, buyNameValue} = msgData + Name name = buyNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , nonEmptyCheck "Value" buyNameValue + , isAuthorCheck "Owner" msg buyNameBuyer + ] diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index 2964b9f4..66bbbb90 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -1,13 +1,12 @@ module Nameservice.Modules.Nameservice.Query where import Data.Proxy -import Nameservice.Modules.Nameservice.Types (Name, Whois) -import Polysemy (Member, Sem) -import Servant.API ((:>)) -import qualified Tendermint.SDK.Router as R -import qualified Tendermint.SDK.Store as Store -import Tendermint.SDK.StoreQueries (QueryApi, - storeQueryHandlers) +import Nameservice.Modules.Nameservice.Keeper (storeKey) +import Nameservice.Modules.Nameservice.Types (Name, Whois) +import Polysemy (Member, Sem) +import Servant.API ((:>)) +import qualified Tendermint.SDK.Query as Q +import qualified Tendermint.SDK.Store as Store -------------------------------------------------------------------------------- -- | Query API @@ -15,8 +14,8 @@ import Tendermint.SDK.StoreQueries (QueryApi, type NameserviceContents = '[(Name, Whois)] -type Api = "nameservice" :> QueryApi NameserviceContents +type Api = "nameservice" :> Q.QueryApi NameserviceContents -server :: Member Store.RawStore r => R.RouteT Api (Sem r) +server :: Member Store.RawStore r => Q.RouteT Api (Sem r) server = - storeQueryHandlers (Proxy :: Proxy NameserviceContents) (Proxy :: Proxy "nameservice") (Proxy :: Proxy (Sem r)) + Q.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index a612fc86..620c3e4d 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -1,18 +1,44 @@ module Nameservice.Modules.Nameservice.Router where +import Data.ByteString (ByteString) +import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (HasNameserviceEff, buyName, deleteName, setName) import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) import Nameservice.Modules.Token (HasTokenEff) -import Polysemy (Sem) +import Polysemy (Members, Sem) +import Polysemy.Error (Error) +import Tendermint.SDK.Auth (AuthError) +import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Errors (AppError, + SDKError (..), + throwSDKError) +import qualified Tendermint.SDK.TxRouter as R +import Tendermint.SDK.Types.Message (DecodingOption (..), + Msg (..), + ParseMessage (..), + formatMessageParseError) +import Tendermint.SDK.Types.Transaction (Tx (..)) router - :: HasTokenEff r + :: Members [Error AppError, Error AuthError] r + => HasTokenEff r => HasNameserviceEff r - => NameserviceMessage + => Tx Secp256k1 ByteString -> Sem r () -router = \case - NSetName msg -> setName msg - NBuyName msg -> buyName msg - NDeleteName msg -> deleteName msg +router tx@Tx{txMsg} = + case decodeMessage (Proxy @'Proto3Suite) $ msgData txMsg of + Left parseErrMsg -> throwSDKError . ParseError . formatMessageParseError $ parseErrMsg + Right msg -> handler $ tx {txMsg = txMsg {msgData = msg}} + where + handler + :: HasTokenEff r + => HasNameserviceEff r + => R.Handler r NameserviceMessage + handler Tx{txMsg} = + let Msg{msgData=msg} = txMsg + in case msg of + NSetName txMsg -> setName txMsg + NBuyName txMsg -> buyName txMsg + NDeleteName txMsg -> deleteName txMsg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index faf59533..b498fd45 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -1,28 +1,33 @@ module Nameservice.Modules.Nameservice.Types where -import Control.Lens (iso) -import Data.Aeson as A -import qualified Data.Binary as Binary -import qualified Data.ByteString.Lazy as BL -import Data.Either.Combinators (mapLeft) -import Data.String.Conversions (cs) -import Data.Text (Text) -import qualified Data.Text.Lazy as TL -import GHC.Generics (Generic) -import Nameservice.Aeson (defaultNameserviceOptions) -import Nameservice.Modules.Token (Amount (..)) -import Proto3.Suite (HasDefault, Message, MessageField, - Named, Primitive (..), - fromByteString, toLazyByteString) -import qualified Proto3.Suite.DotProto as DotProto -import qualified Proto3.Wire.Decode as Decode -import qualified Proto3.Wire.Encode as Encode -import Tendermint.SDK.Auth (Address) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) -import qualified Tendermint.SDK.Router as R -import qualified Tendermint.SDK.Store as Store +import Control.Lens (iso, (&), (.~), (^.)) +import Control.Lens.Wrapped (Wrapped (..)) +import Data.Aeson as A +import Data.Bifunctor (first) +import Data.ByteString (ByteString) +import Data.ByteString as BS +import Data.Maybe (fromJust) +import qualified Data.Serialize as Serialize +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Data.Text.Lazy as TL +import GHC.Generics (Generic) +import Nameservice.Aeson (defaultNameserviceOptions) +import Nameservice.Modules.Token (Amount (..)) +import Proto3.Suite (HasDefault, Message, + MessageField, Named, + Primitive (..), fromByteString, + toLazyByteString) +import qualified Proto3.Suite.DotProto as DotProto +import qualified Proto3.Wire.Decode as Decode +import qualified Proto3.Wire.Encode as Encode +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) +import qualified Tendermint.SDK.Query as Q +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.Types.Address (Address, addressFromBytes, + addressToBytes) -------------------------------------------------------------------------------- @@ -30,7 +35,7 @@ type NameserviceModule = "nameservice" -------------------------------------------------------------------------------- -newtype Name = Name Text deriving (Eq, Show, Generic, Binary.Binary, A.ToJSON, A.FromJSON) +newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON) instance Primitive Name where encodePrimitive n (Name txt) = Encode.text n . TL.fromStrict $ txt decodePrimitive = Name . TL.toStrict <$> Decode.text @@ -38,6 +43,8 @@ instance Primitive Name where instance HasDefault Name instance MessageField Name +instance Q.FromQueryData Name + data Whois = Whois { whoisValue :: Text , whoisOwner :: Address @@ -47,8 +54,8 @@ instance Message Whois instance Named Whois instance HasCodec Whois where - encode = BL.toStrict . toLazyByteString - decode = mapLeft show . fromByteString + encode = cs . toLazyByteString + decode = first (cs . show) . fromByteString instance Store.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) @@ -56,7 +63,7 @@ instance Store.RawKey Name where instance Store.IsKey Name NameserviceModule where type Value Name NameserviceModule = Whois -instance R.Queryable Whois where +instance Q.Queryable Whois where type Name Whois = "whois" -------------------------------------------------------------------------------- diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 8434dede..d0f19bc4 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -27,35 +27,37 @@ module Nameservice.Modules.Token ) where -import Data.Aeson as A -import qualified Data.ByteArray.HexString as Hex -import qualified Data.ByteString.Lazy as BL -import Data.Either.Combinators (mapBoth) -import Data.Maybe (fromMaybe) +import Data.Aeson as A +import Data.Bifunctor (bimap) +import qualified Data.ByteArray.HexString as Hex +import Data.Maybe (fromMaybe) import Data.Proxy -import Data.Text (Text) -import Data.Word (Word64) -import GHC.Generics (Generic) -import Nameservice.Aeson (defaultNameserviceOptions) +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word64) +import GHC.Generics (Generic) +import Nameservice.Aeson (defaultNameserviceOptions) import Polysemy -import Polysemy.Error (Error, mapError, throw) -import Polysemy.Output (Output) -import Proto3.Suite (HasDefault (..), MessageField, - Primitive (..)) -import qualified Proto3.Suite.DotProto as DotProto -import qualified Proto3.Wire.Decode as Decode -import qualified Proto3.Wire.Encode as Encode -import Proto3.Wire.Types (fieldNumber) -import Servant.API ((:>)) -import Tendermint.SDK.Auth (Address (..)) -import Tendermint.SDK.BaseApp (HasBaseApp) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Events (Event, FromEvent, ToEvent (..), - emit) -import Tendermint.SDK.Router (Queryable (..), RouteT) -import qualified Tendermint.SDK.Store as Store -import Tendermint.SDK.StoreQueries (QueryApi, storeQueryHandlers) +import Polysemy.Error (Error, mapError, throw) +import Polysemy.Output (Output) +import Proto3.Suite (HasDefault (..), MessageField, + Primitive (..)) +import qualified Proto3.Suite.DotProto as DotProto +import qualified Proto3.Wire.Decode as Decode +import qualified Proto3.Wire.Encode as Encode +import Proto3.Wire.Types (fieldNumber) +import Servant.API ((:>)) +import Tendermint.SDK.BaseApp (HasBaseAppEff) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Events (Event, FromEvent, ToEvent (..), + emit) +import Tendermint.SDK.Query (FromQueryData, Queryable (..), + RouteT) +import Tendermint.SDK.Query.Store (QueryApi, storeQueryHandlers) +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.Types.Address (Address, addressFromBytes, + addressToBytes) newtype Amount = Amount Word64 deriving (Eq, Show, Num, Generic, Ord, A.ToJSON, A.FromJSON) instance Primitive Amount where @@ -73,8 +75,8 @@ instance HasCodec Amount where encode (Amount b) = -- proto3-wire only exports encoders for message types let dummyMsgEncoder = Encode.uint64 (fieldNumber 1) - in BL.toStrict . Encode.toLazyByteString . dummyMsgEncoder $ b - decode = mapBoth show Amount . Decode.parse dummyMsgParser + in cs . Encode.toLazyByteString . dummyMsgEncoder $ b + decode = bimap (cs . show) Amount . Decode.parse dummyMsgParser where -- field is always present; 0 is an arbitrary value fieldParser = Decode.one Decode.uint64 0 @@ -82,8 +84,8 @@ instance HasCodec Amount where -- orphans instance Primitive Address where - encodePrimitive n (Address hx) = Encode.byteString n (Hex.toBytes hx) - decodePrimitive = Address . Hex.fromBytes <$> Decode.byteString + encodePrimitive n a = Encode.byteString n $ addressToBytes a + decodePrimitive = addressFromBytes <$> Decode.byteString primType _ = DotProto.Bytes instance HasDefault Hex.HexString instance HasDefault Address @@ -146,14 +148,13 @@ storeKey :: Store.StoreKey "token" storeKey = Store.StoreKey "token" eval - :: HasBaseApp r - => Member (Error AppError) r + :: HasBaseAppEff r => Sem (Token ': Error TokenException ': r) a -> Sem r a eval = mapError makeAppError . evalToken where evalToken - :: HasBaseApp r + :: HasBaseAppEff r => Sem (Token ': r) a -> Sem r a evalToken = @@ -173,7 +174,7 @@ type Api = "token" :> QueryApi TokenContents server :: Member Store.RawStore r => RouteT Api (Sem r) server = - storeQueryHandlers (Proxy :: Proxy TokenContents) (Proxy :: Proxy "token") (Proxy :: Proxy (Sem r)) + storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) -------------------------------------------------------------------------------- @@ -227,4 +228,3 @@ mint mint addr amount = do bal <- getBalance addr putBalance addr (bal + amount) - diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index c79d4462..9542eba9 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -3,42 +3,41 @@ module Nameservice.Server (makeAndServeApplication) where import Data.Foldable (fold) import Data.Monoid (Endo (..)) import Data.Proxy -import Nameservice.Application (AppConfig, - AppError, - Handler, +import Nameservice.Application (AppConfig (..), + queryServer, runHandler) +import Nameservice.Handlers (nameserviceApp) import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) import qualified Network.ABCI.Server.Middleware.RequestLogger as ReqLogger import qualified Network.ABCI.Server.Middleware.ResponseLogger as ResLogger +import Polysemy (Sem) import Tendermint.SDK.Application (MakeApplication (..), createApplication) -import Tendermint.SDK.Router (QueryApplication, - serve) +import Tendermint.SDK.BaseApp (BaseApp, eval) +import Tendermint.SDK.Errors (AppError) makeAndServeApplication :: AppConfig -> IO () -makeAndServeApplication cfg = undefined --- let serveRoutes :: QueryApplication Handler --- serveRoutes = serve (Proxy :: Proxy SS.Api) SS.server --- makeApplication :: MakeApplication Handler AppError --- makeApplication = MakeApplication --- { transformer = runHandler cfg --- , appErrorP = Proxy --- , app = simpleStorageApp serveRoutes --- , initialize = [SS.initialize] --- } --- putStrLn "Starting ABCI application..." --- application <- createApplication makeApplication --- serveApp =<< hookInMiddleware application --- where --- mkMiddleware :: IO (Middleware IO) --- mkMiddleware = do --- reqLogger <- ReqLogger.mkLogStdoutDev --- resLogger <- ResLogger.mkLogStdoutDev --- pure . appEndo . fold $ --- [ Endo reqLogger --- , Endo resLogger --- ] --- hookInMiddleware _app = do --- middleware <- mkMiddleware --- pure $ middleware _app +makeAndServeApplication cfg = do + let makeApplication :: MakeApplication (Sem BaseApp) AppError + makeApplication = MakeApplication + { transformer = eval $ baseAppContext cfg + , appErrorP = Proxy + , app = nameserviceApp queryServer + , initialize = [] + } + putStrLn "Starting ABCI application..." + application <- createApplication makeApplication + serveApp =<< hookInMiddleware application + where + mkMiddleware :: IO (Middleware IO) + mkMiddleware = do + reqLogger <- ReqLogger.mkLogStdoutDev + resLogger <- ResLogger.mkLogStdoutDev + pure . appEndo . fold $ + [ Endo reqLogger + , Endo resLogger + ] + hookInMiddleware _app = do + middleware <- mkMiddleware + pure $ middleware _app diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index 8df742ad..cf5e61e1 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -43,8 +43,9 @@ dependencies: - async - avl-auth - base >= 4.7 && < 5 -- binary - bytestring +- cereal +- cereal-text - containers - conduit - cryptonite diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index 11c7dbdb..88d53c90 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -1,5 +1,3 @@ -{-# LANGUAGE UndecidableInstances #-} - module SimpleStorage.Application ( AppError(..) , AppConfig(..) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs index af3ef16a..87b78a85 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs @@ -16,7 +16,7 @@ import SimpleStorage.Types (AppTxMessage (..), decodeAppTxMessage) import Tendermint.SDK.Application (defaultHandler) import Tendermint.SDK.Events (withEventBuffer) -import Tendermint.SDK.Router (QueryApplication) +import Tendermint.SDK.Query (QueryApplication) echoH :: Request 'MTEcho diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index 6f63e10d..c178a7f4 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -24,40 +24,41 @@ module SimpleStorage.Modules.SimpleStorage ) where -import Control.Lens (iso) -import Crypto.Hash (SHA256 (..), hashWith) -import qualified Data.Aeson as A -import qualified Data.Binary as Binary -import Data.ByteArray (convert) -import Data.ByteString (ByteString) -import Data.Int (Int32) -import Data.Maybe (fromJust) +import Control.Lens (iso) +import Crypto.Hash (SHA256 (..), hashWith) +import qualified Data.Aeson as A +import Data.Bifunctor (first) +import Data.ByteArray (convert) +import Data.ByteString (ByteString) +import Data.Int (Int32) +import Data.Maybe (fromJust) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.Generics (Generic) -import Polysemy (Member, Sem, interpret, makeSem) -import Polysemy.Output (Output) -import Servant.API ((:>)) -import Tendermint.SDK.BaseApp (HasBaseApp) -import Tendermint.SDK.Codec (HasCodec (..)) -import qualified Tendermint.SDK.Events as Events -import Tendermint.SDK.Router (EncodeQueryResult, FromQueryData, - Queryable (..), RouteT) -import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, - StoreKey (..), get, put) -import Tendermint.SDK.StoreQueries (QueryApi, storeQueryHandlers) +import qualified Data.Serialize as Serialize +import Data.String.Conversions (cs) +import GHC.Generics (Generic) +import Polysemy (Member, Sem, interpret, makeSem) +import Polysemy.Output (Output) +import Servant.API ((:>)) +import Tendermint.SDK.BaseApp (HasBaseAppEff) +import Tendermint.SDK.Codec (HasCodec (..)) +import qualified Tendermint.SDK.Events as Events +import Tendermint.SDK.Query (FromQueryData, QueryApi, + Queryable (..), RouteT, + storeQueryHandlers) +import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, + StoreKey (..), get, put) -------------------------------------------------------------------------------- -- Types -------------------------------------------------------------------------------- -newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON) +newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON, Serialize.Serialize) data CountKey = CountKey instance HasCodec Count where - encode (Count c) = cs . Binary.encode $ c - decode = Right . Count . Binary.decode . cs + encode = Serialize.encode + decode = first cs . Serialize.decode instance RawKey CountKey where rawKey = iso (\_ -> cs countKey) (const CountKey) @@ -70,8 +71,6 @@ instance IsKey CountKey "simple_storage" where instance FromQueryData CountKey -instance EncodeQueryResult Count - instance Queryable Count where type Name Count = "count" @@ -108,7 +107,7 @@ makeSem ''SimpleStorage eval :: forall r. - HasBaseApp r + HasBaseAppEff r => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) eval = interpret (\case PutCount count -> do @@ -119,7 +118,7 @@ eval = interpret (\case ) initialize - :: HasBaseApp r + :: HasBaseAppEff r => Member (Output Events.Event) r => Sem r () initialize = eval $ do @@ -135,4 +134,4 @@ type Api = "simple_storage" :> QueryApi CountStoreContents server :: Member RawStore r => RouteT Api (Sem r) server = - storeQueryHandlers (Proxy :: Proxy CountStoreContents) (Proxy :: Proxy "simple_storage") (Proxy :: Proxy (Sem r)) + storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs index 887bbff2..c5788da3 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs @@ -15,7 +15,7 @@ import SimpleStorage.Handlers (simpleStorageApp import qualified SimpleStorage.Modules.SimpleStorage as SS import Tendermint.SDK.Application (MakeApplication (..), createApplication) -import Tendermint.SDK.Router (QueryApplication, +import Tendermint.SDK.Query (QueryApplication, serve) makeAndServeApplication :: AppConfig -> IO () diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs index 1042b768..ef508706 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs @@ -3,16 +3,20 @@ module SimpleStorage.Types where import Control.Lens (from, iso, view, (&), (.~), (^.)) import Control.Lens.Wrapped (Wrapped (..), _Unwrapped') -import Data.Binary (Binary) import Data.ByteString (ByteString) import Data.Int (Int32) import qualified Data.ProtoLens as PL import Data.ProtoLens.Message (Message (..)) +import qualified Data.Serialize as Serialize +import Data.Serialize.Text () import Data.Text (Text) import GHC.Generics (Generic) import Proto.SimpleStorage.Messages as M import Proto.SimpleStorage.Messages_Fields as M + + + data AppTxMessage = ATMUpdateCount UpdateCountTx @@ -35,7 +39,7 @@ data UpdateCountTx = UpdateCountTx , updateCountTxCount :: Int32 } deriving (Show, Eq, Generic) -instance Binary UpdateCountTx +instance Serialize.Serialize UpdateCountTx instance Wrapped UpdateCountTx where type Unwrapped UpdateCountTx = M.UpdateCount diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index cdeb1e37..f76df999 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -3,13 +3,12 @@ module SimpleStorage.Test.E2ESpec where import Control.Lens (to, (^.)) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) -import Data.Binary (decode, encode) import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteArray.HexString as Hex -import qualified Data.ByteString.Lazy as LBS import Data.Default.Class (def) import Data.Int (Int32) +import Data.Serialize (decode, encode) import Data.String.Conversions (cs) import qualified Network.ABCI.Types.Messages.Response as Resp import qualified Network.ABCI.Types.Messages.Response as Response @@ -62,10 +61,10 @@ spec = do encodeCount :: Int32 -> Base64String -encodeCount = Base64.fromBytes . LBS.toStrict . encode +encodeCount = Base64.fromBytes . encode decodeCount :: Base64String -> Int32 -decodeCount = decode . LBS.fromStrict . Base64.toBytes +decodeCount = (\(Right a) -> a) . decode . Base64.toBytes runRPC :: forall a. RPC.TendermintM a -> IO a runRPC = RPC.runTendermintM rpcConfig diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs index d12610ac..d32b4200 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs @@ -2,14 +2,13 @@ module SimpleStorage.Test.HandlersSpec where import Control.Lens (to, (&), (.~), (^.)) import Control.Lens.Wrapped (_Unwrapped', _Wrapped') -import Data.Binary (decode, encode) import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteArray.Base64String as Base64 -import qualified Data.ByteString.Lazy as LBS import Data.Int (Int32) import Data.ProtoLens (defMessage) import Data.ProtoLens.Encoding (encodeMessage) import Data.Proxy +import Data.Serialize (decode, encode) import Data.Text (pack) import Network.ABCI.Server.App (Request (..), Response (..)) @@ -21,7 +20,7 @@ import SimpleStorage.Handlers (deliverTxH, queryH) import qualified SimpleStorage.Modules.SimpleStorage as SS import SimpleStorage.Types (UpdateCountTx (..)) import Tendermint.SDK.Logger.Katip -import Tendermint.SDK.Router (serve) +import Tendermint.SDK.Query import Tendermint.SDK.Store (rawKey) import Test.Hspec import Test.QuickCheck @@ -62,7 +61,7 @@ beforeAction :: IO AppConfig beforeAction = mkLogConfig "handler-spec" "SimpleStorage" >>= makeAppConfig encodeCount :: Int32 -> Base64String -encodeCount = Base64.fromBytes . LBS.toStrict . encode +encodeCount = Base64.fromBytes . encode decodeCount :: Base64String -> Int32 -decodeCount = decode . LBS.fromStrict . Base64.toBytes +decodeCount = (\(Right a) -> a) . decode . Base64.toBytes diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 95472792..4b5b36bc 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -33,6 +33,7 @@ default-extensions: - DeriveFunctor - StandaloneDeriving - ConstraintKinds + - PackageImports dependencies: @@ -41,16 +42,15 @@ dependencies: - async - avl-auth - base >= 4.7 && < 5 -- bech32 -- binary - bytestring +- cereal +- cereal-text - containers - conduit - cryptonite - data-default-class - errors - exceptions -- free - hs-abci-types - hs-abci-server - http-types @@ -61,11 +61,16 @@ dependencies: - polysemy - polysemy-plugin - profunctors +- proto-lens +- proto3-wire +- proto3-suite +- secp256k1-haskell - servant - stm - string-conversions - text - uri-bytestring +- validation library: source-dirs: @@ -75,31 +80,38 @@ library: - -Werror - -Wall exposed-modules: - - Tendermint.SDK.Store - - Tendermint.SDK.Codec - - Tendermint.SDK.StoreQueries - - Tendermint.SDK.Subscription + - Tendermint.SDK.Application + - Tendermint.SDK.Aeson + - Tendermint.SDK.Auth - Tendermint.SDK.AuthTreeStore - - Tendermint.SDK.Router.Types - - Tendermint.SDK.Router.Class - - Tendermint.SDK.Router - Tendermint.SDK.BaseApp + - Tendermint.SDK.Codec + - Tendermint.SDK.Crypto + - Tendermint.SDK.Errors + - Tendermint.SDK.Events + - Tendermint.SDK.Query + - Tendermint.SDK.Query.Class + - Tendermint.SDK.Query.Delayed + - Tendermint.SDK.Query.Router + - Tendermint.SDK.Query.Store + - Tendermint.SDK.Query.Types - Tendermint.SDK.Logger - Tendermint.SDK.Logger.Katip - - Tendermint.SDK.Application - - Tendermint.SDK.Auth - - Tendermint.SDK.Aeson - - Tendermint.SDK.Events - - Tendermint.SDK.Errors - + - Tendermint.SDK.Store + - Tendermint.SDK.Subscription + - Tendermint.SDK.Types.Message + - Tendermint.SDK.Types.Transaction + - Tendermint.SDK.Types.Address + - Tendermint.SDK.TxRouter + tests: hs-abci-sdk-test: main: Spec.hs source-dirs: test other-modules: - Tendermint.SDK.Test.AuthTreeStoreSpec - # - Tendermint.SDK.Test.StoreExampleSpec - - Tendermint.SDK.Test.ModuleSpec + - Tendermint.SDK.Test.CryptoSpec + - Tendermint.SDK.Test.EventSpec ghc-options: - -Werror @@ -115,6 +127,6 @@ tests: - hspec - hspec-core - hspec-discover - - binary + - cereal - QuickCheck - quickcheck-instances diff --git a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs index bad34f46..5b9e6a99 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs @@ -2,214 +2,103 @@ module Tendermint.SDK.Auth where -import qualified Codec.Binary.Bech32 as Bech32 -import Control.Lens (iso) -import Control.Monad (when) -import Control.Monad.Catch (Exception, MonadCatch, catch) -import Control.Monad.IO.Class (MonadIO (..)) -import Control.Monad.Reader (ReaderT, asks) -import qualified Data.Aeson as A -import qualified Data.Binary as Binary -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString) -import Data.Int (Int64) -import Data.IORef -import Data.Monoid (Endo (..)) -import Data.Proxy (Proxy (..)) -import Data.String.Conversions -import Data.Text (Text) -import GHC.Generics (Generic) -import GHC.TypeLits (KnownSymbol, symbolVal) +import Data.Bifunctor (first) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.Serialize.Text () +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word +import GHC.Generics (Generic) +import GHC.TypeLits (symbolVal) import Polysemy -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Store (IsKey (..), RawKey (..)) - -newtype Address = Address Hex.HexString deriving (Eq, Show, Generic, Ord, A.ToJSON, A.FromJSON) - -instance Binary.Binary Address where - put (Address a) = Binary.put @ByteString . Hex.toBytes $ a - get = Address . Hex.fromBytes <$> Binary.get @ByteString - -addressToBytes :: Address -> ByteString -addressToBytes (Address addrHex) = Hex.toBytes addrHex - -addressFromBytes :: ByteString -> Address -addressFromBytes = Address . Hex.fromBytes - -newtype AccountAddress prefix = AccountAddress Address deriving (Eq, Show, Ord) - -instance IsHumanReadable prefix => A.ToJSON (AccountAddress prefix) where - toJSON = A.toJSON . toBech32 - -instance IsHumanReadable prefix => A.FromJSON (AccountAddress prefix) where - parseJSON = A.withText "AccountAddress" $ \t -> - either (fail . cs) pure $ fromBech32 t - -fromBech32 :: forall prefix. IsHumanReadable prefix => Text -> Either Text (AccountAddress prefix) -fromBech32 a = case Bech32.decode a of - Left e -> Left . cs . show $ e - Right (_hrp, dp) -> - if getPrefix (Proxy :: Proxy prefix) /= _hrp - then Left "MismatchedHumanReadablePartError" - else case Bech32.dataPartToBytes dp of - Nothing -> Left "FailedToParseDataPartAsBytesError" - Just bs -> Right . AccountAddress . Address $ Hex.fromBytes bs - -toBech32 :: IsHumanReadable prefix => AccountAddress prefix -> Text -toBech32 ((AccountAddress (Address a)) :: AccountAddress prefix) = - Bech32.encodeLenient (getPrefix (Proxy :: Proxy prefix)) (Bech32.dataPartFromBytes . Hex.toBytes $ a) - --- | NOTE: There are rules for valid prefix p, namely --- | 1. 1 <= length p <= 83 --- | 2. min (map chr p) >= 33 --- | 3. max (map chr p) <= 126 -class KnownSymbol prefix => IsHumanReadable prefix where - getPrefix :: Proxy prefix -> Bech32.HumanReadablePart - - default getPrefix :: Proxy prefix -> Bech32.HumanReadablePart - getPrefix p = - case Bech32.humanReadablePartFromText. cs $ symbolVal p of - Left err -> error $ show err - Right hrp -> hrp - -data PubKey = PubKey - { pubKeyAddress :: Address - , pubKeyRaw :: ByteString - } deriving (Eq, Ord, Generic) - -instance Binary.Binary PubKey - -data PrivateKey = PrivateKey - { privateKeyPubKey :: ByteString - , privateKeyRaw :: ByteString - } - -newtype Signature = Signature ByteString deriving Eq - -data Signer = Signer - { signerSign :: PrivateKey -> ByteString -> Either Text Signature - , signerRecover :: Signature -> ByteString -> PubKey - } - -signerVerify - :: Signer - -> PubKey - -> Signature - -> ByteString - -> Bool -signerVerify Signer{signerRecover} pubKey sig msg = - signerRecover sig msg == pubKey +import Polysemy.Error (Error, mapError) +import Tendermint.SDK.BaseApp (HasBaseAppEff) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Query (Queryable (..)) +import Tendermint.SDK.Store (IsKey (..), StoreKey (..), get, + put) +import Tendermint.SDK.Types.Address (Address) + +-------------------------------------------------------------------------------- + +data AuthModule +type AuthModuleSym = "auth" + +-------------------------------------------------------------------------------- data Coin = Coin { coinDenomination :: Text - , coinAmount :: Int64 + , coinAmount :: Word64 } deriving Generic -instance Binary.Binary Coin +instance Serialize.Serialize Coin data Account = Account - { accountPubKey :: PubKey - , accountCoins :: [Coin] - , accountNumber :: Int64 - , accountSequence :: Int64 + { coins :: [Coin] + , nonce :: Word64 } deriving Generic -instance Binary.Binary Account +instance Serialize.Serialize Account -data Accounts m a where - PutAccount :: Account -> Accounts m () - GetAccount :: Address -> Accounts m Account +instance HasCodec Account where + encode = Serialize.encode + decode = first cs . Serialize.decode -makeSem ''Accounts +instance IsKey Address AuthModule where + type Value Address AuthModule = Account -instance HasCodec Account where - encode = cs . Binary.encode - decode = Right . Binary.decode . cs +instance Queryable Account where + type Name Account = "account" -instance RawKey Address where - rawKey = iso (\(Address a) -> Hex.toBytes a) (Address . Hex.fromBytes) +-------------------------------------------------------------------------------- -instance IsKey Address "auth" where - type Value Address "auth" = Account +data AuthError = + RecoveryError Text + | TransactionParseError Text + +instance IsAppError AuthError where + makeAppError (RecoveryError msg) = AppError + { appErrorCode = 1 + , appErrorCodespace = cs . symbolVal $ (Proxy :: Proxy AuthModuleSym) + , appErrorMessage = "Signature Recovery Error: " <> msg + } + makeAppError (TransactionParseError msg) = AppError + { appErrorCode = 2 + , appErrorCodespace = cs . symbolVal $ (Proxy :: Proxy AuthModuleSym) + , appErrorMessage = msg + } -------------------------------------------------------------------------------- -data Msg msg = Msg - { msgRoute :: Text - , msgType :: Text - , msgSignBytes :: ByteString - , msgGetSigners :: [Address] - , msgValidate :: Maybe Text - , msgData :: msg - } - ---verifyAllMsgSignatures :: Signer -> Msg msg -> Bool ---verifyAllMsgSignatures signer Msg{msgGetSigners, msgSignBytes} = --- let isValid (pubKey, sig) = signerVerify signer pubKey sig msgSignBytes --- in getAll . mconcat . map (All . isValid) $ msgGetSigners - - -data Fee = Fee - { feeAmount :: [Coin] - , feeGas :: Int64 - } - -data Tx tx msg = Tx - { txMsgs :: [Msg msg] - , txFee :: Fee - , txSignatures :: [Signature] - , txMemo :: Text - , txValidate :: Maybe Text - , txData :: tx - } - -data GasMeter = GasMeter - { gasMeterLimit :: Int64 - , gasMeterConsumed :: Int64 - } - -data OutOfGasException = OutOfGasException deriving (Show) - -instance Exception OutOfGasException - -data Context = Context - { contextGasMeter :: GasMeter - } - -data TxEnv tx = TxEnv - { transaction :: tx - , context :: IORef Context - } - -type AnteDecorator m tx msg = ReaderT (TxEnv (Tx tx msg)) m () - -newContextDecorator - :: forall m tx msg. - MonadCatch m - => MonadIO m - => Endo (AnteDecorator m tx msg) -newContextDecorator = Endo $ \next -> do - tx <- asks transaction - contextVar <- asks context - let gasMeter = GasMeter (feeGas . txFee $ tx) 0 - liftIO $ writeIORef contextVar (Context gasMeter) - next `catch` (\(_ :: OutOfGasException) -> - pure () - ) - -validateBasicDecorator - :: forall m tx msg. - Monad m - => Endo (AnteDecorator m tx msg) -validateBasicDecorator = Endo $ \next -> do - tx <- asks transaction - let msgSigners = map msgGetSigners $ txMsgs tx - expectedSignersN = length $ txSignatures tx - when (expectedSignersN == 0) $ - error "TODO: There must be signers" - when (any (\signers -> length signers /= expectedSignersN) msgSigners) $ - error "TODO: fill in error for wrong number of signers" - let feeAmounts = map coinAmount . feeAmount . txFee $ tx - when (any (< 0) feeAmounts) $ - error "TODO: No negative fees" - next +data Accounts m a where + PutAccount :: Address -> Account -> Accounts m () + GetAccount :: Address -> Accounts m (Maybe Account) + +makeSem ''Accounts + +type AuthEffR = [Accounts, Error AuthError] +type HasAuthEff r = Members AuthEffR r + +storeKey :: StoreKey AuthModule +storeKey = StoreKey "auth" + +eval + :: HasBaseAppEff r + => Member (Error AppError) r + => Sem (Accounts ': Error AuthError ': r) a + -> Sem r a +eval = mapError makeAppError . evalAuth + where + evalAuth + :: HasBaseAppEff r + => Sem (Accounts ': r) a + -> Sem r a + evalAuth = + interpret (\case + GetAccount addr -> + get storeKey addr + PutAccount addr acnt -> + put storeKey addr acnt + ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs index b60ba552..ecabfa15 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs @@ -1,4 +1,8 @@ -module Tendermint.SDK.AuthTreeStore where +module Tendermint.SDK.AuthTreeStore + ( AuthTree + , initAuthTree + , eval + ) where import Control.Concurrent.STM (atomically) import Control.Concurrent.STM.TVar @@ -11,9 +15,10 @@ import Data.ByteString (ByteString) import Polysemy (Embed, Member, Sem, interpret) import Tendermint.SDK.Store (RawStore (..), StoreKey (..)) --------------------------------------------------------------------------------- --- --------------------------------------------------------------------------------- + +-- At the moment, the 'AuthTreeStore' is our only interpreter for the 'RawStore' effect. +-- It is an in memory merklized key value store. You can find the repository here +-- https://github.com/oscoin/avl-auth newtype AuthTreeHash = AuthTreeHash (Cryptonite.Digest Cryptonite.SHA256) @@ -22,19 +27,19 @@ instance AT.MerkleHash AuthTreeHash where hashLeaf k v = AuthTreeHash $ Cryptonite.hashLeaf k v concatHashes (AuthTreeHash a) (AuthTreeHash b) = AuthTreeHash $ Cryptonite.concatHashes a b -data AuthTreeDriver = AuthTreeDriver +data AuthTree = AuthTree { treeVar :: TVar (AT.Tree ByteString ByteString) } -initAuthTreeDriver :: IO AuthTreeDriver -initAuthTreeDriver = AuthTreeDriver <$> newTVarIO AT.empty +initAuthTree :: IO AuthTree +initAuthTree = AuthTree <$> newTVarIO AT.empty -interpretAuthTreeStore +eval :: Member (Embed IO) r - => AuthTreeDriver + => AuthTree -> Sem (RawStore ': r) a -> Sem r a -interpretAuthTreeStore AuthTreeDriver{treeVar} = +eval AuthTree{treeVar} = interpret (\case RawStorePut (StoreKey sk) k v -> liftIO . atomically $ do diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 9695ef0b..020c8e84 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -3,83 +3,110 @@ module Tendermint.SDK.BaseApp where +import Control.Exception (throwIO) import Control.Lens (over, view) import qualified Katip as K import Polysemy (Embed, Member, Members, Sem, runM) +import Polysemy.Error (Error, runError) import Polysemy.Output (Output) import Polysemy.Reader (Reader, asks, local, runReader) import Polysemy.Resource (Resource, resourceToIO) -import Tendermint.SDK.AuthTreeStore (AuthTreeDriver, - initAuthTreeDriver, - interpretAuthTreeStore) +import qualified Tendermint.SDK.AuthTreeStore as AT +import Tendermint.SDK.Errors (AppError) import Tendermint.SDK.Events (Event, EventBuffer, evalWithBuffer, newEventBuffer) import Tendermint.SDK.Logger (Logger) import qualified Tendermint.SDK.Logger.Katip as KL import Tendermint.SDK.Store (RawStore) -type HasBaseApp r = + +-- | A constraint listing all externally visible effects in the 'BaseApp'. +type HasBaseAppEff r = ( Member Logger r + , Member (Error AppError) r , Member RawStore r , Member (Output Event) r , Member Resource r ) -data Context = Context - { contextLogConfig :: KL.LogConfig - , contextEventBuffer :: EventBuffer - , contextAuthTreeDriver :: AuthTreeDriver - } +-- | Concrete row of effects for the BaseApp +type BaseAppEffR = + [ Output Event + , RawStore + , Logger + , Resource + , Error AppError + ] -type CoreEff = - '[ Reader KL.LogConfig +-- | CoreEff is one level below BaseApp, it as a seperation of BaseApp from +-- | its interpretation. +type CoreEffR = + '[ Reader EventBuffer + , Reader KL.LogConfig , Embed IO ] -type BaseApp = - ( Output Event - ': RawStore - ': Logger - ': Resource - ': Reader EventBuffer - ': CoreEff - ) +-- | This type family gives a nice syntax for combining multiple lists of effects. +type family (as :: [a]) :& (bs :: [a]) :: [a] where + '[] :& bs = bs + (a ': as) :& bs = a ': (as :& bs) + +infixr 5 :& + -instance (Members CoreEff r) => K.Katip (Sem r) where +-- TODO: it would be really nice to not have this CoreEff here, but to just +-- interpret into it as an intermediate step in the total evaluation of BaseApp. +-- Polysemy probably has a way to do this already. +type BaseApp = BaseAppEffR :& CoreEffR + +instance (Members CoreEffR r) => K.Katip (Sem r) where getLogEnv = asks $ view KL.logEnv localLogEnv f m = local (over KL.logEnv f) m -instance (Members CoreEff r) => K.KatipContext (Sem r) where +instance (Members CoreEffR r) => K.KatipContext (Sem r) where getKatipContext = asks $ view KL.logContext localKatipContext f m = local (over KL.logContext f) m getKatipNamespace = asks $ view KL.logNamespace localKatipNamespace f m = local (over KL.logNamespace f) m +-- | 'Context' is the environment required to run 'CoreEff' to 'IO' +data Context = Context + { contextLogConfig :: KL.LogConfig + , contextEventBuffer :: EventBuffer + , contextAuthTree :: AT.AuthTree + } makeContext :: KL.LogConfig -> IO Context makeContext logCfg = do - authTreeD <- initAuthTreeDriver + authTree <- AT.initAuthTree eb <- newEventBuffer pure $ Context { contextLogConfig = logCfg , contextEventBuffer = eb - , contextAuthTreeDriver = authTreeD + , contextAuthTree = authTree } --- NOTE: Do we need this step? I think so because of the logger. --- You don't want to run against a fresh katip context every time. +-- | An intermediary interpeter, bringing 'BaseApp' down to 'CoreEff'. +compileToCoreEff + :: Context + -> Sem BaseApp a + -> Sem CoreEffR (Either AppError a) +compileToCoreEff Context{contextAuthTree} = + runError . + resourceToIO . + KL.evalKatip . + AT.eval contextAuthTree . + evalWithBuffer + +-- | The standard interpeter for 'BaseApp'. eval :: Context -> Sem BaseApp a -> IO a -eval Context{..} action = - runM . - runReader contextLogConfig . - runReader contextEventBuffer . - resourceToIO . - KL.evalKatip . - interpretAuthTreeStore contextAuthTreeDriver . - evalWithBuffer $ action - --------------------------------------------------------------------------------- +eval ctx@Context{..} action = do + eRes <- runM . + runReader contextLogConfig . + runReader contextEventBuffer . + compileToCoreEff ctx $ action + either throwIO return eRes diff --git a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs index 6d1c221d..144f7231 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs @@ -1,13 +1,10 @@ module Tendermint.SDK.Codec where import qualified Data.ByteString as BS +import Data.Text (Text) +-- | This class is used as a codec for all items stored in +-- | the database. class HasCodec a where encode :: a -> BS.ByteString - decode :: BS.ByteString -> Either String a - -class HasCodec c => ContainsCodec c cs where - -instance {-# OVERLAPPING #-} HasCodec c => ContainsCodec c (c : cs) - -instance {-# OVERLAPPABLE #-} (HasCodec c, ContainsCodec c cs) => ContainsCodec c (c' : cs) + decode :: BS.ByteString -> Either Text a diff --git a/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs b/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs new file mode 100644 index 00000000..97c05ac4 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs @@ -0,0 +1,92 @@ +module Tendermint.SDK.Crypto + ( MakeDigest(..) + , SignatureSchema(..) + , RecoverableSignatureSchema(..) + , parsePubKey + , Secp256k1 + ) where + +import Control.Error (hush, note) +import Crypto.Hash (Digest, hashWith) +import Crypto.Hash.Algorithms (Keccak_256 (..), + SHA256) +import qualified Crypto.Secp256k1 as Secp256k1 +import Data.ByteArray (convert) +import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteString as B +import Data.Maybe (fromMaybe) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.Text (Text) +import qualified Network.ABCI.Types.Messages.FieldTypes as FT +import Tendermint.SDK.Types.Address (Address, + addressFromBytes) + +-- | Class encapsulating data which can hashed. +class MakeDigest a where + makeDigest :: a -> Digest SHA256 + +-- | Defines the types and methods for the signature schema parameterized by 'alg'. +class SignatureSchema alg where + type PubKey alg :: * + type PrivateKey alg :: * + type Signature alg :: * + type Message alg :: * + + algorithm :: Proxy alg -> Text + sign :: Proxy alg -> PrivateKey alg -> Message alg -> Signature alg + verify :: Proxy alg -> PubKey alg -> Signature alg -> Message alg -> Bool + + makePubKey :: Proxy alg -> B.ByteString -> Maybe (PubKey alg) + makeSignature :: Proxy alg -> B.ByteString -> Maybe (Signature alg) + addressFromPubKey :: Proxy alg -> PubKey alg -> Address + +-- | Class allowing for signing and recovering signatures for messages. +class SignatureSchema alg => RecoverableSignatureSchema alg where + type RecoverableSignature alg :: * + + signRecoverableMessage :: Proxy alg -> PrivateKey alg -> Message alg -> RecoverableSignature alg + recover :: Proxy alg -> RecoverableSignature alg -> Message alg -> Maybe (PubKey alg) + + makeRecoverableSignature :: Proxy alg -> B.ByteString -> Maybe (RecoverableSignature alg) + +data Secp256k1 + +msgFromSHA256 :: Digest SHA256 -> Secp256k1.Msg +msgFromSHA256 dig = fromMaybe (error "Digest SHA256 wasn't 32 bytes.") $ + Secp256k1.msg $ convert dig + +instance SignatureSchema Secp256k1 where + type PubKey Secp256k1 = Secp256k1.PubKey + type PrivateKey Secp256k1 = Secp256k1.SecKey + type Signature Secp256k1 = Secp256k1.Sig + type Message Secp256k1 = Digest SHA256 + + algorithm _ = "secp256k1" + sign _ priv dig = Secp256k1.signMsg priv (msgFromSHA256 dig) + verify _ pub sig dig = Secp256k1.verifySig pub sig (msgFromSHA256 dig) + + makePubKey _ = Secp256k1.importPubKey + makeSignature _ = Secp256k1.importSig + -- For lack of a better idea, we're just going to use the Ethereum style here + addressFromPubKey _ = addressFromBytes . B.drop 12 . convert . + hashWith Keccak_256 . Secp256k1.exportPubKey False + +instance RecoverableSignatureSchema Secp256k1 where + type RecoverableSignature Secp256k1 = Secp256k1.RecSig + + signRecoverableMessage _ priv dig = Secp256k1.signRecMsg priv (msgFromSHA256 dig) + recover _ sig dig = Secp256k1.recover sig (msgFromSHA256 dig) + -- NOTE: I think the use of Data.Serialize is harmless here, because it basically + -- just peels off bytes + makeRecoverableSignature _ bs = Secp256k1.importCompactRecSig =<< hush (Serialize.decode bs) + +parsePubKey + :: SignatureSchema alg + => Proxy alg + -> FT.PubKey + -> Either Text (PubKey alg) +parsePubKey p FT.PubKey{..} + | pubKeyType == algorithm p = + note "Couldn't parse PubKey" $ makePubKey p (Base64.toBytes pubKeyData) + | otherwise = Left $ "Unsupported curve: " <> pubKeyType diff --git a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs index b790f797..76da2f8b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs @@ -1,81 +1,121 @@ module Tendermint.SDK.Errors ( AppError(..) , IsAppError(..) - , HasAppError(..) - , catchAppError + , queryAppError + , checkTxAppError + , deliverTxAppError + , SDKError(..) + , throwSDKError ) where -import Control.Lens (Lens', lens, (&), (.~)) -import Data.Default.Class (Default (..)) +import Control.Exception (Exception) +import Control.Lens (Lens', lens) import Data.Text (Text) import Data.Word (Word32) import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy -import Polysemy.Error (Error, runError) +import Polysemy.Error (Error, throw) +-- | This type represents a common error response for the query, checkTx, +-- | and deliver tx abci-messages. data AppError = AppError { appErrorCode :: Word32 , appErrorCodespace :: Text , appErrorMessage :: Text - } + } deriving Show +instance Exception AppError + +-- | Allows for custom application error types to be coerced into the +-- standard error resposne. class IsAppError e where makeAppError :: e -> AppError -class HasAppError resp where - appError :: Lens' resp AppError +-- | This class is used to set the 'AppError' data into the appropriate +-- | response fields for the query abci-message. +queryAppError :: Lens' Response.Query AppError +queryAppError = lens g s + where + g Response.Query{..} = AppError + { appErrorCode = queryCode + , appErrorCodespace = queryCodespace + , appErrorMessage = queryLog + } + s query AppError{..} = query + { Response.queryCode = appErrorCode + , Response.queryCodespace = appErrorCodespace + , Response.queryLog = appErrorMessage + } + +-- | This class is used to set the 'AppError' data into the appropriate +-- | response fields for the checkTx abci-message. +checkTxAppError :: Lens' Response.CheckTx AppError +checkTxAppError = lens g s + where + g Response.CheckTx{..} = AppError + { appErrorCode = checkTxCode + , appErrorCodespace = checkTxCodespace + , appErrorMessage = checkTxLog + } + s checkTx AppError{..} = checkTx + { Response.checkTxCode = appErrorCode + , Response.checkTxCodespace = appErrorCodespace + , Response.checkTxLog = appErrorMessage + } + +-- | This class is used to set the 'AppError' data into the appropriate +-- | response fields for the deliverTx abci-message. +deliverTxAppError :: Lens' Response.DeliverTx AppError +deliverTxAppError = lens g s + where + g Response.DeliverTx{..} = AppError + { appErrorCode = deliverTxCode + , appErrorCodespace = deliverTxCodespace + , appErrorMessage = deliverTxLog + } + s deliverTx AppError{..} = deliverTx + { Response.deliverTxCode = appErrorCode + , Response.deliverTxCodespace = appErrorCodespace + , Response.deliverTxLog = appErrorMessage + } + +-------------------------------------------------------------------------------- +-- Stock SDK Errors +-------------------------------------------------------------------------------- + +-- | These errors originate from the SDK itself. The "sdk" namespace is reserved +-- | for this error type and should not be used in modules or applications. +data SDKError = + InternalError + -- ^ Something went wrong and we have no idea what. + | ParseError Text + -- ^ Parsing errors for SDK specific types, e.g. 'RawTransaction' or 'Msg', etc. + | UnmatchedRoute Text + -- ^ The name of the route that failed to match. --- NOTE: We're not actually using this right now as Query messages --- are handled by a special router. -instance HasAppError Response.Query where - appError = lens g s - where - g Response.Query{..} = AppError - { appErrorCode = queryCode - , appErrorCodespace = queryCodespace - , appErrorMessage = queryLog - } - s query AppError{..} = query - { Response.queryCode = appErrorCode - , Response.queryCodespace = appErrorCodespace - , Response.queryLog = appErrorMessage - } +-- | As of right now it's not expected that one can recover from an 'SDKError', +-- | so we are throwing them as 'AppError's directly. +throwSDKError + :: Member (Error AppError) r + => SDKError + -> Sem r a +throwSDKError = throw . makeAppError -instance HasAppError Response.CheckTx where - appError = lens g s - where - g Response.CheckTx{..} = AppError - { appErrorCode = checkTxCode - , appErrorCodespace = checkTxCodespace - , appErrorMessage = checkTxLog - } - s checkTx AppError{..} = checkTx - { Response.checkTxCode = appErrorCode - , Response.checkTxCodespace = appErrorCodespace - , Response.checkTxLog = appErrorMessage - } +instance IsAppError SDKError where + makeAppError InternalError = AppError + { appErrorCode = 1 + , appErrorCodespace = "sdk" + , appErrorMessage = "Internal Error" + } -instance HasAppError Response.DeliverTx where - appError = lens g s - where - g Response.DeliverTx{..} = AppError - { appErrorCode = deliverTxCode - , appErrorCodespace = deliverTxCodespace - , appErrorMessage = deliverTxLog - } - s deliverTx AppError{..} = deliverTx - { Response.deliverTxCode = appErrorCode - , Response.deliverTxCodespace = appErrorCodespace - , Response.deliverTxLog = appErrorMessage - } + makeAppError (ParseError msg) = AppError + { appErrorCode = 2 + , appErrorCodespace = "sdk" + , appErrorMessage = msg + } -catchAppError - :: HasAppError resp - => Default resp - => Sem (Error AppError ': r) resp - -> Sem r resp -catchAppError action = do - eRes <- runError action - case eRes of - Right res -> pure res - Left err -> pure $ def & appError .~ err + makeAppError (UnmatchedRoute route) = AppError + { appErrorCode = 3 + , appErrorCodespace = "sdk" + , appErrorMessage = "Route not recognized: " <> route <> "." + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/Events.hs index c2d28de3..8401d8d7 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Events.hs @@ -3,13 +3,10 @@ module Tendermint.SDK.Events , ToEvent(..) , FromEvent(..) , emit - + , makeEvent , EventBuffer , newEventBuffer - -- , appendEvent - -- , flushEventBuffer , withEventBuffer - , evalWithBuffer ) where @@ -39,26 +36,41 @@ TODO : These JSON instances are fragile but convenient. We should come up with a custom solution. -} +-- | A class representing a type that can be emitted as an event in the +-- | event logs for the deliverTx response. class ToEvent e where makeEventType :: Proxy e -> String makeEventData :: e -> [(BS.ByteString, BS.ByteString)] + default makeEventData :: A.ToJSON e => e -> [(BS.ByteString, BS.ByteString)] makeEventData e = case A.toJSON e of A.Object obj -> bimap cs (cs . A.encode) <$> toList obj _ -> mempty -class FromEvent e where +-- | A class that can parse event log items in the deliverTx response. Primarily +-- | useful for client applications and testing. +class ToEvent e => FromEvent e where fromEvent :: Event -> Either Text e + default fromEvent :: A.FromJSON e => Event -> Either Text e - fromEvent Event{eventAttributes} = - let fromKVPair :: KVPair -> Either String (Text, A.Value) - fromKVPair (KVPair k v) = do - value <- A.eitherDecode . cs @BS.ByteString . Base64.toBytes $ v - return (cs @BS.ByteString . Base64.toBytes $ k, value) - in fmapL cs $ do - kvPairs <- traverse fromKVPair eventAttributes - A.eitherDecode . A.encode . A.Object . fromList $ kvPairs + fromEvent Event{eventType, eventAttributes} = + let expectedType = makeEventType (Proxy @e) + in if cs eventType /= expectedType + then fail ("Couldn't math expected event type " <> expectedType <> + " with found type " <> cs eventType) + else + let fromKVPair :: KVPair -> Either String (Text, A.Value) + fromKVPair (KVPair k v) = do + value <- A.eitherDecode . cs @BS.ByteString . Base64.toBytes $ v + return (cs @BS.ByteString . Base64.toBytes $ k, value) + in fmapL cs $ do + kvPairs <- traverse fromKVPair eventAttributes + A.eitherDecode . A.encode . A.Object . fromList $ kvPairs +-- This is the internal implementation of the interpreter for event +-- logging. We allocate a buffer that can queue events as they are thrown, +-- then flush the buffer at the end of transaction execution. It will +-- also flush in the event that exceptions are thrown. data EventBuffer = EventBuffer (MVar.MVar [Event]) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Logger.hs b/hs-abci-sdk/src/Tendermint/SDK/Logger.hs index 7322c33e..c2f2d0c5 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Logger.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Logger.hs @@ -11,6 +11,7 @@ import Polysemy (makeSem) data Severity = Debug | Info | Warning | Error | Exception deriving (Eq, Ord) +-- | Effect allowing for console logging. data Logger m a where Log :: Severity -> Text -> Logger m () diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Query.hs similarity index 67% rename from hs-abci-sdk/src/Tendermint/SDK/Router.hs rename to hs-abci-sdk/src/Tendermint/SDK/Query.hs index 29e61f28..c20c9f78 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query.hs @@ -1,23 +1,23 @@ -module Tendermint.SDK.Router +module Tendermint.SDK.Query ( serve , serveRouter , QueryApplication - , module Tendermint.SDK.Router.Class - , module Tendermint.SDK.Router.Router - , module Tendermint.SDK.Router.Types - , module Tendermint.SDK.Router.Delayed + , module Tendermint.SDK.Query.Class + , module Tendermint.SDK.Query.Router + , module Tendermint.SDK.Query.Types + , module Tendermint.SDK.Query.Delayed + , module Tendermint.SDK.Query.Store ) where import Control.Monad.IO.Class (MonadIO (..)) import Data.Proxy - import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response - -import Tendermint.SDK.Router.Class -import Tendermint.SDK.Router.Delayed -import Tendermint.SDK.Router.Router -import Tendermint.SDK.Router.Types +import Tendermint.SDK.Query.Class +import Tendermint.SDK.Query.Delayed +import Tendermint.SDK.Query.Router +import Tendermint.SDK.Query.Store +import Tendermint.SDK.Query.Types type QueryApplication m = Request.Query -> m Response.Query diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router/Class.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs similarity index 62% rename from hs-abci-sdk/src/Tendermint/SDK/Router/Class.hs rename to hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs index 89f420ba..afd442f0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router/Class.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs @@ -1,22 +1,23 @@ -module Tendermint.SDK.Router.Class where +module Tendermint.SDK.Query.Class where import Control.Error import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, symbolVal) +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, symbolVal) import Servant.API -import Tendermint.SDK.Router.Delayed (Delayed, addQueryArgs, - delayedFail) -import Tendermint.SDK.Router.Router (Router, Router' (..), choice, - methodRouter, pathRouter) -import Tendermint.SDK.Router.Types (EncodeQueryResult (..), - FromQueryData (..), Leaf, QA, - QueryArgs (..), QueryError (..), - QueryResult) +import Tendermint.SDK.Query.Delayed (Delayed, addQueryArgs, + delayedFail) +import Tendermint.SDK.Query.Router (Router, Router' (..), choice, + methodRouter, pathRouter) +import Tendermint.SDK.Query.Types (FromQueryData (..), Leaf, QA, + QueryArgs (..), QueryError (..), + QueryResult, Queryable (..)) -------------------------------------------------------------------------------- - +-- | This class is used to construct a router given a 'layout' type. The layout +-- | is constructed using the combinators that appear in the instances here, no other +-- | Servant combinators are recognized. class HasRouter layout where -- | A route handler. type RouteT layout (m :: * -> *) :: * @@ -41,7 +42,7 @@ instance (HasRouter sublayout, KnownSymbol path) => HasRouter (path :> sublayout where proxyPath = Proxy :: Proxy path -instance EncodeQueryResult a => HasRouter (Leaf a) where +instance Queryable a => HasRouter (Leaf a) where type RouteT (Leaf a) m = ExceptT QueryError m (QueryResult a) route _ = methodRouter diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Delayed.hs similarity index 95% rename from hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs rename to hs-abci-sdk/src/Tendermint/SDK/Query/Delayed.hs index 402698ae..a601a3e6 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router/Delayed.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Delayed.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Router.Delayed where +module Tendermint.SDK.Query.Delayed where import Control.Error (ExceptT, runExceptT) import Control.Monad.Reader (MonadReader, ReaderT, @@ -9,11 +9,12 @@ import Data.Default.Class (def) import Data.String.Conversions (cs) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response -import Tendermint.SDK.Router.Types (QueryError (..), +import Tendermint.SDK.Query.Types (QueryError (..), RouteResult (..), RouteResultT (..)) -------------------------------------------------------------------------------- +-- NOTE: most of this was vendored and repurposed from servant newtype DelayedM m a = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs similarity index 92% rename from hs-abci-sdk/src/Tendermint/SDK/Router/Router.hs rename to hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs index cef3578b..d1ef54d9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Router.Router where +module Tendermint.SDK.Query.Router where import Control.Error import Control.Lens (to, (&), (.~), (^.)) @@ -12,14 +12,16 @@ import qualified Data.Text.Encoding as T import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response import Network.HTTP.Types (decodePathSegments) -import Tendermint.SDK.Router.Delayed (Delayed, runAction) -import Tendermint.SDK.Router.Types (EncodeQueryResult (..), - QueryArgs (..), +import Tendermint.SDK.Query.Delayed (Delayed, runAction) +import Tendermint.SDK.Query.Types (QueryArgs (..), QueryError (..), QueryResult (..), + Queryable (..), RouteResult (..)) +-- NOTE: most of this was vendored and repurposed from servant + data Router' env a = RChoice (Router' env a) (Router' env a) | RStatic (Map Text (Router' env a)) [env -> a] @@ -44,7 +46,7 @@ choice router1 router2 = RChoice router1 router2 methodRouter :: Monad m - => EncodeQueryResult b + => Queryable b => Delayed m env (ExceptT QueryError m (QueryResult b)) -> Router env m methodRouter action = leafRouter route' diff --git a/hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs similarity index 71% rename from hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs rename to hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs index b89cb701..0fe996b0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/StoreQueries.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs @@ -1,7 +1,5 @@ -module Tendermint.SDK.StoreQueries where +module Tendermint.SDK.Query.Store where ---import Servant.API --- import Tendermint.SDK.Routes import Control.Error (ExceptT, throwE) import Control.Lens (to, (^.)) import Control.Monad.Trans (lift) @@ -11,13 +9,13 @@ import GHC.TypeLits (Symbol) import Polysemy (Member, Sem) import Servant.API ((:<|>) (..), (:>)) import Tendermint.SDK.Codec (HasCodec) -import Tendermint.SDK.Router.Class -import Tendermint.SDK.Router.Types +import Tendermint.SDK.Query.Class +import Tendermint.SDK.Query.Types import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, - get) + StoreKey, get) class StoreQueryHandler a (ns :: Symbol) h where - storeQueryHandler :: Proxy a -> Proxy ns -> h + storeQueryHandler :: Proxy a -> StoreKey ns -> h instance ( IsKey k ns @@ -26,9 +24,9 @@ instance , Member RawStore r ) => StoreQueryHandler a ns (QueryArgs k -> ExceptT QueryError (Sem r) (QueryResult a)) where - storeQueryHandler _ _ QueryArgs{..} = do + storeQueryHandler _ storeKey QueryArgs{..} = do let key = queryArgsData - mRes <- lift $ get undefined key + mRes <- lift $ get storeKey key case mRes of Nothing -> throwE ResourceNotFound Just (res :: a) -> pure $ QueryResult @@ -42,7 +40,7 @@ instance class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) m where type QueryApi kvs :: * - storeQueryHandlers :: Proxy kvs -> Proxy ns -> Proxy m -> RouteT (QueryApi kvs) m + storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy m -> RouteT (QueryApi kvs) m instance ( Queryable a @@ -52,7 +50,7 @@ instance , Member RawStore r ) => StoreQueryHandlers '[(k,a)] ns (Sem r) where type QueryApi '[(k,a)] = Name a :> QA k :> Leaf a - storeQueryHandlers _ pns _ = storeQueryHandler (Proxy :: Proxy a) pns + storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey instance ( Queryable a @@ -63,16 +61,16 @@ instance , Member RawStore r ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns (Sem r) where type (QueryApi ((k, a) ': (k', a') : as)) = (Name a :> QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) - storeQueryHandlers _ pns pm = - storeQueryHandler (Proxy :: Proxy a) pns :<|> - storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) pns pm + storeQueryHandlers _ storeKey pm = + storeQueryHandler (Proxy :: Proxy a) storeKey :<|> + storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pm allStoreHandlers :: forall (contents :: [*]) ns r. StoreQueryHandlers contents ns (Sem r) => Member RawStore r => Proxy contents - -> Proxy ns + -> StoreKey ns -> Proxy r -> RouteT (QueryApi contents) (Sem r) -allStoreHandlers pcs pns _ = storeQueryHandlers pcs pns (Proxy :: Proxy (Sem r)) +allStoreHandlers pcs storeKey _ = storeQueryHandlers pcs storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs similarity index 85% rename from hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs rename to hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs index a2864742..fc01287e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Router/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Router.Types where +module Tendermint.SDK.Query.Types where import Control.Lens (from, (^.)) import Control.Monad (ap) @@ -48,21 +48,27 @@ data QueryResult a = QueryResult -------------------------------------------------------------------------------- +-- | class representing objects which can be queried via the hs-abci query message. +-- | Here the 'Name' is the leaf of the query url, e.g. if you can access a token +-- | balance of type `Balance` at "token/balance", then 'Name Balance ~ "balance"'. class Queryable a where type Name a :: Symbol - -class EncodeQueryResult a where encodeQueryResult :: a -> Base64String default encodeQueryResult :: HasCodec a => a -> Base64String encodeQueryResult = fromBytes . encode +-- | This class is used to parse the 'data' field of the query request message. +-- | The default method assumes that the 'data' is simply the key for the +-- | value being queried. class FromQueryData a where fromQueryData :: Base64String -> Either String a + default fromQueryData :: RawKey a => Base64String -> Either String a fromQueryData bs = Right (toBytes bs ^. from rawKey) -------------------------------------------------------------------------------- +-- NOTE: most of this was vendored and repurposed from servant. data RouteResult a = Fail QueryError diff --git a/hs-abci-sdk/src/Tendermint/SDK/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Store.hs index 5d330742..ca253484 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Store.hs @@ -11,10 +11,11 @@ module Tendermint.SDK.Store , prove ) where -import Control.Lens (Iso', (^.)) -import qualified Data.ByteString as BS -import Polysemy (Member, Sem, makeSem) -import Tendermint.SDK.Codec (HasCodec (..)) +import Control.Lens (Iso', (^.)) +import qualified Data.ByteString as BS +import Data.String.Conversions (cs) +import Polysemy (Member, Sem, makeSem) +import Tendermint.SDK.Codec (HasCodec (..)) newtype StoreKey n = StoreKey BS.ByteString @@ -60,7 +61,7 @@ get sk k = do pure $ case mRes of Nothing -> Nothing Just raw -> case decode raw of - Left e -> error $ "Impossible codec error " <> e + Left e -> error $ "Impossible codec error " <> cs e Right a -> Just a delete diff --git a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs new file mode 100644 index 00000000..f3d70584 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs @@ -0,0 +1,23 @@ +module Tendermint.SDK.TxRouter where + +import Data.ByteString (ByteString) +import Data.Proxy +import Polysemy (Member, Sem) +import Polysemy.Error (Error, throw) +import Tendermint.SDK.Auth (AuthError (..)) +import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Types.Transaction (RawTransaction, Tx (..), + parseTx) + +type Handler r msg = Tx Secp256k1 msg -> Sem r () + +router + :: forall r . + Member (Error AuthError) r + => Handler r ByteString + -> RawTransaction + -> Sem r () +router h rawTx = + case parseTx (Proxy @Secp256k1) rawTx of + Left errMsg -> throw $ TransactionParseError errMsg + Right tx -> h tx diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs new file mode 100644 index 00000000..aa677693 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs @@ -0,0 +1,29 @@ +module Tendermint.SDK.Types.Address where + +import Control.Lens (iso) +import qualified Crypto.Secp256k1 as Crypto +import qualified Data.Aeson as A +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) +import GHC.Generics (Generic) +import Tendermint.SDK.Query (FromQueryData) +import Tendermint.SDK.Store (RawKey (..)) + +-- | Used as a unique identifier for an account. +newtype Address = + Address Hex.HexString + deriving (Eq, Show, Generic, Ord, A.ToJSON, A.FromJSON) + +instance RawKey Address where + rawKey = iso (\(Address a) -> Hex.toBytes a) (Address . Hex.fromBytes) + +instance FromQueryData Address + +addressToBytes :: Address -> ByteString +addressToBytes (Address addrHex) = Hex.toBytes addrHex + +addressFromBytes :: ByteString -> Address +addressFromBytes = Address . Hex.fromBytes + +pubKeyToAddress :: Crypto.PubKey -> Address +pubKeyToAddress = addressFromBytes . Crypto.exportPubKey False diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs new file mode 100644 index 00000000..d89c01c7 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs @@ -0,0 +1,115 @@ +module Tendermint.SDK.Types.Message where + +import Control.Lens (( # )) +import Data.Bifunctor (first) +import Data.ByteString (ByteString) +import qualified Data.ProtoLens as PL +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Data.Validation as V +import qualified Proto3.Suite as Wire +import qualified Proto3.Wire.Decode as Wire +import Tendermint.SDK.Types.Address (Address) + +-- | The basic message format embedded in any transaction. +data Msg msg = Msg + { msgAuthor :: Address + , msgData :: msg + } + +-- | This is a general error type, primarily accomodating protobuf messages being parsed +-- | by either the [proto3-wire](https://hackage.haskell.org/package/proto3-wire) +-- | or the [proto-lens](https://hackage.haskell.org/package/proto-lens) libraries. +data MessageParseError = + -- | A 'WireTypeError' occurs when the type of the data in the protobuf + -- binary format does not match the type encountered by the parser. + WireTypeError Text + -- | A 'BinaryError' occurs when we can't successfully parse the contents of + -- the field. + | BinaryError Text + -- | An 'EmbeddedError' occurs when we encounter an error while parsing an + -- embedded message. + | EmbeddedError Text (Maybe MessageParseError) + -- | Unknown or unstructured parsing error. + | OtherParseError Text + +-- | Useful for returning in error logs or console logging. +formatMessageParseError + :: MessageParseError + -> Text +formatMessageParseError = cs . go + where + go err = + let (context,msg) = case err of + WireTypeError txt -> ("Wire Type Error", txt) + BinaryError txt -> ("Binary Error", txt) + EmbeddedError txt err' -> ("Embedded Error", txt <> ". " <> maybe "" go err') + OtherParseError txt -> ("Other Error", txt) + in "Parse Error [" <> context <> "]: " <> msg + +data DecodingOption = Proto3Suite | ProtoLens | Custom + +-- | Used for parsing messages, default instances given to accomodate both the +-- | the [proto3-wire](https://hackage.haskell.org/package/proto3-wire) +-- | and [proto-lens](https://hackage.haskell.org/package/proto-lens) libraries. +-- | The constraint parameter is used to avoid ambiguous instances, if you would +-- | like to write custom parsers you can use the 'CustomMessage' class constraint +-- | with the empty implementation. +class ParseMessage (codec :: DecodingOption) msg where + decodeMessage :: Proxy codec -> ByteString -> Either MessageParseError msg + + +instance {-# OVERLAPPABLE #-} Wire.Message msg => ParseMessage 'Proto3Suite msg where + decodeMessage _ = first mkErr . Wire.fromByteString + where + mkErr (Wire.WireTypeError txt) = WireTypeError (cs txt) + mkErr (Wire.BinaryError txt) = BinaryError (cs txt) + mkErr (Wire.EmbeddedError txt merr) = EmbeddedError (cs txt) (mkErr <$> merr) + +instance PL.Message msg => ParseMessage 'ProtoLens msg where + decodeMessage _ = first (OtherParseError . cs) . PL.decodeMessage + +-- | Used during message validation to indicate that although the message has parsed +-- | correctly, it fails certain sanity checks. +data MessageSemanticError = + -- | Used to indicate that the message signer does not have the authority to send + -- | this message. + PermissionError Text + -- | Used to indicate that a field isn't valid, e.g. enforces non-negative quantities + -- | or nonempty lists. + | InvalidFieldError Text + -- Catchall for other erors + | OtherSemanticError Text + +formatMessageSemanticError + :: MessageSemanticError + -> Text +formatMessageSemanticError err = + let (context, msg) = case err of + PermissionError m -> ("Permission Error", m) + InvalidFieldError m -> ("Invalid Field Error", m) + OtherSemanticError m -> ("Other Error", m) + in "Semantic Error [" <> context <> "]:" <> msg + +class ValidateMessage msg where + validateMessage :: Msg msg -> V.Validation [MessageSemanticError] () + +nonEmptyCheck + :: Eq a + => Monoid a + => Text + -> a + -> V.Validation [MessageSemanticError] () +nonEmptyCheck fieldName x + | x == mempty = V._Failure # [InvalidFieldError $ fieldName <> " must be nonempty."] + | otherwise = mempty + +isAuthorCheck + :: Text + -> Msg msg + -> (msg -> Address) + -> V.Validation [MessageSemanticError] () +isAuthorCheck fieldName Msg{msgAuthor, msgData} getAuthor + | getAuthor msgData /= msgAuthor = V._Failure # [PermissionError $ fieldName <> " must be message author."] + | otherwise = mempty diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs new file mode 100644 index 00000000..a4d75f3d --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -0,0 +1,84 @@ +module Tendermint.SDK.Types.Transaction where + +import Control.Error (note) +import Crypto.Hash (Digest, hashWith) +import Crypto.Hash.Algorithms (SHA256 (..)) +import Data.ByteString (ByteString) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.Text (Text) +import GHC.Generics (Generic) +import Tendermint.SDK.Crypto (MakeDigest (..), + RecoverableSignatureSchema (..), + SignatureSchema (..)) +import Tendermint.SDK.Types.Message (Msg (..)) + +-- Our standard transaction type parameterized by the signature schema 'alg' +-- and an underlying message type 'msg'. +data Tx alg msg = Tx + { txMsg :: Msg msg + , txSignature :: RecoverableSignature alg + , txSignBytes :: Message alg + , txSigner :: PubKey alg + } + +-------------------------------------------------------------------------------- + +-- TODO: figure out what the actual standards are for these things, if there +-- even are any. + +-- | Raw transaction type coming in over the wire +data RawTransaction = RawTransaction + { rawTransactionData :: ByteString + -- ^ the encoded message via protobuf encoding + , rawTransactionRoute :: ByteString + -- ^ module name + , rawTransactionSignature :: ByteString + } deriving Generic + +instance Serialize.Serialize RawTransaction + +parseRawTransaction + :: ByteString + -> Either Text RawTransaction +parseRawTransaction = error "TODO: implement parseRawTransaction" + +instance MakeDigest RawTransaction where + makeDigest tx = hashWith SHA256 . Serialize.encode $ tx {rawTransactionSignature = ""} + +signRawTransaction + :: forall alg. + RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => Proxy alg + -> PrivateKey alg + -> RawTransaction + -> RecoverableSignature alg +signRawTransaction p priv tx = signRecoverableMessage p priv (makeDigest tx) + +-- | Attempt to parse a 'RawTransaction' as a 'Tx' without attempting +-- | to parse the underlying message. This is done as a preprocessing +-- | step to the router, allowing for failure before the router is ever +-- | reached. +parseTx + :: forall alg. + RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => Proxy alg + -> RawTransaction + -> Either Text (Tx alg ByteString) +parseTx p rawTx@RawTransaction{..} = do + recSig <- note "Unable to parse transaction signature as a recovery signature." $ + makeRecoverableSignature p rawTransactionSignature + let txForSigning = rawTx {rawTransactionSignature = ""} + signBytes = makeDigest txForSigning + signerPubKey <- note "Signature recovery failed." $ recover p recSig signBytes + return Tx + { txMsg = Msg + { msgData = rawTransactionData + , msgAuthor = addressFromPubKey p signerPubKey + } + , txSignature = recSig + , txSignBytes = signBytes + , txSigner = signerPubKey + } diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs index 1019b729..76b7edf6 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs @@ -1,13 +1,12 @@ module Tendermint.SDK.Test.AuthTreeStoreSpec where import Control.Lens (iso) -import qualified Data.Binary as Binary +import Data.Bifunctor (first) import Data.ByteString (ByteString) +import qualified Data.Serialize as Serialize import Data.String.Conversions (cs) import Polysemy (runM) -import Tendermint.SDK.AuthTreeStore (AuthTreeDriver, - initAuthTreeDriver, - interpretAuthTreeStore) +import Tendermint.SDK.AuthTreeStore (AuthTree, eval, initAuthTree) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Store (IsKey (..), RawKey (..), StoreKey (..), get, put) @@ -17,25 +16,25 @@ spec :: Spec spec = beforeAll beforeAction $ describe "AuthTreeStore" $ do it "can fail to query an empty AuthTreeStore" $ \driver -> do - mv <- runM . interpretAuthTreeStore driver $ get storeKey IntStoreKey + mv <- runM . eval driver $ get storeKey IntStoreKey mv `shouldBe` Nothing it "can set a value and query the value" $ \driver -> do - mv <- runM . interpretAuthTreeStore driver $ do + mv <- runM . eval driver $ do put storeKey IntStoreKey (IntStore 1) get storeKey IntStoreKey mv `shouldBe` Just (IntStore 1) -beforeAction :: IO AuthTreeDriver -beforeAction = initAuthTreeDriver +beforeAction :: IO AuthTree +beforeAction = initAuthTree -newtype IntStore = IntStore Int deriving (Eq, Show) +newtype IntStore = IntStore Int deriving (Eq, Show, Serialize.Serialize) data IntStoreKey = IntStoreKey instance HasCodec IntStore where - encode (IntStore c) = cs . Binary.encode $ c - decode = Right . IntStore . Binary.decode . cs + encode = Serialize.encode + decode = first cs . Serialize.decode instance RawKey IntStoreKey where rawKey = iso (\_ -> cs intStoreKey) (const IntStoreKey) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs new file mode 100644 index 00000000..2d6360bf --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs @@ -0,0 +1,35 @@ +module Tendermint.SDK.Test.CryptoSpec where + +import Crypto.Secp256k1 (SecKey, derivePubKey, + exportCompactRecSig, secKey) +import qualified Data.ByteArray.HexString as Hex +import Data.Maybe (fromJust) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.String (fromString) +import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Types.Transaction +import Test.Hspec + +spec :: Spec +spec = describe "Crypto Tests" $ do + it "Can sign a transaction and recover the signature" $ do + let rawTxWithoutSig = RawTransaction + { rawTransactionData = "abcd" + , rawTransactionSignature = "" + , rawTransactionRoute= "dog" + } + signature = signRawTransaction algProxy privateKey rawTxWithoutSig + rawTxWithSig = rawTxWithoutSig {rawTransactionSignature = Serialize.encode $ exportCompactRecSig signature} + eTx = parseTx algProxy rawTxWithSig + Tx{..} = case eTx of + Left errMsg -> error $ show errMsg + Right a -> a + txSigner `shouldBe` derivePubKey privateKey + +privateKey :: SecKey +privateKey = fromJust . secKey . Hex.toBytes . fromString $ + "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" + +algProxy :: Proxy Secp256k1 +algProxy = Proxy diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs new file mode 100644 index 00000000..c0841c53 --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs @@ -0,0 +1,30 @@ +module Tendermint.SDK.Test.EventSpec where + +import qualified Data.Aeson as A +import GHC.Generics (Generic) +import Tendermint.SDK.Events (FromEvent (..), ToEvent (..), makeEvent) +import Test.Hspec + +spec :: Spec +spec = describe "Event Tests" $ do + it "Can serialize and deserialize and event" $ do + let transferEv = Transfer + { to = "me" + , from = "you" + , amount = 1 + } + fromEvent (makeEvent transferEv) `shouldBe` Right transferEv + +data Transfer = Transfer + { to :: String + , from :: String + , amount :: Int + } deriving (Eq, Show, Generic) + +instance A.ToJSON Transfer + +instance ToEvent Transfer where + makeEventType _ = "transfer" + +instance A.FromJSON Transfer +instance FromEvent Transfer diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/ModuleSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/ModuleSpec.hs deleted file mode 100644 index 0abb2c1f..00000000 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/ModuleSpec.hs +++ /dev/null @@ -1,109 +0,0 @@ -module Tendermint.SDK.Test.ModuleSpec where - --- import qualified Control.Concurrent.MVar as MVar --- import Control.Lens (to, (^.)) --- import Control.Monad (void) --- import Control.Monad.Trans --- import Data.ByteArray.Base64String (fromBytes, toBytes) --- import Data.Conduit --- import Data.Proxy --- import qualified Network.ABCI.Types.Messages.Request as Request --- import qualified Network.ABCI.Types.Messages.Response as Response --- import Servant.API ((:>)) --- import Tendermint.SDK.AuthTreeStore --- import Tendermint.SDK.Codec --- import Tendermint.SDK.Module --- import Tendermint.SDK.Router --- import Tendermint.SDK.Store --- import Tendermint.SDK.StoreQueries --- import Tendermint.SDK.Test.StoreExample -import Test.Hspec - -spec :: Spec -spec = - describe "UserModule" $ pure () - -- it "can create the user module and query it via Query msg and from component" $ do - -- TendermintIO {ioQuery, ioServer, ioSubscribe} <- runApp userComponent () - -- logsVar <- MVar.newMVar [] - -- charlesLogsVar <- MVar.newMVar [] - -- let irakli = Buyer { buyerId = "1" - -- , buyerName = "irakli" - -- } - -- irakliKey = BuyerKey "1" - -- charles = Buyer { buyerId = "2" - -- , buyerName = "charles" - -- } - -- logger = awaitForever $ \case - -- StoredBuyer buyer -> lift $ MVar.modifyMVar_ logsVar (pure . (:) buyer) - -- charlesLogger = awaitForever $ \case - -- StoredBuyer buyer -> - -- lift $ if buyerName buyer == "charles" - -- then MVar.modifyMVar_ charlesLogsVar (pure . (:) buyer) - -- else pure () - -- _ <- ioSubscribe logger - -- _ <- ioSubscribe charlesLogger - -- void $ ioQuery $ tell (PutBuyer irakli) - -- void $ ioQuery $ tell (PutBuyer charles) - -- mIrakli <- ioQuery $ request (GetBuyer irakliKey) - -- mIrakli `shouldBe` Just irakli - -- mNobody <- ioQuery $ request (GetBuyer (BuyerKey "3")) - -- mNobody `shouldBe` Nothing - -- logs <- MVar.readMVar logsVar - -- logs `shouldBe` [charles, irakli] - -- charlesLogs <- MVar.readMVar charlesLogsVar - -- charlesLogs `shouldBe` [charles] - - -- let serveRoutes :: Application IO - -- serveRoutes = serve (Proxy :: Proxy UserApi) (Proxy :: Proxy IO) ioServer - -- irakliKeyHex = irakliKey ^. rawKey . to fromBytes - -- irakliQuery = Request.Query irakliKeyHex "user/buyer" 0 False - -- qBuyerRes <- serveRoutes irakliQuery - -- let ebuyer = decode . toBytes . Response.queryValue $ qBuyerRes - -- ebuyer `shouldBe` Right irakli - --------------------------------------------------------------------------------- --- User Module --------------------------------------------------------------------------------- - --- data UserQ a = --- PutBuyer Buyer a --- | GetBuyer BuyerKey (Maybe Buyer -> a) - --- data UserMessage = --- StoredBuyer Buyer - --- evalQuery :: forall a action. UserQ a -> TendermintM UserStore action UserMessage IO a --- evalQuery (PutBuyer buyer a) = do --- withState $ \store -> --- putBuyer (BuyerKey $ buyerId buyer) buyer store --- raise $ StoredBuyer buyer --- pure a --- evalQuery (GetBuyer buyerKey f) = do --- buyer <- withState $ --- \store -> get (undefined :: Root) buyerKey store --- pure $ f buyer - --- type UserApi = "user" :> QueryApi UserStoreContents - --- userComponentSpec :: ComponentSpec UserStore UserQ action input UserMessage UserApi IO --- userComponentSpec = ComponentSpec --- { initialState = const $ do --- rawStore <- mkAuthTreeStore --- pure $ Store --- { storeRawStore = rawStore } --- , eval = evaluator --- , mkServer = userServer --- } --- where --- userServer :: UserStore -> RouteT UserApi IO --- userServer = allStoreHandlers - --- evaluator = mkEval $ EvalSpec --- { handleAction = const $ pure () --- , handleQuery = evalQuery --- , receive = const Nothing --- , initialize = Nothing --- } - --- userComponent :: forall (input :: *). Component UserQ input UserMessage UserApi IO --- userComponent = Component userComponentSpec diff --git a/stack.yaml b/stack.yaml index f0aea6c1..1640e212 100644 --- a/stack.yaml +++ b/stack.yaml @@ -26,7 +26,6 @@ extra-deps: commit: 3f6dd6f612cf2eba3c05798926ff924b0d5ab4fa - git: https://github.com/awakesecurity/proto3-wire commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 - - bech32-1.0.0@sha256:6bd23abe4a11d61a2d41fdb11db9e1361fc43c215cf2b8602cafaa516c8b6d76,2017 - polysemy-1.2.0.0 explicit-setup-deps: diff --git a/stack.yaml.lock b/stack.yaml.lock index 3918934e..e379ced0 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -81,13 +81,6 @@ packages: original: git: https://github.com/awakesecurity/proto3-wire commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 -- completed: - hackage: bech32-1.0.0@sha256:6bd23abe4a11d61a2d41fdb11db9e1361fc43c215cf2b8602cafaa516c8b6d76,2017 - pantry-tree: - size: 424 - sha256: 7193ff37729da6c18519a09d88e7e53b5dbd9a1d06399a11fa2676be1bd254f1 - original: - hackage: bech32-1.0.0@sha256:6bd23abe4a11d61a2d41fdb11db9e1361fc43c215cf2b8602cafaa516c8b6d76,2017 - completed: hackage: polysemy-1.2.0.0@sha256:9313315be0746936c65bc02a5b9bc2abd3bd02bcef9329db272a991939e5b037,5776 pantry-tree: From 2fdb86f9be795248207fa1c7d4855c3462e6db08 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 18 Nov 2019 10:01:46 -0800 Subject: [PATCH 04/70] Update README.md --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index b8d9bbb8..87b92175 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,22 @@ At the moment the project's build is managed by `stack`. You can find everything #### protoc We use a custom setup to generate Haskell bindings to the protobuf files, using the proto-lens library from Google. In order for this to work you need to have the protobuf compiler `protoc` on your machine. You can get installation instructions [here](https://google.github.io/proto-lens/installing-protoc.html) +#### libsecp256k1 +In order to build with stack you will need this. On MacOS you can use brew: + +``` +> brew tap cuber/homebrew-libsecp256k1 +> brew install libsecp256k1 +``` + +On linux: + +``` +> sudo add-apt-repository ppa:tah83/secp256k1 +> sudo apt-get update +> sudo apt-get install libsecp256k1 +``` + #### style You will also need to install `hlint` and `stylish-haskell` for code hygiene during development. In the project root simply run From ce5cb1b24c13bb0fd7e66e53996ea5317fbee866 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 19 Nov 2019 14:11:32 -0800 Subject: [PATCH 05/70] Martin/query client (#106) * added HasClient class * updated simple storage test * stylish * impossible parser error * change storeclient to client module name --- .../test/SimpleStorage/Test/E2ESpec.hs | 49 +++++++++++---- hs-abci-sdk/package.yaml | 1 + .../src/Tendermint/SDK/Query/Client.hs | 60 +++++++++++++++++++ .../src/Tendermint/SDK/Query/Router.hs | 3 +- hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs | 12 ++-- .../Network/ABCI/Types/Messages/Request.hs | 34 +++++++++++ 6 files changed, 142 insertions(+), 17 deletions(-) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index f76df999..1a071497 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -1,22 +1,29 @@ module SimpleStorage.Test.E2ESpec where import Control.Lens (to, (^.)) +import Control.Monad.Reader (ReaderT) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) import Data.Default.Class (def) import Data.Int (Int32) +import Data.Proxy import Data.Serialize (decode, encode) import Data.String.Conversions (cs) +import qualified Network.ABCI.Types.Messages.Request as Req import qualified Network.ABCI.Types.Messages.Response as Resp -import qualified Network.ABCI.Types.Messages.Response as Response import qualified Network.Tendermint.Client as RPC import qualified SimpleStorage.Modules.SimpleStorage as SS import SimpleStorage.Types (AppTxMessage (..), UpdateCountTx (..), encodeAppTxMessage) +import Tendermint.SDK.Query.Client (ClientResponse (..), + HasClient (..), + RunClient (..)) +import Tendermint.SDK.Query.Types (QueryArgs (..)) import Tendermint.SDK.Store (rawKey) import Test.Hspec @@ -30,15 +37,13 @@ spec = do resp `shouldBe` RPC.ResultHealth it "Can query the count and make sure its initialized to 0" $ do - let queryReq = - def { RPC.requestABCIQueryPath = Just "simple_storage/count" - , RPC.requestABCIQueryData = SS.CountKey ^. rawKey . to Hex.fromBytes - - } - queryResp <- fmap RPC.resultABCIQueryResponse . runRPC $ - RPC.abciQuery queryReq - let foundCount = queryResp ^. Resp._queryValue . to decodeCount - foundCount `shouldBe` 0 + let queryReq = QueryArgs + { queryArgsData = SS.CountKey + , queryArgsHeight = 0 + , queryArgsProve = False + } + ClientResponse{clientResponseData}<- runQueryRunner $ getCount queryReq + clientResponseData `shouldBe` SS.Count 0 it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do let tx = UpdateCountTx "irakli" 4 @@ -46,7 +51,7 @@ spec = do { RPC.requestBroadcastTxCommitTx = Base64.fromBytes . encodeAppTxMessage $ ATMUpdateCount tx } deliverResp <- fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ RPC.broadcastTxCommit txReq - let deliverRespCode = deliverResp ^. Response._deliverTxCode + let deliverRespCode = deliverResp ^. Resp._deliverTxCode deliverRespCode `shouldBe` 0 it "can make sure the synchronous tx transaction worked and the count is now 4" $ do @@ -75,3 +80,25 @@ runRPC = RPC.runTendermintM rpcConfig prettyPrint :: forall b. ToJSON b => String -> b -> IO () prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") + +newtype QueryRunner a = QueryRunner + {_runQueryRunner :: ReaderT RPC.Config IO a} + deriving (Functor, Applicative, Monad) + +runQueryRunner :: QueryRunner a -> IO a +runQueryRunner = runRPC . _runQueryRunner + +instance RunClient QueryRunner where + runQuery Req.Query{..} = + let rpcQ = RPC.RequestABCIQuery + { RPC.requestABCIQueryPath = Just queryPath + , RPC.requestABCIQueryData = Hex.fromBytes @ByteString . Base64.toBytes $ queryData + , RPC.requestABCIQueryHeight = Just $ queryHeight + , RPC.requestABCIQueryProve = queryProve + } + in RPC.resultABCIQueryResponse <$> QueryRunner (RPC.abciQuery rpcQ) + +getCount :: QueryArgs SS.CountKey -> QueryRunner (ClientResponse SS.Count) +getCount = genClient (Proxy :: Proxy QueryRunner) (Proxy :: Proxy SS.Api) def + + diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 4b5b36bc..7809e296 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -95,6 +95,7 @@ library: - Tendermint.SDK.Query.Router - Tendermint.SDK.Query.Store - Tendermint.SDK.Query.Types + - Tendermint.SDK.Query.Client - Tendermint.SDK.Logger - Tendermint.SDK.Logger.Katip - Tendermint.SDK.Store diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs new file mode 100644 index 00000000..836d391d --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs @@ -0,0 +1,60 @@ +{-# LANGUAGE UndecidableInstances #-} + +module Tendermint.SDK.Query.Client + ( RunClient(..) + , HasClient(..) + , ClientResponse(..) + ) where + +import Control.Lens (to, (^.)) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Proxy +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, symbolVal) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.Query.Types (Leaf, QA, QueryArgs (..), + Queryable (..)) +import Tendermint.SDK.Store (RawKey (..)) + +class Monad m => RunClient m where + -- | How to make a request. + runQuery :: Req.Query -> m Resp.Query + +class HasClient m layout where + + type ClientT (m :: * -> *) layout :: * + genClient :: Proxy m -> Proxy layout -> Req.Query -> ClientT m layout + +instance (HasClient m a, HasClient m b) => HasClient m (a :<|> b) where + type ClientT m (a :<|> b) = ClientT m a :<|> ClientT m b + genClient pm _ q = genClient pm (Proxy @a) q :<|> genClient pm (Proxy @b) q + +instance (KnownSymbol path, HasClient m a) => HasClient m (path :> a) where + type ClientT m (path :> a) = ClientT m a + genClient pm _ q = genClient pm (Proxy @a) + q {Req.queryPath = Req.queryPath q <> "/" <> cs (symbolVal (Proxy @path))} + +instance (RawKey k, HasClient m a) => HasClient m (QA k :> a) where + type ClientT m (QA k :> a) = QueryArgs k -> ClientT m a + genClient pm _ q QueryArgs{..} = genClient pm (Proxy @a) + q { Req.queryData = queryArgsData ^. rawKey . to Base64.fromBytes + , Req.queryHeight = queryArgsHeight + , Req.queryProve = queryArgsProve + } + +data ClientResponse a = ClientResponse + { clientResponseData :: a + , clientResponseRaw :: Resp.Query + } + +instance (RunClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasClient m (Leaf a) where + type ClientT m (Leaf a) = m (ClientResponse a) + genClient _ _ q = + let leaf = symbolVal (Proxy @(Name a)) + in do + r@Resp.Query{..} <- runQuery q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } + return $ case decodeQueryResult queryValue of + Left err -> error $ "Impossible parse error: " <> cs err + Right a -> ClientResponse a r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs index d1ef54d9..ac7ac55e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs @@ -78,8 +78,7 @@ runRouter router env query = RQueryArgs r' -> let qa = QueryArgs { queryArgsData = query ^. Request._queryData - , queryArgsQueryData = query ^. Request._queryData - , queryArgsBlockHeight = query ^. Request._queryHeight + , queryArgsHeight = query ^. Request._queryHeight , queryArgsProve = query ^. Request._queryProve } in runRouter r' (qa, env) query diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs index fc01287e..447f199d 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs @@ -6,6 +6,7 @@ import Control.Monad.Trans (MonadTrans (..)) import Data.ByteArray.Base64String (Base64String, fromBytes, toBytes) import Data.Int (Int64) +import Data.Text (Text) import GHC.TypeLits (Symbol) import Network.ABCI.Types.Messages.FieldTypes (Proof, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Request @@ -32,10 +33,9 @@ data QueryError = deriving (Show) data QueryArgs a = QueryArgs - { queryArgsProve :: Bool - , queryArgsData :: a - , queryArgsQueryData :: Base64String - , queryArgsBlockHeight :: WrappedVal Int64 + { queryArgsProve :: Bool + , queryArgsData :: a + , queryArgsHeight :: WrappedVal Int64 } deriving Functor data QueryResult a = QueryResult @@ -54,10 +54,14 @@ data QueryResult a = QueryResult class Queryable a where type Name a :: Symbol encodeQueryResult :: a -> Base64String + decodeQueryResult :: Base64String -> Either Text a default encodeQueryResult :: HasCodec a => a -> Base64String encodeQueryResult = fromBytes . encode + default decodeQueryResult :: HasCodec a => Base64String -> Either Text a + decodeQueryResult = decode . toBytes + -- | This class is used to parse the 'data' field of the query request message. -- | The default method assumes that the 'data' is simply the key for the -- | value being queried. diff --git a/hs-abci-types/src/Network/ABCI/Types/Messages/Request.hs b/hs-abci-types/src/Network/ABCI/Types/Messages/Request.hs index c2d38a64..47953d39 100644 --- a/hs-abci-types/src/Network/ABCI/Types/Messages/Request.hs +++ b/hs-abci-types/src/Network/ABCI/Types/Messages/Request.hs @@ -17,6 +17,7 @@ import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteArray.Base64String as Base64 import Data.ByteArray.HexString (HexString) import qualified Data.ByteArray.HexString as Hex +import Data.Default.Class (Default (..)) import Data.Int (Int64) import Data.ProtoLens.Message (Message (defMessage)) import Data.Text (Text) @@ -60,6 +61,9 @@ instance Wrapped Echo where t Echo {..} = defMessage & PT.message .~ echoMessage f message = Echo { echoMessage = message ^. PT.message } +instance Default Echo where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- Flush -------------------------------------------------------------------------------- @@ -80,6 +84,9 @@ instance Wrapped Flush where t = const defMessage f = const Flush +instance Default Flush where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- Info -------------------------------------------------------------------------------- @@ -117,6 +124,9 @@ instance Wrapped Info where , infoP2pVersion = message ^. PT.p2pVersion } +instance Default Info where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- SetOption -------------------------------------------------------------------------------- @@ -148,6 +158,9 @@ instance Wrapped SetOption where , setOptionValue = message ^. PT.value } +instance Default SetOption where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- InitChain -------------------------------------------------------------------------------- @@ -199,6 +212,9 @@ instance Wrapped InitChain where , initChainAppState = Base64.fromBytes $ message ^. PT.appStateBytes } +instance Default InitChain where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- Query -------------------------------------------------------------------------------- @@ -240,6 +256,9 @@ instance Wrapped Query where , queryProve = message ^. PT.prove } +instance Default Query where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- BeginBlock -------------------------------------------------------------------------------- @@ -287,6 +306,9 @@ instance Wrapped BeginBlock where , beginBlockByzantineValidators = message ^.. PT.byzantineValidators . traverse . _Unwrapped' } +instance Default BeginBlock where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- CheckTx -------------------------------------------------------------------------------- @@ -315,6 +337,9 @@ instance Wrapped CheckTx where f message = CheckTx { checkTxTx = Base64.fromBytes $ message ^. PT.tx } +instance Default CheckTx where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- DeliverTx -------------------------------------------------------------------------------- @@ -342,6 +367,9 @@ instance Wrapped DeliverTx where f message = DeliverTx { deliverTxTx = Base64.fromBytes $ message ^. PT.tx } +instance Default DeliverTx where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- EndBlock -------------------------------------------------------------------------------- @@ -370,6 +398,9 @@ instance Wrapped EndBlock where f message = EndBlock { endBlockHeight = WrappedVal $ message ^. PT.height } +instance Default EndBlock where + def = defMessage ^. _Unwrapped' + -------------------------------------------------------------------------------- -- Commit -------------------------------------------------------------------------------- @@ -394,3 +425,6 @@ instance Wrapped Commit where t Commit = defMessage f _ = Commit + +instance Default Commit where + def = defMessage ^. _Unwrapped' From a235496bc793f9df622669381ecf47fe36dc9566 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Wed, 20 Nov 2019 09:04:50 -0800 Subject: [PATCH 06/70] Added module level and application level routers (#112) --- .../src/Nameservice/Application.hs | 33 +++++++---- .../nameservice/src/Nameservice/Handlers.hs | 11 ++-- .../src/Nameservice/Modules/Nameservice.hs | 22 +++++++- .../Modules/Nameservice/Messages.hs | 38 ++++++++++--- .../Nameservice/Modules/Nameservice/Router.hs | 36 ++++-------- .../src/Nameservice/Modules/Token.hs | 2 + hs-abci-sdk/package.yaml | 1 + hs-abci-sdk/src/Tendermint/SDK/Module.hs | 15 +++++ hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs | 55 +++++++++++++++---- .../src/Tendermint/SDK/Types/Message.hs | 40 +++++--------- .../src/Tendermint/SDK/Types/Transaction.hs | 7 +++ 11 files changed, 175 insertions(+), 85 deletions(-) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Module.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 92b1a1f6..5fcbed45 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -5,19 +5,24 @@ module Nameservice.Application , compileToBaseApp , runHandler , queryServer + , router ) where -import Control.Exception (Exception) +import Control.Exception (Exception) import Data.Proxy -import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T -import Polysemy (Sem) -import Servant.API ((:<|>) (..)) -import qualified Tendermint.SDK.Auth as A -import Tendermint.SDK.BaseApp ((:&)) -import qualified Tendermint.SDK.BaseApp as BaseApp -import qualified Tendermint.SDK.Logger.Katip as KL -import Tendermint.SDK.Query (QueryApplication, serve) +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import Polysemy (Sem) +import Servant.API ((:<|>) (..)) +import qualified Tendermint.SDK.Auth as A +import Tendermint.SDK.BaseApp ((:&)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Crypto (Secp256k1) +import qualified Tendermint.SDK.Logger.Katip as KL +import Tendermint.SDK.Module (Modules (..)) +import Tendermint.SDK.Query (QueryApplication, serve) +import qualified Tendermint.SDK.TxRouter as R +import Tendermint.SDK.Types.Transaction (RawTransaction) data AppConfig = AppConfig { baseAppContext :: BaseApp.Context @@ -51,6 +56,14 @@ runHandler AppConfig{baseAppContext} = -------------------------------------------------------------------------------- +modules :: Modules '[N.NameserviceM EffR] EffR +modules = ConsModule N.nameserviceModule NilModules + +router :: RawTransaction -> Sem EffR () +router = R.router (Proxy @Secp256k1) modules + +-------------------------------------------------------------------------------- + type QueryApi = T.Api :<|> N.Api apiP :: Proxy QueryApi diff --git a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs index 150cb2e3..70342c1d 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs @@ -5,8 +5,8 @@ import qualified Data.ByteArray.Base64String as Base64 import Data.Default.Class (def) import Data.Proxy import Nameservice.Application (Handler, - compileToBaseApp) -import Nameservice.Modules.Nameservice as N + compileToBaseApp, router) +import qualified Nameservice.Modules.Nameservice as N import Network.ABCI.Server.App (App (..), MessageType (..), Request (..), @@ -83,10 +83,9 @@ deliverTxH -> Sem BaseApp (Response 'MTDeliverTx) -- Sem BaseApp (Response 'MTDeliverTx) deliverTxH (RequestDeliverTx deliverTx) = let tryToRespond = do - tx <- either (throwSDKError . ParseError) return $ do - rawTx <- parseRawTransaction $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes - parseTx (Proxy @Secp256k1) rawTx - events <- withEventBuffer . compileToBaseApp $ N.router tx + tx <- either (throwSDKError . ParseError) return $ + parseRawTransaction $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes + events <- withEventBuffer . compileToBaseApp $ router tx return $ ResponseDeliverTx $ def & Resp._deliverTxCode .~ 0 & Resp._deliverTxEvents .~ events diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index a1b83ac0..a74d14e6 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -1,10 +1,15 @@ module Nameservice.Modules.Nameservice ( + -- * Module + NameserviceM + , nameserviceModule + -- * types - Name(..) + , Name(..) , Whois (..) , NameserviceException(..) + , NameserviceMessage(..) -- * effects , NameserviceEffR @@ -31,3 +36,18 @@ import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types +import Nameservice.Modules.Token (HasTokenEff) +import Tendermint.SDK.BaseApp ((:&), HasBaseAppEff) +import Tendermint.SDK.Module (Module (..)) + +type NameserviceM r = Module "nameservice" NameserviceMessage Api r + +nameserviceModule + :: HasBaseAppEff r + => HasTokenEff r + => HasNameserviceEff r + => NameserviceM r +nameserviceModule = Module + { moduleRouter = router + , moduleQueryServer = server + } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index b31b4d56..8fd79d2e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -3,6 +3,7 @@ module Nameservice.Modules.Nameservice.Messages where import Control.Applicative ((<|>)) import Control.Lens (( # ), (^.)) import qualified Data.Aeson as A +import Data.Bifunctor (first) import Data.Either (Either) import Data.Foldable (sequenceA_) import Data.Proxy @@ -15,13 +16,16 @@ import Nameservice.Aeson (defaultNameserviceOption import Nameservice.Modules.Nameservice.Types (Name (..), NameserviceModule) import Nameservice.Modules.Token (Amount (..)) -import Proto3.Suite (Message, Named) +import Proto3.Suite (Message, Named, + fromByteString, + toLazyByteString) +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address, addressFromBytes) -import Tendermint.SDK.Types.Message (DecodingOption (..), - Msg (..), - ParseMessage (..), +import Tendermint.SDK.Types.Message (Msg (..), ValidateMessage (..), + coerceProto3Error, + formatMessageParseError, isAuthorCheck, nonEmptyCheck) @@ -42,6 +46,10 @@ data SetName = SetName instance Message SetName instance Named SetName +instance HasCodec SetName where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + data DeleteName = DeleteName { deleteNameName :: Name , deleteNameOwner :: Address @@ -50,6 +58,10 @@ data DeleteName = DeleteName instance Message DeleteName instance Named DeleteName +instance HasCodec DeleteName where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + data BuyName = BuyName { buyNameName :: Name , buyNameValue :: Text @@ -60,11 +72,19 @@ data BuyName = BuyName instance Message BuyName instance Named BuyName -instance {-# OVERLAPPING #-} ParseMessage 'Proto3Suite NameserviceMessage where - decodeMessage p bs = - fmap NSetName (decodeMessage p bs) <> - fmap NBuyName (decodeMessage p bs) <> - fmap NDeleteName (decodeMessage p bs) +instance HasCodec BuyName where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +instance HasCodec NameserviceMessage where + decode bs = + fmap NSetName (decode bs) <> + fmap NBuyName (decode bs) <> + fmap NDeleteName (decode bs) + encode = \case + NSetName msg -> encode msg + NBuyName msg -> encode msg + NDeleteName msg -> encode msg instance ValidateMessage NameserviceMessage where validateMessage m@Msg{msgData} = case msgData of diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 620c3e4d..10123bc5 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -10,35 +10,23 @@ import Nameservice.Modules.Token (HasTokenEff) import Polysemy (Members, Sem) import Polysemy.Error (Error) import Tendermint.SDK.Auth (AuthError) -import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Errors (AppError, SDKError (..), throwSDKError) import qualified Tendermint.SDK.TxRouter as R -import Tendermint.SDK.Types.Message (DecodingOption (..), - Msg (..), - ParseMessage (..), - formatMessageParseError) -import Tendermint.SDK.Types.Transaction (Tx (..)) +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (RoutedTx (..), + Tx (..)) router - :: Members [Error AppError, Error AuthError] r - => HasTokenEff r + :: HasTokenEff r => HasNameserviceEff r - => Tx Secp256k1 ByteString + => RoutedTx NameserviceMessage -> Sem r () -router tx@Tx{txMsg} = - case decodeMessage (Proxy @'Proto3Suite) $ msgData txMsg of - Left parseErrMsg -> throwSDKError . ParseError . formatMessageParseError $ parseErrMsg - Right msg -> handler $ tx {txMsg = txMsg {msgData = msg}} - where - handler - :: HasTokenEff r - => HasNameserviceEff r - => R.Handler r NameserviceMessage - handler Tx{txMsg} = - let Msg{msgData=msg} = txMsg - in case msg of - NSetName txMsg -> setName txMsg - NBuyName txMsg -> buyName txMsg - NDeleteName txMsg -> deleteName txMsg +router (RoutedTx Tx{txMsg}) = + let Msg{msgData=msg} = txMsg + in case msg of + NSetName txMsg -> setName txMsg + NBuyName txMsg -> buyName txMsg + NDeleteName txMsg -> deleteName txMsg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index d0f19bc4..ea29bb55 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -228,3 +228,5 @@ mint mint addr amount = do bal <- getBalance addr putBalance addr (bal + amount) + +-------------------------------------------------------------------------------- diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 7809e296..e5392b26 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -104,6 +104,7 @@ library: - Tendermint.SDK.Types.Transaction - Tendermint.SDK.Types.Address - Tendermint.SDK.TxRouter + - Tendermint.SDK.Module tests: hs-abci-sdk-test: diff --git a/hs-abci-sdk/src/Tendermint/SDK/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Module.hs new file mode 100644 index 00000000..947e3c7f --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Module.hs @@ -0,0 +1,15 @@ +module Tendermint.SDK.Module where + +import GHC.TypeLits (Symbol) +import Polysemy (EffectRow, Sem) +import Tendermint.SDK.Query (RouteT) +import Tendermint.SDK.Types.Transaction (RoutedTx) + +data Module (name :: Symbol) msg api (r :: EffectRow) = Module + { moduleRouter :: RoutedTx msg -> Sem r () + , moduleQueryServer :: RouteT api (Sem r) + } + +data Modules (ms :: [*]) r where + NilModules :: Modules '[] r + ConsModule :: Module name msg api r -> Modules ms r -> Modules (Module name msg api r ': ms) r diff --git a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs index f3d70584..50c47c77 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs @@ -1,23 +1,58 @@ -module Tendermint.SDK.TxRouter where +{-# LANGUAGE UndecidableInstances #-} +module Tendermint.SDK.TxRouter + ( router + , Router(..) + ) where +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) import Data.ByteString (ByteString) import Data.Proxy +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, symbolVal) import Polysemy (Member, Sem) import Polysemy.Error (Error, throw) import Tendermint.SDK.Auth (AuthError (..)) -import Tendermint.SDK.Crypto (Secp256k1) -import Tendermint.SDK.Types.Transaction (RawTransaction, Tx (..), +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (RecoverableSignatureSchema, + SignatureSchema (..)) +import Tendermint.SDK.Errors (AppError, SDKError (..), + throwSDKError) +import Tendermint.SDK.Module +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..), + RoutedTx (..), Tx (..), parseTx) -type Handler r msg = Tx Secp256k1 msg -> Sem r () - router - :: forall r . + :: forall alg ms r . Member (Error AuthError) r - => Handler r ByteString + => RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => Router ms r + => Proxy alg + -> Modules ms r -> RawTransaction -> Sem r () -router h rawTx = - case parseTx (Proxy @Secp256k1) rawTx of +router (p :: Proxy alg) ms rawTx = + case parseTx p rawTx of Left errMsg -> throw $ TransactionParseError errMsg - Right tx -> h tx + Right tx -> route ms tx + +class Router ms r where + route :: forall alg. Modules ms r -> Tx alg ByteString -> Sem r () + +instance (Member (Error AppError) r) => Router '[] r where + route NilModules Tx{txRoute} = + throwSDKError $ UnmatchedRoute txRoute + +instance (Member (Error AppError) r, Router ms r, HasCodec msg, KnownSymbol name) => Router (Module name msg api r ': ms) r where + route (ConsModule m rest) tx@Tx{..} + | symbolVal (Proxy :: Proxy name) == cs txRoute = do + msg <- case decode $ msgData txMsg of + Left err -> throwSDKError $ ParseError err + Right (msg :: msg) -> return msg + let msg' = txMsg {msgData = msg} + tx' = RoutedTx $ tx {txMsg = msg'} + moduleRouter m tx' + | otherwise = route rest tx diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs index d89c01c7..ebd4c4f5 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs @@ -1,14 +1,9 @@ module Tendermint.SDK.Types.Message where import Control.Lens (( # )) -import Data.Bifunctor (first) -import Data.ByteString (ByteString) -import qualified Data.ProtoLens as PL -import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text) import qualified Data.Validation as V -import qualified Proto3.Suite as Wire import qualified Proto3.Wire.Decode as Wire import Tendermint.SDK.Types.Address (Address) @@ -48,27 +43,22 @@ formatMessageParseError = cs . go OtherParseError txt -> ("Other Error", txt) in "Parse Error [" <> context <> "]: " <> msg -data DecodingOption = Proto3Suite | ProtoLens | Custom +-- Used to facilitate writing 'HasCodec' instances for protobuf messages that use +-- the proto3-suite library. +coerceProto3Error + :: Wire.ParseError + -> MessageParseError +coerceProto3Error = \case + Wire.WireTypeError txt -> WireTypeError (cs txt) + Wire.BinaryError txt -> BinaryError (cs txt) + Wire.EmbeddedError txt merr -> EmbeddedError (cs txt) (coerceProto3Error <$> merr) --- | Used for parsing messages, default instances given to accomodate both the --- | the [proto3-wire](https://hackage.haskell.org/package/proto3-wire) --- | and [proto-lens](https://hackage.haskell.org/package/proto-lens) libraries. --- | The constraint parameter is used to avoid ambiguous instances, if you would --- | like to write custom parsers you can use the 'CustomMessage' class constraint --- | with the empty implementation. -class ParseMessage (codec :: DecodingOption) msg where - decodeMessage :: Proxy codec -> ByteString -> Either MessageParseError msg - - -instance {-# OVERLAPPABLE #-} Wire.Message msg => ParseMessage 'Proto3Suite msg where - decodeMessage _ = first mkErr . Wire.fromByteString - where - mkErr (Wire.WireTypeError txt) = WireTypeError (cs txt) - mkErr (Wire.BinaryError txt) = BinaryError (cs txt) - mkErr (Wire.EmbeddedError txt merr) = EmbeddedError (cs txt) (mkErr <$> merr) - -instance PL.Message msg => ParseMessage 'ProtoLens msg where - decodeMessage _ = first (OtherParseError . cs) . PL.decodeMessage +-- Used to facilitate writing 'HasCodec' instances for protobuf messages that use +-- the proto-lens library. +coerceProtoLensError + :: String + -> MessageParseError +coerceProtoLensError = OtherParseError . cs -- | Used during message validation to indicate that although the message has parsed -- | correctly, it fails certain sanity checks. diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index a4d75f3d..e8f427a6 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -6,6 +6,7 @@ import Crypto.Hash.Algorithms (SHA256 (..)) import Data.ByteString (ByteString) import Data.Proxy import qualified Data.Serialize as Serialize +import Data.String.Conversions (cs) import Data.Text (Text) import GHC.Generics (Generic) import Tendermint.SDK.Crypto (MakeDigest (..), @@ -17,6 +18,7 @@ import Tendermint.SDK.Types.Message (Msg (..)) -- and an underlying message type 'msg'. data Tx alg msg = Tx { txMsg :: Msg msg + , txRoute :: Text , txSignature :: RecoverableSignature alg , txSignBytes :: Message alg , txSigner :: PubKey alg @@ -78,7 +80,12 @@ parseTx p rawTx@RawTransaction{..} = do { msgData = rawTransactionData , msgAuthor = addressFromPubKey p signerPubKey } + , txRoute = cs rawTransactionRoute , txSignature = recSig , txSignBytes = signBytes , txSigner = signerPubKey } + +data RoutedTx msg where + RoutedTx :: Tx alg msg -> RoutedTx msg + From 7813ed7113d935b32412a9f3d889fed416d30950 Mon Sep 17 00:00:00 2001 From: Ilya Ostrovskiy Date: Wed, 20 Nov 2019 22:16:43 -0500 Subject: [PATCH 07/70] A more thorough CI pipeline (#116) * break up .travis.yml into build stages * add a stage for testing hs-tendermint-client against abci-cli kvstore and supporting docker stuff * fix compose for kvstore test, use stages/job names correctly for parallelism * potential fix * add requisite flags and versions to tendermint to make kvstore test pass * simplify stage layout, create simplestorage e2e * fix travis.yml syntax * use docker volumes instead of /tmp directory, use consistent naming in composes, move disabled nix test into its own job for when its ready to be used * move Dockerfile to project-wide level, fix dockerignore to not copy stack stuff, have simple-storage E2E compose build using project-wide Dockerfile * Speed up SimpleStorage E2E significantly This takes advantage of docker-compose not building if an image with the same name exists locally. Since Travis is building on Linux, the binaries that get produced by a stack build are compatible and can just be copied wholesale into the container. This significantly speeds up E2E in Travis as we don't have to rebuild the whole app inside Docker and can take advantage of Travis' .stack-work cache as well. In the travis script, we run docker build with the appropriate tag to create the image locally, and all docker-compose.yamls which use these services can take advantage of that prebaked image. * expose the correct port in simple-storage docker-compose so it can actually run * change both queries to use client * merged client-store-fix * fix unused imports * stylish --- .dockerignore | 5 ++- .gitignore | 1 + .travis.yml | 43 +++++++++++++------ Dockerfile | 12 ++++++ Dockerfile.prebake | 8 ++++ Makefile | 9 ++++ hs-abci-examples/simple-storage/Dockerfile | 21 --------- .../simple-storage/docker-compose.yaml | 20 +++++---- .../test/SimpleStorage/Test/E2ESpec.hs | 22 ++++------ hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs | 7 ++- hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs | 4 +- hs-tendermint-client/Dockerfile.abci-cli | 13 ++++++ hs-tendermint-client/docker-compose.yaml | 30 +++++++++++++ 13 files changed, 136 insertions(+), 59 deletions(-) create mode 100644 Dockerfile create mode 100644 Dockerfile.prebake delete mode 100644 hs-abci-examples/simple-storage/Dockerfile create mode 100644 hs-tendermint-client/Dockerfile.abci-cli create mode 100644 hs-tendermint-client/docker-compose.yaml diff --git a/.dockerignore b/.dockerignore index 2762bb5c..4766dffb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,5 @@ */docker-compose* -*/test* +**/.stack-work +**/.stack +**/stack.yaml.lock +**/.git diff --git a/.gitignore b/.gitignore index f8da4173..b40ba9c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .stack-work/ *.cabal *~ +.ci-bins/ diff --git a/.travis.yml b/.travis.yml index e35ed203..4851ba56 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,6 +3,10 @@ ghc: - "8.6.1" cabal: "2.4" sudo: required + +services: +- docker + # Cache .stack for build_times-- addons: apt: @@ -14,6 +18,7 @@ addons: cache: directories: - $HOME/.stack + before_install: # Download and unpack the stack executable - mkdir -p ~/.local/bin @@ -26,16 +31,30 @@ before_install: - rm protoc-release.zip install: - travis_wait 120 stack --skip-ghc-check setup -- travis_wait 120 stack --skip-ghc-check install hlint-2.1.26 stylish-haskell-0.9.4.3 -script: -- make hlint - -# When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty -- if [ "$TRAVIS_BRANCH" == "master" ]; then make stylish && git diff-index --quiet HEAD; fi -- make test-libraries -# commenting for now as this build is broken with the addition of secp256k1 -# and will uncomment and fix when we are using again. -#- curl https://nixos.org/nix/install | sh -#- . $HOME/.nix-profile/etc/profile.d/nix.sh -#- stack --nix test hs-abci-types hs-abci-server hs-abci-sdk +jobs: + include: + - name: "Test Libraries" + script: make test-libraries + - name: "Test Libraries with Nix" + script: + - echo "This stage is currently disabled as Nix doesn't seem to play well with libsecp256k1" + #- curl https://nixos.org/nix/install | sh + #- source $HOME/.nix-profile/etc/profile.d/nix.sh && stack --nix test hs-abci-types hs-abci-server hs-abci-sdk + - name: "Test Tendermint Client" + script: + - docker-compose -f hs-tendermint-client/docker-compose.yaml -p test-hs-tendermint-client up -d + - make test-kv-store + - docker-compose -f hs-tendermint-client/docker-compose.yaml -p test-hs-tendermint-client down -v --rmi local + - name: "Test SimpleStorage Example E2E" + script: + - make docker-test-prebake + - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e up -d + - make test-simple-storage + - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e down -v --rmi local + - name: "Ensure that code matches style guidelines" + # When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty + if: branch = master + script: + - travis_wait 120 stack --skip-ghc-check install hlint-2.1.26 stylish-haskell-0.9.4.3 + - make stylish && git diff-index --quiet HEAD diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..f20b7d7d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,12 @@ +FROM haskell:8 + +RUN apt-get update && apt-get install --assume-yes protobuf-compiler libsecp256k1-dev + +# Install GHC. +WORKDIR /project +COPY . /project + +# Install project to /usr/local/bin +RUN stack setup && stack exec -- ghc --version +RUN stack build --copy-bins --local-bin-path /usr/local/bin + diff --git a/Dockerfile.prebake b/Dockerfile.prebake new file mode 100644 index 00000000..084b21ba --- /dev/null +++ b/Dockerfile.prebake @@ -0,0 +1,8 @@ +# This creates the same image as the regular Dockerfile, but assumes that the binaries +# were compiled into a .ci-bins/ folder in the same directory as this one. This is +# substantially faster than building inside Docker, but only works on Linux systems. +FROM haskell:8 + +RUN apt-get update && apt-get install --assume-yes protobuf-compiler libsecp256k1-dev + +COPY .ci-bins/* /usr/local/bin/ diff --git a/Makefile b/Makefile index 53fc9f3a..82e9a519 100644 --- a/Makefile +++ b/Makefile @@ -60,3 +60,12 @@ test-kv-store: install ## Run the test suite for the client interface test-simple-storage: install ## Run the test suite for the example application stack test simple-storage + + +##################### +# CI Support +##################### +docker-test-prebake: # Precompile all binaries externally and copy them into a docker image to speed up testing instead of building in Docker. Note that this only works on Linux systems. + mkdir -p .ci-bins + stack build --copy-bins --local-bin-path .ci-bins + docker build -t hs-abci:test -f Dockerfile.prebake . diff --git a/hs-abci-examples/simple-storage/Dockerfile b/hs-abci-examples/simple-storage/Dockerfile deleted file mode 100644 index 07be24ab..00000000 --- a/hs-abci-examples/simple-storage/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM haskell:8 - -RUN apt-get update && apt-get install --assume-yes protobuf-compiler - -# Install GHC. -WORKDIR /project -COPY ./stack.yaml /project -RUN stack setup && stack exec -- ghc --version - -# Install dependencies. -COPY hs-abci-types hs-abci-types/ -COPY hs-tendermint-client hs-tendermint-client/ -COPY hs-abci-server hs-abci-server/ -COPY hs-abci-extra hs-abci-extra/ -COPY hs-abci-sdk hs-abci-sdk/ -COPY hs-abci-examples/simple-storage simple-storage/ - -RUN stack build simple-storage --copy-bins --local-bin-path /usr/local/bin - -# Run project. -CMD /usr/local/bin/simple-storage diff --git a/hs-abci-examples/simple-storage/docker-compose.yaml b/hs-abci-examples/simple-storage/docker-compose.yaml index 0be9dd09..4ad1ed9d 100644 --- a/hs-abci-examples/simple-storage/docker-compose.yaml +++ b/hs-abci-examples/simple-storage/docker-compose.yaml @@ -1,25 +1,29 @@ version: '3.7' services: tendermint-init: - image: tendermint/tendermint:v0.32.2 + image: tendermint/tendermint:v0.32.8 command: init volumes: - - /tmp:/tendermint + - tendermint-storage:/tendermint tendermint: depends_on: - tendermint-init - - simple_storage - image: tendermint/tendermint:v0.32.2 - command: node --proxy_app=tcp://simple_storage:26658 + - simple-storage + image: tendermint/tendermint:v0.32.8 + command: node --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://simple-storage:26658 volumes: - - /tmp:/tendermint + - tendermint-storage:/tendermint restart: always ports: - "26656-26657:26656-26657" - simple_storage: + simple-storage: build: context: ../../. - dockerfile: hs-abci-examples/simple-storage/Dockerfile + dockerfile: Dockerfile + image: hs-abci:test restart: always + entrypoint: /usr/local/bin/simple-storage expose: - "26658" +volumes: + tendermint-storage: diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 1a071497..3f049bff 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -1,6 +1,6 @@ module SimpleStorage.Test.E2ESpec where -import Control.Lens (to, (^.)) +import Control.Lens ((^.)) import Control.Monad.Reader (ReaderT) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) @@ -24,7 +24,6 @@ import Tendermint.SDK.Query.Client (ClientResponse (..), HasClient (..), RunClient (..)) import Tendermint.SDK.Query.Types (QueryArgs (..)) -import Tendermint.SDK.Store (rawKey) import Test.Hspec @@ -42,7 +41,7 @@ spec = do , queryArgsHeight = 0 , queryArgsProve = False } - ClientResponse{clientResponseData}<- runQueryRunner $ getCount queryReq + ClientResponse{clientResponseData} <- runQueryRunner $ getCount queryReq clientResponseData `shouldBe` SS.Count 0 it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do @@ -55,14 +54,13 @@ spec = do deliverRespCode `shouldBe` 0 it "can make sure the synchronous tx transaction worked and the count is now 4" $ do - let queryReq = - def { RPC.requestABCIQueryPath = Just "simple_storage/count" - , RPC.requestABCIQueryData = SS.CountKey ^. rawKey . to Hex.fromBytes - } - queryResp <- fmap RPC.resultABCIQueryResponse . runRPC $ - RPC.abciQuery queryReq - let foundCount = queryResp ^. Resp._queryValue . to decodeCount - foundCount `shouldBe` 4 + let queryReq = QueryArgs + { queryArgsData = SS.CountKey + , queryArgsHeight = 0 + , queryArgsProve = False + } + ClientResponse{clientResponseData} <- runQueryRunner $ getCount queryReq + clientResponseData `shouldBe` SS.Count 4 encodeCount :: Int32 -> Base64String @@ -100,5 +98,3 @@ instance RunClient QueryRunner where getCount :: QueryArgs SS.CountKey -> QueryRunner (ClientResponse SS.Count) getCount = genClient (Proxy :: Proxy QueryRunner) (Proxy :: Proxy SS.Api) def - - diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs index afd442f0..47df7042 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.Query.Class where import Control.Error @@ -42,10 +43,12 @@ instance (HasRouter sublayout, KnownSymbol path) => HasRouter (path :> sublayout where proxyPath = Proxy :: Proxy path -instance Queryable a => HasRouter (Leaf a) where +instance (Queryable a, KnownSymbol (Name a)) => HasRouter (Leaf a) where type RouteT (Leaf a) m = ExceptT QueryError m (QueryResult a) - route _ = methodRouter + route _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter + where proxyPath = Proxy :: Proxy (Name a) + instance (FromQueryData a, HasRouter layout) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs index 0fe996b0..e394bdc1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs @@ -49,7 +49,7 @@ instance , HasCodec a , Member RawStore r ) => StoreQueryHandlers '[(k,a)] ns (Sem r) where - type QueryApi '[(k,a)] = Name a :> QA k :> Leaf a + type QueryApi '[(k,a)] = QA k :> Leaf a storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey instance @@ -60,7 +60,7 @@ instance , StoreQueryHandlers ((k', a') ': as) ns (Sem r) , Member RawStore r ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns (Sem r) where - type (QueryApi ((k, a) ': (k', a') : as)) = (Name a :> QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) + type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) storeQueryHandlers _ storeKey pm = storeQueryHandler (Proxy :: Proxy a) storeKey :<|> storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pm diff --git a/hs-tendermint-client/Dockerfile.abci-cli b/hs-tendermint-client/Dockerfile.abci-cli new file mode 100644 index 00000000..287cad9a --- /dev/null +++ b/hs-tendermint-client/Dockerfile.abci-cli @@ -0,0 +1,13 @@ +FROM golang:1.13-alpine +ARG TENDERMINT_VERSION +RUN echo "Will clone tendermint/tendermint#${TENDERMINT_VERSION}" && \ + apk add --no-cache ca-certificates git make && \ + mkdir -p $GOPATH/src/github.com/tendermint && \ + cd $GOPATH/src/github.com/tendermint && \ + git clone --branch "${TENDERMINT_VERSION}" https://github.com/tendermint/tendermint.git && \ + cd tendermint && \ + make tools && \ + make install_abci && \ + ln -s `which abci-cli` /abci-cli && \ + apk del git make +ENTRYPOINT ["/abci-cli"] diff --git a/hs-tendermint-client/docker-compose.yaml b/hs-tendermint-client/docker-compose.yaml new file mode 100644 index 00000000..519fcfa8 --- /dev/null +++ b/hs-tendermint-client/docker-compose.yaml @@ -0,0 +1,30 @@ +version: '3.7' +services: + tendermint-init: + image: tendermint/tendermint:v0.32.8 + command: init + volumes: + - tendermint-storage:/tendermint + tendermint: + depends_on: + - tendermint-init + - kvstore + image: tendermint/tendermint:v0.32.8 + command: node --rpc.laddr tcp://0.0.0.0:26657 --proxy_app=tcp://kvstore:26658 + volumes: + - tendermint-storage:/tendermint + restart: always + ports: + - "26656-26657:26656-26657" + kvstore: + build: + context: ./ + dockerfile: Dockerfile.abci-cli + args: + TENDERMINT_VERSION: v0.32.8 + restart: always + command: kvstore + expose: + - "26658" +volumes: + tendermint-storage: From 245d4cb5adb08ecefc2826a059d96988a56d773a Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Fri, 22 Nov 2019 09:34:36 -0800 Subject: [PATCH 08/70] compiles (#122) --- hs-abci-examples/nameservice/package.yaml | 8 ++++++++ .../nameservice/src/Nameservice/Application.hs | 4 ---- .../nameservice/src/Nameservice/Handlers.hs | 9 ++------- .../src/Nameservice/Modules/Nameservice.hs | 2 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 8 +------- .../Nameservice/Modules/Nameservice/Messages.hs | 14 ++------------ .../Nameservice/Modules/Nameservice/Router.hs | 17 ++++------------- .../Nameservice/Modules/Nameservice/Types.hs | 10 ++-------- .../src/Nameservice/Modules/Token.hs | 6 ++---- .../nameservice/src/Nameservice/Server.hs | 3 +-- hs-abci-examples/simple-storage/package.yaml | 5 +++++ hs-abci-extra/package.yaml | 8 ++++++++ .../ABCI/Server/Middleware/RequestLogger.hs | 3 --- .../ABCI/Server/Middleware/ResponseLogger.hs | 3 --- hs-abci-sdk/package.yaml | 4 ++++ hs-abci-sdk/src/Tendermint/SDK/Auth.hs | 1 - hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs | 3 +-- hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs | 7 ++----- hs-abci-server/package.yaml | 4 ++++ hs-abci-types/package.yaml | 4 ++++ hs-tendermint-client/package.yaml | 4 ++++ .../Tendermint/Client/Internal/RPCClient.hs | 3 +-- 22 files changed, 56 insertions(+), 74 deletions(-) diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index c55afd8d..152bc464 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -74,6 +74,14 @@ dependencies: library: source-dirs: src + ghc-options: + - -Werror + - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-record-updates + - -Wincomplete-uni-patterns + - -Wredundant-constraints exposed-modules: - Nameservice.Application - Nameservice.Server diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 5fcbed45..7fb8658b 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -8,7 +8,6 @@ module Nameservice.Application , router ) where -import Control.Exception (Exception) import Data.Proxy import qualified Nameservice.Modules.Nameservice as N import qualified Nameservice.Modules.Token as T @@ -66,8 +65,5 @@ router = R.router (Proxy @Secp256k1) modules type QueryApi = T.Api :<|> N.Api -apiP :: Proxy QueryApi -apiP = Proxy - queryServer :: QueryApplication (Sem BaseApp.BaseApp) queryServer = serve (Proxy :: Proxy QueryApi) (T.server :<|> N.server) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs index 70342c1d..4818ad3d 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs @@ -3,10 +3,7 @@ module Nameservice.Handlers where import Control.Lens (to, (&), (.~), (^.)) import qualified Data.ByteArray.Base64String as Base64 import Data.Default.Class (def) -import Data.Proxy -import Nameservice.Application (Handler, - compileToBaseApp, router) -import qualified Nameservice.Modules.Nameservice as N +import Nameservice.Application (compileToBaseApp, router) import Network.ABCI.Server.App (App (..), MessageType (..), Request (..), @@ -17,14 +14,12 @@ import Polysemy (Sem) import Polysemy.Error (catch) import Tendermint.SDK.Application (defaultHandler) import Tendermint.SDK.BaseApp (BaseApp) -import Tendermint.SDK.Crypto (Secp256k1) import Tendermint.SDK.Errors (AppError, SDKError (..), deliverTxAppError, throwSDKError) import Tendermint.SDK.Events (withEventBuffer) import Tendermint.SDK.Query (QueryApplication) -import Tendermint.SDK.Types.Transaction (parseRawTransaction, - parseTx) +import Tendermint.SDK.Types.Transaction (parseRawTransaction) echoH :: Request 'MTEcho diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index a74d14e6..5d7cdb69 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -37,7 +37,7 @@ import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types import Nameservice.Modules.Token (HasTokenEff) -import Tendermint.SDK.BaseApp ((:&), HasBaseAppEff) +import Tendermint.SDK.BaseApp (HasBaseAppEff) import Tendermint.SDK.Module (Module (..)) type NameserviceM r = Module "nameservice" NameserviceMessage Api r diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index ac20fd3e..974e2382 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -15,8 +15,7 @@ import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Errors (AppError (..), - IsAppError (..)) +import Tendermint.SDK.Errors (IsAppError (..)) import Tendermint.SDK.Events (Event, emit) import qualified Tendermint.SDK.Store as Store @@ -35,7 +34,6 @@ storeKey = Store.StoreKey . cs . symbolVal $ (Proxy :: Proxy NameserviceModule) eval :: HasBaseAppEff r - => HasTokenEff r => Sem (Nameservice ': Error NameserviceException ': r) a -> Sem r a eval = mapError makeAppError . evalNameservice @@ -79,7 +77,6 @@ setName SetName{..} = do deleteName :: HasTokenEff r => HasNameserviceEff r - => Member (Output Event) r => DeleteName -> Sem r () deleteName DeleteName{..} = do @@ -100,7 +97,6 @@ deleteName DeleteName{..} = do buyName :: HasTokenEff r => HasNameserviceEff r - => Member (Output Event) r => BuyName -> Sem r () -- ^ did it succeed @@ -118,7 +114,6 @@ buyName msg = do buyUnclaimedName :: HasTokenEff r => HasNameserviceEff r - => Member (Output Event) r => BuyName -> Sem r () buyUnclaimedName BuyName{..} = do @@ -139,7 +134,6 @@ buyName msg = do buyClaimedName :: HasNameserviceEff r => HasTokenEff r - => Member (Output Event) r => BuyName -> Whois -> Sem r () diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index 8fd79d2e..74819880 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -1,27 +1,17 @@ module Nameservice.Modules.Nameservice.Messages where -import Control.Applicative ((<|>)) -import Control.Lens (( # ), (^.)) -import qualified Data.Aeson as A import Data.Bifunctor (first) -import Data.Either (Either) import Data.Foldable (sequenceA_) -import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text) -import qualified Data.Validation as V import GHC.Generics (Generic) -import GHC.TypeLits (symbolVal) -import Nameservice.Aeson (defaultNameserviceOptions) -import Nameservice.Modules.Nameservice.Types (Name (..), - NameserviceModule) +import Nameservice.Modules.Nameservice.Types (Name (..)) import Nameservice.Modules.Token (Amount (..)) import Proto3.Suite (Message, Named, fromByteString, toLazyByteString) import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address, - addressFromBytes) +import Tendermint.SDK.Types.Address (Address) import Tendermint.SDK.Types.Message (Msg (..), ValidateMessage (..), coerceProto3Error, diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 10123bc5..18dd5449 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -1,20 +1,11 @@ module Nameservice.Modules.Nameservice.Router where -import Data.ByteString (ByteString) -import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (HasNameserviceEff, buyName, deleteName, setName) import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) import Nameservice.Modules.Token (HasTokenEff) -import Polysemy (Members, Sem) -import Polysemy.Error (Error) -import Tendermint.SDK.Auth (AuthError) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError, - SDKError (..), - throwSDKError) -import qualified Tendermint.SDK.TxRouter as R +import Polysemy (Sem) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) @@ -27,6 +18,6 @@ router router (RoutedTx Tx{txMsg}) = let Msg{msgData=msg} = txMsg in case msg of - NSetName txMsg -> setName txMsg - NBuyName txMsg -> buyName txMsg - NDeleteName txMsg -> deleteName txMsg + NSetName m -> setName m + NBuyName m -> buyName m + NDeleteName m -> deleteName m diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index b498fd45..abf2830b 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -1,13 +1,8 @@ module Nameservice.Modules.Nameservice.Types where -import Control.Lens (iso, (&), (.~), (^.)) -import Control.Lens.Wrapped (Wrapped (..)) +import Control.Lens (iso) import Data.Aeson as A import Data.Bifunctor (first) -import Data.ByteString (ByteString) -import Data.ByteString as BS -import Data.Maybe (fromJust) -import qualified Data.Serialize as Serialize import Data.String.Conversions (cs) import Data.Text (Text) import qualified Data.Text.Lazy as TL @@ -26,8 +21,7 @@ import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) import qualified Tendermint.SDK.Query as Q import qualified Tendermint.SDK.Store as Store -import Tendermint.SDK.Types.Address (Address, addressFromBytes, - addressToBytes) +import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index ea29bb55..72c95aff 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -4,8 +4,7 @@ module Nameservice.Modules.Token ( -- * types - Address(..) - , Amount(..) + Amount(..) , TokenException(..) , Transfer @@ -52,8 +51,7 @@ import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) import Tendermint.SDK.Events (Event, FromEvent, ToEvent (..), emit) -import Tendermint.SDK.Query (FromQueryData, Queryable (..), - RouteT) +import Tendermint.SDK.Query (Queryable (..), RouteT) import Tendermint.SDK.Query.Store (QueryApi, storeQueryHandlers) import qualified Tendermint.SDK.Store as Store import Tendermint.SDK.Types.Address (Address, addressFromBytes, diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index 9542eba9..b87e9cc1 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -4,8 +4,7 @@ import Data.Foldable (fold) import Data.Monoid (Endo (..)) import Data.Proxy import Nameservice.Application (AppConfig (..), - queryServer, - runHandler) + queryServer) import Nameservice.Handlers (nameserviceApp) import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index cf5e61e1..98407bdd 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -106,6 +106,11 @@ executables: ghc-options: - -Werror - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-record-updates + - -Wincomplete-uni-patterns + - -Wredundant-constraints dependencies: - simple-storage diff --git a/hs-abci-extra/package.yaml b/hs-abci-extra/package.yaml index cd752d08..047a7de1 100644 --- a/hs-abci-extra/package.yaml +++ b/hs-abci-extra/package.yaml @@ -48,3 +48,11 @@ default-extensions: library: source-dirs: src + ghc-options: + - -Werror + - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-record-updates + - -Wincomplete-uni-patterns + - -Wredundant-constraints diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs index 28263afd..dc1ad784 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs @@ -6,11 +6,8 @@ module Network.ABCI.Server.Middleware.RequestLogger , mkRequestLogger , mkRequestLoggerM ) where -import Control.Lens (at, (?~)) import Control.Monad.IO.Class (MonadIO, liftIO) import qualified Data.Aeson as A -import qualified Data.HashMap.Strict as H -import Data.Text (Text) import Katip import Network.ABCI.Server.App (App (..), MessageType, Middleware, Request (..)) diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs index 1a50fad4..420e2618 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs @@ -6,11 +6,8 @@ module Network.ABCI.Server.Middleware.ResponseLogger , mkResponseLogger , mkResponseLoggerM ) where -import Control.Lens (at, (?~)) import Control.Monad.IO.Class (MonadIO, liftIO) import qualified Data.Aeson as A -import qualified Data.HashMap.Strict as H -import Data.Text (Text) import Katip import Network.ABCI.Server.App (App (..), MessageType, Middleware, Response (..)) diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index e5392b26..edb0c328 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -79,6 +79,10 @@ library: - -fplugin=Polysemy.Plugin - -Werror - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-uni-patterns + - -Wredundant-constraints exposed-modules: - Tendermint.SDK.Application - Tendermint.SDK.Aeson diff --git a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs index 5b9e6a99..e02157d6 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Auth.hs @@ -86,7 +86,6 @@ storeKey = StoreKey "auth" eval :: HasBaseAppEff r - => Member (Error AppError) r => Sem (Accounts ': Error AuthError ': r) a -> Sem r a eval = mapError makeAppError . evalAuth diff --git a/hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs b/hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs index cf783846..9baeb9a4 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs @@ -29,8 +29,7 @@ mkLogConfig environment processName = do evalKatip :: forall r a. - K.Katip (Sem r) - => K.KatipContext (Sem r) + K.KatipContext (Sem r) => Sem (Logger ': r) a -> Sem r a evalKatip = do diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs index e394bdc1..4a346590 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs @@ -43,8 +43,7 @@ class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) m where storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy m -> RouteT (QueryApi kvs) m instance - ( Queryable a - , IsKey k ns + ( IsKey k ns , a ~ Value k ns , HasCodec a , Member RawStore r @@ -53,8 +52,7 @@ instance storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey instance - ( Queryable a - , IsKey k ns + ( IsKey k ns , a ~ Value k ns , HasCodec a , StoreQueryHandlers ((k', a') ': as) ns (Sem r) @@ -68,7 +66,6 @@ instance allStoreHandlers :: forall (contents :: [*]) ns r. StoreQueryHandlers contents ns (Sem r) - => Member RawStore r => Proxy contents -> StoreKey ns -> Proxy r diff --git a/hs-abci-server/package.yaml b/hs-abci-server/package.yaml index 9ceaba95..d54289cf 100644 --- a/hs-abci-server/package.yaml +++ b/hs-abci-server/package.yaml @@ -50,6 +50,10 @@ library: ghc-options: - -Werror - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-uni-patterns + - -Wredundant-constraints exposed-modules: - Network.ABCI.Server - Network.ABCI.Server.App diff --git a/hs-abci-types/package.yaml b/hs-abci-types/package.yaml index 9624c12d..df6edff7 100644 --- a/hs-abci-types/package.yaml +++ b/hs-abci-types/package.yaml @@ -57,6 +57,10 @@ library: ghc-options: - -Werror - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-uni-patterns + - -Wredundant-constraints exposed-modules: - Data.ByteArray.HexString - Data.ByteArray.Base64String diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index ab18b00f..306427dd 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -55,6 +55,10 @@ library: ghc-options: - -Werror - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-record-updates + - -Wredundant-constraints exposed-modules: - Network.Tendermint.Client diff --git a/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs b/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs index d4532050..3e592dd8 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs @@ -2,7 +2,7 @@ module Network.Tendermint.Client.Internal.RPCClient where import Control.Applicative ((<|>)) import Control.Exception (Exception) -import Control.Monad.Catch (MonadThrow, throwM) +import Control.Monad.Catch (throwM) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (MonadReader, ask) import Data.Aeson (FromJSON (..), Result (..), @@ -99,7 +99,6 @@ data Config = Config remote :: ( MonadIO m - , MonadThrow m , MonadReader Config m , FromJSON output , ToJSON input From cf6954d10bb448301e29260410bcf005841b1b77 Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Fri, 22 Nov 2019 16:43:09 -0500 Subject: [PATCH 09/70] Nameservice tests (#114) * Start impl nameservice tests * wip * typo * Ordering matters * change field orders * nameservice tests without accounts +stylish * add faucet function * successful fauceting * add more buy tests * wip * wip * add message exports * Use client queries * potential fix * add negative tests * Fix deletename order + add tests * remove unused exports * small refactor * hlint * Note possible problem * use default args in delete test * Fixes * hlint * stylish * tests are passing * Attempt to update travis * change nameservice executable * small refactor to test events a bit easier * Add some event tests * typo * filter events by eventType * stub validatemessage for faucet * unused args * address more warnings * stylish + hlint --- .travis.yml | 6 + Makefile | 11 +- .../nameservice/docker-compose.yaml | 29 ++ hs-abci-examples/nameservice/package.yaml | 8 +- .../nameservice/protogen/Protogen.hs | 6 +- .../src/Nameservice/Application.hs | 1 + .../src/Nameservice/Modules/Nameservice.hs | 9 + .../Nameservice/Modules/Nameservice/Keeper.hs | 17 +- .../Modules/Nameservice/Messages.hs | 41 ++- .../Nameservice/Modules/Nameservice/Router.hs | 12 +- .../Nameservice/Modules/Nameservice/Types.hs | 23 +- .../src/Nameservice/Modules/Token.hs | 6 +- .../test/Nameservice/Test/E2ESpec.hs | 302 ++++++++++++++++++ .../test/Nameservice/Test/NameserviceSpec.hs | 30 -- hs-abci-sdk/package.yaml | 1 + hs-abci-sdk/src/Tendermint/SDK/Events.hs | 2 +- .../src/Tendermint/SDK/Query/Client.hs | 22 +- hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs | 1 - .../src/Tendermint/SDK/Types/Transaction.hs | 7 +- .../src/Network/Tendermint/Client.hs | 2 +- 20 files changed, 470 insertions(+), 66 deletions(-) create mode 100644 hs-abci-examples/nameservice/docker-compose.yaml create mode 100644 hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs delete mode 100644 hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs diff --git a/.travis.yml b/.travis.yml index 4851ba56..7a843f17 100644 --- a/.travis.yml +++ b/.travis.yml @@ -52,6 +52,12 @@ jobs: - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e up -d - make test-simple-storage - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e down -v --rmi local + - name: "Test Nameservice Example E2E" + script: + - make docker-test-prebake + - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml -p test-hs-abci-examples-nameservice-e2e up -d + - make test-nameservice + - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local - name: "Ensure that code matches style guidelines" # When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty if: branch = master diff --git a/Makefile b/Makefile index 82e9a519..d9cc6098 100644 --- a/Makefile +++ b/Makefile @@ -52,15 +52,24 @@ test-libraries: install ## Run the haskell test suite for all haskell libraries deploy-simple-storage-docker: install ## run the simple storage docker network docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml up --build +deploy-nameservice-docker: install ## run the nameservice docker network + docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml up --build + deploy-simple-storage-local: install ## run the simple storage locally stack exec simple-storage +deploy-nameservice-local: install ## run the nameservice locally + stack exec nameservice + test-kv-store: install ## Run the test suite for the client interface stack test hs-tendermint-client -test-simple-storage: install ## Run the test suite for the example application +test-simple-storage: install ## Run the test suite for the simple-storage example application stack test simple-storage +test-nameservice: install ## Run the test suite for the nameservice example application + stack test nameservice + ##################### # CI Support diff --git a/hs-abci-examples/nameservice/docker-compose.yaml b/hs-abci-examples/nameservice/docker-compose.yaml new file mode 100644 index 00000000..65b2b811 --- /dev/null +++ b/hs-abci-examples/nameservice/docker-compose.yaml @@ -0,0 +1,29 @@ +version: '3.7' +services: + tendermint-init: + image: tendermint/tendermint:v0.32.8 + command: init + volumes: + - tendermint-storage:/tendermint + tendermint: + depends_on: + - tendermint-init + - nameservice + image: tendermint/tendermint:v0.32.8 + command: node --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://nameservice:26658 + volumes: + - tendermint-storage:/tendermint + restart: always + ports: + - "26656-26657:26656-26657" + nameservice: + build: + context: ../../. + dockerfile: Dockerfile + image: hs-abci:test + restart: always + entrypoint: /usr/local/bin/nameservice + expose: + - "26658" +volumes: + tendermint-storage: diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 152bc464..5208b2a9 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -86,6 +86,7 @@ library: - Nameservice.Application - Nameservice.Server - Nameservice.Handlers + - Nameservice.Modules.Nameservice - Nameservice.Modules.Token - Nameservice.Modules.Nameservice.Messages - Nameservice.Modules.Nameservice.Types @@ -94,7 +95,7 @@ library: - Nameservice.Modules.Nameservice.Router executables: - nameservice-exe: + nameservice: main: Main.hs source-dirs: app ghc-options: @@ -118,10 +119,15 @@ tests: nameservice-test: main: Spec.hs source-dirs: test + other-modules: + - Nameservice.Test.E2ESpec ghc-options: - -threaded - -rtsopts - -with-rtsopts=-N dependencies: - nameservice + - aeson-pretty - hspec + - secp256k1-haskell + - hs-tendermint-client diff --git a/hs-abci-examples/nameservice/protogen/Protogen.hs b/hs-abci-examples/nameservice/protogen/Protogen.hs index 69a74249..3138919c 100644 --- a/hs-abci-examples/nameservice/protogen/Protogen.hs +++ b/hs-abci-examples/nameservice/protogen/Protogen.hs @@ -32,13 +32,13 @@ import qualified Text.PrettyPrint as PP -------------------------------------------------------------------------------- testSN :: SetName -testSN = SetName (Name "satoshi") "cool cats" (Address "01") +testSN = SetName (Name "satoshi") (Address "01") "cool cats" testBN :: BuyName -testBN = BuyName (Name "satoshi") "cool cats" (Address "01") (Amount 999) +testBN = BuyName (Amount 999) (Name "satoshi") "cool cats" (Address "01") testDN :: DeleteName -testDN = DeleteName (Name "satoshi") (Address "01") +testDN = DeleteName (Address "01") (Name "satoshi") -- @NOTE: for some reason, encoding results in a lazy bytestring -- while the provided decoder only accepts a strict bytestring diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 7fb8658b..8c7b6050 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -4,6 +4,7 @@ module Nameservice.Application , Handler , compileToBaseApp , runHandler + , QueryApi , queryServer , router ) where diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index 5d7cdb69..ed30d20c 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -10,6 +10,14 @@ module Nameservice.Modules.Nameservice , Whois (..) , NameserviceException(..) , NameserviceMessage(..) + , NameClaimed(..) + , NameRemapped(..) + , NameDeleted(..) + , Faucetted(..) + , BuyName(..) + , SetName(..) + , DeleteName(..) + , FaucetAccount(..) -- * effects , NameserviceEffR @@ -18,6 +26,7 @@ module Nameservice.Modules.Nameservice , buyName , setName , deleteName + , faucetAccount -- * interpreter , eval diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 974e2382..a85306b8 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -54,6 +54,17 @@ eval = mapError makeAppError . evalNameservice -------------------------------------------------------------------------------- +faucetAccount + :: HasTokenEff r + => FaucetAccount + -> Sem r () +faucetAccount FaucetAccount{..} = do + mint faucetAccountTo faucetAccountAmount + emit Faucetted + { faucettedAccount = faucetAccountTo + , faucettedAmount = faucetAccountAmount + } + setName :: HasTokenEff r => HasNameserviceEff r @@ -142,7 +153,11 @@ buyName msg = do in if buyNameBid > forsalePrice then do transfer buyNameBuyer buyNameBid previousOwner - putWhois buyNameName currentWhois {whoisOwner = buyNameBuyer} + -- update new owner, price and value based on BuyName + putWhois buyNameName currentWhois { whoisOwner = buyNameBuyer + , whoisPrice = buyNameBid + , whoisValue = buyNameValue + } emit NameClaimed { nameClaimedOwner = buyNameBuyer , nameClaimedName = buyNameName diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index 74819880..41f0f9f8 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -23,14 +23,27 @@ data NameserviceMessage = NSetName SetName | NBuyName BuyName | NDeleteName DeleteName + | NFaucetAccount FaucetAccount deriving (Eq, Show, Generic) +data FaucetAccount = FaucetAccount + { faucetAccountTo :: Address + , faucetAccountAmount :: Amount + } deriving (Eq, Show, Generic) + +instance Message FaucetAccount +instance Named FaucetAccount + +instance HasCodec FaucetAccount where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + -- @NOTE: .proto genration will use these type names as is -- only field names stripped of prefixes during generation data SetName = SetName { setNameName :: Name - , setNameValue :: Text , setNameOwner :: Address + , setNameValue :: Text } deriving (Eq, Show, Generic) instance Message SetName @@ -41,8 +54,8 @@ instance HasCodec SetName where decode = first (formatMessageParseError . coerceProto3Error) . fromByteString data DeleteName = DeleteName - { deleteNameName :: Name - , deleteNameOwner :: Address + { deleteNameOwner :: Address + , deleteNameName :: Name } deriving (Eq, Show, Generic) instance Message DeleteName @@ -53,10 +66,10 @@ instance HasCodec DeleteName where decode = first (formatMessageParseError . coerceProto3Error) . fromByteString data BuyName = BuyName - { buyNameName :: Name + { buyNameBid :: Amount + , buyNameName :: Name , buyNameValue :: Text , buyNameBuyer :: Address - , buyNameBid :: Amount } deriving (Eq, Show, Generic) instance Message BuyName @@ -68,19 +81,27 @@ instance HasCodec BuyName where instance HasCodec NameserviceMessage where decode bs = - fmap NSetName (decode bs) <> + -- @NOTE: tests pass iff NBuyName is first fmap NBuyName (decode bs) <> - fmap NDeleteName (decode bs) + fmap NSetName (decode bs) <> + fmap NDeleteName (decode bs) <> + fmap NFaucetAccount (decode bs) encode = \case NSetName msg -> encode msg NBuyName msg -> encode msg NDeleteName msg -> encode msg + NFaucetAccount msg -> encode msg instance ValidateMessage NameserviceMessage where validateMessage m@Msg{msgData} = case msgData of - NSetName msg -> validateMessage m {msgData = msg} - NBuyName msg -> validateMessage m {msgData = msg} - NDeleteName msg -> validateMessage m {msgData = msg} + NBuyName msg -> validateMessage m {msgData = msg} + NSetName msg -> validateMessage m {msgData = msg} + NDeleteName msg -> validateMessage m {msgData = msg} + NFaucetAccount msg -> validateMessage m {msgData = msg} + +-- @TODO: Move this to token module +instance ValidateMessage FaucetAccount where + validateMessage _ = mempty -- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg instance ValidateMessage SetName where diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 18dd5449..4390b150 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -2,6 +2,7 @@ module Nameservice.Modules.Nameservice.Router where import Nameservice.Modules.Nameservice.Keeper (HasNameserviceEff, buyName, deleteName, + faucetAccount, setName) import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) import Nameservice.Modules.Token (HasTokenEff) @@ -16,8 +17,9 @@ router => RoutedTx NameserviceMessage -> Sem r () router (RoutedTx Tx{txMsg}) = - let Msg{msgData=msg} = txMsg - in case msg of - NSetName m -> setName m - NBuyName m -> buyName m - NDeleteName m -> deleteName m + let Msg{msgData} = txMsg + in case msgData of + NSetName msg -> setName msg + NBuyName msg -> buyName msg + NDeleteName msg -> deleteName msg + NFaucetAccount msg -> faucetAccount msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index abf2830b..d778cb4c 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -93,12 +93,28 @@ instance IsAppError NameserviceException where -- Events -------------------------------------------------------------------------------- +data Faucetted = Faucetted + { faucettedAccount :: Address + , faucettedAmount :: Amount + } deriving (Eq, Show, Generic) + +faucettedAesonOptions :: A.Options +faucettedAesonOptions = defaultNameserviceOptions "faucetted" + +instance ToJSON Faucetted where + toJSON = A.genericToJSON faucettedAesonOptions +instance FromJSON Faucetted where + parseJSON = A.genericParseJSON faucettedAesonOptions +instance ToEvent Faucetted where + makeEventType _ = "Faucetted" +instance FromEvent Faucetted + data NameClaimed = NameClaimed { nameClaimedOwner :: Address , nameClaimedName :: Name , nameClaimedValue :: Text , nameClaimedBid :: Amount - } deriving (Generic) + } deriving (Eq, Show, Generic) nameClaimedAesonOptions :: A.Options nameClaimedAesonOptions = defaultNameserviceOptions "nameClaimed" @@ -109,12 +125,13 @@ instance FromJSON NameClaimed where parseJSON = A.genericParseJSON nameClaimedAesonOptions instance ToEvent NameClaimed where makeEventType _ = "NameClaimed" +instance FromEvent NameClaimed data NameRemapped = NameRemapped { nameRemappedName :: Name , nameRemappedOldValue :: Text , nameRemappedNewValue :: Text - } deriving Generic + } deriving (Eq, Show, Generic) nameRemappedAesonOptions :: A.Options nameRemappedAesonOptions = defaultNameserviceOptions "nameRemapped" @@ -129,7 +146,7 @@ instance FromEvent NameRemapped data NameDeleted = NameDeleted { nameDeletedName :: Name - } deriving Generic + } deriving (Eq, Show, Generic) nameDeletedAesonOptions :: A.Options nameDeletedAesonOptions = defaultNameserviceOptions "nameDeleted" diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 72c95aff..42d8bb35 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -6,7 +6,7 @@ module Nameservice.Modules.Token -- * types Amount(..) , TokenException(..) - , Transfer + , Transfer(..) -- * effects , Token @@ -100,7 +100,7 @@ data Transfer = Transfer { transferAmount :: Amount , transferTo :: Address , transferFrom :: Address - } deriving Generic + } deriving (Eq, Show, Generic) transferAesonOptions :: A.Options transferAesonOptions = defaultNameserviceOptions "transfer" @@ -215,7 +215,7 @@ burn burn addr amount = do bal <- getBalance addr if bal < amount - then throw $ InsufficientFunds "Insuffient funds for burn." + then throw $ InsufficientFunds "Insufficient funds for burn." else putBalance addr (bal - amount) mint diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs new file mode 100644 index 00000000..7cd7f71e --- /dev/null +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -0,0 +1,302 @@ +module Nameservice.Test.E2ESpec where + +import Control.Lens ((^.)) +import Crypto.Secp256k1 (SecKey, derivePubKey, + exportCompactRecSig, + secKey) +import Data.Aeson (ToJSON) +import Data.Aeson.Encode.Pretty (encodePretty) +import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteArray.HexString as Hex +import qualified Data.ByteString as BS +import qualified Data.ByteString.Lazy as BL +import Data.Default.Class (def) +import Data.Either (partitionEithers) +import Data.Maybe (fromJust) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.String (fromString) +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word32) +import Nameservice.Application (QueryApi) +import Nameservice.Modules.Nameservice (BuyName (..), + DeleteName (..), + FaucetAccount (..), + Name (..), + NameClaimed (..), + NameDeleted (..), + NameRemapped (..), + SetName (..), + Whois (..)) +import Nameservice.Modules.Token (Amount (..), + Transfer (..)) +import Network.ABCI.Types.Messages.FieldTypes (Event (..)) +import qualified Network.ABCI.Types.Messages.Response as Response +import qualified Network.Tendermint.Client as RPC +import Proto3.Suite (Message, + toLazyByteString) +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (Secp256k1, + addressFromPubKey) +import Tendermint.SDK.Events (FromEvent (..)) +import Tendermint.SDK.Query.Client (ClientResponse (..), + genClient) +import Tendermint.SDK.Query.Types (QueryArgs (..)) +import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..), + signRawTransaction) +import Test.Hspec + +spec :: Spec +spec = do + let satoshi = Name "satoshi" + addr1 = userAddress user1 + privateKey1 = userPrivKey user1 + addr2 = userAddress user2 + privateKey2 = userPrivKey user2 + + beforeAll (do faucetAccount user1; faucetAccount user2) $ + describe "Nameservice Spec" $ do + it "Can query /health to make sure the node is alive" $ do + resp <- runRPC RPC.health + resp `shouldBe` RPC.ResultHealth + + it "Can query account balances" $ do + let queryReq = defaultReqWithData addr1 + ClientResponse{clientResponseData = foundAmount} <- runRPC $ getBalance queryReq + foundAmount `shouldBe` Amount 1000 + + it "Can create a name" $ do + let val = "hello world" + msg = BuyName 0 satoshi val addr1 + claimedLog = NameClaimed addr1 satoshi val 0 + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 0 + (errs, events) <- deliverTxEvents deliverResp "NameClaimed" + errs `shouldBe` mempty + events `shouldSatisfy` elem claimedLog + + it "Can query for a name" $ do + let queryReq = defaultReqWithData satoshi + ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq + whoisValue foundWhois `shouldBe` "hello world" + whoisOwner foundWhois `shouldBe` addr1 + whoisPrice foundWhois `shouldBe` 0 + + it "Can query for a name that doesn't exist" $ do + let nope = Name "nope" + queryReq = defaultReqWithData nope + ClientResponse{ clientResponseData = emptyWhois + , clientResponseRaw + } <- runRPC $ getWhois queryReq + let queryRespCode = clientResponseRaw ^. Response._queryCode + -- storage failure + queryRespCode `shouldBe` 1 + -- empty whois (defaults) + whoisPrice emptyWhois `shouldBe` 0 + whoisOwner emptyWhois `shouldBe` Address "" + whoisValue emptyWhois `shouldBe` "" + + it "Can set a name value" $ do + let oldVal = "hello world" + newVal = "goodbye to a world" + msg = SetName satoshi addr1 newVal + remappedLog = NameRemapped satoshi oldVal newVal + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 0 + (errs, events) <- deliverTxEvents deliverResp "NameRemapped" + errs `shouldBe` mempty + events `shouldSatisfy` elem remappedLog + -- check for changes + let queryReq = defaultReqWithData satoshi + ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq + whoisValue foundWhois `shouldBe` "goodbye to a world" + -- eveyrthing else should remain the same + whoisOwner foundWhois `shouldBe` addr1 + whoisPrice foundWhois `shouldBe` 0 + + it "Can fail to set a name" $ do + -- try to set a name without being the owner + let msg = SetName satoshi addr2 "goodbye to a world" + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 2 + + it "Can buy an existing name" $ do + -- how to do both types of logs? + let oldVal = "goodbye to a world" + newVal = "hello (again) world" + msg = BuyName 300 satoshi newVal addr2 + claimedLog = NameClaimed addr2 satoshi newVal 300 + -- transferLog = Transfer 300 addr1 addr2 + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 0 + (errs, events) <- deliverTxEvents deliverResp "NameClaimed" + errs `shouldBe` mempty + events `shouldSatisfy` elem claimedLog + -- events `shouldSatisfy` elem transferLog + -- check for ownership changes + let queryReq = defaultReqWithData satoshi + ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq + whoisOwner foundWhois `shouldBe` addr2 + whoisPrice foundWhois `shouldBe` 300 + whoisValue foundWhois `shouldBe` "hello (again) world" + + -- @NOTE: this is possibly a problem with the go application too + -- https://cosmos.network/docs/tutorial/buy-name.html#msg + it "Can buy self-owned names (and make a profit)" $ do + -- check balance before + let queryReq = defaultReqWithData addr2 + ClientResponse{clientResponseData = beforeBuyAmount} <- runRPC $ getBalance queryReq + -- buy + let val = "hello (again) world" + msg = BuyName 500 satoshi val addr2 + claimedLog = NameClaimed addr2 satoshi val 500 + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 0 + (errs, events) <- deliverTxEvents deliverResp "NameClaimed" + errs `shouldBe` mempty + events `shouldSatisfy` elem claimedLog + -- check balance after + ClientResponse{clientResponseData = afterBuyAmount} <- runRPC $ getBalance queryReq + -- owner/buyer still profits + beforeBuyAmount `shouldSatisfy` (< afterBuyAmount) + + it "Can fail to buy a name" $ do + -- try to buy at a lower price + let msg = BuyName 100 satoshi "hello (again) world" addr1 + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 1 + + it "Can delete names" $ do + let msg = DeleteName addr2 satoshi + deletedLog = NameDeleted satoshi + rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 0 + (errs, events) <- deliverTxEvents deliverResp "NameDeleted" + errs `shouldBe` mempty + events `shouldSatisfy` elem deletedLog + -- name shouldn't exist + let queryReq = defaultReqWithData satoshi + ClientResponse{ clientResponseData = emptyWhois + , clientResponseRaw + } <- runRPC $ getWhois queryReq + let queryRespCode = clientResponseRaw ^. Response._queryCode + -- storage failure + queryRespCode `shouldBe` 1 + -- should be a default whois + whoisPrice emptyWhois `shouldBe` 0 + whoisOwner emptyWhois `shouldBe` Address "" + whoisValue emptyWhois `shouldBe` "" + + -- @TODO: make transfer messages + it "Can fail a transfer" $ do + -- try to give addr1 2000 from addr2 + pendingWith "Split token module" + +runRPC :: forall a. RPC.TendermintM a -> IO a +runRPC = RPC.runTendermintM rpcConfig + where + rpcConfig :: RPC.Config + rpcConfig = + let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 + prettyPrint :: forall b. ToJSON b => String -> b -> IO () + prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) + in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") + +faucetAccount :: User -> IO () +faucetAccount User{userAddress, userPrivKey} = do + let msg = FaucetAccount userAddress 1000 + -- @NOTE: why is this `nameservice` and not `token`? + rawTx = mkSignedRawTransactionWithRoute "nameservice" userPrivKey msg + txReq = + RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + _ <- runRPC $ RPC.broadcastTxCommit txReq + return () + +-- executes a request, then returns the deliverTx response +getDeliverTxResponse :: RawTransaction -> IO Response.DeliverTx +getDeliverTxResponse rawTx = do + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ + RPC.broadcastTxCommit txReq + +-- get the logged events from a deliver response, +-- ensures there are no errors when parsing event logs +deliverTxEvents :: FromEvent e => Response.DeliverTx -> Text -> IO ([Text],[e]) +deliverTxEvents deliverResp eventName = do + let deliverEvents = deliverResp ^. Response._deliverTxEvents + filtered = filter ((== eventName) . eventType) deliverEvents + return . partitionEithers . map fromEvent $ filtered + +-- check for a specific deliver response code +ensureDeliverResponseCode :: Response.DeliverTx -> Word32 -> IO () +ensureDeliverResponseCode deliverResp code = do + let deliverRespCode = deliverResp ^. Response._deliverTxCode + deliverRespCode `shouldBe` code + +-- this probably should be in a default type class +defaultReqWithData :: a -> QueryArgs a +defaultReqWithData x = QueryArgs + { queryArgsData = x + , queryArgsHeight = 0 + , queryArgsProve = False + } + +-------------------------------------------------------------------------------- + +decodeValue :: HasCodec a => Base64.Base64String -> a +decodeValue = (\(Right a) -> a) . decode . Base64.toBytes + +encodeRawTx :: RawTransaction -> Base64.Base64String +encodeRawTx = Base64.fromBytes . Serialize.encode + +encodeMsgData :: Message a => a -> BS.ByteString +encodeMsgData = BL.toStrict . toLazyByteString + +-- sign a trx with a user's private key +mkSignedRawTransactionWithRoute :: Message a => BS.ByteString -> SecKey -> a -> RawTransaction +mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned + where unsigned = RawTransaction { rawTransactionData = encodeMsgData msg + , rawTransactionRoute = route + , rawTransactionSignature = "" + } + sig = signRawTransaction algProxy privateKey unsigned + sign rt = rt { rawTransactionSignature = Serialize.encode $ exportCompactRecSig sig } + +data User = User + { userPrivKey :: SecKey + , userAddress :: Address + } + +user1 :: User +user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" + +user2 :: User +user2 = makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" + +makeUser :: String -> User +makeUser privKeyStr = + let privateKey = fromJust . secKey . Hex.toBytes . fromString $ privKeyStr + pubKey = derivePubKey privateKey + address = addressFromPubKey (Proxy @Secp256k1) pubKey + in User privateKey address + +algProxy :: Proxy Secp256k1 +algProxy = Proxy + +-------------------------------------------------------------------------------- + +getWhois :: QueryArgs Name -> RPC.TendermintM (ClientResponse Whois) +getBalance :: QueryArgs Address -> RPC.TendermintM (ClientResponse Amount) + +(getBalance :<|> getWhois) = + genClient (Proxy :: Proxy RPC.TendermintM) (Proxy :: Proxy QueryApi) def diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs deleted file mode 100644 index 6ba4f6ff..00000000 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/NameserviceSpec.hs +++ /dev/null @@ -1,30 +0,0 @@ -module Nameservice.Test.NameserviceSpec where - -import Test.Hspec - -spec :: Spec -spec = do - describe "Nameservice Spec" $ do - it "Can query account balances" $ do - pending - - it "Can create a name" $ do - pending - - it "Can query for a name that exists" $ do - pending - - it "Can query for a name that doesn't exist" $ do - pending - - it "Can set a name value" $ do - pending - - it "Can buy a name" $ do - pending - - it "Can fail to buy a name" $ do - pending - - it "Can fail a transfer" $ do - pending diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index edb0c328..87f1f59e 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -53,6 +53,7 @@ dependencies: - exceptions - hs-abci-types - hs-abci-server +- hs-tendermint-client - http-types - katip - lens diff --git a/hs-abci-sdk/src/Tendermint/SDK/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/Events.hs index 8401d8d7..3ea03c4e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Events.hs @@ -56,7 +56,7 @@ class ToEvent e => FromEvent e where fromEvent Event{eventType, eventAttributes} = let expectedType = makeEventType (Proxy @e) in if cs eventType /= expectedType - then fail ("Couldn't math expected event type " <> expectedType <> + then fail ("Couldn't match expected event type " <> expectedType <> " with found type " <> cs eventType) else let fromKVPair :: KVPair -> Either String (Text, A.Value) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs index 836d391d..9ffdd803 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs @@ -7,12 +7,16 @@ module Tendermint.SDK.Query.Client ) where import Control.Lens (to, (^.)) +import Control.Monad.Reader (ReaderT) import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) import Data.Proxy import Data.String.Conversions (cs) import GHC.TypeLits (KnownSymbol, symbolVal) import qualified Network.ABCI.Types.Messages.Request as Req import qualified Network.ABCI.Types.Messages.Response as Resp +import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..), (:>)) import Tendermint.SDK.Query.Types (Leaf, QA, QueryArgs (..), Queryable (..)) @@ -22,6 +26,16 @@ class Monad m => RunClient m where -- | How to make a request. runQuery :: Req.Query -> m Resp.Query +instance RunClient (ReaderT RPC.Config IO) where + runQuery Req.Query{..} = + let rpcQ = RPC.RequestABCIQuery + { RPC.requestABCIQueryPath = Just queryPath + , RPC.requestABCIQueryData = Hex.fromBytes @ByteString . Base64.toBytes $ queryData + , RPC.requestABCIQueryHeight = Just $ queryHeight + , RPC.requestABCIQueryProve = queryProve + } + in RPC.resultABCIQueryResponse <$> RPC.abciQuery rpcQ + class HasClient m layout where type ClientT (m :: * -> *) layout :: * @@ -54,7 +68,7 @@ instance (RunClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasCli genClient _ _ q = let leaf = symbolVal (Proxy @(Name a)) in do - r@Resp.Query{..} <- runQuery q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } - return $ case decodeQueryResult queryValue of - Left err -> error $ "Impossible parse error: " <> cs err - Right a -> ClientResponse a r + r@Resp.Query{..} <- runQuery q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } + return $ case decodeQueryResult queryValue of + Left err -> error $ "Impossible parse error: " <> cs err + Right a -> ClientResponse a r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs index 447f199d..1f5a7f52 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs @@ -14,7 +14,6 @@ import qualified Network.ABCI.Types.Messages.Response as Response import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Store (RawKey (..)) - data Leaf (a :: *) data QA (a :: *) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index e8f427a6..c00a86d4 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -3,6 +3,7 @@ module Tendermint.SDK.Types.Transaction where import Control.Error (note) import Crypto.Hash (Digest, hashWith) import Crypto.Hash.Algorithms (SHA256 (..)) +import Data.Bifunctor (first) import Data.ByteString (ByteString) import Data.Proxy import qualified Data.Serialize as Serialize @@ -38,12 +39,14 @@ data RawTransaction = RawTransaction , rawTransactionSignature :: ByteString } deriving Generic +-- basically also a message type instance Serialize.Serialize RawTransaction parseRawTransaction :: ByteString -> Either Text RawTransaction -parseRawTransaction = error "TODO: implement parseRawTransaction" +parseRawTransaction = first cs . Serialize.decode +-- error "TODO: implement parseRawTransaction" instance MakeDigest RawTransaction where makeDigest tx = hashWith SHA256 . Serialize.encode $ tx {rawTransactionSignature = ""} @@ -53,7 +56,7 @@ signRawTransaction RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => Proxy alg - -> PrivateKey alg + -> PrivateKey alg -- -> RawTransaction -> RecoverableSignature alg signRawTransaction p priv tx = signRecoverableMessage p priv (makeDigest tx) diff --git a/hs-tendermint-client/src/Network/Tendermint/Client.hs b/hs-tendermint-client/src/Network/Tendermint/Client.hs index 59f03ba2..255d363d 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client.hs @@ -31,7 +31,7 @@ import qualified Network.Tendermint.Client.Internal.RPCClient as RPC -type TendermintM a = ReaderT RPC.Config IO a +type TendermintM = ReaderT RPC.Config IO -- | Execute an RPC request with the given configuration. runTendermintM :: RPC.Config -> TendermintM a -> IO a From abbf3931275ac4fa6f4aa124e7e27f1e72613aab Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Fri, 22 Nov 2019 13:46:09 -0800 Subject: [PATCH 10/70] Added SDK readme (#126) --- hs-abci-sdk/README.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 hs-abci-sdk/README.md diff --git a/hs-abci-sdk/README.md b/hs-abci-sdk/README.md new file mode 100644 index 00000000..c635480a --- /dev/null +++ b/hs-abci-sdk/README.md @@ -0,0 +1,36 @@ +# hs-abci-sdk + +## Introduction +This package lays out an SDK for rapidly developing blockchain applications in haskell backed by the Tendermint replication engine. It relies on the [hs-abci-server](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-server) to communicate to Tendermint core via the ABCI protocol. + +## Requirements + +### libsecp256k +You will need to have the `libsecp256k` `C` library installed on your machine to build this package, or anything depedning on it, as it is not statically linked to its haskell wrapper. You +can find instructions for this [here](https://github.com/f-o-a-m/hs-abci#libsecp256k1). + +## Archetecture + +The SDK makes heavy use of an effects system to separate different components of your application. Specifically it is using the [polysemy](https://hackage.haskell.org/package/polysemy) effects library in its implementation. + +### BaseApp Effects + +`BaseApp` is the set of effects that the SDK is written in. Every other module developed during the course of application development must eventually be compiled to this set of effects. As of now, `BaseApp` effects allows for things like access to storage, error handling, event logging, console logging, etc. + +### Application Specific Effects + +It is assumed that you will want to define your own application specific effects, for example +in the way that the [Nameservice example app](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/nameservice) does. Application specific effects are useful for defining module level storage capabilities, custom errors and handling, and explicit dependencies on other modules' effects. There are many hooks in this SDK to facilitate compiling application effects +to `BaseApp`. For examples, see `Tendermint.SDK.Errors` or `Tendermint.SDK.Store`. + +### Core Effects + +The `CoreEffects` system is what's used to interpret `BaseApp` to `IO`, which is where the application must end up at eventually. It provides things like a loging context (e.g. `Katip`), +a context for executing database transactions, and various buffers and vars to facilitate ABCI message handling. + +## Example Applications +There are currenlty two official example applications + +1. [Simple Storage](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/simple-storage): This is a trivial application developed around a single module that allows get and set operations on an integer value. + +2. [Nameservice](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/nameservice): This is an implementation of the official example application for the [cosmos-sdk](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). It is built to support a simple name resolution market place. From 719745af7679f76fffd4329acbb11a3e60f9e67e Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 25 Nov 2019 08:09:50 -0800 Subject: [PATCH 11/70] added raw transaction proto (#119) * added raw transaction proto * Stylish * stylish, ugh --- .../nameservice/src/Nameservice/Handlers.hs | 4 +- .../test/Nameservice/Test/E2ESpec.hs | 4 +- hs-abci-sdk/Setup.hs | 4 +- hs-abci-sdk/package.yaml | 14 +++++ hs-abci-sdk/protos/types/transaction.proto | 8 +++ .../src/Tendermint/SDK/Types/Transaction.hs | 60 ++++++++++++------- 6 files changed, 66 insertions(+), 28 deletions(-) create mode 100644 hs-abci-sdk/protos/types/transaction.proto diff --git a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs index 4818ad3d..cef99448 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs @@ -14,12 +14,12 @@ import Polysemy (Sem) import Polysemy.Error (catch) import Tendermint.SDK.Application (defaultHandler) import Tendermint.SDK.BaseApp (BaseApp) +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Errors (AppError, SDKError (..), deliverTxAppError, throwSDKError) import Tendermint.SDK.Events (withEventBuffer) import Tendermint.SDK.Query (QueryApplication) -import Tendermint.SDK.Types.Transaction (parseRawTransaction) echoH :: Request 'MTEcho @@ -79,7 +79,7 @@ deliverTxH deliverTxH (RequestDeliverTx deliverTx) = let tryToRespond = do tx <- either (throwSDKError . ParseError) return $ - parseRawTransaction $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes + decode $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes events <- withEventBuffer . compileToBaseApp $ router tx return $ ResponseDeliverTx $ def & Resp._deliverTxCode .~ 0 diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 7cd7f71e..0c002f95 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -257,7 +257,7 @@ decodeValue :: HasCodec a => Base64.Base64String -> a decodeValue = (\(Right a) -> a) . decode . Base64.toBytes encodeRawTx :: RawTransaction -> Base64.Base64String -encodeRawTx = Base64.fromBytes . Serialize.encode +encodeRawTx = Base64.fromBytes . encode encodeMsgData :: Message a => a -> BS.ByteString encodeMsgData = BL.toStrict . toLazyByteString @@ -266,7 +266,7 @@ encodeMsgData = BL.toStrict . toLazyByteString mkSignedRawTransactionWithRoute :: Message a => BS.ByteString -> SecKey -> a -> RawTransaction mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned where unsigned = RawTransaction { rawTransactionData = encodeMsgData msg - , rawTransactionRoute = route + , rawTransactionRoute = cs route , rawTransactionSignature = "" } sig = signRawTransaction algProxy privateKey unsigned diff --git a/hs-abci-sdk/Setup.hs b/hs-abci-sdk/Setup.hs index 44671092..c81784fa 100644 --- a/hs-abci-sdk/Setup.hs +++ b/hs-abci-sdk/Setup.hs @@ -1,2 +1,2 @@ -import Distribution.Simple -main = defaultMain +import Data.ProtoLens.Setup +main = defaultMainGeneratingProtos "protos" diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 87f1f59e..a9c6de41 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -8,6 +8,15 @@ copyright: "2019 Martin Allen" description: Please see the README on GitHub at +extra-source-files: +- protos/**/*.proto + +custom-setup: + dependencies: + - base + - Cabal + - proto-lens-setup + default-extensions: - DeriveGeneric - NamedFieldPuns @@ -63,6 +72,7 @@ dependencies: - polysemy-plugin - profunctors - proto-lens +- proto-lens-runtime - proto3-wire - proto3-suite - secp256k1-haskell @@ -111,6 +121,10 @@ library: - Tendermint.SDK.TxRouter - Tendermint.SDK.Module + generated-exposed-modules: + - Proto.Types.Transaction + - Proto.Types.Transaction_Fields + tests: hs-abci-sdk-test: main: Spec.hs diff --git a/hs-abci-sdk/protos/types/transaction.proto b/hs-abci-sdk/protos/types/transaction.proto new file mode 100644 index 00000000..e810079a --- /dev/null +++ b/hs-abci-sdk/protos/types/transaction.proto @@ -0,0 +1,8 @@ +syntax = "proto3"; +package Transaction; + +message RawTransaction { + bytes data = 1; + bytes signature = 2; + string route = 3; +} \ No newline at end of file diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index c00a86d4..fe357210 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -1,19 +1,24 @@ module Tendermint.SDK.Types.Transaction where -import Control.Error (note) -import Crypto.Hash (Digest, hashWith) -import Crypto.Hash.Algorithms (SHA256 (..)) -import Data.Bifunctor (first) -import Data.ByteString (ByteString) +import Control.Error (note) +import Control.Lens (Wrapped (..), from, iso, view, + (&), (.~), (^.)) +import Crypto.Hash (Digest, hashWith) +import Crypto.Hash.Algorithms (SHA256 (..)) +import Data.Bifunctor (bimap) +import Data.ByteString (ByteString) +import qualified Data.ProtoLens as P import Data.Proxy -import qualified Data.Serialize as Serialize -import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.Generics (Generic) -import Tendermint.SDK.Crypto (MakeDigest (..), - RecoverableSignatureSchema (..), - SignatureSchema (..)) -import Tendermint.SDK.Types.Message (Msg (..)) +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) +import qualified Proto.Types.Transaction as T +import qualified Proto.Types.Transaction_Fields as T +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (MakeDigest (..), + RecoverableSignatureSchema (..), + SignatureSchema (..)) +import Tendermint.SDK.Types.Message (Msg (..)) -- Our standard transaction type parameterized by the signature schema 'alg' -- and an underlying message type 'msg'. @@ -34,22 +39,33 @@ data Tx alg msg = Tx data RawTransaction = RawTransaction { rawTransactionData :: ByteString -- ^ the encoded message via protobuf encoding - , rawTransactionRoute :: ByteString + , rawTransactionRoute :: Text -- ^ module name , rawTransactionSignature :: ByteString } deriving Generic --- basically also a message type -instance Serialize.Serialize RawTransaction +instance Wrapped RawTransaction where + type Unwrapped RawTransaction = T.RawTransaction -parseRawTransaction - :: ByteString - -> Either Text RawTransaction -parseRawTransaction = first cs . Serialize.decode --- error "TODO: implement parseRawTransaction" + _Wrapped' = iso t f + where + t RawTransaction {..} = + P.defMessage + & T.data' .~ rawTransactionData + & T.route .~ cs rawTransactionRoute + & T.signature .~ rawTransactionSignature + f message = RawTransaction + { rawTransactionData = message ^. T.data' + , rawTransactionRoute = message ^. T.route + , rawTransactionSignature = message ^. T.signature + } + +instance HasCodec RawTransaction where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage instance MakeDigest RawTransaction where - makeDigest tx = hashWith SHA256 . Serialize.encode $ tx {rawTransactionSignature = ""} + makeDigest tx = hashWith SHA256 . encode $ tx {rawTransactionSignature = ""} signRawTransaction :: forall alg. From 3df5c6df943e0f5b887da212161efcbc3ae7828a Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 25 Nov 2019 08:10:06 -0800 Subject: [PATCH 12/70] added gas metering and tests (#125) * added gas metering and tests * add control flow tets * stylish --- hs-abci-sdk/package.yaml | 2 + hs-abci-sdk/src/Tendermint/SDK/Errors.hs | 7 +++ hs-abci-sdk/src/Tendermint/SDK/Gas.hs | 34 +++++++++++ .../test/Tendermint/SDK/Test/GasSpec.hs | 61 +++++++++++++++++++ 4 files changed, 104 insertions(+) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Gas.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index a9c6de41..75a28e0c 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -120,6 +120,7 @@ library: - Tendermint.SDK.Types.Address - Tendermint.SDK.TxRouter - Tendermint.SDK.Module + - Tendermint.SDK.Gas generated-exposed-modules: - Proto.Types.Transaction @@ -133,6 +134,7 @@ tests: - Tendermint.SDK.Test.AuthTreeStoreSpec - Tendermint.SDK.Test.CryptoSpec - Tendermint.SDK.Test.EventSpec + - Tendermint.SDK.Test.GasSpec ghc-options: - -Werror diff --git a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs index 76da2f8b..84ece40c 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs @@ -92,6 +92,7 @@ data SDKError = -- ^ Parsing errors for SDK specific types, e.g. 'RawTransaction' or 'Msg', etc. | UnmatchedRoute Text -- ^ The name of the route that failed to match. + | OutOfGasException -- | As of right now it's not expected that one can recover from an 'SDKError', -- | so we are throwing them as 'AppError's directly. @@ -119,3 +120,9 @@ instance IsAppError SDKError where , appErrorCodespace = "sdk" , appErrorMessage = "Route not recognized: " <> route <> "." } + + makeAppError OutOfGasException = AppError + { appErrorCode = 4 + , appErrorCodespace = "sdk" + , appErrorMessage = "Out of gas exception" + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Gas.hs b/hs-abci-sdk/src/Tendermint/SDK/Gas.hs new file mode 100644 index 00000000..ac267af2 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Gas.hs @@ -0,0 +1,34 @@ +{-# LANGUAGE TemplateHaskell #-} +module Tendermint.SDK.Gas where + +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Int (Int64) +import qualified Data.IORef as Ref +import Polysemy (Embed, Members, Sem, interpretH, + makeSem, raise, runT) +import Polysemy.Error (Error) +import Tendermint.SDK.Errors (AppError, SDKError (OutOfGasException), + throwSDKError) + +data GasMeter m a where + WithGas :: forall m a. Int64 -> m a -> GasMeter m a + +makeSem ''GasMeter + +eval + :: Members [Error AppError, Embed IO] r + => Ref.IORef Int64 + -> Sem (GasMeter ': r) a + -> Sem r a +eval meter = interpretH (\case + WithGas gasCost action -> do + remainingGas <- liftIO $ Ref.readIORef meter + let balanceAfterAction = remainingGas - gasCost + if balanceAfterAction < 0 + then throwSDKError OutOfGasException + else do + liftIO $ Ref.writeIORef meter balanceAfterAction + a <- runT action + raise $ eval meter a + ) + diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs new file mode 100644 index 00000000..dd3c8095 --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs @@ -0,0 +1,61 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.Test.GasSpec where + +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Either (isRight) +import Data.Int (Int64) +import qualified Data.IORef as Ref +import Polysemy +import Polysemy.Error (Error, runError) +import Tendermint.SDK.Errors (AppError (..)) +import qualified Tendermint.SDK.Gas as G +import Test.Hspec + +data Dog m a where + Bark :: Dog m () + +makeSem ''Dog + +evalDog :: Sem (Dog ': r) a -> Sem r a +evalDog = interpret $ \case + Bark -> pure () + +eval + :: Ref.IORef Int64 + -> Sem [Dog, G.GasMeter, Error AppError, Embed IO] a + -> IO (Either AppError a) +eval meter = runM . runError . G.eval meter . evalDog + +spec :: Spec +spec = describe "Gas Tests" $ do + it "Can perform a computation without running out of gas" $ do + meter <- Ref.newIORef 1 + eRes <- eval meter $ G.withGas 1 bark + eRes `shouldSatisfy` isRight + remainingGas <- Ref.readIORef meter + remainingGas `shouldBe` 0 + + it "Can perform a computation with surplus gas" $ do + meter <- Ref.newIORef 2 + eRes <- eval meter $ G.withGas 1 bark + eRes `shouldSatisfy` isRight + remainingGas <- Ref.readIORef meter + remainingGas `shouldBe` 1 + + it "Can perform a computation and run out of gas" $ do + meter <- Ref.newIORef 0 + var <- Ref.newIORef (1 :: Int) + eRes <- eval meter $ do + G.withGas 1 bark + -- this shouldn't execute + liftIO $ Ref.modifyIORef var (+ 1) + let AppError{..} = case eRes of + Left e -> e + Right _ -> error "Was supposed to run out of gas" + appErrorCode `shouldBe` 4 + remainingGas <- Ref.readIORef meter + remainingGas `shouldBe` 0 + varVal <- Ref.readIORef var + varVal `shouldBe` 1 + From 27e389fae40ef8be9dbfca7edc203ec96bf58657 Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Mon, 25 Nov 2019 14:58:17 -0500 Subject: [PATCH 13/70] Token modules (#118) * WIP: token keeper, query + types * finish keeper * finish token messages + router * import cleanup * Add token module to application * move faucet to token module * move faucet completely * annotate response code checking tests for clarity + fix imports * fix order for token messeages * add test to check balances * Add transfer test * stylish * clean up --- hs-abci-examples/nameservice/package.yaml | 5 + .../src/Nameservice/Application.hs | 4 +- .../src/Nameservice/Modules/Nameservice.hs | 3 - .../Nameservice/Modules/Nameservice/Keeper.hs | 11 - .../Modules/Nameservice/Messages.hs | 28 +-- .../Nameservice/Modules/Nameservice/Router.hs | 8 +- .../Nameservice/Modules/Nameservice/Types.hs | 16 -- .../src/Nameservice/Modules/Token.hs | 232 +++--------------- .../src/Nameservice/Modules/Token/Keeper.hs | 113 +++++++++ .../src/Nameservice/Modules/Token/Messages.hs | 82 +++++++ .../src/Nameservice/Modules/Token/Query.hs | 23 ++ .../src/Nameservice/Modules/Token/Router.hs | 27 ++ .../src/Nameservice/Modules/Token/Types.hs | 121 +++++++++ .../test/Nameservice/Test/E2ESpec.hs | 133 +++++----- hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs | 8 + 15 files changed, 491 insertions(+), 323 deletions(-) create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 5208b2a9..26857090 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -88,6 +88,11 @@ library: - Nameservice.Handlers - Nameservice.Modules.Nameservice - Nameservice.Modules.Token + - Nameservice.Modules.Token.Messages + - Nameservice.Modules.Token.Types + - Nameservice.Modules.Token.Keeper + - Nameservice.Modules.Token.Query + - Nameservice.Modules.Token.Router - Nameservice.Modules.Nameservice.Messages - Nameservice.Modules.Nameservice.Types - Nameservice.Modules.Nameservice.Keeper diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 8c7b6050..4bb2d6d3 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -56,8 +56,8 @@ runHandler AppConfig{baseAppContext} = -------------------------------------------------------------------------------- -modules :: Modules '[N.NameserviceM EffR] EffR -modules = ConsModule N.nameserviceModule NilModules +modules :: Modules '[T.TokenM EffR ,N.NameserviceM EffR] EffR +modules = ConsModule T.tokenModule $ ConsModule N.nameserviceModule NilModules router :: RawTransaction -> Sem EffR () router = R.router (Proxy @Secp256k1) modules diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index ed30d20c..57017a69 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -13,11 +13,9 @@ module Nameservice.Modules.Nameservice , NameClaimed(..) , NameRemapped(..) , NameDeleted(..) - , Faucetted(..) , BuyName(..) , SetName(..) , DeleteName(..) - , FaucetAccount(..) -- * effects , NameserviceEffR @@ -26,7 +24,6 @@ module Nameservice.Modules.Nameservice , buyName , setName , deleteName - , faucetAccount -- * interpreter , eval diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index a85306b8..58083358 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -54,17 +54,6 @@ eval = mapError makeAppError . evalNameservice -------------------------------------------------------------------------------- -faucetAccount - :: HasTokenEff r - => FaucetAccount - -> Sem r () -faucetAccount FaucetAccount{..} = do - mint faucetAccountTo faucetAccountAmount - emit Faucetted - { faucettedAccount = faucetAccountTo - , faucettedAmount = faucetAccountAmount - } - setName :: HasTokenEff r => HasNameserviceEff r diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index 41f0f9f8..a67981e4 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -23,21 +23,8 @@ data NameserviceMessage = NSetName SetName | NBuyName BuyName | NDeleteName DeleteName - | NFaucetAccount FaucetAccount deriving (Eq, Show, Generic) -data FaucetAccount = FaucetAccount - { faucetAccountTo :: Address - , faucetAccountAmount :: Amount - } deriving (Eq, Show, Generic) - -instance Message FaucetAccount -instance Named FaucetAccount - -instance HasCodec FaucetAccount where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString - -- @NOTE: .proto genration will use these type names as is -- only field names stripped of prefixes during generation data SetName = SetName @@ -84,24 +71,17 @@ instance HasCodec NameserviceMessage where -- @NOTE: tests pass iff NBuyName is first fmap NBuyName (decode bs) <> fmap NSetName (decode bs) <> - fmap NDeleteName (decode bs) <> - fmap NFaucetAccount (decode bs) + fmap NDeleteName (decode bs) encode = \case NSetName msg -> encode msg NBuyName msg -> encode msg NDeleteName msg -> encode msg - NFaucetAccount msg -> encode msg instance ValidateMessage NameserviceMessage where validateMessage m@Msg{msgData} = case msgData of - NBuyName msg -> validateMessage m {msgData = msg} - NSetName msg -> validateMessage m {msgData = msg} - NDeleteName msg -> validateMessage m {msgData = msg} - NFaucetAccount msg -> validateMessage m {msgData = msg} - --- @TODO: Move this to token module -instance ValidateMessage FaucetAccount where - validateMessage _ = mempty + NBuyName msg -> validateMessage m {msgData = msg} + NSetName msg -> validateMessage m {msgData = msg} + NDeleteName msg -> validateMessage m {msgData = msg} -- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg instance ValidateMessage SetName where diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 4390b150..1de7c6fb 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -2,7 +2,6 @@ module Nameservice.Modules.Nameservice.Router where import Nameservice.Modules.Nameservice.Keeper (HasNameserviceEff, buyName, deleteName, - faucetAccount, setName) import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) import Nameservice.Modules.Token (HasTokenEff) @@ -19,7 +18,6 @@ router router (RoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of - NSetName msg -> setName msg - NBuyName msg -> buyName msg - NDeleteName msg -> deleteName msg - NFaucetAccount msg -> faucetAccount msg + NSetName msg -> setName msg + NBuyName msg -> buyName msg + NDeleteName msg -> deleteName msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index d778cb4c..bcd95af1 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -93,22 +93,6 @@ instance IsAppError NameserviceException where -- Events -------------------------------------------------------------------------------- -data Faucetted = Faucetted - { faucettedAccount :: Address - , faucettedAmount :: Amount - } deriving (Eq, Show, Generic) - -faucettedAesonOptions :: A.Options -faucettedAesonOptions = defaultNameserviceOptions "faucetted" - -instance ToJSON Faucetted where - toJSON = A.genericToJSON faucettedAesonOptions -instance FromJSON Faucetted where - parseJSON = A.genericParseJSON faucettedAesonOptions -instance ToEvent Faucetted where - makeEventType _ = "Faucetted" -instance FromEvent Faucetted - data NameClaimed = NameClaimed { nameClaimedOwner :: Address , nameClaimedName :: Name diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 42d8bb35..7152eff8 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -1,10 +1,12 @@ -{-# LANGUAGE TemplateHaskell #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - module Nameservice.Modules.Token ( + + -- * Module + TokenM + , tokenModule -- * types - Amount(..) + , Address + , Amount(..) , TokenException(..) , Transfer(..) @@ -12,7 +14,11 @@ module Nameservice.Modules.Token , Token , TokenEffR , HasTokenEff + , Faucetted(..) + , TransferEvent(..) + , FaucetAccount(..) , getBalance + , faucetAccount , transfer , mint , burn @@ -20,211 +26,31 @@ module Nameservice.Modules.Token -- * interpreter , eval + -- * router + , router + -- * Query Api , Api , server ) where -import Data.Aeson as A -import Data.Bifunctor (bimap) -import qualified Data.ByteArray.HexString as Hex -import Data.Maybe (fromMaybe) -import Data.Proxy -import Data.String.Conversions (cs) -import Data.Text (Text) -import Data.Word (Word64) -import GHC.Generics (Generic) -import Nameservice.Aeson (defaultNameserviceOptions) -import Polysemy -import Polysemy.Error (Error, mapError, throw) -import Polysemy.Output (Output) -import Proto3.Suite (HasDefault (..), MessageField, - Primitive (..)) -import qualified Proto3.Suite.DotProto as DotProto -import qualified Proto3.Wire.Decode as Decode -import qualified Proto3.Wire.Encode as Encode -import Proto3.Wire.Types (fieldNumber) -import Servant.API ((:>)) -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Events (Event, FromEvent, ToEvent (..), - emit) -import Tendermint.SDK.Query (Queryable (..), RouteT) -import Tendermint.SDK.Query.Store (QueryApi, storeQueryHandlers) -import qualified Tendermint.SDK.Store as Store -import Tendermint.SDK.Types.Address (Address, addressFromBytes, - addressToBytes) - -newtype Amount = Amount Word64 deriving (Eq, Show, Num, Generic, Ord, A.ToJSON, A.FromJSON) -instance Primitive Amount where - encodePrimitive n (Amount amt) = Encode.uint64 n amt - decodePrimitive = Amount <$> Decode.uint64 - primType _ = DotProto.UInt64 -instance HasDefault Amount -instance MessageField Amount - -instance Queryable Amount where - type Name Amount = "balance" - --- @NOTE: hacks -instance HasCodec Amount where - encode (Amount b) = - -- proto3-wire only exports encoders for message types - let dummyMsgEncoder = Encode.uint64 (fieldNumber 1) - in cs . Encode.toLazyByteString . dummyMsgEncoder $ b - decode = bimap (cs . show) Amount . Decode.parse dummyMsgParser - where - -- field is always present; 0 is an arbitrary value - fieldParser = Decode.one Decode.uint64 0 - dummyMsgParser = Decode.at fieldParser (fieldNumber 1) - --- orphans -instance Primitive Address where - encodePrimitive n a = Encode.byteString n $ addressToBytes a - decodePrimitive = addressFromBytes <$> Decode.byteString - primType _ = DotProto.Bytes -instance HasDefault Hex.HexString -instance HasDefault Address -instance MessageField Address - -instance Store.IsKey Address "token" where - type Value Address "token" = Amount - --------------------------------------------------------------------------------- --- Events --------------------------------------------------------------------------------- - -data Transfer = Transfer - { transferAmount :: Amount - , transferTo :: Address - , transferFrom :: Address - } deriving (Eq, Show, Generic) - -transferAesonOptions :: A.Options -transferAesonOptions = defaultNameserviceOptions "transfer" - -instance A.ToJSON Transfer where - toJSON = A.genericToJSON transferAesonOptions - -instance A.FromJSON Transfer where - parseJSON = A.genericParseJSON transferAesonOptions - -instance ToEvent Transfer where - makeEventType _ = "Transfer" - -instance FromEvent Transfer - --------------------------------------------------------------------------------- --- Exceptions --------------------------------------------------------------------------------- +import Nameservice.Modules.Token.Keeper +import Nameservice.Modules.Token.Messages +import Nameservice.Modules.Token.Query +import Nameservice.Modules.Token.Router +import Nameservice.Modules.Token.Types +import Tendermint.SDK.BaseApp (HasBaseAppEff) +import Tendermint.SDK.Module (Module (..)) +import Tendermint.SDK.Types.Address (Address) -data TokenException = - InsufficientFunds Text +type TokenM r = Module "token" TokenMessage Api r -instance IsAppError TokenException where - makeAppError (InsufficientFunds msg) = - AppError - { appErrorCode = 1 - , appErrorCodespace = "token" - , appErrorMessage = msg - } - --------------------------------------------------------------------------------- - -data Token m a where - PutBalance :: Address -> Amount -> Token m () - GetBalance' :: Address -> Token m (Maybe Amount) - -makeSem ''Token - -type TokenEffR = '[Token, Error TokenException] -type HasTokenEff r = (Members TokenEffR r, Member (Output Event) r) - -storeKey :: Store.StoreKey "token" -storeKey = Store.StoreKey "token" - -eval +tokenModule :: HasBaseAppEff r - => Sem (Token ': Error TokenException ': r) a - -> Sem r a -eval = mapError makeAppError . evalToken - where - evalToken - :: HasBaseAppEff r - => Sem (Token ': r) a - -> Sem r a - evalToken = - interpret (\case - GetBalance' address -> - Store.get storeKey address - PutBalance address balance -> - Store.put storeKey address balance - ) --------------------------------------------------------------------------------- --- | Server --------------------------------------------------------------------------------- - -type TokenContents = '[(Address, Amount)] - -type Api = "token" :> QueryApi TokenContents - -server :: Member Store.RawStore r => RouteT Api (Sem r) -server = - storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) - --------------------------------------------------------------------------------- - -getBalance - :: Member Token r - => Address - -> Sem r Amount -getBalance address = - fromMaybe (Amount 0) <$> getBalance' address - -transfer - :: HasTokenEff r - => Address - -> Amount - -> Address - -> Sem r () -transfer addr1 amount addr2 = do - -- check if addr1 has amt - addr1Bal <- getBalance addr1 - if addr1Bal > amount - then do - addr2Bal <- getBalance addr2 - let newBalance1 = addr1Bal - amount - newBalance2 = addr2Bal + amount - -- update both balances - putBalance addr1 newBalance1 - putBalance addr2 newBalance2 - emit $ Transfer - { transferAmount = amount - , transferTo = addr2 - , transferFrom = addr1 - } - else throw (InsufficientFunds "Insufficient funds for transfer.") - -burn - :: Members '[Token, Error TokenException] r - => Address - -> Amount - -> Sem r () -burn addr amount = do - bal <- getBalance addr - if bal < amount - then throw $ InsufficientFunds "Insufficient funds for burn." - else putBalance addr (bal - amount) - -mint - :: Members '[Token, Error TokenException] r - => Address - -> Amount - -> Sem r () -mint addr amount = do - bal <- getBalance addr - putBalance addr (bal + amount) - --------------------------------------------------------------------------------- + => HasTokenEff r + => TokenM r +tokenModule = Module + { moduleRouter = router + , moduleQueryServer = server + } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs new file mode 100644 index 00000000..085ed259 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs @@ -0,0 +1,113 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Nameservice.Modules.Token.Keeper where + +import Data.Maybe (fromMaybe) +import Nameservice.Modules.Token.Messages (FaucetAccount (..)) +import Nameservice.Modules.Token.Types (Amount (..), + Faucetted (..), + TokenException (..), + TransferEvent (..)) +import Polysemy +import Polysemy.Error (Error, mapError, throw) +import Polysemy.Output (Output) +import Tendermint.SDK.BaseApp (HasBaseAppEff) +import Tendermint.SDK.Errors (IsAppError (..)) +import Tendermint.SDK.Events (Event, emit) +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.Types.Address (Address) + + +data Token m a where + PutBalance :: Address -> Amount -> Token m () + GetBalance' :: Address -> Token m (Maybe Amount) + +makeSem ''Token + +type TokenEffR = '[Token, Error TokenException] +type HasTokenEff r = (Members TokenEffR r, Member (Output Event) r) + +storeKey :: Store.StoreKey "token" +storeKey = Store.StoreKey "token" + +eval + :: HasBaseAppEff r + => Sem (Token ': Error TokenException ': r) a + -> Sem r a +eval = mapError makeAppError . evalToken + where + evalToken + :: HasBaseAppEff r + => Sem (Token ': r) a + -> Sem r a + evalToken = + interpret (\case + GetBalance' address -> + Store.get storeKey address + PutBalance address balance -> + Store.put storeKey address balance + ) + +-------------------------------------------------------------------------------- + +faucetAccount + :: HasTokenEff r + => FaucetAccount + -> Sem r () +faucetAccount FaucetAccount{..} = do + mint faucetAccountTo faucetAccountAmount + emit Faucetted + { faucettedAccount = faucetAccountTo + , faucettedAmount = faucetAccountAmount + } + +getBalance + :: Member Token r + => Address + -> Sem r Amount +getBalance address = + fromMaybe (Amount 0) <$> getBalance' address + +transfer + :: HasTokenEff r + => Address + -> Amount + -> Address + -> Sem r () +transfer addr1 amount addr2 = do + -- check if addr1 has amt + addr1Bal <- getBalance addr1 + if addr1Bal > amount + then do + addr2Bal <- getBalance addr2 + let newBalance1 = addr1Bal - amount + newBalance2 = addr2Bal + amount + -- update both balances + putBalance addr1 newBalance1 + putBalance addr2 newBalance2 + emit $ TransferEvent + { transferEventAmount = amount + , transferEventTo = addr2 + , transferEventFrom = addr1 + } + else throw (InsufficientFunds "Insufficient funds for transfer.") + +burn + :: Members '[Token, Error TokenException] r + => Address + -> Amount + -> Sem r () +burn addr amount = do + bal <- getBalance addr + if bal < amount + then throw $ InsufficientFunds "Insuffient funds for burn." + else putBalance addr (bal - amount) + +mint + :: Members '[Token, Error TokenException] r + => Address + -> Amount + -> Sem r () +mint addr amount = do + bal <- getBalance addr + putBalance addr (bal + amount) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs new file mode 100644 index 00000000..253e18f3 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs @@ -0,0 +1,82 @@ +module Nameservice.Modules.Token.Messages where + +import Data.Bifunctor (first) +import Data.String.Conversions (cs) +import GHC.Generics (Generic) +import Nameservice.Modules.Token.Types (Amount) +import Proto3.Suite (Message, Named, + fromByteString, + toLazyByteString) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (coerceProto3Error, + formatMessageParseError) + +data TokenMessage = + TTransfer Transfer + | TFaucetAccount FaucetAccount + | TBurn Burn + | TMint Mint + deriving (Eq, Show, Generic) + +data FaucetAccount = FaucetAccount + { faucetAccountTo :: Address + , faucetAccountAmount :: Amount + } deriving (Eq, Show, Generic) + +instance Message FaucetAccount +instance Named FaucetAccount + +instance HasCodec FaucetAccount where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +data Transfer = Transfer + { transferTo :: Address + , transferFrom :: Address + , transferAmount :: Amount + } deriving (Eq, Show, Generic) + +instance Message Transfer +instance Named Transfer + +instance HasCodec Transfer where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +data Burn = Burn + { burnAddress :: Address + , burnAmount :: Amount + } deriving (Eq, Show, Generic) + +instance Message Burn +instance Named Burn + +instance HasCodec Burn where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +data Mint = Mint + { mintAmount :: Amount + , mintAddress :: Address + } deriving (Eq, Show, Generic) + +instance Message Mint +instance Named Mint + +instance HasCodec Mint where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +instance HasCodec TokenMessage where + decode bs = + fmap TTransfer (decode bs) <> + -- @NOTE: TFaucetAccount and TBurn have to be in this order + fmap TFaucetAccount (decode bs) <> + fmap TBurn (decode bs) <> + fmap TMint (decode bs) + encode = \case + TTransfer msg -> encode msg + TBurn msg -> encode msg + TMint msg -> encode msg + TFaucetAccount msg -> encode msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs new file mode 100644 index 00000000..a2d8aee2 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs @@ -0,0 +1,23 @@ +module Nameservice.Modules.Token.Query where + +import Data.Proxy +import Nameservice.Modules.Token.Keeper (storeKey) +import Nameservice.Modules.Token.Types (Amount) +import Polysemy +import Servant.API ((:>)) +import Tendermint.SDK.Query (RouteT) +import Tendermint.SDK.Query.Store (QueryApi, storeQueryHandlers) +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.Types.Address (Address) + +-------------------------------------------------------------------------------- +-- | Query Api +-------------------------------------------------------------------------------- + +type TokenContents = '[(Address, Amount)] + +type Api = "token" :> QueryApi TokenContents + +server :: Member Store.RawStore r => RouteT Api (Sem r) +server = + storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs new file mode 100644 index 00000000..eced4eeb --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs @@ -0,0 +1,27 @@ +module Nameservice.Modules.Token.Router where + +import Nameservice.Modules.Token.Keeper (HasTokenEff, burn, + faucetAccount, mint, + transfer) +import Nameservice.Modules.Token.Messages (Burn (..), Mint (..), + TokenMessage (..), + Transfer (..)) +import Polysemy (Sem) +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) + +router + :: HasTokenEff r + => RoutedTx TokenMessage + -> Sem r () +router (RoutedTx Tx{txMsg}) = + let Msg{msgData} = txMsg + in case msgData of + TFaucetAccount faucet -> + faucetAccount faucet + TTransfer Transfer{..} -> + transfer transferFrom transferAmount transferTo + TBurn Burn{..} -> + burn burnAddress burnAmount + TMint Mint{..} -> + mint mintAddress mintAmount diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs new file mode 100644 index 00000000..1f6006cc --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs @@ -0,0 +1,121 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Nameservice.Modules.Token.Types where + +import Data.Aeson as A +import Data.Bifunctor (bimap) +import qualified Data.ByteArray.HexString as Hex +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word64) +import GHC.Generics (Generic) +import Nameservice.Aeson (defaultNameserviceOptions) +import Proto3.Suite (HasDefault (..), MessageField, + Primitive (..)) +import qualified Proto3.Suite.DotProto as DotProto +import qualified Proto3.Wire.Decode as Decode +import qualified Proto3.Wire.Encode as Encode +import Proto3.Wire.Types (fieldNumber) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) +import Tendermint.SDK.Events (FromEvent, ToEvent (..)) +import Tendermint.SDK.Query (Queryable (..)) +import qualified Tendermint.SDK.Store as Store +import Tendermint.SDK.Types.Address (Address, addressFromBytes, + addressToBytes) + +-------------------------------------------------------------------------------- + +type TokenModule = "token" + +-------------------------------------------------------------------------------- + +newtype Amount = Amount Word64 deriving (Eq, Show, Num, Generic, Ord, A.ToJSON, A.FromJSON) +instance Primitive Amount where + encodePrimitive n (Amount amt) = Encode.uint64 n amt + decodePrimitive = Amount <$> Decode.uint64 + primType _ = DotProto.UInt64 +instance HasDefault Amount +instance MessageField Amount + +instance Queryable Amount where + type Name Amount = "balance" + +-- @NOTE: hacks +instance HasCodec Amount where + encode (Amount b) = + -- proto3-wire only exports encoders for message types + let dummyMsgEncoder = Encode.uint64 (fieldNumber 1) + in cs . Encode.toLazyByteString . dummyMsgEncoder $ b + decode = bimap (cs . show) Amount . Decode.parse dummyMsgParser + where + -- field is always present; 0 is an arbitrary value + fieldParser = Decode.one Decode.uint64 0 + dummyMsgParser = Decode.at fieldParser (fieldNumber 1) + +-- orphans +instance Primitive Address where + encodePrimitive n a = Encode.byteString n $ addressToBytes a + decodePrimitive = addressFromBytes <$> Decode.byteString + primType _ = DotProto.Bytes +instance HasDefault Hex.HexString +instance HasDefault Address +instance MessageField Address + +instance Store.IsKey Address "token" where + type Value Address "token" = Amount + +-------------------------------------------------------------------------------- +-- Exceptions +-------------------------------------------------------------------------------- + +data TokenException = + InsufficientFunds Text + +instance IsAppError TokenException where + makeAppError (InsufficientFunds msg) = + AppError + { appErrorCode = 1 + , appErrorCodespace = "token" + , appErrorMessage = msg + } + +-------------------------------------------------------------------------------- +-- Events +-------------------------------------------------------------------------------- + +data Faucetted = Faucetted + { faucettedAccount :: Address + , faucettedAmount :: Amount + } deriving (Eq, Show, Generic) + +faucettedAesonOptions :: A.Options +faucettedAesonOptions = defaultNameserviceOptions "faucetted" + +instance ToJSON Faucetted where + toJSON = A.genericToJSON faucettedAesonOptions +instance FromJSON Faucetted where + parseJSON = A.genericParseJSON faucettedAesonOptions +instance ToEvent Faucetted where + makeEventType _ = "Faucetted" +instance FromEvent Faucetted + +data TransferEvent = TransferEvent + { transferEventAmount :: Amount + , transferEventTo :: Address + , transferEventFrom :: Address + } deriving (Eq, Show, Generic) + +transferEventAesonOptions :: A.Options +transferEventAesonOptions = defaultNameserviceOptions "transferEvent" + +instance A.ToJSON TransferEvent where + toJSON = A.genericToJSON transferEventAesonOptions + +instance A.FromJSON TransferEvent where + parseJSON = A.genericParseJSON transferEventAesonOptions + +instance ToEvent TransferEvent where + makeEventType _ = "TransferEvent" + +instance FromEvent TransferEvent diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 0c002f95..932d6912 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -22,7 +22,6 @@ import Data.Word (Word32) import Nameservice.Application (QueryApi) import Nameservice.Modules.Nameservice (BuyName (..), DeleteName (..), - FaucetAccount (..), Name (..), NameClaimed (..), NameDeleted (..), @@ -30,7 +29,10 @@ import Nameservice.Modules.Nameservice (BuyName (..), SetName (..), Whois (..)) import Nameservice.Modules.Token (Amount (..), - Transfer (..)) + FaucetAccount (..), + Faucetted (..), + Transfer (..), + TransferEvent (..)) import Network.ABCI.Types.Messages.FieldTypes (Event (..)) import qualified Network.ABCI.Types.Messages.Response as Response import qualified Network.Tendermint.Client as RPC @@ -43,7 +45,8 @@ import Tendermint.SDK.Crypto (Secp256k1, import Tendermint.SDK.Events (FromEvent (..)) import Tendermint.SDK.Query.Client (ClientResponse (..), genClient) -import Tendermint.SDK.Query.Types (QueryArgs (..)) +import Tendermint.SDK.Query.Types (QueryArgs (..), + defaultQueryWithData) import Tendermint.SDK.Types.Address (Address (..)) import Tendermint.SDK.Types.Transaction (RawTransaction (..), signRawTransaction) @@ -64,11 +67,11 @@ spec = do resp `shouldBe` RPC.ResultHealth it "Can query account balances" $ do - let queryReq = defaultReqWithData addr1 + let queryReq = defaultQueryWithData addr1 ClientResponse{clientResponseData = foundAmount} <- runRPC $ getBalance queryReq foundAmount `shouldBe` Amount 1000 - it "Can create a name" $ do + it "Can create a name (success 0)" $ do let val = "hello world" msg = BuyName 0 satoshi val addr1 claimedLog = NameClaimed addr1 satoshi val 0 @@ -80,31 +83,30 @@ spec = do events `shouldSatisfy` elem claimedLog it "Can query for a name" $ do - let queryReq = defaultReqWithData satoshi + let queryReq = defaultQueryWithData satoshi + expectedWhois = Whois "hello world" addr1 0 ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq - whoisValue foundWhois `shouldBe` "hello world" - whoisOwner foundWhois `shouldBe` addr1 - whoisPrice foundWhois `shouldBe` 0 + foundWhois `shouldBe` expectedWhois it "Can query for a name that doesn't exist" $ do let nope = Name "nope" - queryReq = defaultReqWithData nope - ClientResponse{ clientResponseData = emptyWhois + -- empty whois (defaults) + emptyWhois = Whois "" (Address "") 0 + queryReq = defaultQueryWithData nope + ClientResponse{ clientResponseData = foundWhois , clientResponseRaw } <- runRPC $ getWhois queryReq let queryRespCode = clientResponseRaw ^. Response._queryCode - -- storage failure + -- storage failure with defaults queryRespCode `shouldBe` 1 - -- empty whois (defaults) - whoisPrice emptyWhois `shouldBe` 0 - whoisOwner emptyWhois `shouldBe` Address "" - whoisValue emptyWhois `shouldBe` "" + foundWhois `shouldBe` emptyWhois - it "Can set a name value" $ do + it "Can set a name value (success 0)" $ do let oldVal = "hello world" newVal = "goodbye to a world" msg = SetName satoshi addr1 newVal remappedLog = NameRemapped satoshi oldVal newVal + expectedWhois = Whois "goodbye to a world" addr1 0 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 @@ -112,26 +114,23 @@ spec = do errs `shouldBe` mempty events `shouldSatisfy` elem remappedLog -- check for changes - let queryReq = defaultReqWithData satoshi + let queryReq = defaultQueryWithData satoshi ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq - whoisValue foundWhois `shouldBe` "goodbye to a world" - -- eveyrthing else should remain the same - whoisOwner foundWhois `shouldBe` addr1 - whoisPrice foundWhois `shouldBe` 0 + foundWhois `shouldBe` expectedWhois - it "Can fail to set a name" $ do + it "Can fail to set a name (failure 2)" $ do -- try to set a name without being the owner let msg = SetName satoshi addr2 "goodbye to a world" rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 2 - it "Can buy an existing name" $ do - -- how to do both types of logs? + it "Can buy an existing name (success 0)" $ do let oldVal = "goodbye to a world" newVal = "hello (again) world" msg = BuyName 300 satoshi newVal addr2 claimedLog = NameClaimed addr2 satoshi newVal 300 + expectedWhois = Whois "hello (again) world" addr2 300 -- transferLog = Transfer 300 addr1 addr2 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx @@ -139,19 +138,23 @@ spec = do (errs, events) <- deliverTxEvents deliverResp "NameClaimed" errs `shouldBe` mempty events `shouldSatisfy` elem claimedLog - -- events `shouldSatisfy` elem transferLog + -- check for updated balances - seller: addr1, buyer: addr2 + let sellerQueryReq = defaultQueryWithData addr1 + ClientResponse{clientResponseData = sellerFoundAmount} <- runRPC $ getBalance sellerQueryReq + sellerFoundAmount `shouldBe` Amount 1300 + let buyerQueryReq = defaultQueryWithData addr2 + ClientResponse{clientResponseData = buyerFoundAmount} <- runRPC $ getBalance buyerQueryReq + buyerFoundAmount `shouldBe` Amount 700 -- check for ownership changes - let queryReq = defaultReqWithData satoshi + let queryReq = defaultQueryWithData satoshi ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq - whoisOwner foundWhois `shouldBe` addr2 - whoisPrice foundWhois `shouldBe` 300 - whoisValue foundWhois `shouldBe` "hello (again) world" + foundWhois `shouldBe` expectedWhois -- @NOTE: this is possibly a problem with the go application too -- https://cosmos.network/docs/tutorial/buy-name.html#msg - it "Can buy self-owned names (and make a profit)" $ do + it "Can buy self-owned names and make a profit (success 0)" $ do -- check balance before - let queryReq = defaultReqWithData addr2 + let queryReq = defaultQueryWithData addr2 ClientResponse{clientResponseData = beforeBuyAmount} <- runRPC $ getBalance queryReq -- buy let val = "hello (again) world" @@ -166,18 +169,19 @@ spec = do -- check balance after ClientResponse{clientResponseData = afterBuyAmount} <- runRPC $ getBalance queryReq -- owner/buyer still profits - beforeBuyAmount `shouldSatisfy` (< afterBuyAmount) + afterBuyAmount `shouldSatisfy` (> beforeBuyAmount) - it "Can fail to buy a name" $ do + it "Can fail to buy a name (failure 1)" $ do -- try to buy at a lower price let msg = BuyName 100 satoshi "hello (again) world" addr1 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 1 - it "Can delete names" $ do + it "Can delete names (success 0)" $ do let msg = DeleteName addr2 satoshi deletedLog = NameDeleted satoshi + emptyWhois = Whois "" (Address "") 0 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 @@ -185,22 +189,40 @@ spec = do errs `shouldBe` mempty events `shouldSatisfy` elem deletedLog -- name shouldn't exist - let queryReq = defaultReqWithData satoshi - ClientResponse{ clientResponseData = emptyWhois + let queryReq = defaultQueryWithData satoshi + ClientResponse{ clientResponseData = foundWhois , clientResponseRaw } <- runRPC $ getWhois queryReq let queryRespCode = clientResponseRaw ^. Response._queryCode - -- storage failure + -- storage failure with defaults queryRespCode `shouldBe` 1 - -- should be a default whois - whoisPrice emptyWhois `shouldBe` 0 - whoisOwner emptyWhois `shouldBe` Address "" - whoisValue emptyWhois `shouldBe` "" + foundWhois `shouldBe` emptyWhois + + it "Can fail a transfer (failure 1)" $ do + let msg = Transfer addr2 addr1 2000 + rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 1 - -- @TODO: make transfer messages - it "Can fail a transfer" $ do - -- try to give addr1 2000 from addr2 - pendingWith "Split token module" + it "Can transfer (success 0)" $ do + let senderBeforeQueryReq = defaultQueryWithData addr2 + ClientResponse{clientResponseData = senderBeforeFoundAmount} <- runRPC $ getBalance senderBeforeQueryReq + senderBeforeFoundAmount `shouldBe` Amount 1700 + let msg = Transfer addr1 addr2 500 + transferEvent = TransferEvent 500 addr1 addr2 + rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg + deliverResp <- getDeliverTxResponse rawTx + ensureDeliverResponseCode deliverResp 0 + (errs, events) <- deliverTxEvents deliverResp "TransferEvent" + errs `shouldBe` mempty + events `shouldSatisfy` elem transferEvent + -- check balances + let receiverQueryReq = defaultQueryWithData addr1 + ClientResponse{clientResponseData = receiverFoundAmount} <- runRPC $ getBalance receiverQueryReq + receiverFoundAmount `shouldBe` Amount 1800 + let senderAfterQueryReq = defaultQueryWithData addr2 + ClientResponse{clientResponseData = senderAfterFoundAmount} <- runRPC $ getBalance senderAfterQueryReq + senderAfterFoundAmount `shouldBe` Amount 1200 runRPC :: forall a. RPC.TendermintM a -> IO a runRPC = RPC.runTendermintM rpcConfig @@ -215,11 +237,12 @@ runRPC = RPC.runTendermintM rpcConfig faucetAccount :: User -> IO () faucetAccount User{userAddress, userPrivKey} = do let msg = FaucetAccount userAddress 1000 - -- @NOTE: why is this `nameservice` and not `token`? - rawTx = mkSignedRawTransactionWithRoute "nameservice" userPrivKey msg - txReq = - RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - _ <- runRPC $ RPC.broadcastTxCommit txReq + faucetEvent = Faucetted userAddress 1000 + rawTx = mkSignedRawTransactionWithRoute "token" userPrivKey msg + deliverResp <- getDeliverTxResponse rawTx + (errs, events) <- deliverTxEvents deliverResp "Faucetted" + errs `shouldBe` mempty + events `shouldSatisfy` elem faucetEvent return () -- executes a request, then returns the deliverTx response @@ -243,14 +266,6 @@ ensureDeliverResponseCode deliverResp code = do let deliverRespCode = deliverResp ^. Response._deliverTxCode deliverRespCode `shouldBe` code --- this probably should be in a default type class -defaultReqWithData :: a -> QueryArgs a -defaultReqWithData x = QueryArgs - { queryArgsData = x - , queryArgsHeight = 0 - , queryArgsProve = False - } - -------------------------------------------------------------------------------- decodeValue :: HasCodec a => Base64.Base64String -> a diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs index 1f5a7f52..bb50c59b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs @@ -37,6 +37,14 @@ data QueryArgs a = QueryArgs , queryArgsHeight :: WrappedVal Int64 } deriving Functor +-- wrap data with default query fields +defaultQueryWithData :: a -> QueryArgs a +defaultQueryWithData x = QueryArgs + { queryArgsData = x + , queryArgsHeight = 0 + , queryArgsProve = False + } + data QueryResult a = QueryResult { queryResultData :: a , queryResultIndex :: WrappedVal Int64 From 6d7e754c8cec6e003c5ad38c379990bc9ac1d2d7 Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Mon, 25 Nov 2019 17:21:05 -0500 Subject: [PATCH 14/70] Don't decode query data into defaults (#127) * Don't decode query data into defaults * ensure response code 0 before asserting Just * event log tests change * use in faucet --- .../test/Nameservice/Test/E2ESpec.hs | 97 ++++++++----------- .../test/SimpleStorage/Test/E2ESpec.hs | 8 +- .../src/Tendermint/SDK/Query/Client.hs | 13 ++- 3 files changed, 56 insertions(+), 62 deletions(-) diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 932d6912..b34a56aa 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -68,7 +68,7 @@ spec = do it "Can query account balances" $ do let queryReq = defaultQueryWithData addr1 - ClientResponse{clientResponseData = foundAmount} <- runRPC $ getBalance queryReq + foundAmount <- getQueryResponseSuccess $ getBalance queryReq foundAmount `shouldBe` Amount 1000 it "Can create a name (success 0)" $ do @@ -78,45 +78,35 @@ spec = do rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 - (errs, events) <- deliverTxEvents deliverResp "NameClaimed" - errs `shouldBe` mempty - events `shouldSatisfy` elem claimedLog + ensureEventLogged deliverResp "NameClaimed" claimedLog it "Can query for a name" $ do let queryReq = defaultQueryWithData satoshi - expectedWhois = Whois "hello world" addr1 0 - ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq - foundWhois `shouldBe` expectedWhois + foundWhois <- getQueryResponseSuccess $ getWhois queryReq + foundWhois `shouldBe` Whois "hello world" addr1 0 it "Can query for a name that doesn't exist" $ do let nope = Name "nope" - -- empty whois (defaults) - emptyWhois = Whois "" (Address "") 0 queryReq = defaultQueryWithData nope - ClientResponse{ clientResponseData = foundWhois - , clientResponseRaw - } <- runRPC $ getWhois queryReq + ClientResponse{ clientResponseData, clientResponseRaw } <- runRPC $ getWhois queryReq let queryRespCode = clientResponseRaw ^. Response._queryCode - -- storage failure with defaults + -- storage failure queryRespCode `shouldBe` 1 - foundWhois `shouldBe` emptyWhois + clientResponseData `shouldBe` Nothing it "Can set a name value (success 0)" $ do let oldVal = "hello world" newVal = "goodbye to a world" msg = SetName satoshi addr1 newVal remappedLog = NameRemapped satoshi oldVal newVal - expectedWhois = Whois "goodbye to a world" addr1 0 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 - (errs, events) <- deliverTxEvents deliverResp "NameRemapped" - errs `shouldBe` mempty - events `shouldSatisfy` elem remappedLog + ensureEventLogged deliverResp "NameRemapped" remappedLog -- check for changes let queryReq = defaultQueryWithData satoshi - ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq - foundWhois `shouldBe` expectedWhois + foundWhois <- getQueryResponseSuccess $ getWhois queryReq + foundWhois `shouldBe` Whois "goodbye to a world" addr1 0 it "Can fail to set a name (failure 2)" $ do -- try to set a name without being the owner @@ -130,32 +120,28 @@ spec = do newVal = "hello (again) world" msg = BuyName 300 satoshi newVal addr2 claimedLog = NameClaimed addr2 satoshi newVal 300 - expectedWhois = Whois "hello (again) world" addr2 300 - -- transferLog = Transfer 300 addr1 addr2 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 - (errs, events) <- deliverTxEvents deliverResp "NameClaimed" - errs `shouldBe` mempty - events `shouldSatisfy` elem claimedLog + ensureEventLogged deliverResp "NameClaimed" claimedLog -- check for updated balances - seller: addr1, buyer: addr2 let sellerQueryReq = defaultQueryWithData addr1 - ClientResponse{clientResponseData = sellerFoundAmount} <- runRPC $ getBalance sellerQueryReq + sellerFoundAmount <- getQueryResponseSuccess $ getBalance sellerQueryReq sellerFoundAmount `shouldBe` Amount 1300 let buyerQueryReq = defaultQueryWithData addr2 - ClientResponse{clientResponseData = buyerFoundAmount} <- runRPC $ getBalance buyerQueryReq + buyerFoundAmount <- getQueryResponseSuccess $ getBalance buyerQueryReq buyerFoundAmount `shouldBe` Amount 700 -- check for ownership changes let queryReq = defaultQueryWithData satoshi - ClientResponse{clientResponseData = foundWhois} <- runRPC $ getWhois queryReq - foundWhois `shouldBe` expectedWhois + foundWhois <- getQueryResponseSuccess $ getWhois queryReq + foundWhois `shouldBe` Whois "hello (again) world" addr2 300 -- @NOTE: this is possibly a problem with the go application too -- https://cosmos.network/docs/tutorial/buy-name.html#msg it "Can buy self-owned names and make a profit (success 0)" $ do -- check balance before let queryReq = defaultQueryWithData addr2 - ClientResponse{clientResponseData = beforeBuyAmount} <- runRPC $ getBalance queryReq + beforeBuyAmount <- getQueryResponseSuccess $ getBalance queryReq -- buy let val = "hello (again) world" msg = BuyName 500 satoshi val addr2 @@ -163,11 +149,9 @@ spec = do rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 - (errs, events) <- deliverTxEvents deliverResp "NameClaimed" - errs `shouldBe` mempty - events `shouldSatisfy` elem claimedLog + ensureEventLogged deliverResp "NameClaimed" claimedLog -- check balance after - ClientResponse{clientResponseData = afterBuyAmount} <- runRPC $ getBalance queryReq + afterBuyAmount <- getQueryResponseSuccess $ getBalance queryReq -- owner/buyer still profits afterBuyAmount `shouldSatisfy` (> beforeBuyAmount) @@ -181,22 +165,17 @@ spec = do it "Can delete names (success 0)" $ do let msg = DeleteName addr2 satoshi deletedLog = NameDeleted satoshi - emptyWhois = Whois "" (Address "") 0 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 - (errs, events) <- deliverTxEvents deliverResp "NameDeleted" - errs `shouldBe` mempty - events `shouldSatisfy` elem deletedLog + ensureEventLogged deliverResp "NameDeleted" deletedLog -- name shouldn't exist let queryReq = defaultQueryWithData satoshi - ClientResponse{ clientResponseData = foundWhois - , clientResponseRaw - } <- runRPC $ getWhois queryReq + ClientResponse{ clientResponseData, clientResponseRaw } <- runRPC $ getWhois queryReq let queryRespCode = clientResponseRaw ^. Response._queryCode - -- storage failure with defaults + -- storage failure queryRespCode `shouldBe` 1 - foundWhois `shouldBe` emptyWhois + clientResponseData `shouldBe` Nothing it "Can fail a transfer (failure 1)" $ do let msg = Transfer addr2 addr1 2000 @@ -206,22 +185,20 @@ spec = do it "Can transfer (success 0)" $ do let senderBeforeQueryReq = defaultQueryWithData addr2 - ClientResponse{clientResponseData = senderBeforeFoundAmount} <- runRPC $ getBalance senderBeforeQueryReq + senderBeforeFoundAmount <- getQueryResponseSuccess $ getBalance senderBeforeQueryReq senderBeforeFoundAmount `shouldBe` Amount 1700 let msg = Transfer addr1 addr2 500 transferEvent = TransferEvent 500 addr1 addr2 rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 - (errs, events) <- deliverTxEvents deliverResp "TransferEvent" - errs `shouldBe` mempty - events `shouldSatisfy` elem transferEvent + ensureEventLogged deliverResp "TransferEvent" transferEvent -- check balances let receiverQueryReq = defaultQueryWithData addr1 - ClientResponse{clientResponseData = receiverFoundAmount} <- runRPC $ getBalance receiverQueryReq + receiverFoundAmount <- getQueryResponseSuccess $ getBalance receiverQueryReq receiverFoundAmount `shouldBe` Amount 1800 let senderAfterQueryReq = defaultQueryWithData addr2 - ClientResponse{clientResponseData = senderAfterFoundAmount} <- runRPC $ getBalance senderAfterQueryReq + senderAfterFoundAmount <- getQueryResponseSuccess $ getBalance senderAfterQueryReq senderAfterFoundAmount `shouldBe` Amount 1200 runRPC :: forall a. RPC.TendermintM a -> IO a @@ -240,10 +217,16 @@ faucetAccount User{userAddress, userPrivKey} = do faucetEvent = Faucetted userAddress 1000 rawTx = mkSignedRawTransactionWithRoute "token" userPrivKey msg deliverResp <- getDeliverTxResponse rawTx - (errs, events) <- deliverTxEvents deliverResp "Faucetted" - errs `shouldBe` mempty - events `shouldSatisfy` elem faucetEvent - return () + ensureDeliverResponseCode deliverResp 0 + ensureEventLogged deliverResp "Faucetted" faucetEvent + +-- executes a query and ensures a 0 response code +getQueryResponseSuccess :: RPC.TendermintM (ClientResponse a) -> IO a +getQueryResponseSuccess query = do + ClientResponse{clientResponseData,clientResponseRaw} <- runRPC query + let responseCode = clientResponseRaw ^. Response._queryCode + responseCode `shouldBe` 0 + return . fromJust $ clientResponseData -- executes a request, then returns the deliverTx response getDeliverTxResponse :: RawTransaction -> IO Response.DeliverTx @@ -253,13 +236,19 @@ getDeliverTxResponse rawTx = do RPC.broadcastTxCommit txReq -- get the logged events from a deliver response, --- ensures there are no errors when parsing event logs deliverTxEvents :: FromEvent e => Response.DeliverTx -> Text -> IO ([Text],[e]) deliverTxEvents deliverResp eventName = do let deliverEvents = deliverResp ^. Response._deliverTxEvents filtered = filter ((== eventName) . eventType) deliverEvents return . partitionEithers . map fromEvent $ filtered +-- ensures there are no errors when parsing event logs and contains the expectedEvent +ensureEventLogged :: (Eq e, Show e, FromEvent e) => Response.DeliverTx -> Text -> e -> IO () +ensureEventLogged deliverResp eventName expectedEvent = do + (errs, events) <- deliverTxEvents deliverResp eventName + errs `shouldBe` mempty + events `shouldSatisfy` elem expectedEvent + -- check for a specific deliver response code ensureDeliverResponseCode :: Response.DeliverTx -> Word32 -> IO () ensureDeliverResponseCode deliverResp code = do diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 3f049bff..6b82befc 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -41,8 +41,8 @@ spec = do , queryArgsHeight = 0 , queryArgsProve = False } - ClientResponse{clientResponseData} <- runQueryRunner $ getCount queryReq - clientResponseData `shouldBe` SS.Count 0 + ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq + foundCount `shouldBe` SS.Count 0 it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do let tx = UpdateCountTx "irakli" 4 @@ -59,8 +59,8 @@ spec = do , queryArgsHeight = 0 , queryArgsProve = False } - ClientResponse{clientResponseData} <- runQueryRunner $ getCount queryReq - clientResponseData `shouldBe` SS.Count 4 + ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq + foundCount `shouldBe` SS.Count 4 encodeCount :: Int32 -> Base64String diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs index 9ffdd803..1a61acd65 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs @@ -58,8 +58,9 @@ instance (RawKey k, HasClient m a) => HasClient m (QA k :> a) where , Req.queryProve = queryArgsProve } +-- | Data is Nothing iff Raw includes a non-0 response value data ClientResponse a = ClientResponse - { clientResponseData :: a + { clientResponseData :: Maybe a , clientResponseRaw :: Resp.Query } @@ -69,6 +70,10 @@ instance (RunClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasCli let leaf = symbolVal (Proxy @(Name a)) in do r@Resp.Query{..} <- runQuery q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } - return $ case decodeQueryResult queryValue of - Left err -> error $ "Impossible parse error: " <> cs err - Right a -> ClientResponse a r + -- anything other than 0 code is a failure: https://tendermint.readthedocs.io/en/latest/abci-spec.html + -- and will result in queryValue decoding to a "empty/default" object + return $ case queryCode of + 0 -> case decodeQueryResult queryValue of + Left err -> error $ "Impossible parse error: " <> cs err + Right a -> ClientResponse (Just a) r + _ -> ClientResponse Nothing r From afc4a9cb2c486749ca7ef02d60e184ce2dd106cd Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Tue, 26 Nov 2019 12:52:33 -0500 Subject: [PATCH 15/70] use a typed message wrapper to disambiguate decoding messages (#129) * use a typed message wrapper to disambiguate decoding messages * Add TypedMessage to token * fix imports * fix build * Use typed message in faucet --- hs-abci-examples/nameservice/package.yaml | 1 + .../Modules/Nameservice/Messages.hs | 15 +++++--- .../src/Nameservice/Modules/Token/Messages.hs | 37 ++++++++++--------- .../src/Nameservice/Modules/TypedMessage.hs | 25 +++++++++++++ .../test/Nameservice/Test/E2ESpec.hs | 21 ++++++----- 5 files changed, 66 insertions(+), 33 deletions(-) create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 26857090..780d5f31 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -86,6 +86,7 @@ library: - Nameservice.Application - Nameservice.Server - Nameservice.Handlers + - Nameservice.Modules.TypedMessage - Nameservice.Modules.Nameservice - Nameservice.Modules.Token - Nameservice.Modules.Token.Messages diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index a67981e4..d25aa00e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -7,11 +7,12 @@ import Data.Text (Text) import GHC.Generics (Generic) import Nameservice.Modules.Nameservice.Types (Name (..)) import Nameservice.Modules.Token (Amount (..)) +import Nameservice.Modules.TypedMessage (TypedMessage (..)) import Proto3.Suite (Message, Named, fromByteString, toLazyByteString) import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Address (Address (..)) import Tendermint.SDK.Types.Message (Msg (..), ValidateMessage (..), coerceProto3Error, @@ -67,11 +68,13 @@ instance HasCodec BuyName where decode = first (formatMessageParseError . coerceProto3Error) . fromByteString instance HasCodec NameserviceMessage where - decode bs = - -- @NOTE: tests pass iff NBuyName is first - fmap NBuyName (decode bs) <> - fmap NSetName (decode bs) <> - fmap NDeleteName (decode bs) + decode bs = do + TypedMessage{..} <- decode bs + case typedMessageType of + "SetName" -> NSetName <$> decode typedMessageContents + "DeleteName" -> NDeleteName <$> decode typedMessageContents + "BuyName" -> NBuyName <$> decode typedMessageContents + _ -> Left . cs $ "Unknown Nameservice message type " ++ cs typedMessageType encode = \case NSetName msg -> encode msg NBuyName msg -> encode msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs index 253e18f3..f5404d5e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs @@ -1,16 +1,17 @@ module Nameservice.Modules.Token.Messages where -import Data.Bifunctor (first) -import Data.String.Conversions (cs) -import GHC.Generics (Generic) -import Nameservice.Modules.Token.Types (Amount) -import Proto3.Suite (Message, Named, - fromByteString, - toLazyByteString) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (coerceProto3Error, - formatMessageParseError) +import Data.Bifunctor (first) +import Data.String.Conversions (cs) +import GHC.Generics (Generic) +import Nameservice.Modules.Token.Types (Amount) +import Nameservice.Modules.TypedMessage (TypedMessage (..)) +import Proto3.Suite (Message, Named, + fromByteString, + toLazyByteString) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (coerceProto3Error, + formatMessageParseError) data TokenMessage = TTransfer Transfer @@ -69,12 +70,14 @@ instance HasCodec Mint where decode = first (formatMessageParseError . coerceProto3Error) . fromByteString instance HasCodec TokenMessage where - decode bs = - fmap TTransfer (decode bs) <> - -- @NOTE: TFaucetAccount and TBurn have to be in this order - fmap TFaucetAccount (decode bs) <> - fmap TBurn (decode bs) <> - fmap TMint (decode bs) + decode bs = do + TypedMessage{..} <- decode bs + case typedMessageType of + "Transfer" -> TTransfer <$> decode typedMessageContents + "Burn" -> TBurn <$> decode typedMessageContents + "Mint" -> TMint <$> decode typedMessageContents + "FaucetAccount" -> TFaucetAccount <$> decode typedMessageContents + _ -> Left . cs $ "Unknown Token message type " ++ cs typedMessageType encode = \case TTransfer msg -> encode msg TBurn msg -> encode msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs new file mode 100644 index 00000000..e7bdb768 --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs @@ -0,0 +1,25 @@ +module Nameservice.Modules.TypedMessage where + +import Data.Bifunctor (first) +import qualified Data.ByteString as BS +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) +import Proto3.Suite (Message, Named, fromByteString, + toLazyByteString) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Message (coerceProto3Error, + formatMessageParseError) + +-- Tags messages to disambiguate decoding instances +data TypedMessage = TypedMessage + { typedMessageType :: Text + , typedMessageContents :: BS.ByteString + } deriving (Eq, Show, Generic) + +instance Message TypedMessage +instance Named TypedMessage + +instance HasCodec TypedMessage where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index b34a56aa..43d92c72 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -33,6 +33,7 @@ import Nameservice.Modules.Token (Amount (..), Faucetted (..), Transfer (..), TransferEvent (..)) +import Nameservice.Modules.TypedMessage (TypedMessage (..)) import Network.ABCI.Types.Messages.FieldTypes (Event (..)) import qualified Network.ABCI.Types.Messages.Response as Response import qualified Network.Tendermint.Client as RPC @@ -73,7 +74,7 @@ spec = do it "Can create a name (success 0)" $ do let val = "hello world" - msg = BuyName 0 satoshi val addr1 + msg = TypedMessage "BuyName" (encode $ BuyName 0 satoshi val addr1) claimedLog = NameClaimed addr1 satoshi val 0 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx @@ -97,7 +98,7 @@ spec = do it "Can set a name value (success 0)" $ do let oldVal = "hello world" newVal = "goodbye to a world" - msg = SetName satoshi addr1 newVal + msg = TypedMessage "SetName" (encode $ SetName satoshi addr1 newVal) remappedLog = NameRemapped satoshi oldVal newVal rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx @@ -110,7 +111,7 @@ spec = do it "Can fail to set a name (failure 2)" $ do -- try to set a name without being the owner - let msg = SetName satoshi addr2 "goodbye to a world" + let msg = TypedMessage "SetName" (encode $ SetName satoshi addr2 "goodbye to a world") rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 2 @@ -118,7 +119,7 @@ spec = do it "Can buy an existing name (success 0)" $ do let oldVal = "goodbye to a world" newVal = "hello (again) world" - msg = BuyName 300 satoshi newVal addr2 + msg = TypedMessage "BuyName" (encode $ BuyName 300 satoshi newVal addr2) claimedLog = NameClaimed addr2 satoshi newVal 300 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx @@ -144,7 +145,7 @@ spec = do beforeBuyAmount <- getQueryResponseSuccess $ getBalance queryReq -- buy let val = "hello (again) world" - msg = BuyName 500 satoshi val addr2 + msg = TypedMessage "BuyName" (encode $ BuyName 500 satoshi val addr2) claimedLog = NameClaimed addr2 satoshi val 500 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx @@ -157,13 +158,13 @@ spec = do it "Can fail to buy a name (failure 1)" $ do -- try to buy at a lower price - let msg = BuyName 100 satoshi "hello (again) world" addr1 + let msg = TypedMessage "BuyName" (encode $ BuyName 100 satoshi "hello (again) world" addr1) rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 1 it "Can delete names (success 0)" $ do - let msg = DeleteName addr2 satoshi + let msg = TypedMessage "DeleteName" (encode $ DeleteName addr2 satoshi) deletedLog = NameDeleted satoshi rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx @@ -178,7 +179,7 @@ spec = do clientResponseData `shouldBe` Nothing it "Can fail a transfer (failure 1)" $ do - let msg = Transfer addr2 addr1 2000 + let msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 2000) rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 1 @@ -187,7 +188,7 @@ spec = do let senderBeforeQueryReq = defaultQueryWithData addr2 senderBeforeFoundAmount <- getQueryResponseSuccess $ getBalance senderBeforeQueryReq senderBeforeFoundAmount `shouldBe` Amount 1700 - let msg = Transfer addr1 addr2 500 + let msg = TypedMessage "Transfer" (encode $ Transfer addr1 addr2 500) transferEvent = TransferEvent 500 addr1 addr2 rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx @@ -213,7 +214,7 @@ runRPC = RPC.runTendermintM rpcConfig faucetAccount :: User -> IO () faucetAccount User{userAddress, userPrivKey} = do - let msg = FaucetAccount userAddress 1000 + let msg = TypedMessage "FaucetAccount" (encode $ FaucetAccount userAddress 1000) faucetEvent = Faucetted userAddress 1000 rawTx = mkSignedRawTransactionWithRoute "token" userPrivKey msg deliverResp <- getDeliverTxResponse rawTx From 1bdfb338ed485f0fb1f988b61b566c0b19cbd16a Mon Sep 17 00:00:00 2001 From: Alexandre Esteves <2335822+alexfmpe@users.noreply.github.com> Date: Tue, 26 Nov 2019 17:53:04 +0000 Subject: [PATCH 16/70] Fix typos (#131) --- hs-abci-sdk/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hs-abci-sdk/README.md b/hs-abci-sdk/README.md index c635480a..169892f9 100644 --- a/hs-abci-sdk/README.md +++ b/hs-abci-sdk/README.md @@ -9,7 +9,7 @@ This package lays out an SDK for rapidly developing blockchain applications in h You will need to have the `libsecp256k` `C` library installed on your machine to build this package, or anything depedning on it, as it is not statically linked to its haskell wrapper. You can find instructions for this [here](https://github.com/f-o-a-m/hs-abci#libsecp256k1). -## Archetecture +## Architecture The SDK makes heavy use of an effects system to separate different components of your application. Specifically it is using the [polysemy](https://hackage.haskell.org/package/polysemy) effects library in its implementation. @@ -25,7 +25,7 @@ to `BaseApp`. For examples, see `Tendermint.SDK.Errors` or `Tendermint.SDK.Store ### Core Effects -The `CoreEffects` system is what's used to interpret `BaseApp` to `IO`, which is where the application must end up at eventually. It provides things like a loging context (e.g. `Katip`), +The `CoreEffects` system is what's used to interpret `BaseApp` to `IO`, which is where the application must end up at eventually. It provides things like a logging context (e.g. `Katip`), a context for executing database transactions, and various buffers and vars to facilitate ABCI message handling. ## Example Applications From 3e757347de093cead554a2ef445602d3c2bd783e Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 26 Nov 2019 12:25:03 -0800 Subject: [PATCH 17/70] add optional prefix (#128) --- hs-abci-sdk/src/Tendermint/SDK/Store.hs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Store.hs index ca253484..d8589a75 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Store.hs @@ -13,6 +13,7 @@ module Tendermint.SDK.Store import Control.Lens (Iso', (^.)) import qualified Data.ByteString as BS +import Data.Proxy import Data.String.Conversions (cs) import Polysemy (Member, Sem, makeSem) import Tendermint.SDK.Codec (HasCodec (..)) @@ -32,6 +33,10 @@ class RawKey k where class RawKey k => IsKey k ns where type Value k ns = a | a -> ns k + prefixWith :: Proxy k -> Proxy ns -> BS.ByteString + + default prefixWith :: Proxy k -> Proxy ns -> BS.ByteString + prefixWith _ _ = "" put :: forall k r ns. @@ -43,7 +48,7 @@ put -> Value k ns -> Sem r () put sk k a = - let key = k ^. rawKey + let key = prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey val = encode a in rawStorePut sk key val @@ -56,7 +61,7 @@ get -> k -> Sem r (Maybe (Value k ns)) get sk k = do - let key = k ^. rawKey + let key = prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey mRes <- rawStoreGet sk key pure $ case mRes of Nothing -> Nothing @@ -65,17 +70,21 @@ get sk k = do Right a -> Just a delete - :: IsKey k ns + :: forall k ns r. + IsKey k ns => Member RawStore r => StoreKey ns -> k -> Sem r () -delete sk k = rawStoreDelete sk (k ^. rawKey) +delete sk k = rawStoreDelete sk $ + prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey prove - :: IsKey k ns + :: forall k ns r. + IsKey k ns => Member RawStore r => StoreKey ns -> k -> Sem r (Maybe BS.ByteString) -prove sk k = rawStoreProve sk (k ^. rawKey) +prove sk k = rawStoreProve sk $ + prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey From bb5a3e0b16dd97030076a217d25027480bcfe636 Mon Sep 17 00:00:00 2001 From: Charles Crain Date: Mon, 2 Dec 2019 08:25:56 -0800 Subject: [PATCH 18/70] Msg validation (#133) * validation step in tx router * sketch * tests for authtree store * withTransaction commit flag * TxResult intermediary type, withTransaction tests updated, nameservice using withTransaction * validate message instances for token * WIP | validation passing in tests, no longer 2 rollbacks on failure * return default handler for checkTx * stylish --- .../nameservice/src/Nameservice/Handlers.hs | 33 +++++---- .../Nameservice/Modules/Nameservice/Query.hs | 7 +- .../src/Nameservice/Modules/Token/Messages.hs | 24 ++++++- .../src/Nameservice/Modules/Token/Query.hs | 4 +- .../src/SimpleStorage/Handlers.hs | 11 +-- .../SimpleStorage/Modules/SimpleStorage.hs | 7 +- hs-abci-sdk/package.yaml | 2 + .../src/Tendermint/SDK/AuthTreeStore.hs | 28 ++++++-- hs-abci-sdk/src/Tendermint/SDK/Errors.hs | 10 ++- hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs | 5 ++ hs-abci-sdk/src/Tendermint/SDK/Store.hs | 38 ++++++++-- hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs | 12 +++- .../src/Tendermint/SDK/Types/Message.hs | 4 +- .../src/Tendermint/SDK/Types/TxResult.hs | 72 +++++++++++++++++++ .../Tendermint/SDK/Test/AuthTreeStoreSpec.hs | 41 ++++++++++- .../Network/ABCI/Types/Messages/Response.hs | 3 +- 16 files changed, 259 insertions(+), 42 deletions(-) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs index cef99448..abe2bb87 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs @@ -2,6 +2,7 @@ module Nameservice.Handlers where import Control.Lens (to, (&), (.~), (^.)) import qualified Data.ByteArray.Base64String as Base64 +import Data.ByteString (ByteString) import Data.Default.Class (def) import Nameservice.Application (compileToBaseApp, router) import Network.ABCI.Server.App (App (..), @@ -20,6 +21,10 @@ import Tendermint.SDK.Errors (AppError, SDKError (..), throwSDKError) import Tendermint.SDK.Events (withEventBuffer) import Tendermint.SDK.Query (QueryApplication) +import Tendermint.SDK.Store (withTransaction) +import Tendermint.SDK.Types.TxResult (TxResult, + deliverTxTxResult, + txResultEvents) echoH :: Request 'MTEcho @@ -62,28 +67,32 @@ beginBlockH -> Sem BaseApp (Response 'MTBeginBlock) beginBlockH = defaultHandler +-- Common function between checkTx and deliverTx +transactionHandler :: ByteString -> Sem BaseApp TxResult +transactionHandler bs = do + tx <- either (throwSDKError . ParseError) return $ decode bs + events <- withEventBuffer . compileToBaseApp $ router tx + pure $ def & txResultEvents .~ events + -- only checks to see if the tx parses checkTxH :: Request 'MTCheckTx -> Sem BaseApp (Response 'MTCheckTx) -checkTxH = defaultHandler--(RequestCheckTx checkTx) = +checkTxH = defaultHandler + -- let tryToRespond = withTransaction False $ do + -- txResult <- transactionHandler $ checkTx ^. Req._checkTxTx . to Base64.toBytes + -- return $ ResponseCheckTx $ def & checkTxTxResult .~ txResult + -- in tryToRespond `catch` \(err :: AppError) -> + -- return . ResponseCheckTx $ def & checkTxAppError .~ err - -- pure . ResponseCheckTx $ - -- case decodeAppTxMessage $ checkTx ^. Req._checkTxTx . to convert of - -- Left _ -> def & Resp._checkTxCode .~ 1 - -- Right (ATMUpdateCount _) -> def & Resp._checkTxCode .~ 0 deliverTxH :: Request 'MTDeliverTx -> Sem BaseApp (Response 'MTDeliverTx) -- Sem BaseApp (Response 'MTDeliverTx) deliverTxH (RequestDeliverTx deliverTx) = - let tryToRespond = do - tx <- either (throwSDKError . ParseError) return $ - decode $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes - events <- withEventBuffer . compileToBaseApp $ router tx - return $ ResponseDeliverTx $ - def & Resp._deliverTxCode .~ 0 - & Resp._deliverTxEvents .~ events + let tryToRespond = withTransaction True $ do + txResult <- transactionHandler $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes + return $ ResponseDeliverTx $ def & deliverTxTxResult .~ txResult in tryToRespond `catch` \(err :: AppError) -> return . ResponseDeliverTx $ def & deliverTxAppError .~ err diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index 66bbbb90..a9b7f48e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -4,7 +4,9 @@ import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Name, Whois) import Polysemy (Member, Sem) +import Polysemy.Error (Error) import Servant.API ((:>)) +import Tendermint.SDK.Errors (AppError) import qualified Tendermint.SDK.Query as Q import qualified Tendermint.SDK.Store as Store @@ -16,6 +18,9 @@ type NameserviceContents = '[(Name, Whois)] type Api = "nameservice" :> Q.QueryApi NameserviceContents -server :: Member Store.RawStore r => Q.RouteT Api (Sem r) +server + :: Member Store.RawStore r + => Member (Error AppError) r + => Q.RouteT Api (Sem r) server = Q.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs index f5404d5e..67717417 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs @@ -2,6 +2,7 @@ module Nameservice.Modules.Token.Messages where import Data.Bifunctor (first) import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) import GHC.Generics (Generic) import Nameservice.Modules.Token.Types (Amount) import Nameservice.Modules.TypedMessage (TypedMessage (..)) @@ -10,7 +11,9 @@ import Proto3.Suite (Message, Named, toLazyByteString) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (coerceProto3Error, +import Tendermint.SDK.Types.Message (Msg (..), + ValidateMessage (..), + coerceProto3Error, formatMessageParseError) data TokenMessage = @@ -83,3 +86,22 @@ instance HasCodec TokenMessage where TBurn msg -> encode msg TMint msg -> encode msg TFaucetAccount msg -> encode msg + +instance ValidateMessage TokenMessage where + validateMessage m@Msg{msgData} = case msgData of + TTransfer msg -> validateMessage m {msgData = msg} + TFaucetAccount msg -> validateMessage m {msgData = msg} + TBurn msg -> validateMessage m {msgData = msg} + TMint msg -> validateMessage m {msgData = msg} + +instance ValidateMessage Transfer where + validateMessage _ = Success () + +instance ValidateMessage FaucetAccount where + validateMessage _ = Success () + +instance ValidateMessage Burn where + validateMessage _ = Success () + +instance ValidateMessage Mint where + validateMessage _ = Success () diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs index a2d8aee2..fdd16092 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs @@ -4,7 +4,9 @@ import Data.Proxy import Nameservice.Modules.Token.Keeper (storeKey) import Nameservice.Modules.Token.Types (Amount) import Polysemy +import Polysemy.Error (Error) import Servant.API ((:>)) +import Tendermint.SDK.Errors (AppError) import Tendermint.SDK.Query (RouteT) import Tendermint.SDK.Query.Store (QueryApi, storeQueryHandlers) import qualified Tendermint.SDK.Store as Store @@ -18,6 +20,6 @@ type TokenContents = '[(Address, Amount)] type Api = "token" :> QueryApi TokenContents -server :: Member Store.RawStore r => RouteT Api (Sem r) +server :: Member Store.RawStore r => Member (Error AppError) r => RouteT Api (Sem r) server = storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs index 87b78a85..fa99a25c 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs @@ -17,6 +17,9 @@ import SimpleStorage.Types (AppTxMessage (..), import Tendermint.SDK.Application (defaultHandler) import Tendermint.SDK.Events (withEventBuffer) import Tendermint.SDK.Query (QueryApplication) +import Tendermint.SDK.Store (withTransaction) +import Tendermint.SDK.Types.TxResult (deliverTxTxResult, + txResultEvents) echoH :: Request 'MTEcho @@ -63,7 +66,7 @@ beginBlockH = defaultHandler checkTxH :: Request 'MTCheckTx -> Handler (Response 'MTCheckTx) -checkTxH (RequestCheckTx checkTx) = pure . ResponseCheckTx $ +checkTxH (RequestCheckTx checkTx) = withTransaction False $ pure . ResponseCheckTx $ case decodeAppTxMessage $ checkTx ^. Req._checkTxTx . to convert of Left _ -> def & Resp._checkTxCode .~ 1 Right (ATMUpdateCount _) -> def & Resp._checkTxCode .~ 0 @@ -71,16 +74,16 @@ checkTxH (RequestCheckTx checkTx) = pure . ResponseCheckTx $ deliverTxH :: Request 'MTDeliverTx -> Handler (Response 'MTDeliverTx) -deliverTxH (RequestDeliverTx deliverTx) = do +deliverTxH (RequestDeliverTx deliverTx) = withTransaction True $ case decodeAppTxMessage $ deliverTx ^. Req._deliverTxTx . to convert of Left _ -> return . ResponseDeliverTx $ def & Resp._deliverTxCode .~ 1 Right (ATMUpdateCount updateCountTx) -> do let count = SS.Count $ updateCountTxCount updateCountTx events <- withEventBuffer $ putCount count + let txResult = def & txResultEvents .~ events return $ ResponseDeliverTx $ - def & Resp._deliverTxCode .~ 0 - & Resp._deliverTxEvents .~ events + def & deliverTxTxResult .~ txResult endBlockH :: Request 'MTEndBlock diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index c178a7f4..e949eeec 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -37,10 +37,12 @@ import qualified Data.Serialize as Serialize import Data.String.Conversions (cs) import GHC.Generics (Generic) import Polysemy (Member, Sem, interpret, makeSem) +import Polysemy.Error (Error) import Polysemy.Output (Output) import Servant.API ((:>)) import Tendermint.SDK.BaseApp (HasBaseAppEff) import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError) import qualified Tendermint.SDK.Events as Events import Tendermint.SDK.Query (FromQueryData, QueryApi, Queryable (..), RouteT, @@ -132,6 +134,9 @@ type CountStoreContents = '[(CountKey, Count)] type Api = "simple_storage" :> QueryApi CountStoreContents -server :: Member RawStore r => RouteT Api (Sem r) +server + :: Member RawStore r + => Member (Error AppError) r + => RouteT Api (Sem r) server = storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 75a28e0c..bb2e0a69 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -118,6 +118,7 @@ library: - Tendermint.SDK.Types.Message - Tendermint.SDK.Types.Transaction - Tendermint.SDK.Types.Address + - Tendermint.SDK.Types.TxResult - Tendermint.SDK.TxRouter - Tendermint.SDK.Module - Tendermint.SDK.Gas @@ -137,6 +138,7 @@ tests: - Tendermint.SDK.Test.GasSpec ghc-options: + - -fplugin=Polysemy.Plugin - -Werror - -Wall - -threaded diff --git a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs index ecabfa15..010f7d3e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs @@ -12,6 +12,7 @@ import qualified Crypto.Data.Auth.Tree.Class as AT import qualified Crypto.Data.Auth.Tree.Cryptonite as Cryptonite import qualified Crypto.Hash as Cryptonite import Data.ByteString (ByteString) +import qualified Data.List.NonEmpty as NE import Polysemy (Embed, Member, Sem, interpret) import Tendermint.SDK.Store (RawStore (..), StoreKey (..)) @@ -28,11 +29,11 @@ instance AT.MerkleHash AuthTreeHash where concatHashes (AuthTreeHash a) (AuthTreeHash b) = AuthTreeHash $ Cryptonite.concatHashes a b data AuthTree = AuthTree - { treeVar :: TVar (AT.Tree ByteString ByteString) + { treeVar :: TVar (NE.NonEmpty (AT.Tree ByteString ByteString)) } initAuthTree :: IO AuthTree -initAuthTree = AuthTree <$> newTVarIO AT.empty +initAuthTree = AuthTree <$> newTVarIO (pure AT.empty) eval :: Member (Embed IO) r @@ -43,13 +44,26 @@ eval AuthTree{treeVar} = interpret (\case RawStorePut (StoreKey sk) k v -> liftIO . atomically $ do - tree <- readTVar treeVar - writeTVar treeVar $ AT.insert (sk <> k) v tree + tree NE.:| ts <- readTVar treeVar + writeTVar treeVar $ AT.insert (sk <> k) v tree NE.:| ts RawStoreGet (StoreKey sk) k -> liftIO . atomically $ do - tree <- readTVar treeVar + tree NE.:| _ <- readTVar treeVar pure $ AT.lookup (sk <> k) tree RawStoreProve _ _ -> pure Nothing RawStoreDelete (StoreKey sk) k -> liftIO . atomically $ do - tree <- readTVar treeVar - writeTVar treeVar $ AT.delete (sk <> k) tree + tree NE.:| ts <- readTVar treeVar + writeTVar treeVar $ AT.delete (sk <> k) tree NE.:| ts + RawStoreBeginTransaction -> liftIO . atomically $ do + tree NE.:| ts <- readTVar treeVar + writeTVar treeVar $ tree NE.:| tree : ts + RawStoreRollback -> liftIO . atomically $ do + trees <- readTVar treeVar + writeTVar treeVar $ case trees of + t NE.:| [] -> t NE.:| [] + _ NE.:| t' : ts -> t' NE.:| ts + RawStoreCommit -> liftIO . atomically $ do + trees <- readTVar treeVar + writeTVar treeVar $ case trees of + t NE.:| [] -> t NE.:| [] + t NE.:| _ : ts -> t NE.:| ts ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs index 84ece40c..26caf33e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Errors.hs @@ -10,7 +10,7 @@ module Tendermint.SDK.Errors import Control.Exception (Exception) import Control.Lens (Lens', lens) -import Data.Text (Text) +import Data.Text (Text, intercalate) import Data.Word (Word32) import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy @@ -93,6 +93,8 @@ data SDKError = | UnmatchedRoute Text -- ^ The name of the route that failed to match. | OutOfGasException + | MessageValidation [Text] + -- ^ Message validation errors. -- | As of right now it's not expected that one can recover from an 'SDKError', -- | so we are throwing them as 'AppError's directly. @@ -126,3 +128,9 @@ instance IsAppError SDKError where , appErrorCodespace = "sdk" , appErrorMessage = "Out of gas exception" } + + makeAppError (MessageValidation errors) = AppError + { appErrorCode = 5 + , appErrorCodespace = "sdk" + , appErrorMessage = "Message failed validation: " <> intercalate "\n" errors + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs index 4a346590..533420a0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs @@ -7,8 +7,10 @@ import Data.ByteArray.Base64String (fromBytes) import Data.Proxy import GHC.TypeLits (Symbol) import Polysemy (Member, Sem) +import Polysemy.Error (Error) import Servant.API ((:<|>) (..), (:>)) import Tendermint.SDK.Codec (HasCodec) +import Tendermint.SDK.Errors (AppError) import Tendermint.SDK.Query.Class import Tendermint.SDK.Query.Types import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, @@ -22,6 +24,7 @@ instance , a ~ Value k ns , HasCodec a , Member RawStore r + , Member (Error AppError) r ) => StoreQueryHandler a ns (QueryArgs k -> ExceptT QueryError (Sem r) (QueryResult a)) where storeQueryHandler _ storeKey QueryArgs{..} = do @@ -47,6 +50,7 @@ instance , a ~ Value k ns , HasCodec a , Member RawStore r + , Member (Error AppError) r ) => StoreQueryHandlers '[(k,a)] ns (Sem r) where type QueryApi '[(k,a)] = QA k :> Leaf a storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey @@ -57,6 +61,7 @@ instance , HasCodec a , StoreQueryHandlers ((k', a') ': as) ns (Sem r) , Member RawStore r + , Member (Error AppError) r ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns (Sem r) where type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) storeQueryHandlers _ storeKey pm = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/Store.hs index d8589a75..4595d761 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Store.hs @@ -9,14 +9,21 @@ module Tendermint.SDK.Store , put , delete , prove + , withTransaction + , rawStoreBeginTransaction + , rawStoreRollback + , rawStoreCommit ) where import Control.Lens (Iso', (^.)) import qualified Data.ByteString as BS import Data.Proxy import Data.String.Conversions (cs) -import Polysemy (Member, Sem, makeSem) +import Polysemy (Member, Members, Sem, makeSem) +import Polysemy.Error (Error, catch, throw) import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError, SDKError (ParseError), + throwSDKError) newtype StoreKey n = StoreKey BS.ByteString @@ -25,6 +32,9 @@ data RawStore m a where RawStoreGet :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) RawStoreDelete :: StoreKey ns -> BS.ByteString -> RawStore m () RawStoreProve :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) + RawStoreBeginTransaction :: RawStore m () + RawStoreRollback :: RawStore m () + RawStoreCommit :: RawStore m () makeSem ''RawStore @@ -56,6 +66,7 @@ get :: forall k r ns. IsKey k ns => HasCodec (Value k ns) + => Member (Error AppError) r => Member RawStore r => StoreKey ns -> k @@ -63,11 +74,11 @@ get get sk k = do let key = prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey mRes <- rawStoreGet sk key - pure $ case mRes of - Nothing -> Nothing + case mRes of + Nothing -> pure Nothing Just raw -> case decode raw of - Left e -> error $ "Impossible codec error " <> cs e - Right a -> Just a + Left e -> throwSDKError (ParseError $ "Impossible codec error " <> cs e) + Right a -> pure $ Just a delete :: forall k ns r. @@ -88,3 +99,20 @@ prove -> Sem r (Maybe BS.ByteString) prove sk k = rawStoreProve sk $ prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey + +withTransaction + :: Members [RawStore, Error AppError] r + => Bool + -> Sem r a + -> Sem r a +withTransaction commit m = do + rawStoreBeginTransaction + runTx `catch` (\e -> rawStoreRollback *> throw e) + where + runTx = do + res <- m + if commit + then rawStoreCommit + else rawStoreRollback + pure res + diff --git a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs index 50c47c77..3d105b2e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs @@ -9,6 +9,7 @@ import Crypto.Hash.Algorithms (SHA256) import Data.ByteString (ByteString) import Data.Proxy import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) import GHC.TypeLits (KnownSymbol, symbolVal) import Polysemy (Member, Sem) import Polysemy.Error (Error, throw) @@ -19,7 +20,10 @@ import Tendermint.SDK.Crypto (RecoverableSignatureSchema, import Tendermint.SDK.Errors (AppError, SDKError (..), throwSDKError) import Tendermint.SDK.Module -import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Message (Msg (..), + ValidateMessage (..), + formatMessageSemanticError) +-- import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RawTransaction (..), RoutedTx (..), Tx (..), parseTx) @@ -46,7 +50,7 @@ instance (Member (Error AppError) r) => Router '[] r where route NilModules Tx{txRoute} = throwSDKError $ UnmatchedRoute txRoute -instance (Member (Error AppError) r, Router ms r, HasCodec msg, KnownSymbol name) => Router (Module name msg api r ': ms) r where +instance (Member (Error AppError) r, Router ms r, HasCodec msg, ValidateMessage msg, KnownSymbol name) => Router (Module name msg api r ': ms) r where route (ConsModule m rest) tx@Tx{..} | symbolVal (Proxy :: Proxy name) == cs txRoute = do msg <- case decode $ msgData txMsg of @@ -54,5 +58,9 @@ instance (Member (Error AppError) r, Router ms r, HasCodec msg, KnownSymbol name Right (msg :: msg) -> return msg let msg' = txMsg {msgData = msg} tx' = RoutedTx $ tx {txMsg = msg'} + case validateMessage msg' of + Failure err -> + throwSDKError . MessageValidation . map formatMessageSemanticError $ err + Success _ -> pure () moduleRouter m tx' | otherwise = route rest tx diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs index ebd4c4f5..7e77db98 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs @@ -93,7 +93,7 @@ nonEmptyCheck -> V.Validation [MessageSemanticError] () nonEmptyCheck fieldName x | x == mempty = V._Failure # [InvalidFieldError $ fieldName <> " must be nonempty."] - | otherwise = mempty + | otherwise = V.Success () isAuthorCheck :: Text @@ -102,4 +102,4 @@ isAuthorCheck -> V.Validation [MessageSemanticError] () isAuthorCheck fieldName Msg{msgAuthor, msgData} getAuthor | getAuthor msgData /= msgAuthor = V._Failure # [PermissionError $ fieldName <> " must be message author."] - | otherwise = mempty + | otherwise = V.Success () diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs new file mode 100644 index 00000000..6d0f6ab1 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs @@ -0,0 +1,72 @@ +{-# LANGUAGE TemplateHaskell #-} +module Tendermint.SDK.Types.TxResult where + +import Control.Lens (Lens', lens) +import Control.Lens.TH (makeLenses) +import Data.ByteArray.Base64String (Base64String) +import Data.Default.Class (Default (..)) +import Data.Int (Int64) +import Data.Text (Text) +import Network.ABCI.Types.Messages.FieldTypes (Event, WrappedVal (..)) +import qualified Network.ABCI.Types.Messages.Response as Response + +-- | This type represents a common transaction result for the CheckTx +-- | and DeliverTx abci-messages. +data TxResult = TxResult + { _txResultData :: Base64String + , _txResultInfo :: Text + , _txResultGasWanted :: Int64 + , _txResultGasUsed :: Int64 + , _txResultEvents :: [Event] + } deriving Show + +makeLenses ''TxResult + +instance Default TxResult where + def = TxResult + { _txResultData = "" + , _txResultInfo = "" + , _txResultGasWanted = 0 + , _txResultGasUsed = 0 + , _txResultEvents = [] + } + +-- | This class is used to set the 'TxResult' data into the appropriate +-- | response fields for the CheckTx abci-message. +checkTxTxResult :: Lens' Response.CheckTx TxResult +checkTxTxResult = lens g s + where + g Response.CheckTx{..} = TxResult + { _txResultData = checkTxData + , _txResultInfo = checkTxInfo + , _txResultGasWanted = unWrappedVal checkTxGasWanted + , _txResultGasUsed = unWrappedVal checkTxGasUsed + , _txResultEvents = checkTxEvents + } + s checkTx TxResult{..} = checkTx + { Response.checkTxData = _txResultData + , Response.checkTxInfo = _txResultInfo + , Response.checkTxGasWanted = WrappedVal _txResultGasWanted + , Response.checkTxGasUsed = WrappedVal _txResultGasUsed + , Response.checkTxEvents = _txResultEvents + } + +-- | This class is used to set the 'TxResult' data into the appropriate +-- | response fields for the DeliverTx abci-message. +deliverTxTxResult :: Lens' Response.DeliverTx TxResult +deliverTxTxResult = lens g s + where + g Response.DeliverTx{..} = TxResult + { _txResultData = deliverTxData + , _txResultInfo = deliverTxInfo + , _txResultGasWanted = unWrappedVal deliverTxGasWanted + , _txResultGasUsed = unWrappedVal deliverTxGasUsed + , _txResultEvents = deliverTxEvents + } + s deliverTx TxResult{..} = deliverTx + { Response.deliverTxData = _txResultData + , Response.deliverTxInfo = _txResultInfo + , Response.deliverTxGasWanted = WrappedVal _txResultGasWanted + , Response.deliverTxGasUsed = WrappedVal _txResultGasUsed + , Response.deliverTxEvents = _txResultEvents + } diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs index 76b7edf6..c470e9fb 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs @@ -6,23 +6,58 @@ import Data.ByteString (ByteString) import qualified Data.Serialize as Serialize import Data.String.Conversions (cs) import Polysemy (runM) +import Polysemy.Error (runError) import Tendermint.SDK.AuthTreeStore (AuthTree, eval, initAuthTree) import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Errors (AppError (..), + SDKError (InternalError), + throwSDKError) import Tendermint.SDK.Store (IsKey (..), RawKey (..), - StoreKey (..), get, put) + StoreKey (..), delete, get, put, + rawStoreBeginTransaction, + rawStoreRollback, + withTransaction) import Test.Hspec spec :: Spec spec = beforeAll beforeAction $ describe "AuthTreeStore" $ do it "can fail to query an empty AuthTreeStore" $ \driver -> do - mv <- runM . eval driver $ get storeKey IntStoreKey + Right mv <- runM . runError . eval driver $ get storeKey IntStoreKey mv `shouldBe` Nothing it "can set a value and query the value" $ \driver -> do - mv <- runM . eval driver $ do + Right mv <- runM . runError . eval driver $ do put storeKey IntStoreKey (IntStore 1) get storeKey IntStoreKey mv `shouldBe` Just (IntStore 1) + it "can make changes and roll back" $ \driver -> do + Right mv <- runM . runError . eval driver $ do + rawStoreBeginTransaction + put storeKey IntStoreKey (IntStore 5) + get storeKey IntStoreKey + mv `shouldBe` Just (IntStore 5) + + Right mv' <- runM . runError . eval driver $ do + delete storeKey IntStoreKey + get storeKey IntStoreKey + mv' `shouldBe` Nothing + + Right mv'' <- runM . runError . eval driver $ do + rawStoreRollback + get storeKey IntStoreKey + mv'' `shouldBe` Just (IntStore 1) + it "can roll back if an error occurs during a transaction" $ \driver -> do + Left apperr <- runM . runError . eval driver . withTransaction True $ do + put storeKey IntStoreKey (IntStore 5) + throwSDKError InternalError + appErrorCode apperr `shouldBe` 1 + Right mv <- runM . runError . eval driver $ get storeKey IntStoreKey + mv `shouldBe` Just (IntStore 1) + it "can make changes with a transaction" $ \driver -> do + Right mv <- runM . runError . eval driver . withTransaction True $ do + put storeKey IntStoreKey (IntStore 5) + get storeKey IntStoreKey + mv `shouldBe` Just (IntStore 5) beforeAction :: IO AuthTree diff --git a/hs-abci-types/src/Network/ABCI/Types/Messages/Response.hs b/hs-abci-types/src/Network/ABCI/Types/Messages/Response.hs index 6ff44fa7..1ffd69e5 100644 --- a/hs-abci-types/src/Network/ABCI/Types/Messages/Response.hs +++ b/hs-abci-types/src/Network/ABCI/Types/Messages/Response.hs @@ -21,8 +21,7 @@ import Data.Default.Class (Default (..)) import Data.Int (Int64) import Data.ProtoLens.Message (Message (defMessage)) import Data.Text (Text) -import Data.Word (Word64) -import Data.Word (Word32) +import Data.Word (Word32, Word64) import GHC.Generics (Generic) import Network.ABCI.Types.Messages.Common (defaultABCIOptions, makeABCILenses) From b336c409736f330d8e6ca4fff03830111ab870a3 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 10 Dec 2019 07:30:19 -0800 Subject: [PATCH 19/70] Separate different roots, abstract away application setup (#136) * simpler withTransaction * separate the 3 connections * wip, sdk compiles, tests not compiling * tests compile * nameservice app compiles * nameservice app compiles * all tests compile * added MergeScopes effect * try to fix test * test failing * nameservice compiles with scoped effects * update authtree tests * simple storage compiles * fail at check tx * another checktx tweak * nest store modules * added handlers module * handlers module compiles * made query router builder class * nameservice compiles * nest base app * Simple Storage compiles * tests compile * comment out failing test * added transaction signing to ss e2e --- hs-abci-examples/nameservice/app/Main.hs | 13 +- hs-abci-examples/nameservice/package.yaml | 5 +- hs-abci-examples/nameservice/src/Lib.hs | 6 - .../src/Nameservice/Application.hs | 80 +++----- .../nameservice/src/Nameservice/Handlers.hs | 132 ------------- .../src/Nameservice/Modules/Nameservice.hs | 18 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 64 +++--- .../Nameservice/Modules/Nameservice/Query.hs | 15 +- .../Nameservice/Modules/Nameservice/Router.hs | 12 +- .../Nameservice/Modules/Nameservice/Types.hs | 35 ++-- .../src/Nameservice/Modules/Token.hs | 14 +- .../src/Nameservice/Modules/Token/Keeper.hs | 52 +++-- .../src/Nameservice/Modules/Token/Messages.hs | 19 -- .../src/Nameservice/Modules/Token/Query.hs | 14 +- .../src/Nameservice/Modules/Token/Router.hs | 16 +- .../src/Nameservice/Modules/Token/Types.hs | 24 ++- .../nameservice/src/Nameservice/Server.hs | 23 +-- .../test/Nameservice/Test/E2ESpec.hs | 47 +++-- hs-abci-examples/simple-storage/app/Main.hs | 13 +- hs-abci-examples/simple-storage/package.yaml | 22 +-- .../src/SimpleStorage/Application.hs | 55 +++--- .../src/SimpleStorage/Handlers.hs | 110 ----------- .../SimpleStorage/Modules/SimpleStorage.hs | 162 +++------------- .../Modules/SimpleStorage/Keeper.hs | 39 ++++ .../Modules/SimpleStorage/Message.hs | 58 ++++++ .../Modules/SimpleStorage/Query.hs | 24 +++ .../Modules/SimpleStorage/Router.hs | 25 +++ .../Modules/SimpleStorage/Types.hs | 54 ++++++ .../src/SimpleStorage/Server.hs | 31 +-- .../simple-storage/src/SimpleStorage/Types.hs | 55 ------ .../test/SimpleStorage/Test/E2ESpec.hs | 80 ++++++-- .../test/SimpleStorage/Test/HandlersSpec.hs | 67 ------- hs-abci-sdk/package.yaml | 44 +++-- hs-abci-sdk/src/Tendermint/SDK/Aeson.hs | 9 - hs-abci-sdk/src/Tendermint/SDK/Application.hs | 57 +----- .../src/Tendermint/SDK/Application/App.hs | 29 +++ .../Tendermint/SDK/Application/Handlers.hs | 183 ++++++++++++++++++ .../src/Tendermint/SDK/Application/Module.hs | 103 ++++++++++ .../src/Tendermint/SDK/AuthTreeStore.hs | 69 ------- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 169 ++++++---------- .../src/Tendermint/SDK/BaseApp/BaseApp.hs | 72 +++++++ .../src/Tendermint/SDK/BaseApp/CoreEff.hs | 73 +++++++ .../Tendermint/SDK/{ => BaseApp}/Errors.hs | 2 +- .../Tendermint/SDK/{ => BaseApp}/Events.hs | 2 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs | 39 ++++ .../Tendermint/SDK/{ => BaseApp}/Logger.hs | 4 +- .../SDK/{ => BaseApp}/Logger/Katip.hs | 16 +- .../src/Tendermint/SDK/BaseApp/Query.hs | 41 ++++ .../SDK/{ => BaseApp}/Query/Class.hs | 33 +++- .../SDK/{ => BaseApp}/Query/Client.hs | 6 +- .../SDK/{ => BaseApp}/Query/Delayed.hs | 4 +- .../SDK/{ => BaseApp}/Query/Router.hs | 6 +- .../SDK/{ => BaseApp}/Query/Store.hs | 41 ++-- .../SDK/{ => BaseApp}/Query/Types.hs | 9 +- .../src/Tendermint/SDK/BaseApp/Store.hs | 7 + .../SDK/BaseApp/Store/AuthTreeStore.hs | 125 ++++++++++++ .../{Store.hs => BaseApp/Store/RawStore.hs} | 83 +++++--- .../src/Tendermint/SDK/BaseApp/Store/Scope.hs | 34 ++++ hs-abci-sdk/src/Tendermint/SDK/Codec.hs | 16 +- hs-abci-sdk/src/Tendermint/SDK/Gas.hs | 34 ---- hs-abci-sdk/src/Tendermint/SDK/Module.hs | 15 -- .../src/Tendermint/SDK/{ => Modules}/Auth.hs | 18 +- hs-abci-sdk/src/Tendermint/SDK/Query.hs | 47 ----- .../src/Tendermint/SDK/Subscription.hs | 57 ------ hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs | 66 ------- .../src/Tendermint/SDK/Types/Address.hs | 8 - hs-abci-sdk/src/wreq.png | Bin 0 -> 15524 bytes .../Tendermint/SDK/Test/AuthTreeStoreSpec.hs | 163 ++++++++++++---- .../test/Tendermint/SDK/Test/EventSpec.hs | 7 +- .../test/Tendermint/SDK/Test/GasSpec.hs | 14 +- stack.yaml | 3 +- stack.yaml.lock | 15 +- 72 files changed, 1635 insertions(+), 1472 deletions(-) delete mode 100644 hs-abci-examples/nameservice/src/Lib.hs delete mode 100644 hs-abci-examples/nameservice/src/Nameservice/Handlers.hs delete mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs create mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs create mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs create mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs create mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs create mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs delete mode 100644 hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/Aeson.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Application/App.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Errors.hs (99%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Events.hs (99%) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Logger.hs (82%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Logger/Katip.hs (66%) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Query/Class.hs (58%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Query/Client.hs (95%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Query/Delayed.hs (96%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Query/Router.hs (95%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Query/Store.hs (63%) rename hs-abci-sdk/src/Tendermint/SDK/{ => BaseApp}/Query/Types.hs (93%) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs rename hs-abci-sdk/src/Tendermint/SDK/{Store.hs => BaseApp/Store/RawStore.hs} (53%) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/Gas.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/Module.hs rename hs-abci-sdk/src/Tendermint/SDK/{ => Modules}/Auth.hs (85%) delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/Query.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/Subscription.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs create mode 100644 hs-abci-sdk/src/wreq.png diff --git a/hs-abci-examples/nameservice/app/Main.hs b/hs-abci-examples/nameservice/app/Main.hs index 1f30b888..b906fffd 100644 --- a/hs-abci-examples/nameservice/app/Main.hs +++ b/hs-abci-examples/nameservice/app/Main.hs @@ -1,11 +1,12 @@ module Main where -import Control.Exception (bracket) -import qualified Katip as K -import Nameservice.Application (makeAppConfig) -import Nameservice.Server (makeAndServeApplication) -import System.IO (stdout) -import Tendermint.SDK.Logger.Katip (LogConfig (..), mkLogConfig) +import Control.Exception (bracket) +import qualified Katip as K +import Nameservice.Application (makeAppConfig) +import Nameservice.Server (makeAndServeApplication) +import System.IO (stdout) +import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), + mkLogConfig) main :: IO () diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 780d5f31..30db5e5a 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -61,6 +61,8 @@ dependencies: - hs-abci-types - lens - polysemy +- polysemy-plugin +- polysemy-zoo - pretty - proto-lens - proto-lens-runtime @@ -75,6 +77,7 @@ dependencies: library: source-dirs: src ghc-options: + - -fplugin=Polysemy.Plugin - -Werror - -Wall - -Wcompat @@ -85,7 +88,6 @@ library: exposed-modules: - Nameservice.Application - Nameservice.Server - - Nameservice.Handlers - Nameservice.Modules.TypedMessage - Nameservice.Modules.Nameservice - Nameservice.Modules.Token @@ -108,6 +110,7 @@ executables: - -threaded - -rtsopts - -with-rtsopts=-N + - -fplugin=Polysemy.Plugin dependencies: - nameservice diff --git a/hs-abci-examples/nameservice/src/Lib.hs b/hs-abci-examples/nameservice/src/Lib.hs deleted file mode 100644 index d36ff271..00000000 --- a/hs-abci-examples/nameservice/src/Lib.hs +++ /dev/null @@ -1,6 +0,0 @@ -module Lib - ( someFunc - ) where - -someFunc :: IO () -someFunc = putStrLn "someFunc" diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 4bb2d6d3..7032224a 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -1,28 +1,21 @@ module Nameservice.Application ( AppConfig(..) , makeAppConfig - , Handler - , compileToBaseApp - , runHandler - , QueryApi - , queryServer - , router + , EffR + , NameserviceModules + , handlersContext ) where import Data.Proxy -import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T -import Polysemy (Sem) -import Servant.API ((:<|>) (..)) -import qualified Tendermint.SDK.Auth as A -import Tendermint.SDK.BaseApp ((:&)) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.Crypto (Secp256k1) -import qualified Tendermint.SDK.Logger.Katip as KL -import Tendermint.SDK.Module (Modules (..)) -import Tendermint.SDK.Query (QueryApplication, serve) -import qualified Tendermint.SDK.TxRouter as R -import Tendermint.SDK.Types.Transaction (RawTransaction) +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import Polysemy (Sem) +import Tendermint.SDK.Application (HandlersContext (..), + Modules (..)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.Crypto (Secp256k1) +import qualified Tendermint.SDK.Modules.Auth as A data AppConfig = AppConfig { baseAppContext :: BaseApp.Context @@ -37,34 +30,23 @@ makeAppConfig logCfg = do -------------------------------------------------------------------------------- type EffR = - N.NameserviceEffR :& T.TokenEffR :& A.AuthEffR :& BaseApp.BaseApp - -type Handler = Sem EffR - -compileToBaseApp - :: Sem EffR a - -> Sem BaseApp.BaseApp a -compileToBaseApp = A.eval . T.eval . N.eval - --- NOTE: this should probably go in the library -runHandler - :: AppConfig - -> Handler a - -> IO a -runHandler AppConfig{baseAppContext} = - BaseApp.eval baseAppContext . compileToBaseApp - --------------------------------------------------------------------------------- - -modules :: Modules '[T.TokenM EffR ,N.NameserviceM EffR] EffR -modules = ConsModule T.tokenModule $ ConsModule N.nameserviceModule NilModules - -router :: RawTransaction -> Sem EffR () -router = R.router (Proxy @Secp256k1) modules - --------------------------------------------------------------------------------- - -type QueryApi = T.Api :<|> N.Api + N.NameserviceEffs BaseApp.:& + T.TokenEffs BaseApp.:& + A.AuthEffs BaseApp.:& + BaseApp.BaseApp BaseApp.CoreEffs + +type NameserviceModules = '[T.TokenM EffR, N.NameserviceM EffR] + +handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR BaseApp.CoreEffs +handlersContext = HandlersContext + { signatureAlgP = Proxy @Secp256k1 + , modules = nameserviceModules + , compileToBaseApp = compileNameserviceToBaseApp + , compileToCore = BaseApp.compileScopedEff + } + where + nameserviceModules :: Modules NameserviceModules EffR + nameserviceModules = ConsModule T.tokenModule $ ConsModule N.nameserviceModule NilModules -queryServer :: QueryApplication (Sem BaseApp.BaseApp) -queryServer = serve (Proxy :: Proxy QueryApi) (T.server :<|> N.server) + compileNameserviceToBaseApp :: Sem EffR a -> Sem (BaseApp.BaseApp BaseApp.CoreEffs) a + compileNameserviceToBaseApp = A.eval . T.eval . N.eval diff --git a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs b/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs deleted file mode 100644 index abe2bb87..00000000 --- a/hs-abci-examples/nameservice/src/Nameservice/Handlers.hs +++ /dev/null @@ -1,132 +0,0 @@ -module Nameservice.Handlers where - -import Control.Lens (to, (&), (.~), (^.)) -import qualified Data.ByteArray.Base64String as Base64 -import Data.ByteString (ByteString) -import Data.Default.Class (def) -import Nameservice.Application (compileToBaseApp, router) -import Network.ABCI.Server.App (App (..), - MessageType (..), - Request (..), - Response (..)) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp -import Polysemy (Sem) -import Polysemy.Error (catch) -import Tendermint.SDK.Application (defaultHandler) -import Tendermint.SDK.BaseApp (BaseApp) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError, SDKError (..), - deliverTxAppError, - throwSDKError) -import Tendermint.SDK.Events (withEventBuffer) -import Tendermint.SDK.Query (QueryApplication) -import Tendermint.SDK.Store (withTransaction) -import Tendermint.SDK.Types.TxResult (TxResult, - deliverTxTxResult, - txResultEvents) - -echoH - :: Request 'MTEcho - -> Sem BaseApp (Response 'MTEcho) -echoH (RequestEcho echo) = - pure . ResponseEcho $ def & Resp._echoMessage .~ echo ^. Req._echoMessage - -flushH - :: Request 'MTFlush - -> Sem BaseApp (Response 'MTFlush) -flushH = defaultHandler - -infoH - :: Request 'MTInfo - -> Sem BaseApp (Response 'MTInfo) -infoH = defaultHandler - -setOptionH - :: Request 'MTSetOption - -> Sem BaseApp (Response 'MTSetOption) -setOptionH = defaultHandler - --- TODO: this one might be useful for initializing to 0 --- instead of doing that manually in code -initChainH - :: Request 'MTInitChain - -> Sem BaseApp (Response 'MTInitChain) -initChainH = defaultHandler - -queryH - :: QueryApplication (Sem BaseApp) - -> Request 'MTQuery - -> Sem BaseApp (Response 'MTQuery) -queryH serveRoutes (RequestQuery query) = do - queryResp <- serveRoutes query - pure $ ResponseQuery queryResp - -beginBlockH - :: Request 'MTBeginBlock - -> Sem BaseApp (Response 'MTBeginBlock) -beginBlockH = defaultHandler - --- Common function between checkTx and deliverTx -transactionHandler :: ByteString -> Sem BaseApp TxResult -transactionHandler bs = do - tx <- either (throwSDKError . ParseError) return $ decode bs - events <- withEventBuffer . compileToBaseApp $ router tx - pure $ def & txResultEvents .~ events - --- only checks to see if the tx parses -checkTxH - :: Request 'MTCheckTx - -> Sem BaseApp (Response 'MTCheckTx) -checkTxH = defaultHandler - -- let tryToRespond = withTransaction False $ do - -- txResult <- transactionHandler $ checkTx ^. Req._checkTxTx . to Base64.toBytes - -- return $ ResponseCheckTx $ def & checkTxTxResult .~ txResult - -- in tryToRespond `catch` \(err :: AppError) -> - -- return . ResponseCheckTx $ def & checkTxAppError .~ err - - -deliverTxH - :: Request 'MTDeliverTx - -> Sem BaseApp (Response 'MTDeliverTx) -- Sem BaseApp (Response 'MTDeliverTx) -deliverTxH (RequestDeliverTx deliverTx) = - let tryToRespond = withTransaction True $ do - txResult <- transactionHandler $ deliverTx ^. Req._deliverTxTx . to Base64.toBytes - return $ ResponseDeliverTx $ def & deliverTxTxResult .~ txResult - in tryToRespond `catch` \(err :: AppError) -> - return . ResponseDeliverTx $ def & deliverTxAppError .~ err - - - --case decodeAppTxMessage $ deliverTx ^. Req._deliverTxTx . to convert of - -- Left _ -> return . ResponseDeliverTx $ - -- def & Resp._deliverTxCode .~ 1 - -- Right (ATMUpdateCount updateCountTx) -> do - -- let count = SS.Count $ updateCountTxCount updateCountTx - -- events <- withEventBuffer $ putCount count - -- return $ ResponseDeliverTx $ - -- def & Resp._deliverTxCode .~ 0 - -- & Resp._deliverTxEvents .~ events - -endBlockH - :: Request 'MTEndBlock - -> Sem BaseApp (Response 'MTEndBlock) -endBlockH = defaultHandler - -commitH - :: Request 'MTCommit - -> Sem BaseApp (Response 'MTCommit) -commitH = defaultHandler - -nameserviceApp :: QueryApplication (Sem BaseApp) -> App (Sem BaseApp) -nameserviceApp serveRoutes = App $ \case - msg@(RequestEcho _) -> echoH msg - msg@(RequestFlush _) -> flushH msg - msg@(RequestInfo _) -> infoH msg - msg@(RequestSetOption _) -> setOptionH msg - msg@(RequestInitChain _) -> initChainH msg - msg@(RequestQuery _) -> queryH serveRoutes msg - msg@(RequestBeginBlock _) -> beginBlockH msg - msg@(RequestCheckTx _) -> checkTxH msg - msg@(RequestDeliverTx _) -> deliverTxH msg - msg@(RequestEndBlock _) -> endBlockH msg - msg@(RequestCommit _) -> commitH msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index 57017a69..bce0670d 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -8,7 +8,7 @@ module Nameservice.Modules.Nameservice -- * types , Name(..) , Whois (..) - , NameserviceException(..) + , NameserviceError(..) , NameserviceMessage(..) , NameClaimed(..) , NameRemapped(..) @@ -18,8 +18,7 @@ module Nameservice.Modules.Nameservice , DeleteName(..) -- * effects - , NameserviceEffR - , HasNameserviceEff + , NameserviceEffs , getWhois , buyName , setName @@ -42,16 +41,17 @@ import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types -import Nameservice.Modules.Token (HasTokenEff) -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Module (Module (..)) +import Nameservice.Modules.Token (TokenEffs) +import Polysemy (Members) +import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.BaseApp (BaseAppEffs) type NameserviceM r = Module "nameservice" NameserviceMessage Api r nameserviceModule - :: HasBaseAppEff r - => HasTokenEff r - => HasNameserviceEff r + :: Members BaseAppEffs r + => Members TokenEffs r + => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module { moduleRouter = router diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 58083358..df9ad693 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -7,17 +7,14 @@ import Data.String.Conversions (cs) import GHC.TypeLits (symbolVal) import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Types -import Nameservice.Modules.Token (HasTokenEff, burn, - mint, transfer) +import Nameservice.Modules.Token (Token, TokenEffs, + burn, mint, transfer) import Polysemy (Member, Members, Sem, interpret, makeSem) import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Errors (IsAppError (..)) -import Tendermint.SDK.Events (Event, emit) -import qualified Tendermint.SDK.Store as Store +import qualified Tendermint.SDK.BaseApp as BaseApp data Nameservice m a where PutWhois :: Name -> Whois -> Nameservice m () @@ -26,37 +23,35 @@ data Nameservice m a where makeSem ''Nameservice -type NameserviceEffR = '[Nameservice, Error NameserviceException] -type HasNameserviceEff r = (Members NameserviceEffR r, Member (Output Event) r) +type NameserviceEffs = '[Nameservice, Error NameserviceError] -storeKey :: Store.StoreKey NameserviceModule -storeKey = Store.StoreKey . cs . symbolVal $ (Proxy :: Proxy NameserviceModule) +storeKey :: BaseApp.StoreKey NameserviceModule +storeKey = BaseApp.StoreKey . cs . symbolVal $ (Proxy :: Proxy NameserviceModule) eval - :: HasBaseAppEff r - => Sem (Nameservice ': Error NameserviceException ': r) a + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => forall a. Sem (Nameservice ': Error NameserviceError ': r) a -> Sem r a -eval = mapError makeAppError . evalNameservice +eval = mapError BaseApp.makeAppError . evalNameservice where evalNameservice - :: HasBaseAppEff r - => Sem (Nameservice ': r) a - -> Sem r a + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => Sem (Nameservice ': r) a -> Sem r a evalNameservice = interpret (\case GetWhois name -> - Store.get storeKey name + BaseApp.get storeKey name PutWhois name whois -> - Store.put storeKey name whois + BaseApp.put storeKey name whois DeleteWhois name -> - Store.delete storeKey name + BaseApp.delete storeKey name ) -------------------------------------------------------------------------------- setName - :: HasTokenEff r - => HasNameserviceEff r + :: Member (Output BaseApp.Event) r + => Members NameserviceEffs r => SetName -> Sem r () setName SetName{..} = do @@ -68,15 +63,15 @@ setName SetName{..} = do then throw $ UnauthorizedSet "Setter must be the owner of the Name." else do putWhois setNameName currentWhois {whoisValue = setNameValue} - emit NameRemapped + BaseApp.emit NameRemapped { nameRemappedName = setNameName , nameRemappedNewValue = setNameValue , nameRemappedOldValue = whoisValue } deleteName - :: HasTokenEff r - => HasNameserviceEff r + :: Members [Token, Output BaseApp.Event] r + => Members NameserviceEffs r => DeleteName -> Sem r () deleteName DeleteName{..} = do @@ -89,14 +84,15 @@ deleteName DeleteName{..} = do else do mint deleteNameOwner whoisPrice deleteWhois deleteNameName - emit NameDeleted + BaseApp.emit NameDeleted { nameDeletedName = deleteNameName } buyName - :: HasTokenEff r - => HasNameserviceEff r + :: Member (Output BaseApp.Event) r + => Members TokenEffs r + => Members NameserviceEffs r => BuyName -> Sem r () -- ^ did it succeed @@ -112,8 +108,9 @@ buyName msg = do Just whois -> buyClaimedName msg whois where buyUnclaimedName - :: HasTokenEff r - => HasNameserviceEff r + :: Member (Output BaseApp.Event) r + => Members TokenEffs r + => Members NameserviceEffs r => BuyName -> Sem r () buyUnclaimedName BuyName{..} = do @@ -124,7 +121,7 @@ buyName msg = do , whoisPrice = buyNameBid } putWhois buyNameName whois - emit NameClaimed + BaseApp.emit NameClaimed { nameClaimedOwner = buyNameBuyer , nameClaimedName = buyNameName , nameClaimedValue = buyNameValue @@ -132,8 +129,9 @@ buyName msg = do } buyClaimedName - :: HasNameserviceEff r - => HasTokenEff r + :: Members NameserviceEffs r + => Members TokenEffs r + => Member (Output BaseApp.Event) r => BuyName -> Whois -> Sem r () @@ -147,7 +145,7 @@ buyName msg = do , whoisPrice = buyNameBid , whoisValue = buyNameValue } - emit NameClaimed + BaseApp.emit NameClaimed { nameClaimedOwner = buyNameBuyer , nameClaimedName = buyNameName , nameClaimedValue = buyNameValue diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index a9b7f48e..3c00ac49 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -3,12 +3,10 @@ module Nameservice.Modules.Nameservice.Query where import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Name, Whois) -import Polysemy (Member, Sem) +import Polysemy (Members, Sem) import Polysemy.Error (Error) import Servant.API ((:>)) -import Tendermint.SDK.Errors (AppError) -import qualified Tendermint.SDK.Query as Q -import qualified Tendermint.SDK.Store as Store +import qualified Tendermint.SDK.BaseApp as BaseApp -------------------------------------------------------------------------------- -- | Query API @@ -16,11 +14,10 @@ import qualified Tendermint.SDK.Store as Store type NameserviceContents = '[(Name, Whois)] -type Api = "nameservice" :> Q.QueryApi NameserviceContents +type Api = "nameservice" :> BaseApp.QueryApi NameserviceContents server - :: Member Store.RawStore r - => Member (Error AppError) r - => Q.RouteT Api (Sem r) + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => BaseApp.RouteT Api (Sem r) server = - Q.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) + BaseApp.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 1de7c6fb..0d503fcb 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -1,18 +1,20 @@ module Nameservice.Modules.Nameservice.Router where -import Nameservice.Modules.Nameservice.Keeper (HasNameserviceEff, +import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, buyName, deleteName, setName) import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) -import Nameservice.Modules.Token (HasTokenEff) -import Polysemy (Sem) +import Nameservice.Modules.Token (TokenEffs) +import Polysemy (Members, Sem) +import Tendermint.SDK.BaseApp (BaseAppEffs) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) router - :: HasTokenEff r - => HasNameserviceEff r + :: Members TokenEffs r + => Members NameserviceEffs r + => Members BaseAppEffs r => RoutedTx NameserviceMessage -> Sem r () router (RoutedTx Tx{txMsg}) = diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index bcd95af1..90bbac3e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -16,11 +16,8 @@ import Proto3.Suite (HasDefault, Message, import qualified Proto3.Suite.DotProto as DotProto import qualified Proto3.Wire.Decode as Decode import qualified Proto3.Wire.Encode as Encode +import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Events (FromEvent (..), ToEvent (..)) -import qualified Tendermint.SDK.Query as Q -import qualified Tendermint.SDK.Store as Store import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- @@ -37,7 +34,7 @@ instance Primitive Name where instance HasDefault Name instance MessageField Name -instance Q.FromQueryData Name +instance BaseApp.FromQueryData Name data Whois = Whois { whoisValue :: Text @@ -51,39 +48,39 @@ instance HasCodec Whois where encode = cs . toLazyByteString decode = first (cs . show) . fromByteString -instance Store.RawKey Name where +instance BaseApp.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) -instance Store.IsKey Name NameserviceModule where +instance BaseApp.IsKey Name NameserviceModule where type Value Name NameserviceModule = Whois -instance Q.Queryable Whois where +instance BaseApp.Queryable Whois where type Name Whois = "whois" -------------------------------------------------------------------------------- -- Exceptions -------------------------------------------------------------------------------- -data NameserviceException = +data NameserviceError = InsufficientBid Text | UnauthorizedSet Text | InvalidDelete Text -instance IsAppError NameserviceException where +instance BaseApp.IsAppError NameserviceError where makeAppError (InsufficientBid msg) = - AppError + BaseApp.AppError { appErrorCode = 1 , appErrorCodespace = "nameservice" , appErrorMessage = msg } makeAppError (UnauthorizedSet msg) = - AppError + BaseApp.AppError { appErrorCode = 2 , appErrorCodespace = "nameservice" , appErrorMessage = msg } makeAppError (InvalidDelete msg) = - AppError + BaseApp.AppError { appErrorCode = 3 , appErrorCodespace = "nameservice" , appErrorMessage = msg @@ -107,9 +104,9 @@ instance ToJSON NameClaimed where toJSON = A.genericToJSON nameClaimedAesonOptions instance FromJSON NameClaimed where parseJSON = A.genericParseJSON nameClaimedAesonOptions -instance ToEvent NameClaimed where +instance BaseApp.ToEvent NameClaimed where makeEventType _ = "NameClaimed" -instance FromEvent NameClaimed +instance BaseApp.FromEvent NameClaimed data NameRemapped = NameRemapped { nameRemappedName :: Name @@ -124,9 +121,9 @@ instance ToJSON NameRemapped where toJSON = A.genericToJSON nameRemappedAesonOptions instance FromJSON NameRemapped where parseJSON = A.genericParseJSON nameRemappedAesonOptions -instance ToEvent NameRemapped where +instance BaseApp.ToEvent NameRemapped where makeEventType _ = "NameRemapped" -instance FromEvent NameRemapped +instance BaseApp.FromEvent NameRemapped data NameDeleted = NameDeleted { nameDeletedName :: Name @@ -139,6 +136,6 @@ instance ToJSON NameDeleted where toJSON = A.genericToJSON nameDeletedAesonOptions instance FromJSON NameDeleted where parseJSON = A.genericParseJSON nameDeletedAesonOptions -instance ToEvent NameDeleted where +instance BaseApp.ToEvent NameDeleted where makeEventType _ = "NameDeleted" -instance FromEvent NameDeleted +instance BaseApp.FromEvent NameDeleted diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 7152eff8..4b87de77 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -7,13 +7,12 @@ module Nameservice.Modules.Token -- * types , Address , Amount(..) - , TokenException(..) + , TokenError(..) , Transfer(..) -- * effects , Token - , TokenEffR - , HasTokenEff + , TokenEffs , Faucetted(..) , TransferEvent(..) , FaucetAccount(..) @@ -40,15 +39,16 @@ import Nameservice.Modules.Token.Messages import Nameservice.Modules.Token.Query import Nameservice.Modules.Token.Router import Nameservice.Modules.Token.Types -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Module (Module (..)) +import Polysemy (Members) +import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.BaseApp (BaseAppEffs) import Tendermint.SDK.Types.Address (Address) type TokenM r = Module "token" TokenMessage Api r tokenModule - :: HasBaseAppEff r - => HasTokenEff r + :: Members BaseAppEffs r + => Members TokenEffs r => TokenM r tokenModule = Module { moduleRouter = router diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs index 085ed259..65d498e7 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs @@ -6,57 +6,51 @@ import Data.Maybe (fromMaybe) import Nameservice.Modules.Token.Messages (FaucetAccount (..)) import Nameservice.Modules.Token.Types (Amount (..), Faucetted (..), - TokenException (..), + TokenError (..), TransferEvent (..)) import Polysemy import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Errors (IsAppError (..)) -import Tendermint.SDK.Events (Event, emit) -import qualified Tendermint.SDK.Store as Store +import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Types.Address (Address) - data Token m a where PutBalance :: Address -> Amount -> Token m () GetBalance' :: Address -> Token m (Maybe Amount) makeSem ''Token -type TokenEffR = '[Token, Error TokenException] -type HasTokenEff r = (Members TokenEffR r, Member (Output Event) r) +type TokenEffs = '[Token, Error TokenError] -storeKey :: Store.StoreKey "token" -storeKey = Store.StoreKey "token" +storeKey :: BaseApp.StoreKey "token" +storeKey = BaseApp.StoreKey "token" eval - :: HasBaseAppEff r - => Sem (Token ': Error TokenException ': r) a - -> Sem r a -eval = mapError makeAppError . evalToken + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => forall a. Sem (Token ': Error TokenError ': r) a -> Sem r a +eval = mapError BaseApp.makeAppError . evalToken where evalToken - :: HasBaseAppEff r - => Sem (Token ': r) a - -> Sem r a + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => forall a. Sem (Token ': r) a -> Sem r a evalToken = - interpret (\case - GetBalance' address -> - Store.get storeKey address - PutBalance address balance -> - Store.put storeKey address balance - ) + interpret + (\case + GetBalance' address -> + BaseApp.get storeKey address + PutBalance address balance -> + BaseApp.put storeKey address balance + ) -------------------------------------------------------------------------------- faucetAccount - :: HasTokenEff r + :: Members [Error TokenError, Output BaseApp.Event, Token] r => FaucetAccount -> Sem r () faucetAccount FaucetAccount{..} = do mint faucetAccountTo faucetAccountAmount - emit Faucetted + BaseApp.emit Faucetted { faucettedAccount = faucetAccountTo , faucettedAmount = faucetAccountAmount } @@ -69,7 +63,7 @@ getBalance address = fromMaybe (Amount 0) <$> getBalance' address transfer - :: HasTokenEff r + :: Members [Error TokenError, Output BaseApp.Event, Token] r => Address -> Amount -> Address @@ -85,7 +79,7 @@ transfer addr1 amount addr2 = do -- update both balances putBalance addr1 newBalance1 putBalance addr2 newBalance2 - emit $ TransferEvent + BaseApp.emit $ TransferEvent { transferEventAmount = amount , transferEventTo = addr2 , transferEventFrom = addr1 @@ -93,7 +87,7 @@ transfer addr1 amount addr2 = do else throw (InsufficientFunds "Insufficient funds for transfer.") burn - :: Members '[Token, Error TokenException] r + :: Members [Error TokenError, Token] r => Address -> Amount -> Sem r () @@ -104,7 +98,7 @@ burn addr amount = do else putBalance addr (bal - amount) mint - :: Members '[Token, Error TokenException] r + :: Member Token r => Address -> Amount -> Sem r () diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs index 67717417..9ce3c885 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs @@ -20,7 +20,6 @@ data TokenMessage = TTransfer Transfer | TFaucetAccount FaucetAccount | TBurn Burn - | TMint Mint deriving (Eq, Show, Generic) data FaucetAccount = FaucetAccount @@ -60,31 +59,17 @@ instance HasCodec Burn where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -data Mint = Mint - { mintAmount :: Amount - , mintAddress :: Address - } deriving (Eq, Show, Generic) - -instance Message Mint -instance Named Mint - -instance HasCodec Mint where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString - instance HasCodec TokenMessage where decode bs = do TypedMessage{..} <- decode bs case typedMessageType of "Transfer" -> TTransfer <$> decode typedMessageContents "Burn" -> TBurn <$> decode typedMessageContents - "Mint" -> TMint <$> decode typedMessageContents "FaucetAccount" -> TFaucetAccount <$> decode typedMessageContents _ -> Left . cs $ "Unknown Token message type " ++ cs typedMessageType encode = \case TTransfer msg -> encode msg TBurn msg -> encode msg - TMint msg -> encode msg TFaucetAccount msg -> encode msg instance ValidateMessage TokenMessage where @@ -92,7 +77,6 @@ instance ValidateMessage TokenMessage where TTransfer msg -> validateMessage m {msgData = msg} TFaucetAccount msg -> validateMessage m {msgData = msg} TBurn msg -> validateMessage m {msgData = msg} - TMint msg -> validateMessage m {msgData = msg} instance ValidateMessage Transfer where validateMessage _ = Success () @@ -102,6 +86,3 @@ instance ValidateMessage FaucetAccount where instance ValidateMessage Burn where validateMessage _ = Success () - -instance ValidateMessage Mint where - validateMessage _ = Success () diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs index fdd16092..6e1cea20 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs @@ -5,11 +5,7 @@ import Nameservice.Modules.Token.Keeper (storeKey) import Nameservice.Modules.Token.Types (Amount) import Polysemy import Polysemy.Error (Error) -import Servant.API ((:>)) -import Tendermint.SDK.Errors (AppError) -import Tendermint.SDK.Query (RouteT) -import Tendermint.SDK.Query.Store (QueryApi, storeQueryHandlers) -import qualified Tendermint.SDK.Store as Store +import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- @@ -18,8 +14,10 @@ import Tendermint.SDK.Types.Address (Address) type TokenContents = '[(Address, Amount)] -type Api = "token" :> QueryApi TokenContents +type Api = BaseApp.QueryApi TokenContents -server :: Member Store.RawStore r => Member (Error AppError) r => RouteT Api (Sem r) +server + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => BaseApp.RouteT Api (Sem r) server = - storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) + BaseApp.storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs index eced4eeb..38e8db31 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs @@ -1,17 +1,19 @@ module Nameservice.Modules.Token.Router where -import Nameservice.Modules.Token.Keeper (HasTokenEff, burn, - faucetAccount, mint, - transfer) -import Nameservice.Modules.Token.Messages (Burn (..), Mint (..), +import Nameservice.Modules.Token.Keeper (TokenEffs, burn, + faucetAccount, transfer) +import Nameservice.Modules.Token.Messages (Burn (..), TokenMessage (..), Transfer (..)) -import Polysemy (Sem) +import Polysemy (Member, Members, Sem) +import Polysemy.Output (Output) +import Tendermint.SDK.BaseApp (Event) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) router - :: HasTokenEff r + :: Members TokenEffs r + => Member (Output Event) r => RoutedTx TokenMessage -> Sem r () router (RoutedTx Tx{txMsg}) = @@ -23,5 +25,3 @@ router (RoutedTx Tx{txMsg}) = transfer transferFrom transferAmount transferTo TBurn Burn{..} -> burn burnAddress burnAmount - TMint Mint{..} -> - mint mintAddress mintAmount diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs index 1f6006cc..a575cb5a 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs @@ -16,11 +16,8 @@ import qualified Proto3.Suite.DotProto as DotProto import qualified Proto3.Wire.Decode as Decode import qualified Proto3.Wire.Encode as Encode import Proto3.Wire.Types (fieldNumber) +import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Events (FromEvent, ToEvent (..)) -import Tendermint.SDK.Query (Queryable (..)) -import qualified Tendermint.SDK.Store as Store import Tendermint.SDK.Types.Address (Address, addressFromBytes, addressToBytes) @@ -31,6 +28,7 @@ type TokenModule = "token" -------------------------------------------------------------------------------- newtype Amount = Amount Word64 deriving (Eq, Show, Num, Generic, Ord, A.ToJSON, A.FromJSON) + instance Primitive Amount where encodePrimitive n (Amount amt) = Encode.uint64 n amt decodePrimitive = Amount <$> Decode.uint64 @@ -38,7 +36,7 @@ instance Primitive Amount where instance HasDefault Amount instance MessageField Amount -instance Queryable Amount where +instance BaseApp.Queryable Amount where type Name Amount = "balance" -- @NOTE: hacks @@ -62,19 +60,19 @@ instance HasDefault Hex.HexString instance HasDefault Address instance MessageField Address -instance Store.IsKey Address "token" where +instance BaseApp.IsKey Address "token" where type Value Address "token" = Amount -------------------------------------------------------------------------------- -- Exceptions -------------------------------------------------------------------------------- -data TokenException = +data TokenError = InsufficientFunds Text -instance IsAppError TokenException where +instance BaseApp.IsAppError TokenError where makeAppError (InsufficientFunds msg) = - AppError + BaseApp.AppError { appErrorCode = 1 , appErrorCodespace = "token" , appErrorMessage = msg @@ -96,9 +94,9 @@ instance ToJSON Faucetted where toJSON = A.genericToJSON faucettedAesonOptions instance FromJSON Faucetted where parseJSON = A.genericParseJSON faucettedAesonOptions -instance ToEvent Faucetted where +instance BaseApp.ToEvent Faucetted where makeEventType _ = "Faucetted" -instance FromEvent Faucetted +instance BaseApp.FromEvent Faucetted data TransferEvent = TransferEvent { transferEventAmount :: Amount @@ -115,7 +113,7 @@ instance A.ToJSON TransferEvent where instance A.FromJSON TransferEvent where parseJSON = A.genericParseJSON transferEventAesonOptions -instance ToEvent TransferEvent where +instance BaseApp.ToEvent TransferEvent where makeEventType _ = "TransferEvent" -instance FromEvent TransferEvent +instance BaseApp.FromEvent TransferEvent diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index b87e9cc1..6dbafa75 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -2,31 +2,24 @@ module Nameservice.Server (makeAndServeApplication) where import Data.Foldable (fold) import Data.Monoid (Endo (..)) -import Data.Proxy import Nameservice.Application (AppConfig (..), - queryServer) -import Nameservice.Handlers (nameserviceApp) + handlersContext) import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) import qualified Network.ABCI.Server.Middleware.RequestLogger as ReqLogger import qualified Network.ABCI.Server.Middleware.ResponseLogger as ResLogger import Polysemy (Sem) -import Tendermint.SDK.Application (MakeApplication (..), - createApplication) -import Tendermint.SDK.BaseApp (BaseApp, eval) -import Tendermint.SDK.Errors (AppError) +import Tendermint.SDK.Application (createIOApp, + makeApp) +import Tendermint.SDK.BaseApp (CoreEffs, + runCoreEffs) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication cfg = do - let makeApplication :: MakeApplication (Sem BaseApp) AppError - makeApplication = MakeApplication - { transformer = eval $ baseAppContext cfg - , appErrorP = Proxy - , app = nameserviceApp queryServer - , initialize = [] - } putStrLn "Starting ABCI application..." - application <- createApplication makeApplication + let nat :: forall a. Sem CoreEffs a -> IO a + nat = runCoreEffs $ baseAppContext cfg + application = createIOApp nat $ makeApp handlersContext serveApp =<< hookInMiddleware application where mkMiddleware :: IO (Middleware IO) diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 43d92c72..3faf5018 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -19,7 +19,6 @@ import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word (Word32) -import Nameservice.Application (QueryApi) import Nameservice.Modules.Nameservice (BuyName (..), DeleteName (..), Name (..), @@ -28,26 +27,30 @@ import Nameservice.Modules.Nameservice (BuyName (..), NameRemapped (..), SetName (..), Whois (..)) +import qualified Nameservice.Modules.Nameservice as N (Api) import Nameservice.Modules.Token (Amount (..), FaucetAccount (..), Faucetted (..), Transfer (..), TransferEvent (..)) +import qualified Nameservice.Modules.Token as T (Api) import Nameservice.Modules.TypedMessage (TypedMessage (..)) import Network.ABCI.Types.Messages.FieldTypes (Event (..)) import qualified Network.ABCI.Types.Messages.Response as Response import qualified Network.Tendermint.Client as RPC import Proto3.Suite (Message, toLazyByteString) -import Servant.API ((:<|>) (..)) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp (FromEvent (..), + QueryApi) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + defaultQueryWithData) +import Tendermint.SDK.BaseApp.Query.Client (ClientResponse (..), + HasClient (..), + RunClient (..)) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Crypto (Secp256k1, addressFromPubKey) -import Tendermint.SDK.Events (FromEvent (..)) -import Tendermint.SDK.Query.Client (ClientResponse (..), - genClient) -import Tendermint.SDK.Query.Types (QueryArgs (..), - defaultQueryWithData) import Tendermint.SDK.Types.Address (Address (..)) import Tendermint.SDK.Types.Transaction (RawTransaction (..), signRawTransaction) @@ -113,8 +116,8 @@ spec = do -- try to set a name without being the owner let msg = TypedMessage "SetName" (encode $ SetName satoshi addr2 "goodbye to a world") rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg - deliverResp <- getDeliverTxResponse rawTx - ensureDeliverResponseCode deliverResp 2 + checkResp <- getCheckTxResponse rawTx + ensureCheckResponseCode checkResp 2 it "Can buy an existing name (success 0)" $ do let oldVal = "goodbye to a world" @@ -160,8 +163,8 @@ spec = do -- try to buy at a lower price let msg = TypedMessage "BuyName" (encode $ BuyName 100 satoshi "hello (again) world" addr1) rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg - deliverResp <- getDeliverTxResponse rawTx - ensureDeliverResponseCode deliverResp 1 + checkResp <- getCheckTxResponse rawTx + ensureCheckResponseCode checkResp 1 it "Can delete names (success 0)" $ do let msg = TypedMessage "DeleteName" (encode $ DeleteName addr2 satoshi) @@ -181,8 +184,8 @@ spec = do it "Can fail a transfer (failure 1)" $ do let msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 2000) rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg - deliverResp <- getDeliverTxResponse rawTx - ensureDeliverResponseCode deliverResp 1 + checkResp <- getCheckTxResponse rawTx + ensureCheckResponseCode checkResp 1 it "Can transfer (success 0)" $ do let senderBeforeQueryReq = defaultQueryWithData addr2 @@ -229,6 +232,13 @@ getQueryResponseSuccess query = do responseCode `shouldBe` 0 return . fromJust $ clientResponseData +-- executes a request, then returns the checkTx response +getCheckTxResponse :: RawTransaction -> IO Response.CheckTx +getCheckTxResponse rawTx = do + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + fmap RPC.resultBroadcastTxCommitCheckTx . runRPC $ + RPC.broadcastTxCommit txReq + -- executes a request, then returns the deliverTx response getDeliverTxResponse :: RawTransaction -> IO Response.DeliverTx getDeliverTxResponse rawTx = do @@ -250,6 +260,12 @@ ensureEventLogged deliverResp eventName expectedEvent = do errs `shouldBe` mempty events `shouldSatisfy` elem expectedEvent +-- check for a specific check response code +ensureCheckResponseCode :: Response.CheckTx -> Word32 -> IO () +ensureCheckResponseCode checkResp code = do + let checkRespCode = checkResp ^. Response._checkTxCode + checkRespCode `shouldBe` code + -- check for a specific deliver response code ensureDeliverResponseCode :: Response.DeliverTx -> Word32 -> IO () ensureDeliverResponseCode deliverResp code = do @@ -303,5 +319,8 @@ algProxy = Proxy getWhois :: QueryArgs Name -> RPC.TendermintM (ClientResponse Whois) getBalance :: QueryArgs Address -> RPC.TendermintM (ClientResponse Amount) +apiP :: Proxy ("token" :> T.Api :<|> ("nameservice" :> N.Api)) +apiP = Proxy + (getBalance :<|> getWhois) = - genClient (Proxy :: Proxy RPC.TendermintM) (Proxy :: Proxy QueryApi) def + genClient (Proxy :: Proxy RPC.TendermintM) apiP def diff --git a/hs-abci-examples/simple-storage/app/Main.hs b/hs-abci-examples/simple-storage/app/Main.hs index 0183034f..0dfd1190 100644 --- a/hs-abci-examples/simple-storage/app/Main.hs +++ b/hs-abci-examples/simple-storage/app/Main.hs @@ -1,11 +1,12 @@ module Main where -import Control.Exception (bracket) -import qualified Katip as K -import SimpleStorage.Application (makeAppConfig) -import SimpleStorage.Server (makeAndServeApplication) -import System.IO (stdout) -import Tendermint.SDK.Logger.Katip (LogConfig (..), mkLogConfig) +import Control.Exception (bracket) +import qualified Katip as K +import SimpleStorage.Application (makeAppConfig) +import SimpleStorage.Server (makeAndServeApplication) +import System.IO (stdout) +import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), + mkLogConfig) main :: IO () diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index 98407bdd..ee5a77d8 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -40,27 +40,18 @@ default-extensions: dependencies: - aeson -- async -- avl-auth - base >= 4.7 && < 5 - bytestring - cereal - cereal-text -- containers -- conduit - cryptonite -- data-default-class - errors - exceptions -- free - hs-abci-extra - hs-abci-server - hs-abci-types - hs-abci-extra - hs-abci-sdk -- http-types -- kan-extensions -- katip - lens - memory - mtl @@ -69,10 +60,9 @@ dependencies: - proto-lens - proto-lens-runtime - servant -- stm - string-conversions - text -- uri-bytestring +- validation custom-setup: dependencies: @@ -90,8 +80,12 @@ library: exposed-modules: - SimpleStorage.Server - SimpleStorage.Application - - SimpleStorage.Handlers - SimpleStorage.Modules.SimpleStorage + - SimpleStorage.Modules.SimpleStorage.Types + - SimpleStorage.Modules.SimpleStorage.Message + - SimpleStorage.Modules.SimpleStorage.Keeper + - SimpleStorage.Modules.SimpleStorage.Router + - SimpleStorage.Modules.SimpleStorage.Query - SimpleStorage.Types generated-exposed-modules: @@ -112,6 +106,7 @@ executables: - -Wincomplete-uni-patterns - -Wredundant-constraints dependencies: + - katip - simple-storage tests: @@ -120,7 +115,6 @@ tests: source-dirs: test other-modules: - SimpleStorage.Test.E2ESpec - - SimpleStorage.Test.HandlersSpec ghc-options: - -Werror - -Wall @@ -130,8 +124,10 @@ tests: dependencies: - aeson - aeson-pretty + - data-default-class - simple-storage - hs-abci-types - hs-tendermint-client - hspec - QuickCheck + - secp256k1-haskell diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index 88d53c90..7ad38a79 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -1,18 +1,19 @@ module SimpleStorage.Application - ( AppError(..) - , AppConfig(..) + ( AppConfig(..) , makeAppConfig - , Handler - , runHandler + , SimpelStorageEffs + , handlersContext ) where -import Control.Exception (Exception) -import Control.Monad.Catch (throwM) +import Data.Proxy import Polysemy (Sem) -import Polysemy.Error (Error, runError) import SimpleStorage.Modules.SimpleStorage as SimpleStorage +import Tendermint.SDK.Application (HandlersContext (..), + Modules (..)) import qualified Tendermint.SDK.BaseApp as BaseApp -import qualified Tendermint.SDK.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.Crypto (Secp256k1) +import qualified Tendermint.SDK.Modules.Auth as A data AppConfig = AppConfig { baseAppContext :: BaseApp.Context @@ -26,28 +27,22 @@ makeAppConfig logCfg = do -------------------------------------------------------------------------------- -data AppError = AppError String deriving (Show) +type SimpelStorageEffs = + SimpleStorage.SimpleStorage ': A.AuthEffs BaseApp.:& BaseApp.BaseApp BaseApp.CoreEffs -instance Exception AppError +type SimpleStorageModules = + '[SimpleStorage.SimpleStorageM SimpelStorageEffs] -type EffR = - ( SimpleStorage - ': Error AppError - ': BaseApp.BaseApp - ) - -type Handler = Sem EffR - --- NOTE: this should probably go in the library -runHandler - :: AppConfig - -> Handler a - -> IO a -runHandler AppConfig{baseAppContext} m = do - eRes <- BaseApp.eval baseAppContext . - runError . - SimpleStorage.eval $ m - case eRes of - Left e -> throwM e - Right a -> pure a +handlersContext :: HandlersContext Secp256k1 SimpleStorageModules SimpelStorageEffs BaseApp.CoreEffs +handlersContext = HandlersContext + { signatureAlgP = Proxy @Secp256k1 + , modules = simpleStorageModules + , compileToBaseApp = compileSimpleStorageToBaseApp + , compileToCore = BaseApp.compileScopedEff + } + where + simpleStorageModules :: Modules SimpleStorageModules SimpelStorageEffs + simpleStorageModules = ConsModule SimpleStorage.simpleStorageModule NilModules + compileSimpleStorageToBaseApp :: Sem SimpelStorageEffs a -> Sem (BaseApp.BaseApp BaseApp.CoreEffs) a + compileSimpleStorageToBaseApp = A.eval . SimpleStorage.eval diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs deleted file mode 100644 index fa99a25c..00000000 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Handlers.hs +++ /dev/null @@ -1,110 +0,0 @@ -module SimpleStorage.Handlers where - -import Control.Lens (to, (&), (.~), (^.)) -import Data.ByteArray (convert) -import Data.Default.Class (def) -import Network.ABCI.Server.App (App (..), - MessageType (..), - Request (..), - Response (..)) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp -import SimpleStorage.Application (Handler) -import SimpleStorage.Modules.SimpleStorage as SS -import SimpleStorage.Types (AppTxMessage (..), - UpdateCountTx (..), - decodeAppTxMessage) -import Tendermint.SDK.Application (defaultHandler) -import Tendermint.SDK.Events (withEventBuffer) -import Tendermint.SDK.Query (QueryApplication) -import Tendermint.SDK.Store (withTransaction) -import Tendermint.SDK.Types.TxResult (deliverTxTxResult, - txResultEvents) - -echoH - :: Request 'MTEcho - -> Handler (Response 'MTEcho) -echoH (RequestEcho echo) = - pure . ResponseEcho $ def & Resp._echoMessage .~ echo ^. Req._echoMessage - -flushH - :: Request 'MTFlush - -> Handler (Response 'MTFlush) -flushH = defaultHandler - -infoH - :: Request 'MTInfo - -> Handler (Response 'MTInfo) -infoH = defaultHandler - -setOptionH - :: Request 'MTSetOption - -> Handler (Response 'MTSetOption) -setOptionH = defaultHandler - --- TODO: this one might be useful for initializing to 0 --- instead of doing that manually in code -initChainH - :: Request 'MTInitChain - -> Handler (Response 'MTInitChain) -initChainH = defaultHandler - -queryH - :: QueryApplication Handler - -> Request 'MTQuery - -> Handler (Response 'MTQuery) -queryH serveRoutes (RequestQuery query) = do - queryResp <- serveRoutes query - pure $ ResponseQuery queryResp - -beginBlockH - :: Request 'MTBeginBlock - -> Handler (Response 'MTBeginBlock) -beginBlockH = defaultHandler - --- only checks to see if the tx parses -checkTxH - :: Request 'MTCheckTx - -> Handler (Response 'MTCheckTx) -checkTxH (RequestCheckTx checkTx) = withTransaction False $ pure . ResponseCheckTx $ - case decodeAppTxMessage $ checkTx ^. Req._checkTxTx . to convert of - Left _ -> def & Resp._checkTxCode .~ 1 - Right (ATMUpdateCount _) -> def & Resp._checkTxCode .~ 0 - -deliverTxH - :: Request 'MTDeliverTx - -> Handler (Response 'MTDeliverTx) -deliverTxH (RequestDeliverTx deliverTx) = withTransaction True $ - case decodeAppTxMessage $ deliverTx ^. Req._deliverTxTx . to convert of - Left _ -> return . ResponseDeliverTx $ - def & Resp._deliverTxCode .~ 1 - Right (ATMUpdateCount updateCountTx) -> do - let count = SS.Count $ updateCountTxCount updateCountTx - events <- withEventBuffer $ putCount count - let txResult = def & txResultEvents .~ events - return $ ResponseDeliverTx $ - def & deliverTxTxResult .~ txResult - -endBlockH - :: Request 'MTEndBlock - -> Handler (Response 'MTEndBlock) -endBlockH = defaultHandler - -commitH - :: Request 'MTCommit - -> Handler (Response 'MTCommit) -commitH = defaultHandler - -simpleStorageApp :: QueryApplication Handler -> App Handler -simpleStorageApp serveRoutes = App $ \case - msg@(RequestEcho _) -> echoH msg - msg@(RequestFlush _) -> flushH msg - msg@(RequestInfo _) -> infoH msg - msg@(RequestSetOption _) -> setOptionH msg - msg@(RequestInitChain _) -> initChainH msg - msg@(RequestQuery _) -> queryH serveRoutes msg - msg@(RequestBeginBlock _) -> beginBlockH msg - msg@(RequestCheckTx _) -> checkTxH msg - msg@(RequestDeliverTx _) -> deliverTxH msg - msg@(RequestEndBlock _) -> endBlockH msg - msg@(RequestCommit _) -> commitH msg diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index e949eeec..2dfab3bc 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -1,142 +1,30 @@ -{-# LANGUAGE TemplateHaskell #-} - module SimpleStorage.Modules.SimpleStorage - ( - -- * Component - SimpleStorage - , putCount - , getCount - + ( SimpleStorage + , SimpleStorageM , Api - , server + , simpleStorageModule , eval - , initialize - - -- * Store - , CountStoreContents - - -- * Types - , Count(..) - , CountKey(..) - - -- * Events - , CountSet - + , module SimpleStorage.Modules.SimpleStorage.Message + , module SimpleStorage.Modules.SimpleStorage.Types ) where -import Control.Lens (iso) -import Crypto.Hash (SHA256 (..), hashWith) -import qualified Data.Aeson as A -import Data.Bifunctor (first) -import Data.ByteArray (convert) -import Data.ByteString (ByteString) -import Data.Int (Int32) -import Data.Maybe (fromJust) -import Data.Proxy -import qualified Data.Serialize as Serialize -import Data.String.Conversions (cs) -import GHC.Generics (Generic) -import Polysemy (Member, Sem, interpret, makeSem) -import Polysemy.Error (Error) -import Polysemy.Output (Output) -import Servant.API ((:>)) -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError) -import qualified Tendermint.SDK.Events as Events -import Tendermint.SDK.Query (FromQueryData, QueryApi, - Queryable (..), RouteT, - storeQueryHandlers) -import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, - StoreKey (..), get, put) - --------------------------------------------------------------------------------- --- Types --------------------------------------------------------------------------------- - -newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON, Serialize.Serialize) - -data CountKey = CountKey - -instance HasCodec Count where - encode = Serialize.encode - decode = first cs . Serialize.decode - -instance RawKey CountKey where - rawKey = iso (\_ -> cs countKey) (const CountKey) - where - countKey :: ByteString - countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) - -instance IsKey CountKey "simple_storage" where - type Value CountKey "simple_storage" = Count - -instance FromQueryData CountKey - -instance Queryable Count where - type Name Count = "count" - --------------------------------------------------------------------------------- --- Events --------------------------------------------------------------------------------- - -data CountSet = CountSet { newCount :: Count } deriving Generic - -countSetOptions :: A.Options -countSetOptions = A.defaultOptions - -instance A.ToJSON CountSet where - toJSON = A.genericToJSON countSetOptions - -instance A.FromJSON CountSet where - parseJSON = A.genericParseJSON countSetOptions - -instance Events.ToEvent CountSet where - makeEventType _ = "count_set" - --------------------------------------------------------------------------------- --- SimpleStorage Module --------------------------------------------------------------------------------- - -storeKey :: StoreKey "simple_storage" -storeKey = StoreKey "simple_storage" - -data SimpleStorage m a where - PutCount :: Count -> SimpleStorage m () - GetCount :: SimpleStorage m Count - -makeSem ''SimpleStorage - -eval - :: forall r. - HasBaseAppEff r - => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) -eval = interpret (\case - PutCount count -> do - put storeKey CountKey count - Events.emit $ CountSet count - - GetCount -> fromJust <$> get storeKey CountKey - ) - -initialize - :: HasBaseAppEff r - => Member (Output Events.Event) r - => Sem r () -initialize = eval $ do - putCount (Count 0) - --------------------------------------------------------------------------------- --- Query Api --------------------------------------------------------------------------------- - -type CountStoreContents = '[(CountKey, Count)] - -type Api = "simple_storage" :> QueryApi CountStoreContents - -server - :: Member RawStore r - => Member (Error AppError) r - => RouteT Api (Sem r) -server = - storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy (Sem r)) +import Polysemy (Member, Members) +import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, + eval) +import SimpleStorage.Modules.SimpleStorage.Message +import SimpleStorage.Modules.SimpleStorage.Query (Api, server) +import SimpleStorage.Modules.SimpleStorage.Router (router) +import SimpleStorage.Modules.SimpleStorage.Types +import Tendermint.SDK.Application (Module (..)) +import qualified Tendermint.SDK.BaseApp as BaseApp + +type SimpleStorageM r = Module "simple_storage" SimpleStorageMessage Api r + +simpleStorageModule + :: Member SimpleStorage r + => Members BaseApp.BaseAppEffs r + => SimpleStorageM r +simpleStorageModule = Module + { moduleRouter = router + , moduleQueryServer = server + } diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs new file mode 100644 index 00000000..edbec565 --- /dev/null +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -0,0 +1,39 @@ +{-# LANGUAGE TemplateHaskell #-} +module SimpleStorage.Modules.SimpleStorage.Keeper + ( SimpleStorage + , storeKey + , putCount + , getCount + , eval + ) where + +import Data.Maybe (fromJust) +import Polysemy (Members, Sem, + interpret, makeSem) +import Polysemy.Error (Error) +import Polysemy.Output (Output) +import SimpleStorage.Modules.SimpleStorage.Types (Count, + CountKey (..), + CountSet (..)) +import qualified Tendermint.SDK.BaseApp as BaseApp + +storeKey :: BaseApp.StoreKey "simple_storage" +storeKey = BaseApp.StoreKey "simple_storage" + +data SimpleStorage m a where + PutCount :: Count -> SimpleStorage m () + GetCount :: SimpleStorage m Count + +makeSem ''SimpleStorage + +eval + :: forall r. + Members '[BaseApp.RawStore, Output BaseApp.Event, Error BaseApp.AppError] r + => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) +eval = interpret (\case + PutCount count -> do + BaseApp.put storeKey CountKey count + BaseApp.emit $ CountSet count + + GetCount -> fromJust <$> BaseApp.get storeKey CountKey + ) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs new file mode 100644 index 00000000..0d99be75 --- /dev/null +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs @@ -0,0 +1,58 @@ +module SimpleStorage.Modules.SimpleStorage.Message + ( SimpleStorageMessage(..) + , UpdateCountTx(..) + )where + +import Control.Lens (from, iso, view, (&), + (.~), (^.)) +import Control.Lens.Wrapped (Wrapped (..), _Unwrapped') +import Data.Bifunctor (first) +import Data.Int (Int32) +import qualified Data.ProtoLens as PL +import Data.ProtoLens.Message (Message (..)) +import Data.Serialize.Text () +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Validation (Validation (..)) +import GHC.Generics (Generic) +import Proto.SimpleStorage.Messages as M +import Proto.SimpleStorage.Messages_Fields as M +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Message (Msg (..), + ValidateMessage (..)) + +data SimpleStorageMessage = + UpdateCount UpdateCountTx + +instance ValidateMessage SimpleStorageMessage where + validateMessage m@Msg{msgData} = case msgData of + UpdateCount msg -> validateMessage m {msgData = msg} + +instance HasCodec SimpleStorageMessage where + decode = first cs . fmap (UpdateCount . view _Unwrapped') . PL.decodeMessage + encode = \case + UpdateCount a -> PL.encodeMessage $ a ^. from _Unwrapped' + +-------------------------------------------------------------------------------- + +data UpdateCountTx = UpdateCountTx + { updateCountTxUsername :: Text + , updateCountTxCount :: Int32 + } deriving (Show, Eq, Generic) + +instance ValidateMessage UpdateCountTx where + validateMessage _ = Success () + +instance Wrapped UpdateCountTx where + type Unwrapped UpdateCountTx = M.UpdateCount + + _Wrapped' = iso t f + where + t UpdateCountTx{..} = + defMessage + & M.username .~ updateCountTxUsername + & M.count .~ updateCountTxCount + f msg = + UpdateCountTx { updateCountTxUsername = msg ^. M.username + , updateCountTxCount = msg ^. M.count + } diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs new file mode 100644 index 00000000..e5a5f9a9 --- /dev/null +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs @@ -0,0 +1,24 @@ +module SimpleStorage.Modules.SimpleStorage.Query + ( CountStoreContents + , Api + , server + ) where + +import Data.Proxy +import Polysemy (Members, Sem) +import Polysemy.Error (Error) +import SimpleStorage.Modules.SimpleStorage.Keeper (storeKey) +import SimpleStorage.Modules.SimpleStorage.Types (Count, CountKey) +import qualified Tendermint.SDK.BaseApp as BaseApp + + +type CountStoreContents = '[(CountKey, Count)] + +type Api = BaseApp.QueryApi CountStoreContents + +server + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => BaseApp.RouteT Api (Sem r) +server = + BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) + storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs new file mode 100644 index 00000000..cc066094 --- /dev/null +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -0,0 +1,25 @@ +module SimpleStorage.Modules.SimpleStorage.Router + ( router + ) where + +import Polysemy (Members, Sem) +import Polysemy.Output (Output) +import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, + putCount) +import SimpleStorage.Modules.SimpleStorage.Message +import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) +import Tendermint.SDK.BaseApp (Event) +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (RoutedTx (..), + Tx (..)) + + +router + :: Members [SimpleStorage, Output Event] r + => RoutedTx SimpleStorageMessage + -> Sem r () +router (RoutedTx Tx{txMsg}) = + let Msg{msgData} = txMsg + in case msgData of + UpdateCount UpdateCountTx{updateCountTxCount} -> + putCount (Count updateCountTxCount) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs new file mode 100644 index 00000000..55502363 --- /dev/null +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -0,0 +1,54 @@ +module SimpleStorage.Modules.SimpleStorage.Types where + +import Control.Lens (iso) +import Crypto.Hash (SHA256 (..), hashWith) +import qualified Data.Aeson as A +import Data.Bifunctor (first) +import Data.ByteArray (convert) +import Data.ByteString (ByteString) +import Data.Int (Int32) +import qualified Data.Serialize as Serialize +import Data.String.Conversions (cs) +import GHC.Generics (Generic) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Codec (HasCodec (..)) + +newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON, Serialize.Serialize) + +data CountKey = CountKey + +instance HasCodec Count where + encode = Serialize.encode + decode = first cs . Serialize.decode + +instance BaseApp.RawKey CountKey where + rawKey = iso (\_ -> cs countKey) (const CountKey) + where + countKey :: ByteString + countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) + +instance BaseApp.IsKey CountKey "simple_storage" where + type Value CountKey "simple_storage" = Count + +instance BaseApp.FromQueryData CountKey + +instance BaseApp.Queryable Count where + type Name Count = "count" + +-------------------------------------------------------------------------------- +-- Events +-------------------------------------------------------------------------------- + +data CountSet = CountSet { newCount :: Count } deriving Generic + +countSetOptions :: A.Options +countSetOptions = A.defaultOptions + +instance A.ToJSON CountSet where + toJSON = A.genericToJSON countSetOptions + +instance A.FromJSON CountSet where + parseJSON = A.genericParseJSON countSetOptions + +instance BaseApp.ToEvent CountSet where + makeEventType _ = "count_set" diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs index c5788da3..bac6db60 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs @@ -2,35 +2,24 @@ module SimpleStorage.Server (makeAndServeApplication) where import Data.Foldable (fold) import Data.Monoid (Endo (..)) -import Data.Proxy import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) import qualified Network.ABCI.Server.Middleware.RequestLogger as ReqLogger import qualified Network.ABCI.Server.Middleware.ResponseLogger as ResLogger -import SimpleStorage.Application (AppConfig, - AppError, - Handler, - runHandler) -import SimpleStorage.Handlers (simpleStorageApp) -import qualified SimpleStorage.Modules.SimpleStorage as SS -import Tendermint.SDK.Application (MakeApplication (..), - createApplication) -import Tendermint.SDK.Query (QueryApplication, - serve) +import Polysemy (Sem) +import SimpleStorage.Application (AppConfig (..), + handlersContext) +import Tendermint.SDK.Application (createIOApp, + makeApp) +import Tendermint.SDK.BaseApp (CoreEffs, + runCoreEffs) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication cfg = do - let serveRoutes :: QueryApplication Handler - serveRoutes = serve (Proxy :: Proxy SS.Api) SS.server - makeApplication :: MakeApplication Handler AppError - makeApplication = MakeApplication - { transformer = runHandler cfg - , appErrorP = Proxy - , app = simpleStorageApp serveRoutes - , initialize = [SS.initialize] - } putStrLn "Starting ABCI application..." - application <- createApplication makeApplication + let nat :: forall a. Sem CoreEffs a -> IO a + nat = runCoreEffs $ baseAppContext cfg + application = createIOApp nat $ makeApp handlersContext serveApp =<< hookInMiddleware application where mkMiddleware :: IO (Middleware IO) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs index ef508706..11d223bc 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs @@ -1,56 +1 @@ module SimpleStorage.Types where - -import Control.Lens (from, iso, view, (&), - (.~), (^.)) -import Control.Lens.Wrapped (Wrapped (..), _Unwrapped') -import Data.ByteString (ByteString) -import Data.Int (Int32) -import qualified Data.ProtoLens as PL -import Data.ProtoLens.Message (Message (..)) -import qualified Data.Serialize as Serialize -import Data.Serialize.Text () -import Data.Text (Text) -import GHC.Generics (Generic) -import Proto.SimpleStorage.Messages as M -import Proto.SimpleStorage.Messages_Fields as M - - - - -data AppTxMessage = - ATMUpdateCount UpdateCountTx - -decodeAppTxMessage - :: ByteString - -> Either String AppTxMessage -decodeAppTxMessage = fmap (ATMUpdateCount . view _Unwrapped') . PL.decodeMessage - -encodeAppTxMessage - :: AppTxMessage - -> ByteString -encodeAppTxMessage = \case - ATMUpdateCount a -> PL.encodeMessage $ a ^. from _Unwrapped' - - --------------------------------------------------------------------------------- - -data UpdateCountTx = UpdateCountTx - { updateCountTxUsername :: Text - , updateCountTxCount :: Int32 - } deriving (Show, Eq, Generic) - -instance Serialize.Serialize UpdateCountTx - -instance Wrapped UpdateCountTx where - type Unwrapped UpdateCountTx = M.UpdateCount - - _Wrapped' = iso t f - where - t UpdateCountTx{..} = - defMessage - & M.username .~ updateCountTxUsername - & M.count .~ updateCountTxCount - f msg = - UpdateCountTx { updateCountTxUsername = msg ^. M.username - , updateCountTxCount = msg ^. M.count - } diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 6b82befc..91c9244f 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -2,6 +2,9 @@ module SimpleStorage.Test.E2ESpec where import Control.Lens ((^.)) import Control.Monad.Reader (ReaderT) +import Crypto.Secp256k1 (SecKey, derivePubKey, + exportCompactRecSig, + secKey) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) import Data.ByteArray.Base64String (Base64String) @@ -10,20 +13,26 @@ import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString) import Data.Default.Class (def) import Data.Int (Int32) +import Data.Maybe (fromJust) import Data.Proxy -import Data.Serialize (decode, encode) +import qualified Data.Serialize as Serial +import Data.String (fromString) import Data.String.Conversions (cs) import qualified Network.ABCI.Types.Messages.Request as Req import qualified Network.ABCI.Types.Messages.Response as Resp import qualified Network.Tendermint.Client as RPC +import Servant.API ((:>)) import qualified SimpleStorage.Modules.SimpleStorage as SS -import SimpleStorage.Types (AppTxMessage (..), - UpdateCountTx (..), - encodeAppTxMessage) -import Tendermint.SDK.Query.Client (ClientResponse (..), +import Tendermint.SDK.BaseApp.Query (QueryArgs (..)) +import Tendermint.SDK.BaseApp.Query.Client (ClientResponse (..), HasClient (..), RunClient (..)) -import Tendermint.SDK.Query.Types (QueryArgs (..)) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (Secp256k1, + addressFromPubKey) +import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..), + signRawTransaction) import Test.Hspec @@ -35,19 +44,20 @@ spec = do resp <- runRPC RPC.health resp `shouldBe` RPC.ResultHealth - it "Can query the count and make sure its initialized to 0" $ do - let queryReq = QueryArgs - { queryArgsData = SS.CountKey - , queryArgsHeight = 0 - , queryArgsProve = False - } - ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq - foundCount `shouldBe` SS.Count 0 + --it "Can query the count and make sure its initialized to 0" $ do + -- let queryReq = QueryArgs + -- { queryArgsData = SS.CountKey + -- , queryArgsHeight = 0 + -- , queryArgsProve = False + -- } + -- ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq + -- foundCount `shouldBe` SS.Count 0 it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do - let tx = UpdateCountTx "irakli" 4 + let txMsg = SS.UpdateCount $ SS.UpdateCountTx "irakli" 4 + tx = mkSignedRawTransaction (userPrivKey user1) txMsg txReq = RPC.RequestBroadcastTxCommit - { RPC.requestBroadcastTxCommitTx = Base64.fromBytes . encodeAppTxMessage $ ATMUpdateCount tx + { RPC.requestBroadcastTxCommitTx = Base64.fromBytes . encode $ tx } deliverResp <- fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ RPC.broadcastTxCommit txReq let deliverRespCode = deliverResp ^. Resp._deliverTxCode @@ -64,10 +74,10 @@ spec = do encodeCount :: Int32 -> Base64String -encodeCount = Base64.fromBytes . encode +encodeCount = Base64.fromBytes . Serial.encode decodeCount :: Base64String -> Int32 -decodeCount = (\(Right a) -> a) . decode . Base64.toBytes +decodeCount = (\(Right a) -> a) . Serial.decode . Base64.toBytes runRPC :: forall a. RPC.TendermintM a -> IO a runRPC = RPC.runTendermintM rpcConfig @@ -96,5 +106,37 @@ instance RunClient QueryRunner where } in RPC.resultABCIQueryResponse <$> QueryRunner (RPC.abciQuery rpcQ) +-------------------------------------------------------------------------------- + getCount :: QueryArgs SS.CountKey -> QueryRunner (ClientResponse SS.Count) -getCount = genClient (Proxy :: Proxy QueryRunner) (Proxy :: Proxy SS.Api) def +getCount = + let apiP = Proxy :: Proxy ("simple_storage" :> SS.Api) + in genClient (Proxy :: Proxy QueryRunner) apiP def + +-- sign a tx with a user's private key +mkSignedRawTransaction :: SecKey -> SS.SimpleStorageMessage -> RawTransaction +mkSignedRawTransaction privateKey msg = sign unsigned + where unsigned = RawTransaction { rawTransactionData = encode msg + , rawTransactionRoute = "simple_storage" + , rawTransactionSignature = "" + } + sig = signRawTransaction algProxy privateKey unsigned + sign rt = rt { rawTransactionSignature = Serial.encode $ exportCompactRecSig sig } + +data User = User + { userPrivKey :: SecKey + , userAddress :: Address + } + +user1 :: User +user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" + +makeUser :: String -> User +makeUser privKeyStr = + let privateKey = fromJust . secKey . Hex.toBytes . fromString $ privKeyStr + pubKey = derivePubKey privateKey + address = addressFromPubKey (Proxy @Secp256k1) pubKey + in User privateKey address + +algProxy :: Proxy Secp256k1 +algProxy = Proxy diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs deleted file mode 100644 index d32b4200..00000000 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/HandlersSpec.hs +++ /dev/null @@ -1,67 +0,0 @@ -module SimpleStorage.Test.HandlersSpec where - -import Control.Lens (to, (&), (.~), (^.)) -import Control.Lens.Wrapped (_Unwrapped', _Wrapped') -import Data.ByteArray.Base64String (Base64String) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Int (Int32) -import Data.ProtoLens (defMessage) -import Data.ProtoLens.Encoding (encodeMessage) -import Data.Proxy -import Data.Serialize (decode, encode) -import Data.Text (pack) -import Network.ABCI.Server.App (Request (..), - Response (..)) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp -import SimpleStorage.Application (AppConfig, makeAppConfig, - runHandler) -import SimpleStorage.Handlers (deliverTxH, queryH) -import qualified SimpleStorage.Modules.SimpleStorage as SS -import SimpleStorage.Types (UpdateCountTx (..)) -import Tendermint.SDK.Logger.Katip -import Tendermint.SDK.Query -import Tendermint.SDK.Store (rawKey) -import Test.Hspec -import Test.QuickCheck - - -spec :: Spec -spec = beforeAll beforeAction $ do - describe "SimpleStorage E2E - via handlers" $ do - let serveRoutes = serve (Proxy :: Proxy SS.Api) SS.server - it "Can update count and make sure it increments" $ \cfg -> do - genUsername <- pack . getPrintableString <$> generate arbitrary - genCount <- abs <$> generate arbitrary - let - handleDeliver = runHandler cfg . deliverTxH - handleQuery = runHandler cfg . queryH serveRoutes - updateTx = (defMessage ^. _Unwrapped') { updateCountTxUsername = genUsername - , updateCountTxCount = genCount - } - encodedUpdateTx = Base64.fromBytes $ encodeMessage (updateTx ^. _Wrapped') - (ResponseDeliverTx deliverResp) <- handleDeliver - ( RequestDeliverTx - $ defMessage - ^. _Unwrapped' - & Req._deliverTxTx - .~ encodedUpdateTx - ) - (deliverResp ^. Resp._deliverTxCode) `shouldBe` 0 - -- TODO: check for logs - (ResponseQuery queryResp) <- handleQuery - ( RequestQuery $ defMessage ^. _Unwrapped' - & Req._queryPath .~ "simple_storage/count" - & Req._queryData .~ SS.CountKey ^. rawKey . to Base64.fromBytes - ) - let foundCount = queryResp ^. Resp._queryValue . to decodeCount - foundCount `shouldBe` genCount - -beforeAction :: IO AppConfig -beforeAction = mkLogConfig "handler-spec" "SimpleStorage" >>= makeAppConfig - -encodeCount :: Int32 -> Base64String -encodeCount = Base64.fromBytes . encode - -decodeCount :: Base64String -> Int32 -decodeCount = (\(Right a) -> a) . decode . Base64.toBytes diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index bb2e0a69..e9459e88 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -55,7 +55,6 @@ dependencies: - cereal - cereal-text - containers -- conduit - cryptonite - data-default-class - errors @@ -67,9 +66,11 @@ dependencies: - katip - lens - memory +- mmorph - mtl - polysemy - polysemy-plugin +- polysemy-zoo - profunctors - proto-lens - proto-lens-runtime @@ -96,32 +97,35 @@ library: - -Wredundant-constraints exposed-modules: - Tendermint.SDK.Application - - Tendermint.SDK.Aeson - - Tendermint.SDK.Auth - - Tendermint.SDK.AuthTreeStore + - Tendermint.SDK.Application.App + - Tendermint.SDK.Application.Module + - Tendermint.SDK.Application.Handlers - Tendermint.SDK.BaseApp + - Tendermint.SDK.BaseApp.BaseApp + - Tendermint.SDK.BaseApp.CoreEff + - Tendermint.SDK.BaseApp.Errors + - Tendermint.SDK.BaseApp.Events + - Tendermint.SDK.BaseApp.Gas + - Tendermint.SDK.BaseApp.Logger + - Tendermint.SDK.BaseApp.Logger.Katip + - Tendermint.SDK.BaseApp.Query + - Tendermint.SDK.BaseApp.Query.Class + - Tendermint.SDK.BaseApp.Query.Client + - Tendermint.SDK.BaseApp.Query.Delayed + - Tendermint.SDK.BaseApp.Query.Router + - Tendermint.SDK.BaseApp.Query.Store + - Tendermint.SDK.BaseApp.Query.Types + - Tendermint.SDK.BaseApp.Store + - Tendermint.SDK.BaseApp.Store.AuthTreeStore + - Tendermint.SDK.BaseApp.Store.RawStore + - Tendermint.SDK.BaseApp.Store.Scope - Tendermint.SDK.Codec - Tendermint.SDK.Crypto - - Tendermint.SDK.Errors - - Tendermint.SDK.Events - - Tendermint.SDK.Query - - Tendermint.SDK.Query.Class - - Tendermint.SDK.Query.Delayed - - Tendermint.SDK.Query.Router - - Tendermint.SDK.Query.Store - - Tendermint.SDK.Query.Types - - Tendermint.SDK.Query.Client - - Tendermint.SDK.Logger - - Tendermint.SDK.Logger.Katip - - Tendermint.SDK.Store - - Tendermint.SDK.Subscription + - Tendermint.SDK.Modules.Auth - Tendermint.SDK.Types.Message - Tendermint.SDK.Types.Transaction - Tendermint.SDK.Types.Address - Tendermint.SDK.Types.TxResult - - Tendermint.SDK.TxRouter - - Tendermint.SDK.Module - - Tendermint.SDK.Gas generated-exposed-modules: - Proto.Types.Transaction diff --git a/hs-abci-sdk/src/Tendermint/SDK/Aeson.hs b/hs-abci-sdk/src/Tendermint/SDK/Aeson.hs deleted file mode 100644 index 4b957524..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/Aeson.hs +++ /dev/null @@ -1,9 +0,0 @@ -module Tendermint.SDK.Aeson - ( defaultSDKAesonOptions - ) where - -import Data.Aeson (Options) -import Data.Aeson.Casing (aesonDrop, snakeCase) - -defaultSDKAesonOptions :: String -> Options -defaultSDKAesonOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application.hs b/hs-abci-sdk/src/Tendermint/SDK/Application.hs index d478377c..8d878f19 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application.hs @@ -1,52 +1,11 @@ module Tendermint.SDK.Application - ( MakeApplication(..) - , createApplication - , defaultHandler + ( Modules(..) + , Module(..) + , HandlersContext(..) + , createIOApp + , makeApp ) where -import Control.Exception -import Control.Lens ((&), (.~)) -import Data.Default.Class (Default (..)) -import Data.Proxy -import Data.String.Conversions (cs) -import Network.ABCI.Server.App (App, MessageType, - Response (..), - transformApp) -import qualified Network.ABCI.Types.Messages.Response as Resp - -data MakeApplication m e = MakeApplication - { app :: App m - , transformer :: forall a. m a -> IO a - , appErrorP :: Proxy e - , initialize :: [m ()] - } - -defaultHandler - :: ( Default a - , Applicative m - ) - => b - -> m a -defaultHandler = const $ pure def - -transformResponse - :: forall e m. - Exception e - => MakeApplication m e - -> (forall (t :: MessageType). m (Response t) -> IO (Response t)) -transformResponse MakeApplication{transformer} m = do - eRes :: Either e (Response t) <- try $ transformer m - case eRes of - Left e -> pure $ ResponseException $ - def & Resp._exceptionError .~ cs (displayException e) - Right a -> pure a - -createApplication - :: ( Exception e - , Monad m - ) - => MakeApplication m e - -> IO (App IO) -createApplication ma@MakeApplication{app, transformer, initialize} = do - transformer $ sequence_ initialize - pure $ transformApp (transformResponse ma) app +import Tendermint.SDK.Application.App +import Tendermint.SDK.Application.Handlers +import Tendermint.SDK.Application.Module diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/App.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/App.hs new file mode 100644 index 00000000..35b950c2 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/App.hs @@ -0,0 +1,29 @@ +module Tendermint.SDK.Application.App + ( createIOApp + ) where + +import Control.Exception +import Control.Lens ((&), (.~)) +import Data.Default.Class (Default (..)) +import Data.String.Conversions (cs) +import Network.ABCI.Server.App (App (..), MessageType, + Response (..), + transformApp) +import qualified Network.ABCI.Types.Messages.Response as Resp +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Errors (AppError) + +createIOApp + :: forall r. + (forall a. (Sem r) a -> IO a) + -> App (Sem r) + -> App IO +createIOApp nat app = transformApp transformResponse app + where + transformResponse :: (forall (t :: MessageType). Sem r (Response t) -> IO (Response t)) + transformResponse (resp :: Sem r (Response t)) = do + eRes :: Either AppError (Response t) <- try $ nat $ resp + case eRes of + Left e -> pure $ ResponseException $ + def & Resp._exceptionError .~ cs (displayException e) + Right a -> pure a diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs new file mode 100644 index 00000000..788fcc1b --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -0,0 +1,183 @@ +module Tendermint.SDK.Application.Handlers + ( Handler + , HandlersContext(..) + , makeApp + ) where + +import Control.Lens (to, (&), (.~), (^.)) +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import qualified Data.ByteArray.Base64String as Base64 +import Data.ByteString (ByteString) +import Data.Default.Class (Default (..)) +import Data.Proxy +import Network.ABCI.Server.App (App (..), + MessageType (..), + Request (..), + Response (..)) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import Polysemy +import Polysemy.Error (Error, catch) +import qualified Tendermint.SDK.Application.Module as M +import qualified Tendermint.SDK.BaseApp.BaseApp as BA +import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) +import Tendermint.SDK.BaseApp.Errors (AppError, + checkTxAppError, + deliverTxAppError, + queryAppError) +import Tendermint.SDK.BaseApp.Events (withEventBuffer) +import Tendermint.SDK.BaseApp.Query (HasRouter) +import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) +import qualified Tendermint.SDK.BaseApp.Store as Store +import Tendermint.SDK.Crypto (RecoverableSignatureSchema, + SignatureSchema (..)) +import Tendermint.SDK.Modules.Auth (AuthError) +import Tendermint.SDK.Types.TxResult (TxResult, + checkTxTxResult, + deliverTxTxResult, + txResultEvents) + +type Handler mt r = Request mt -> Sem r (Response mt) + +data Handlers core = Handlers + { info :: Handler 'MTInfo (BA.ScopedBaseApp 'Query core) + , setOption :: Handler 'MTSetOption (BA.ScopedBaseApp 'Query core) + , initChain :: Handler 'MTInitChain (BA.ScopedBaseApp 'Consensus core) + , query :: Handler 'MTQuery (BA.ScopedBaseApp 'Query core) + , checkTx :: Handler 'MTCheckTx (BA.ScopedBaseApp 'Mempool core) + , beginBlock :: Handler 'MTBeginBlock (BA.ScopedBaseApp 'Consensus core) + , deliverTx :: Handler 'MTDeliverTx (BA.ScopedBaseApp 'Consensus core) + , endBlock :: Handler 'MTEndBlock (BA.ScopedBaseApp 'Consensus core) + , commit :: Handler 'MTCommit (BA.ScopedBaseApp 'Consensus core) + } + +defaultHandlers :: forall core. Handlers core +defaultHandlers = Handlers + { info = defaultHandler + , setOption = defaultHandler + , initChain = defaultHandler + , query = defaultHandler + , checkTx = defaultHandler + , beginBlock = defaultHandler + , deliverTx = defaultHandler + , endBlock = defaultHandler + , commit = defaultHandler + } + where + defaultHandler + :: Default a + => Applicative m + => b + -> m a + defaultHandler = const $ pure def + +data HandlersContext alg ms r core = HandlersContext + { signatureAlgP :: Proxy alg + , modules :: M.Modules ms r + , compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a + , compileToCore :: forall a. BA.ScopedEff core a -> Sem core a + } + +-- Common function between checkTx and deliverTx +makeHandlers + :: forall alg ms r core. + Member (Error AuthError) r + => RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => M.TxRouter ms r + => M.QueryRouter ms r + => HasRouter (M.Api ms) + => Members CoreEffs core + => HandlersContext alg ms r core + -> Handlers core +makeHandlers HandlersContext{..} = + let + txRouter = M.txRouter signatureAlgP modules + queryRouter = compileToBaseApp . M.queryRouter modules + + query (RequestQuery q) = Store.applyScope $ + catch + (do + queryResp <- queryRouter q + pure $ ResponseQuery queryResp + ) + (\(err :: AppError) -> + return . ResponseQuery $ def & queryAppError .~ err + ) + + beginBlock _ = Store.applyScope (def <$ Store.beginBlock) + + checkTx (RequestCheckTx _checkTx) = Store.applyScope $ + catch + (do + txResult <- transactionHandler txRouter $ + _checkTx ^. Req._checkTxTx . to Base64.toBytes + return $ ResponseCheckTx $ def & checkTxTxResult .~ txResult + ) + (\(err :: AppError) -> + return . ResponseCheckTx $ def & checkTxAppError .~ err + ) + + deliverTx (RequestDeliverTx _deliverTx) = Store.applyScope $ + catch @AppError + (do + txResult <- transactionHandler txRouter $ + _deliverTx ^. Req._deliverTxTx . to Base64.toBytes + return $ ResponseDeliverTx $ def & deliverTxTxResult .~ txResult + ) + (\(err :: AppError) -> + return . ResponseDeliverTx $ def & deliverTxAppError .~ err + ) + + commit :: Handler 'MTCommit (BA.ScopedBaseApp 'Consensus core) + commit _ = Store.applyScope $ do + Store.commitBlock + Store.mergeScopes + rootHash <- Store.storeRoot + return . ResponseCommit $ def + & Resp._commitData .~ Base64.fromBytes rootHash + + in defaultHandlers + { query = query + , checkTx = checkTx + , beginBlock = beginBlock + , deliverTx = deliverTx + , commit = commit + } + where + transactionHandler + :: (ByteString -> Sem r ()) + -> ByteString + -> Sem (BA.BaseApp core) TxResult + transactionHandler txRouter bs = do + events <- withEventBuffer . compileToBaseApp $ txRouter bs + pure $ def & txResultEvents .~ events + +makeApp + :: forall alg ms r core. + Member (Error AuthError) r + => RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => M.TxRouter ms r + => M.QueryRouter ms r + => HasRouter (M.Api ms) + => Members CoreEffs core + => HandlersContext alg ms r core + -> App (Sem core) +makeApp handlersContext@HandlersContext{compileToCore} = + let Handlers{..} = makeHandlers handlersContext + in App $ \case + RequestEcho echo -> + pure . ResponseEcho $ def + & Resp._echoMessage .~ echo ^. Req._echoMessage + RequestFlush _ -> pure def + msg@(RequestInfo _) -> compileToCore . BA.QueryScoped $ info msg + msg@(RequestSetOption _) -> compileToCore . BA.QueryScoped $ setOption msg + msg@(RequestInitChain _) -> compileToCore . BA.ConsensusScoped $ initChain msg + msg@(RequestQuery _) -> compileToCore . BA.QueryScoped $ query msg + msg@(RequestBeginBlock _) -> compileToCore . BA.ConsensusScoped $ beginBlock msg + msg@(RequestCheckTx _) -> compileToCore . BA.MempoolScoped $ checkTx msg + msg@(RequestDeliverTx _) -> compileToCore . BA.ConsensusScoped $ deliverTx msg + msg@(RequestEndBlock _) -> compileToCore . BA.ConsensusScoped $ endBlock msg + msg@(RequestCommit _) -> compileToCore . BA.ConsensusScoped $ commit msg diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs new file mode 100644 index 00000000..5a1c0036 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -0,0 +1,103 @@ +{-# LANGUAGE UndecidableInstances #-} +module Tendermint.SDK.Application.Module + ( Module(..) + , Modules(..) + , QueryRouter(Api) + , queryRouter + , TxRouter + , txRouter + ) where + +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import Data.ByteString (ByteString) +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) +import GHC.TypeLits (KnownSymbol, Symbol, + symbolVal) +import Polysemy (EffectRow, Member, Sem) +import Polysemy.Error (Error, throw) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), + throwSDKError) +import qualified Tendermint.SDK.BaseApp.Query as Q +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (RecoverableSignatureSchema, + SignatureSchema (..)) +import Tendermint.SDK.Modules.Auth (AuthError (..)) +import Tendermint.SDK.Types.Message (Msg (..), + ValidateMessage (..), + formatMessageSemanticError) +import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..), + parseTx) + +data Module (name :: Symbol) msg (api :: *) (r :: EffectRow) = Module + { moduleRouter :: RoutedTx msg -> Sem r () + , moduleQueryServer :: Q.RouteT api (Sem r) + } + +data Modules (ms :: [*]) r where + NilModules :: Modules '[] r + ConsModule :: Module name msg api r -> Modules ms r -> Modules (Module name msg api r ': ms) r + +-------------------------------------------------------------------------------- + +queryRouter + :: QueryRouter ms r + => Q.HasRouter (Api ms) + => Modules ms r + -> Q.QueryApplication (Sem r) +queryRouter (ms :: Modules ms r) = Q.serve (Proxy :: Proxy (Api ms)) (routeQuery ms) + +class QueryRouter ms r where + type Api ms :: * + routeQuery :: Modules ms r -> Q.RouteT (Api ms) (Sem r) + +instance QueryRouter (Module name msg api r ': '[]) r where + type Api (Module name msg api r ': '[]) = name :> api + routeQuery (ConsModule m NilModules) = moduleQueryServer m + +instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api r ': m' ': ms) r where + type Api (Module name msg api r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) + routeQuery (ConsModule m rest) = moduleQueryServer m :<|> routeQuery rest + +-------------------------------------------------------------------------------- + +txRouter + :: forall alg ms r . + Member (Error AuthError) r + => RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => TxRouter ms r + => Proxy alg + -> Modules ms r + -> ByteString + -> Sem r () +txRouter (p :: Proxy alg) ms bs = + let etx = decode bs >>= parseTx p + in case etx of + Left errMsg -> throw $ TransactionParseError errMsg + Right tx -> routeTx ms tx + +class TxRouter ms r where + routeTx :: forall alg. Modules ms r -> Tx alg ByteString -> Sem r () + +instance (Member (Error AppError) r) => TxRouter '[] r where + routeTx NilModules Tx{txRoute} = + throwSDKError $ UnmatchedRoute txRoute + +instance (Member (Error AppError) r, TxRouter ms r, HasCodec msg, ValidateMessage msg, KnownSymbol name) => TxRouter (Module name msg api r ': ms) r where + routeTx (ConsModule m rest) tx@Tx{..} + | symbolVal (Proxy :: Proxy name) == cs txRoute = do + msg <- case decode $ msgData txMsg of + Left err -> throwSDKError $ ParseError err + Right (msg :: msg) -> return msg + let msg' = txMsg {msgData = msg} + tx' = RoutedTx $ tx {txMsg = msg'} + case validateMessage msg' of + Failure err -> + throwSDKError . MessageValidation . map formatMessageSemanticError $ err + Success _ -> pure () + moduleRouter m tx' + | otherwise = routeTx rest tx diff --git a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs b/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs deleted file mode 100644 index 010f7d3e..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/AuthTreeStore.hs +++ /dev/null @@ -1,69 +0,0 @@ -module Tendermint.SDK.AuthTreeStore - ( AuthTree - , initAuthTree - , eval - ) where - -import Control.Concurrent.STM (atomically) -import Control.Concurrent.STM.TVar -import Control.Monad.IO.Class -import qualified Crypto.Data.Auth.Tree as AT -import qualified Crypto.Data.Auth.Tree.Class as AT -import qualified Crypto.Data.Auth.Tree.Cryptonite as Cryptonite -import qualified Crypto.Hash as Cryptonite -import Data.ByteString (ByteString) -import qualified Data.List.NonEmpty as NE -import Polysemy (Embed, Member, Sem, - interpret) -import Tendermint.SDK.Store (RawStore (..), StoreKey (..)) - --- At the moment, the 'AuthTreeStore' is our only interpreter for the 'RawStore' effect. --- It is an in memory merklized key value store. You can find the repository here --- https://github.com/oscoin/avl-auth - -newtype AuthTreeHash = AuthTreeHash (Cryptonite.Digest Cryptonite.SHA256) - -instance AT.MerkleHash AuthTreeHash where - emptyHash = AuthTreeHash Cryptonite.emptyHash - hashLeaf k v = AuthTreeHash $ Cryptonite.hashLeaf k v - concatHashes (AuthTreeHash a) (AuthTreeHash b) = AuthTreeHash $ Cryptonite.concatHashes a b - -data AuthTree = AuthTree - { treeVar :: TVar (NE.NonEmpty (AT.Tree ByteString ByteString)) - } - -initAuthTree :: IO AuthTree -initAuthTree = AuthTree <$> newTVarIO (pure AT.empty) - -eval - :: Member (Embed IO) r - => AuthTree - -> Sem (RawStore ': r) a - -> Sem r a -eval AuthTree{treeVar} = - interpret - (\case - RawStorePut (StoreKey sk) k v -> liftIO . atomically $ do - tree NE.:| ts <- readTVar treeVar - writeTVar treeVar $ AT.insert (sk <> k) v tree NE.:| ts - RawStoreGet (StoreKey sk) k -> liftIO . atomically $ do - tree NE.:| _ <- readTVar treeVar - pure $ AT.lookup (sk <> k) tree - RawStoreProve _ _ -> pure Nothing - RawStoreDelete (StoreKey sk) k -> liftIO . atomically $ do - tree NE.:| ts <- readTVar treeVar - writeTVar treeVar $ AT.delete (sk <> k) tree NE.:| ts - RawStoreBeginTransaction -> liftIO . atomically $ do - tree NE.:| ts <- readTVar treeVar - writeTVar treeVar $ tree NE.:| tree : ts - RawStoreRollback -> liftIO . atomically $ do - trees <- readTVar treeVar - writeTVar treeVar $ case trees of - t NE.:| [] -> t NE.:| [] - _ NE.:| t' : ts -> t' NE.:| ts - RawStoreCommit -> liftIO . atomically $ do - trees <- readTVar treeVar - writeTVar treeVar $ case trees of - t NE.:| [] -> t NE.:| [] - t NE.:| _ : ts -> t NE.:| ts - ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 020c8e84..c5497aa4 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -1,112 +1,61 @@ -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Tendermint.SDK.BaseApp where - -import Control.Exception (throwIO) -import Control.Lens (over, view) -import qualified Katip as K -import Polysemy (Embed, Member, Members, Sem, - runM) -import Polysemy.Error (Error, runError) -import Polysemy.Output (Output) -import Polysemy.Reader (Reader, asks, local, runReader) -import Polysemy.Resource (Resource, resourceToIO) -import qualified Tendermint.SDK.AuthTreeStore as AT -import Tendermint.SDK.Errors (AppError) -import Tendermint.SDK.Events (Event, EventBuffer, - evalWithBuffer, newEventBuffer) -import Tendermint.SDK.Logger (Logger) -import qualified Tendermint.SDK.Logger.Katip as KL -import Tendermint.SDK.Store (RawStore) - - --- | A constraint listing all externally visible effects in the 'BaseApp'. -type HasBaseAppEff r = - ( Member Logger r - , Member (Error AppError) r - , Member RawStore r - , Member (Output Event) r - , Member Resource r - ) - --- | Concrete row of effects for the BaseApp -type BaseAppEffR = - [ Output Event +module Tendermint.SDK.BaseApp + ( -- * BaseApp + BaseAppEffs + , (:&) + , BaseApp + , ScopedBaseApp + , compileToCoreEffs + , compileScopedEff + + -- * CoreEff + , CoreEffs + , Context + , makeContext + , runCoreEffs + + -- * Store , RawStore + , RawKey(..) + , IsKey(..) + , StoreKey(..) + , put + , get + , delete + + -- * Errors + , AppError(..) + , IsAppError(..) + , SDKError(..) + , throwSDKError + + -- * Events + , Event(..) + , FromEvent(..) + , ToEvent(..) + , emit + + -- * Gas + , GasMeter + + -- * Logger , Logger - , Resource - , Error AppError - ] - --- | CoreEff is one level below BaseApp, it as a seperation of BaseApp from --- | its interpretation. -type CoreEffR = - '[ Reader EventBuffer - , Reader KL.LogConfig - , Embed IO - ] - --- | This type family gives a nice syntax for combining multiple lists of effects. -type family (as :: [a]) :& (bs :: [a]) :: [a] where - '[] :& bs = bs - (a ': as) :& bs = a ': (as :& bs) - -infixr 5 :& - - --- TODO: it would be really nice to not have this CoreEff here, but to just --- interpret into it as an intermediate step in the total evaluation of BaseApp. --- Polysemy probably has a way to do this already. -type BaseApp = BaseAppEffR :& CoreEffR - -instance (Members CoreEffR r) => K.Katip (Sem r) where - getLogEnv = asks $ view KL.logEnv - localLogEnv f m = local (over KL.logEnv f) m - -instance (Members CoreEffR r) => K.KatipContext (Sem r) where - getKatipContext = asks $ view KL.logContext - localKatipContext f m = local (over KL.logContext f) m - getKatipNamespace = asks $ view KL.logNamespace - localKatipNamespace f m = local (over KL.logNamespace f) m - --- | 'Context' is the environment required to run 'CoreEff' to 'IO' -data Context = Context - { contextLogConfig :: KL.LogConfig - , contextEventBuffer :: EventBuffer - , contextAuthTree :: AT.AuthTree - } - -makeContext :: KL.LogConfig -> IO Context -makeContext logCfg = do - authTree <- AT.initAuthTree - eb <- newEventBuffer - pure $ Context - { contextLogConfig = logCfg - , contextEventBuffer = eb - , contextAuthTree = authTree - } - --- | An intermediary interpeter, bringing 'BaseApp' down to 'CoreEff'. -compileToCoreEff - :: Context - -> Sem BaseApp a - -> Sem CoreEffR (Either AppError a) -compileToCoreEff Context{contextAuthTree} = - runError . - resourceToIO . - KL.evalKatip . - AT.eval contextAuthTree . - evalWithBuffer - --- | The standard interpeter for 'BaseApp'. -eval - :: Context - -> Sem BaseApp a - -> IO a -eval ctx@Context{..} action = do - eRes <- runM . - runReader contextLogConfig . - runReader contextEventBuffer . - compileToCoreEff ctx $ action - either throwIO return eRes + , Tendermint.SDK.BaseApp.Logger.log + , Severity(..) + + -- * Query + , Queryable(..) + , FromQueryData(..) + , QueryApi + , RouteT + , storeQueryHandlers + + ) where + +import Tendermint.SDK.BaseApp.BaseApp +import Tendermint.SDK.BaseApp.CoreEff +import Tendermint.SDK.BaseApp.Errors +import Tendermint.SDK.BaseApp.Events +import Tendermint.SDK.BaseApp.Gas +import Tendermint.SDK.BaseApp.Logger +import Tendermint.SDK.BaseApp.Query +import Tendermint.SDK.BaseApp.Store diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs new file mode 100644 index 00000000..fce6c0df --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs @@ -0,0 +1,72 @@ +module Tendermint.SDK.BaseApp.BaseApp + ( BaseAppEffs + , BaseApp + , ScopedBaseApp + , ScopedEff(..) + , compileScopedEff + , compileToCoreEffs + , (:&) + ) where + +import Control.Exception (throwIO) +import Control.Monad.IO.Class (liftIO) +import Polysemy (Sem) +import Polysemy.Error (Error, runError) +import Polysemy.Output (Output) +import Polysemy.Resource (Resource, + resourceToIO) +import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) +import Tendermint.SDK.BaseApp.Errors (AppError) +import Tendermint.SDK.BaseApp.Events (Event, + evalWithBuffer) +import Tendermint.SDK.BaseApp.Logger (Logger) +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.BaseApp.Store (ApplyScope, ConnectionScope (..), + RawStore, + ResolveScope (..)) +import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT + +-- | Concrete row of effects for the BaseApp. Note that because there does +-- | not exist an interpreter for an untagged 'RawStore', you must scope +-- | these effects before they can be interpreted. +type BaseAppEffs = + [ RawStore + , Output Event + , Logger + , Resource + , Error AppError + ] + +-- | This type family gives a nice syntax for combining multiple lists of effects. +type family (as :: [a]) :& (bs :: [a]) :: [a] where + '[] :& bs = bs + (a ': as) :& bs = a ': (as :& bs) + +infixr 5 :& + +type BaseApp r = BaseAppEffs :& r + +type ScopedBaseApp (s :: ConnectionScope) r = ApplyScope s (BaseApp r) + +-- | An intermediary interpeter, bringing 'BaseApp' down to 'CoreEff'. +compileToCoreEffs + :: AT.AuthTreeGetter s + => forall a. Sem (ScopedBaseApp s CoreEffs) a -> Sem CoreEffs a +compileToCoreEffs action = do + eRes <- runError . + resourceToIO . + KL.evalKatip . + evalWithBuffer . + resolveScope $ action + either (liftIO . throwIO) return eRes + +data ScopedEff r a where + QueryScoped :: Sem (ScopedBaseApp 'Query r) a -> ScopedEff r a + MempoolScoped :: Sem (ScopedBaseApp 'Mempool r) a -> ScopedEff r a + ConsensusScoped :: Sem (ScopedBaseApp 'Consensus r) a -> ScopedEff r a + +compileScopedEff :: ScopedEff CoreEffs a -> Sem CoreEffs a +compileScopedEff = \case + QueryScoped m -> compileToCoreEffs m + MempoolScoped m -> compileToCoreEffs m + ConsensusScoped m -> compileToCoreEffs m diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs new file mode 100644 index 00000000..5a7b5bcf --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs @@ -0,0 +1,73 @@ +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Tendermint.SDK.BaseApp.CoreEff + ( CoreEffs + , Context(..) + , makeContext + , runCoreEffs + ) where + +import Control.Lens (over, view) +import qualified Katip as K +import Polysemy (Embed, Members, + Sem, runM) +import Polysemy.Reader (Reader, asks, + local, runReader) +import Tendermint.SDK.BaseApp.Events (EventBuffer, + newEventBuffer) +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.BaseApp.Store (MergeScopes, + ResolveScope (..)) +import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT + +-- | CoreEffs is one level below BaseAppEffs, and provides one possible +-- | interpretation for its effects to IO. +type CoreEffs = + '[ Reader EventBuffer + , MergeScopes + , Reader KL.LogConfig + , Reader AT.AuthTreeState + , Embed IO + ] + +instance (Members CoreEffs r) => K.Katip (Sem r) where + getLogEnv = asks $ view KL.logEnv + localLogEnv f m = local (over KL.logEnv f) m + +instance (Members CoreEffs r) => K.KatipContext (Sem r) where + getKatipContext = asks $ view KL.logContext + localKatipContext f m = local (over KL.logContext f) m + getKatipNamespace = asks $ view KL.logNamespace + localKatipNamespace f m = local (over KL.logNamespace f) m + +-- | 'Context' is the environment required to run 'CoreEffs' to 'IO' +data Context = Context + { contextLogConfig :: KL.LogConfig + , contextEventBuffer :: EventBuffer + , contextAuthTree :: AT.AuthTreeState + } + +makeContext :: KL.LogConfig -> IO Context +makeContext logCfg = do + authTreeState <- AT.initAuthTreeState + eb <- newEventBuffer + pure $ Context + { contextLogConfig = logCfg + , contextEventBuffer = eb + , contextAuthTree = authTreeState + } + +instance (Members CoreEffs r, AT.AuthTreeGetter s) => ResolveScope s r where + resolveScope = AT.evalTagged + +-- | The standard interpeter for 'CoreEffs'. +runCoreEffs + :: Context + -> forall a. Sem CoreEffs a -> IO a +runCoreEffs Context{..} = + runM . + runReader contextAuthTree . + runReader contextLogConfig . + AT.evalMergeScopes . + runReader contextEventBuffer diff --git a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs similarity index 99% rename from hs-abci-sdk/src/Tendermint/SDK/Errors.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index 26caf33e..97ac827b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Errors +module Tendermint.SDK.BaseApp.Errors ( AppError(..) , IsAppError(..) , queryAppError diff --git a/hs-abci-sdk/src/Tendermint/SDK/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs similarity index 99% rename from hs-abci-sdk/src/Tendermint/SDK/Events.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index 3ea03c4e..80b9de94 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Events +module Tendermint.SDK.BaseApp.Events ( Event(..) , ToEvent(..) , FromEvent(..) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs new file mode 100644 index 00000000..c2b91d4e --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs @@ -0,0 +1,39 @@ +{-# LANGUAGE TemplateHaskell #-} +module Tendermint.SDK.BaseApp.Gas + ( GasMeter(..) + , withGas + , eval + ) where + +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Int (Int64) +import qualified Data.IORef as Ref +import Polysemy (Embed, Members, Sem, interpretH, + makeSem, raise, runT) +import Polysemy.Error (Error) +import Tendermint.SDK.BaseApp.Errors (AppError, + SDKError (OutOfGasException), + throwSDKError) + +data GasMeter m a where + WithGas :: forall m a. Int64 -> m a -> GasMeter m a + +makeSem ''GasMeter + +eval + :: Members [Error AppError, Embed IO] r + => Ref.IORef Int64 + -> Sem (GasMeter ': r) a + -> Sem r a +eval meter = interpretH (\case + WithGas gasCost action -> do + remainingGas <- liftIO $ Ref.readIORef meter + let balanceAfterAction = remainingGas - gasCost + if balanceAfterAction < 0 + then throwSDKError OutOfGasException + else do + liftIO $ Ref.writeIORef meter balanceAfterAction + a <- runT action + raise $ eval meter a + ) + diff --git a/hs-abci-sdk/src/Tendermint/SDK/Logger.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs similarity index 82% rename from hs-abci-sdk/src/Tendermint/SDK/Logger.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs index c2f2d0c5..4c549973 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Logger.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs @@ -1,8 +1,8 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Logger +module Tendermint.SDK.BaseApp.Logger ( Logger(..) - , Tendermint.SDK.Logger.log + , Tendermint.SDK.BaseApp.Logger.log , Severity(..) ) where diff --git a/hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs similarity index 66% rename from hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs index 9baeb9a4..c7e0b01e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Logger/Katip.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs @@ -1,13 +1,13 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Logger.Katip where +module Tendermint.SDK.BaseApp.Logger.Katip where -import Control.Lens.TH (makeLenses) -import Data.String (fromString) -import Data.String.Conversions (cs) -import Data.Text (Text) -import qualified Katip as K -import Polysemy (Sem, interpret) -import Tendermint.SDK.Logger (Logger (..), Severity (..)) +import Control.Lens.TH (makeLenses) +import Data.String (fromString) +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Katip as K +import Polysemy (Sem, interpret) +import Tendermint.SDK.BaseApp.Logger (Logger (..), Severity (..)) data LogConfig = LogConfig { _logNamespace :: K.Namespace diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs new file mode 100644 index 00000000..fd861a61 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs @@ -0,0 +1,41 @@ +module Tendermint.SDK.BaseApp.Query + ( serve + , serveRouter + , module Tendermint.SDK.BaseApp.Query.Class + , module Tendermint.SDK.BaseApp.Query.Router + , module Tendermint.SDK.BaseApp.Query.Types + , module Tendermint.SDK.BaseApp.Query.Delayed + , module Tendermint.SDK.BaseApp.Query.Store + ) where + +import Data.Proxy +import Tendermint.SDK.BaseApp.Query.Class +import Tendermint.SDK.BaseApp.Query.Delayed +import Tendermint.SDK.BaseApp.Query.Router +import Tendermint.SDK.BaseApp.Query.Store +import Tendermint.SDK.BaseApp.Query.Types + +serveRouter + :: Monad m + => Router () m + -> QueryApplication m +serveRouter r = toApplication $ runRouter r () + +serve + :: HasRouter layout + => Monad m + => Proxy layout + -> RouteT layout m + -> QueryApplication m +serve p server = + toApplication (runRouter (route p (emptyDelayed (Route server))) ()) + +toApplication + :: Monad m + => RoutingApplication m -> QueryApplication m +toApplication ra query = do + res <- ra query + case res of + Fail e -> pure $ responseQueryError query e + FailFatal e -> pure $ responseQueryError query e + Route a -> pure a diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs similarity index 58% rename from hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs index 47df7042..8670b2b8 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Class.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs @@ -1,18 +1,22 @@ {-# LANGUAGE UndecidableInstances #-} -module Tendermint.SDK.Query.Class where +module Tendermint.SDK.BaseApp.Query.Class where import Control.Error +import Control.Monad.Morph (hoist) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, symbolVal) +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, symbolVal) import Servant.API -import Tendermint.SDK.Query.Delayed (Delayed, addQueryArgs, - delayedFail) -import Tendermint.SDK.Query.Router (Router, Router' (..), choice, - methodRouter, pathRouter) -import Tendermint.SDK.Query.Types (FromQueryData (..), Leaf, QA, - QueryArgs (..), QueryError (..), - QueryResult, Queryable (..)) +import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, addQueryArgs, + delayedFail) +import Tendermint.SDK.BaseApp.Query.Router (Router, Router' (..), + choice, methodRouter, + pathRouter) +import Tendermint.SDK.BaseApp.Query.Types (FromQueryData (..), Leaf, + QA, QueryArgs (..), + QueryError (..), + QueryResult, + Queryable (..)) -------------------------------------------------------------------------------- @@ -24,6 +28,7 @@ class HasRouter layout where type RouteT layout (m :: * -> *) :: * -- | Transform a route handler into a 'Router'. route :: Monad m => Proxy layout -> Delayed m env (RouteT layout m) -> Router env m + hoistRoute :: (Monad m, Monad n) => Proxy layout -> (forall a. m a -> n a) -> RouteT layout m -> RouteT layout n instance (HasRouter a, HasRouter b) => HasRouter (a :<|> b) where @@ -34,6 +39,10 @@ instance (HasRouter a, HasRouter b) => HasRouter (a :<|> b) where where pa = Proxy :: Proxy a pb = Proxy :: Proxy b + hoistRoute _ phi (a :<|> b) = + hoistRoute (Proxy :: Proxy a) phi a :<|> + hoistRoute (Proxy :: Proxy b) phi b + instance (HasRouter sublayout, KnownSymbol path) => HasRouter (path :> sublayout) where type RouteT (path :> sublayout) m = RouteT sublayout m @@ -42,12 +51,15 @@ instance (HasRouter sublayout, KnownSymbol path) => HasRouter (path :> sublayout pathRouter (cs (symbolVal proxyPath)) (route (Proxy :: Proxy sublayout) subserver) where proxyPath = Proxy :: Proxy path + hoistRoute _ = hoistRoute (Proxy :: Proxy sublayout) + instance (Queryable a, KnownSymbol (Name a)) => HasRouter (Leaf a) where type RouteT (Leaf a) m = ExceptT QueryError m (QueryResult a) route _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter where proxyPath = Proxy :: Proxy (Name a) + hoistRoute _ = hoist @@ -63,3 +75,4 @@ instance (FromQueryData a, HasRouter layout) Left e -> delayedFail $ InvalidQuery e Right v -> return qa {queryArgsData = v} ) + hoistRoute _ phi f = hoistRoute (Proxy :: Proxy layout) phi . f diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Client.hs similarity index 95% rename from hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Client.hs index 1a61acd65..466e84b9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Client.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Client.hs @@ -1,6 +1,6 @@ {-# LANGUAGE UndecidableInstances #-} -module Tendermint.SDK.Query.Client +module Tendermint.SDK.BaseApp.Query.Client ( RunClient(..) , HasClient(..) , ClientResponse(..) @@ -18,9 +18,9 @@ import qualified Network.ABCI.Types.Messages.Request as Req import qualified Network.ABCI.Types.Messages.Response as Resp import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.Query.Types (Leaf, QA, QueryArgs (..), +import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), Queryable (..)) -import Tendermint.SDK.Store (RawKey (..)) +import Tendermint.SDK.BaseApp.Store (RawKey (..)) class Monad m => RunClient m where -- | How to make a request. diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs similarity index 96% rename from hs-abci-sdk/src/Tendermint/SDK/Query/Delayed.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs index a601a3e6..1364d34c 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Delayed.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Query.Delayed where +module Tendermint.SDK.BaseApp.Query.Delayed where import Control.Error (ExceptT, runExceptT) import Control.Monad.Reader (MonadReader, ReaderT, @@ -9,7 +9,7 @@ import Data.Default.Class (def) import Data.String.Conversions (cs) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response -import Tendermint.SDK.Query.Types (QueryError (..), +import Tendermint.SDK.BaseApp.Query.Types (QueryError (..), RouteResult (..), RouteResultT (..)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs similarity index 95% rename from hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs index ac7ac55e..d85b385f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Query.Router where +module Tendermint.SDK.BaseApp.Query.Router where import Control.Error import Control.Lens (to, (&), (.~), (^.)) @@ -12,8 +12,8 @@ import qualified Data.Text.Encoding as T import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response import Network.HTTP.Types (decodePathSegments) -import Tendermint.SDK.Query.Delayed (Delayed, runAction) -import Tendermint.SDK.Query.Types (QueryArgs (..), +import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, runAction) +import Tendermint.SDK.BaseApp.Query.Types (QueryArgs (..), QueryError (..), QueryResult (..), Queryable (..), diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs similarity index 63% rename from hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index 533420a0..48b73893 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -1,20 +1,22 @@ -module Tendermint.SDK.Query.Store where +{-# LANGUAGE UndecidableInstances #-} -import Control.Error (ExceptT, throwE) -import Control.Lens (to, (^.)) -import Control.Monad.Trans (lift) -import Data.ByteArray.Base64String (fromBytes) +module Tendermint.SDK.BaseApp.Query.Store where + +import Control.Error (ExceptT, throwE) +import Control.Lens (to, (^.)) +import Control.Monad.Trans (lift) +import Data.ByteArray.Base64String (fromBytes) import Data.Proxy -import GHC.TypeLits (Symbol) -import Polysemy (Member, Sem) -import Polysemy.Error (Error) -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.Codec (HasCodec) -import Tendermint.SDK.Errors (AppError) -import Tendermint.SDK.Query.Class -import Tendermint.SDK.Query.Types -import Tendermint.SDK.Store (IsKey (..), RawKey (..), RawStore, - StoreKey, get) +import GHC.TypeLits (Symbol) +import Polysemy (Members, Sem) +import Polysemy.Error (Error) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Errors (AppError) +import Tendermint.SDK.BaseApp.Query.Class +import Tendermint.SDK.BaseApp.Query.Types +import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), + RawStore, StoreKey, get) +import Tendermint.SDK.Codec (HasCodec) class StoreQueryHandler a (ns :: Symbol) h where storeQueryHandler :: Proxy a -> StoreKey ns -> h @@ -23,8 +25,7 @@ instance ( IsKey k ns , a ~ Value k ns , HasCodec a - , Member RawStore r - , Member (Error AppError) r + , Members [RawStore, Error AppError] r ) => StoreQueryHandler a ns (QueryArgs k -> ExceptT QueryError (Sem r) (QueryResult a)) where storeQueryHandler _ storeKey QueryArgs{..} = do @@ -49,8 +50,7 @@ instance ( IsKey k ns , a ~ Value k ns , HasCodec a - , Member RawStore r - , Member (Error AppError) r + , Members [RawStore, Error AppError] r ) => StoreQueryHandlers '[(k,a)] ns (Sem r) where type QueryApi '[(k,a)] = QA k :> Leaf a storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey @@ -60,8 +60,7 @@ instance , a ~ Value k ns , HasCodec a , StoreQueryHandlers ((k', a') ': as) ns (Sem r) - , Member RawStore r - , Member (Error AppError) r + , Members [RawStore, Error AppError] r ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns (Sem r) where type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) storeQueryHandlers _ storeKey pm = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs similarity index 93% rename from hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs index bb50c59b..bbe3dc23 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Query.Types where +module Tendermint.SDK.BaseApp.Query.Types where import Control.Lens (from, (^.)) import Control.Monad (ap) @@ -11,8 +11,9 @@ import GHC.TypeLits (Symbol) import Network.ABCI.Types.Messages.FieldTypes (Proof, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response +import Tendermint.SDK.BaseApp.Store (RawKey (..)) import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Store (RawKey (..)) +import Tendermint.SDK.Types.Address (Address) data Leaf (a :: *) @@ -20,7 +21,7 @@ data QA (a :: *) -------------------------------------------------------------------------------- -type Application m = Request.Query -> m Response.Query +type QueryApplication m = Request.Query -> m Response.Query -------------------------------------------------------------------------------- @@ -78,6 +79,8 @@ class FromQueryData a where default fromQueryData :: RawKey a => Base64String -> Either String a fromQueryData bs = Right (toBytes bs ^. from rawKey) +instance FromQueryData Address + -------------------------------------------------------------------------------- -- NOTE: most of this was vendored and repurposed from servant. diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs new file mode 100644 index 00000000..fb303f5f --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs @@ -0,0 +1,7 @@ +module Tendermint.SDK.BaseApp.Store + ( module Tendermint.SDK.BaseApp.Store.RawStore + , module Tendermint.SDK.BaseApp.Store.Scope + ) where + +import Tendermint.SDK.BaseApp.Store.RawStore +import Tendermint.SDK.BaseApp.Store.Scope diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs new file mode 100644 index 00000000..17ccf360 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs @@ -0,0 +1,125 @@ +module Tendermint.SDK.BaseApp.Store.AuthTreeStore + ( AuthTreeState(..) + , AuthTreeGetter(..) + , initAuthTreeState + , evalMergeScopes + , evalTagged + ) where + +import Control.Concurrent.STM (atomically) +import Control.Concurrent.STM.TVar +import Control.Monad (forM_) +import Control.Monad.IO.Class +import qualified Crypto.Data.Auth.Tree as AT +import qualified Crypto.Data.Auth.Tree.Class as AT +import qualified Crypto.Data.Auth.Tree.Cryptonite as Cryptonite +import qualified Crypto.Hash as Cryptonite +import Data.ByteArray (convert) +import Data.ByteString (ByteString) +import qualified Data.List.NonEmpty as NE +import Data.Proxy +import Polysemy (Embed, Members, Sem, + interpret) +import Polysemy.Reader (Reader, ask, asks) +import Polysemy.Tagged (Tagged (..)) +import Tendermint.SDK.BaseApp.Store.RawStore (RawStore (..), + StoreKey (..)) +import Tendermint.SDK.BaseApp.Store.Scope (ConnectionScope (..), + MergeScopes (..)) + +-- At the moment, the 'AuthTreeStore' is our only interpreter for the 'RawStore' effect. +-- It is an in memory merklized key value store. You can find the repository here +-- https://github.com/oscoin/avl-auth + +newtype AuthTreeHash = AuthTreeHash (Cryptonite.Digest Cryptonite.SHA256) + +instance AT.MerkleHash AuthTreeHash where + emptyHash = AuthTreeHash Cryptonite.emptyHash + hashLeaf k v = AuthTreeHash $ Cryptonite.hashLeaf k v + concatHashes (AuthTreeHash a) (AuthTreeHash b) = AuthTreeHash $ Cryptonite.concatHashes a b + +data AuthTree (c :: ConnectionScope) = AuthTree + { treeVar :: TVar (NE.NonEmpty (AT.Tree ByteString ByteString)) + } + +initAuthTree :: IO (AuthTree c) +initAuthTree = AuthTree <$> newTVarIO (pure AT.empty) + +data AuthTreeState = AuthTreeState + { query :: AuthTree 'Query + , mempool :: AuthTree 'Mempool + , consensus :: AuthTree 'Consensus + } + +initAuthTreeState :: IO AuthTreeState +initAuthTreeState = AuthTreeState <$> initAuthTree <*> initAuthTree <*> initAuthTree + + +class AuthTreeGetter (s :: ConnectionScope) where + getAuthTree :: Proxy s -> AuthTreeState -> AuthTree s + +instance AuthTreeGetter 'Query where + getAuthTree _ = query + +instance AuthTreeGetter 'Mempool where + getAuthTree _ = mempool + +instance AuthTreeGetter 'Consensus where + getAuthTree _ = consensus + +evalTagged + :: forall (s :: ConnectionScope) r. + Members [Reader AuthTreeState, Embed IO] r + => AuthTreeGetter s + => forall a. Sem (Tagged s RawStore ': r) a -> Sem r a +evalTagged m = do + AuthTree{treeVar} <- asks (getAuthTree (Proxy :: Proxy s)) + interpret + (\(Tagged action) -> case action of + RawStorePut (StoreKey sk) k v -> liftIO . atomically $ do + tree NE.:| ts <- readTVar treeVar + writeTVar treeVar $ AT.insert (sk <> k) v tree NE.:| ts + RawStoreGet (StoreKey sk) k -> liftIO . atomically $ do + tree NE.:| _ <- readTVar treeVar + pure $ AT.lookup (sk <> k) tree + RawStoreProve _ _ -> pure Nothing + RawStoreDelete (StoreKey sk) k -> liftIO . atomically $ do + tree NE.:| ts <- readTVar treeVar + writeTVar treeVar $ AT.delete (sk <> k) tree NE.:| ts + RawStoreRoot -> liftIO . atomically $ do + tree NE.:| _ <- readTVar treeVar + let AuthTreeHash hash = AT.merkleHash tree + pure $ convert hash + RawStoreBeginTransaction -> liftIO . atomically $ do + tree NE.:| ts <- readTVar treeVar + writeTVar treeVar $ tree NE.:| tree : ts + RawStoreRollback -> liftIO . atomically $ do + trees <- readTVar treeVar + writeTVar treeVar $ case trees of + t NE.:| [] -> t NE.:| [] + _ NE.:| t' : ts -> t' NE.:| ts + RawStoreCommit -> liftIO . atomically $ do + trees <- readTVar treeVar + writeTVar treeVar $ case trees of + t NE.:| [] -> t NE.:| [] + t NE.:| _ : ts -> t NE.:| ts + ) m + +evalMergeScopes + :: Members [Reader AuthTreeState, Embed IO] r + => Sem (MergeScopes ': r) a + -> Sem r a +evalMergeScopes = + interpret + (\case + MergeScopes -> do + AuthTreeState{query, mempool, consensus} <- ask + liftIO . atomically $ do + let AuthTree queryV = query + AuthTree mempoolV = mempool + AuthTree consensusV = consensus + consensusTrees <- readTVar consensusV + let t = NE.last consensusTrees + forM_ [queryV, mempoolV, consensusV] $ \v -> + writeTVar v $ pure t + ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs similarity index 53% rename from hs-abci-sdk/src/Tendermint/SDK/Store.hs rename to hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs index 4595d761..b3d6fb9e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs @@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Store +module Tendermint.SDK.BaseApp.Store.RawStore ( RawStore(..) , RawKey(..) , IsKey(..) @@ -9,21 +9,25 @@ module Tendermint.SDK.Store , put , delete , prove + , storeRoot , withTransaction - , rawStoreBeginTransaction - , rawStoreRollback - , rawStoreCommit + , withSandbox + , beginBlock + , commitBlock ) where -import Control.Lens (Iso', (^.)) -import qualified Data.ByteString as BS +import Control.Lens (Iso', iso, (^.)) +import qualified Data.ByteString as BS import Data.Proxy -import Data.String.Conversions (cs) -import Polysemy (Member, Members, Sem, makeSem) -import Polysemy.Error (Error, catch, throw) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError, SDKError (ParseError), - throwSDKError) +import Data.String.Conversions (cs) +import Polysemy (Member, Members, Sem, makeSem) +import Polysemy.Error (Error, catch, throw) +import Polysemy.Resource (Resource, finally, onException) +import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (ParseError), + throwSDKError) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address, addressFromBytes, + addressToBytes) newtype StoreKey n = StoreKey BS.ByteString @@ -32,6 +36,7 @@ data RawStore m a where RawStoreGet :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) RawStoreDelete :: StoreKey ns -> BS.ByteString -> RawStore m () RawStoreProve :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) + RawStoreRoot :: RawStore m BS.ByteString RawStoreBeginTransaction :: RawStore m () RawStoreRollback :: RawStore m () RawStoreCommit :: RawStore m () @@ -41,6 +46,10 @@ makeSem ''RawStore class RawKey k where rawKey :: Iso' k BS.ByteString +instance RawKey Address where + rawKey = iso addressToBytes addressFromBytes + + class RawKey k => IsKey k ns where type Value k ns = a | a -> ns k prefixWith :: Proxy k -> Proxy ns -> BS.ByteString @@ -48,6 +57,7 @@ class RawKey k => IsKey k ns where default prefixWith :: Proxy k -> Proxy ns -> BS.ByteString prefixWith _ _ = "" + put :: forall k r ns. IsKey k ns @@ -66,8 +76,7 @@ get :: forall k r ns. IsKey k ns => HasCodec (Value k ns) - => Member (Error AppError) r - => Member RawStore r + => Members [RawStore, Error AppError] r => StoreKey ns -> k -> Sem r (Maybe (Value k ns)) @@ -100,19 +109,39 @@ prove prove sk k = rawStoreProve sk $ prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey +beginBlock + :: Member RawStore r + => Sem r () +beginBlock = rawStoreBeginTransaction + +commitBlock + :: Member RawStore r + => Sem r () +commitBlock = rawStoreCommit + +storeRoot + :: Member RawStore r + => Sem r BS.ByteString +storeRoot = rawStoreRoot + withTransaction - :: Members [RawStore, Error AppError] r - => Bool + :: forall r a. + Members [RawStore, Resource, Error AppError] r + => Sem r a -> Sem r a - -> Sem r a -withTransaction commit m = do - rawStoreBeginTransaction - runTx `catch` (\e -> rawStoreRollback *> throw e) - where - runTx = do - res <- m - if commit - then rawStoreCommit - else rawStoreRollback - pure res +withTransaction m = + let tryTx = m `catch` (\e -> rawStoreRollback *> throw e) + in do + rawStoreBeginTransaction + onException (tryTx <* rawStoreCommit) rawStoreRollback +withSandbox + :: forall r a. + Members [RawStore, Resource, Error AppError] r + => Sem r a + -> Sem r a +withSandbox m = + let tryTx = m `catch` (\e -> rawStoreRollback *> throw e) + in do + rawStoreBeginTransaction + finally (tryTx <* rawStoreRollback) rawStoreRollback diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs new file mode 100644 index 00000000..8adc4271 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs @@ -0,0 +1,34 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.BaseApp.Store.Scope + ( ConnectionScope(..) + , ApplyScope + , applyScope + , ResolveScope(..) + , MergeScopes(..) + , mergeScopes + ) where + +import Polysemy (EffectRow, Sem, makeSem, + rewrite) +import Polysemy.Tagged (Tagged (..)) +import Tendermint.SDK.BaseApp.Store.RawStore (RawStore) + +data ConnectionScope = Query | Mempool | Consensus + +type family ApplyScope (s :: ConnectionScope) (es :: EffectRow) :: EffectRow where + ApplyScope s (RawStore ': as) = Tagged s RawStore ': as + ApplyScope s (a ': as) = a ': ApplyScope s as + +applyScope + :: forall s r. + forall a. Sem (RawStore ': r) a -> Sem (Tagged s RawStore ': r) a +applyScope = rewrite (Tagged @s) + +class ResolveScope s r where + resolveScope :: Sem (Tagged s RawStore ': r) a -> Sem r a + +data MergeScopes m a where + MergeScopes :: MergeScopes m () + +makeSem ''MergeScopes diff --git a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs index 144f7231..acdb6c47 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs @@ -1,10 +1,18 @@ -module Tendermint.SDK.Codec where +module Tendermint.SDK.Codec + ( HasCodec(..) + , defaultSDKAesonOptions + ) where -import qualified Data.ByteString as BS -import Data.Text (Text) +import Data.Aeson (Options) +import Data.Aeson.Casing (aesonDrop, snakeCase) +import qualified Data.ByteString as BS +import Data.Text (Text) -- | This class is used as a codec for all items stored in --- | the database. +-- | the database as well as incoming transaction messages. class HasCodec a where encode :: a -> BS.ByteString decode :: BS.ByteString -> Either Text a + +defaultSDKAesonOptions :: String -> Options +defaultSDKAesonOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-sdk/src/Tendermint/SDK/Gas.hs b/hs-abci-sdk/src/Tendermint/SDK/Gas.hs deleted file mode 100644 index ac267af2..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/Gas.hs +++ /dev/null @@ -1,34 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Gas where - -import Control.Monad.IO.Class (MonadIO (..)) -import Data.Int (Int64) -import qualified Data.IORef as Ref -import Polysemy (Embed, Members, Sem, interpretH, - makeSem, raise, runT) -import Polysemy.Error (Error) -import Tendermint.SDK.Errors (AppError, SDKError (OutOfGasException), - throwSDKError) - -data GasMeter m a where - WithGas :: forall m a. Int64 -> m a -> GasMeter m a - -makeSem ''GasMeter - -eval - :: Members [Error AppError, Embed IO] r - => Ref.IORef Int64 - -> Sem (GasMeter ': r) a - -> Sem r a -eval meter = interpretH (\case - WithGas gasCost action -> do - remainingGas <- liftIO $ Ref.readIORef meter - let balanceAfterAction = remainingGas - gasCost - if balanceAfterAction < 0 - then throwSDKError OutOfGasException - else do - liftIO $ Ref.writeIORef meter balanceAfterAction - a <- runT action - raise $ eval meter a - ) - diff --git a/hs-abci-sdk/src/Tendermint/SDK/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Module.hs deleted file mode 100644 index 947e3c7f..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/Module.hs +++ /dev/null @@ -1,15 +0,0 @@ -module Tendermint.SDK.Module where - -import GHC.TypeLits (Symbol) -import Polysemy (EffectRow, Sem) -import Tendermint.SDK.Query (RouteT) -import Tendermint.SDK.Types.Transaction (RoutedTx) - -data Module (name :: Symbol) msg api (r :: EffectRow) = Module - { moduleRouter :: RoutedTx msg -> Sem r () - , moduleQueryServer :: RouteT api (Sem r) - } - -data Modules (ms :: [*]) r where - NilModules :: Modules '[] r - ConsModule :: Module name msg api r -> Modules ms r -> Modules (Module name msg api r ': ms) r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs similarity index 85% rename from hs-abci-sdk/src/Tendermint/SDK/Auth.hs rename to hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index e02157d6..b45a63e9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Auth where +module Tendermint.SDK.Modules.Auth where import Data.Bifunctor (first) import Data.Proxy @@ -13,12 +13,11 @@ import GHC.Generics (Generic) import GHC.TypeLits (symbolVal) import Polysemy import Polysemy.Error (Error, mapError) -import Tendermint.SDK.BaseApp (HasBaseAppEff) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), IsAppError (..)) -import Tendermint.SDK.Query (Queryable (..)) -import Tendermint.SDK.Store (IsKey (..), StoreKey (..), get, +import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..), + IsKey (..), Queryable (..), + RawStore, StoreKey (..), get, put) +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- @@ -78,20 +77,19 @@ data Accounts m a where makeSem ''Accounts -type AuthEffR = [Accounts, Error AuthError] -type HasAuthEff r = Members AuthEffR r +type AuthEffs = [Accounts, Error AuthError] storeKey :: StoreKey AuthModule storeKey = StoreKey "auth" eval - :: HasBaseAppEff r + :: Members [RawStore, Error AppError] r => Sem (Accounts ': Error AuthError ': r) a -> Sem r a eval = mapError makeAppError . evalAuth where evalAuth - :: HasBaseAppEff r + :: Members [RawStore, Error AppError] r => Sem (Accounts ': r) a -> Sem r a evalAuth = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Query.hs deleted file mode 100644 index c20c9f78..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/Query.hs +++ /dev/null @@ -1,47 +0,0 @@ -module Tendermint.SDK.Query - ( serve - , serveRouter - , QueryApplication - , module Tendermint.SDK.Query.Class - , module Tendermint.SDK.Query.Router - , module Tendermint.SDK.Query.Types - , module Tendermint.SDK.Query.Delayed - , module Tendermint.SDK.Query.Store - ) where - -import Control.Monad.IO.Class (MonadIO (..)) -import Data.Proxy -import qualified Network.ABCI.Types.Messages.Request as Request -import qualified Network.ABCI.Types.Messages.Response as Response -import Tendermint.SDK.Query.Class -import Tendermint.SDK.Query.Delayed -import Tendermint.SDK.Query.Router -import Tendermint.SDK.Query.Store -import Tendermint.SDK.Query.Types - -type QueryApplication m = Request.Query -> m Response.Query - -serveRouter - :: Monad m - => Router () m - -> QueryApplication m -serveRouter r = toApplication $ runRouter r () - -serve - :: HasRouter layout - => MonadIO m - => Proxy layout - -> RouteT layout m - -> QueryApplication m -serve p server = - toApplication (runRouter (route p (emptyDelayed (Route server))) ()) - -toApplication - :: Monad m - => RoutingApplication m -> QueryApplication m -toApplication ra query = do - res <- ra query - case res of - Fail e -> pure $ responseQueryError query e - FailFatal e -> pure $ responseQueryError query e - Route a -> pure a diff --git a/hs-abci-sdk/src/Tendermint/SDK/Subscription.hs b/hs-abci-sdk/src/Tendermint/SDK/Subscription.hs deleted file mode 100644 index fc1d178f..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/Subscription.hs +++ /dev/null @@ -1,57 +0,0 @@ -module Tendermint.SDK.Subscription where - -import Control.Concurrent.Async as Async -import qualified Control.Concurrent.MVar as MVar -import Control.Monad (forM_, forever) -import Control.Monad.IO.Class (MonadIO (..)) -import Data.Conduit -import qualified Data.IORef as IORef -import qualified Data.Map as M -import Polysemy -import Polysemy.Output - -data SubscriptionDriver o = SubscriptionDriver - { listenersVar :: IORef.IORef (M.Map Int (MVar.MVar o)) - , freshVar :: IORef.IORef Int - } - -initSubscriptionDriver :: IO (SubscriptionDriver o) -initSubscriptionDriver = do - ls <- IORef.newIORef M.empty - f <- IORef.newIORef 0 - pure $ SubscriptionDriver ls f - -eval - :: MonadIO (Sem r) - => SubscriptionDriver o - -> Sem (Output o ': r) a - -> Sem r a -eval SubscriptionDriver{listenersVar} = - interpret (\case - Output o -> liftIO $ do - listeners <- IORef.readIORef listenersVar - forM_ listeners $ \listener -> MVar.putMVar listener o - ) - -subscribe - :: SubscriptionDriver o - -> ConduitT o Void IO () - -> IO (Async.Async ()) -subscribe SubscriptionDriver{freshVar, listenersVar} consumer = do - inputVar <- MVar.newEmptyMVar - listenerId <- do - listenerId <- IORef.readIORef freshVar - IORef.modifyIORef freshVar (1 +) - IORef.modifyIORef listenersVar (M.insert listenerId inputVar) - pure listenerId - let producer = mkProducer inputVar - Async.async $ do - runConduit (producer .| consumer) - IORef.modifyIORef listenersVar (M.delete listenerId) - where - mkProducer :: MonadIO m => MVar.MVar o -> ConduitT () o m () - mkProducer var = forever $ do - mInput <- liftIO $ MVar.tryTakeMVar var - case mInput of - Nothing -> pure () - Just a -> yield a diff --git a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs b/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs deleted file mode 100644 index 3d105b2e..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/TxRouter.hs +++ /dev/null @@ -1,66 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -module Tendermint.SDK.TxRouter - ( router - , Router(..) - ) where - -import Crypto.Hash (Digest) -import Crypto.Hash.Algorithms (SHA256) -import Data.ByteString (ByteString) -import Data.Proxy -import Data.String.Conversions (cs) -import Data.Validation (Validation (..)) -import GHC.TypeLits (KnownSymbol, symbolVal) -import Polysemy (Member, Sem) -import Polysemy.Error (Error, throw) -import Tendermint.SDK.Auth (AuthError (..)) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (RecoverableSignatureSchema, - SignatureSchema (..)) -import Tendermint.SDK.Errors (AppError, SDKError (..), - throwSDKError) -import Tendermint.SDK.Module -import Tendermint.SDK.Types.Message (Msg (..), - ValidateMessage (..), - formatMessageSemanticError) --- import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (RawTransaction (..), - RoutedTx (..), Tx (..), - parseTx) - -router - :: forall alg ms r . - Member (Error AuthError) r - => RecoverableSignatureSchema alg - => Message alg ~ Digest SHA256 - => Router ms r - => Proxy alg - -> Modules ms r - -> RawTransaction - -> Sem r () -router (p :: Proxy alg) ms rawTx = - case parseTx p rawTx of - Left errMsg -> throw $ TransactionParseError errMsg - Right tx -> route ms tx - -class Router ms r where - route :: forall alg. Modules ms r -> Tx alg ByteString -> Sem r () - -instance (Member (Error AppError) r) => Router '[] r where - route NilModules Tx{txRoute} = - throwSDKError $ UnmatchedRoute txRoute - -instance (Member (Error AppError) r, Router ms r, HasCodec msg, ValidateMessage msg, KnownSymbol name) => Router (Module name msg api r ': ms) r where - route (ConsModule m rest) tx@Tx{..} - | symbolVal (Proxy :: Proxy name) == cs txRoute = do - msg <- case decode $ msgData txMsg of - Left err -> throwSDKError $ ParseError err - Right (msg :: msg) -> return msg - let msg' = txMsg {msgData = msg} - tx' = RoutedTx $ tx {txMsg = msg'} - case validateMessage msg' of - Failure err -> - throwSDKError . MessageValidation . map formatMessageSemanticError $ err - Success _ -> pure () - moduleRouter m tx' - | otherwise = route rest tx diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs index aa677693..b1823e0c 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs @@ -1,24 +1,16 @@ module Tendermint.SDK.Types.Address where -import Control.Lens (iso) import qualified Crypto.Secp256k1 as Crypto import qualified Data.Aeson as A import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString) import GHC.Generics (Generic) -import Tendermint.SDK.Query (FromQueryData) -import Tendermint.SDK.Store (RawKey (..)) -- | Used as a unique identifier for an account. newtype Address = Address Hex.HexString deriving (Eq, Show, Generic, Ord, A.ToJSON, A.FromJSON) -instance RawKey Address where - rawKey = iso (\(Address a) -> Hex.toBytes a) (Address . Hex.fromBytes) - -instance FromQueryData Address - addressToBytes :: Address -> ByteString addressToBytes (Address addrHex) = Hex.toBytes addrHex diff --git a/hs-abci-sdk/src/wreq.png b/hs-abci-sdk/src/wreq.png new file mode 100644 index 0000000000000000000000000000000000000000..6a5ccff20b1379b53ff063536004b556cc469024 GIT binary patch literal 15524 zcmZ{LWmp_RyCoV1cX!v|?yf-+AlM8}a1SuJyAw26fCP7k!QC~%o#5^cck=Dt-56FM26zYkEp48DgPverKPgDV zR8Ei`K>x^E=qi0tRfS=JwgE7(;npw+|As&h66gWF{Rb=zJoF6vukQ!ke_w^~`T_sn zZ5XzH1C=u-++kouF_dJbw7{?@t4M8P3fh9_$aH~pR1xn{6la;_qw;knt+AP-_*jJe@8- z`0Sqedf!cqF2O1Ag;4t{r`UOY?t713t|?`#%r*la0L`%!vuZ z5RNt3yp)OeMtXQRd8o7r@&-KQW`_klyd8DE9{of;&l|_xK=zs%K+T=Tz0MmYoDjQP zkpTzF7YnoutJdZ?4-ku>M|(PLOT*;+5VFDqK0>E?7qtG{_T|{Wb`bgs-pES*jLwLO zp94Kkyt0pgdoYBjIrNd-%>@Qk-2-JiH7C>|Xe*e2sXOc^3x%>Np>TyRVjcrk&BeCBNhE z1TR+NqG7-vLXuti;PKo&!J-k1BdS=vT9DpFQGS zyKsqH(osVhkxZQb%e=oB9h)^NP5aAY3y;Kl+g=9wbQ7M0!j z&t|UGuN;375GfYud>FsV@!R7@M=00hg^6q4La&@-HPk2*lFwxoZ*KJL#ZoV>&wEdo z{=}l&g~|(;J4Gmkr}jhYeCzpYlH**mpA_im9BuH|V^-4K!3_k!dS&VO!Mr+oip?@! z^H8Pp6;^!pen3D?c(y3LRmT$xXggoJGUx47!P0l5pJHwnVyj)^VB{ONm*Q_7-d6D0 zP*JzyUeCGSH?*p5tBw6#?3>7y;lc9q+HK=cSF2T{=YFG~E5U>%yX7PYWm|_fr)A}u zRobsogDfDKz3b3aH&!5{S+{fZOsd;-t^Tm|W9BF?R(0crKwAZ96Heb^R$Z=WjO$hN z0i>-9nhuIW1$FGb@lVS5Zva;IN2K;Cz86Sjs-4F^Yg^FNhkW>5asG9FG?BF{Icxns zcAW{j1Un7<&hGrGo?p-2{0vp8sOu`MKFOA1*#y#DE?4)+DE1Z67~yvgsLZ6Kxoo4`)6#C`B6`5Ac7JOUC2P~Z}G;=Aj7$n4bfJ8JSPYukD6$lUp|+?kDn zW*R;9xqo(rcF6eH23B30^IcS96!r5o<;!#+4Tcf3^?;?{pXJq4_nirk4%e+CEwiO? zckIQost)132h7(EOy!Dy&LO?W(4eF|mlhxC85~*L$7w>{DX?TS1ez>1i%WU?kmiS|Vgz$Qr#qLl1psj!YJ#D`I)NMxx>Rv`O*PnNJ34Ea6 z1f0jF!NAdW-m{{*Xt980Lu`vxRa*Zx!}0P71&{IqoATpt$ok8v(4;2IAq0(9I)m?( zweym-1!IgaEIvUKhDAyi4_i^>WU!^W*38gG`*;%|6;7WT@FU-i^5!FDij&~PnaIEb zF`?hdykEuhCC8h~7K$!*?G%h497pNa{+0933@bzJEf8|6qt7;}rd~HLAl7LM8gXnd zBe^!baF%`y=^Jg7=zH8(H4G}sM;e3Sw#nH0Iid7*j+O(C)x7)B4%Nq^+ zsx*nm9&D{&7E9~L`oYw;%U7i&9x>OxiSDTQY|e?)TPhjEeD-Vm6HzkLX+x6hw4*YK zSK-=;g9oceb1%7z%yx6zJl?kXZ@H^+3BeL`>H)@cYE*6d>68mILUjAGo-jC|1SXF*E zW#3GKy9fP_2UDZjTbU5Ie6^C>Aa|kgRbGF}Twqs@4DXa_VL}w>?uL8p<^xS9yS9Ci z@$pPtpOtd2mAu;^YW%PN)O2}9pinw@*JjDj$dCHmql2bEaQgJ3250Y*Cv?p!6(IH}OMee#0SsVovCtt@TUQ4MuH;j->SjqXFy z^6~)wV}m{)S|)u@zpZgRVT8}NQ&LA!>oxchks-&s_mmdl=Y6#JP*8q?t1d+DQ~0`8 zD7TtN)0JdFL;+|_CLbt0JdjTpzkwB4l}k;Yd;Tm3`S1)n@N z^yiUQ;*u6uZ4M2rsyeEm6F~hsYV>+}8Ku)k!$?8PxKYgTMu$5n{E&Yj^e?(f}B6V0ZlT* zckG;=!&9keOH+JDbEL@cu!zEYGj$Mt>y=01if89vbkme<7(hM;q_v0-BlrOr4}A$f z%69f7JZ%m7t_-GluRpCRp*HuP$kb@weUo_jrnE<-$mw4XlVj|{69*I-#X}c`b?lNk zF<-muxKr&;Of+9dhi_vdLM+9ejzfM978lwW1sqS-(>)j zDAi^irY8^hQ^Lhi`(LrhqEpXFZ;B0x?Nff>JmGk+^L<=Rtf)CW@&u^zO4(_M1IWhY|#B8<+PkG;Uxq&2cztEzxy!xC-`H*-SMA$~< zfxzU2f42YJ2NUAs_H*BjZW4Ck{h70kMh051+GM5SW+!Gs}w+*3D^7n>@d9o9@vFZxxlAsLF9kbnhnT8kY<;5m*>&V3&v zf6dckU!4Bc{h&k#a__+|x?@YIli9>w#A*%0r9W>T9{meyuR1QflL5u}$|^q<7AYMw ztD*a%MI;0}ZPjPj((emy*lz~+$wsb_>Bk7r+3v@9FD+c9h$R2PfytOATWM&5uWfXr zjqU?b59~_>?Go;5+MUx$@V{aD-}h#o|F)L?Xn+fMHTgME(yo?I-S6^SCTCpX$}_#u zCA})q@@MOW@Ahr{)*X7)lGi-5XI{Dgl?0I)s5&#Ih zY-6f=R(a72YG!`}y$*h<_F2SDADi3D26btX(6p#rq6VpeMfqx?`So&Ldm*T>qGuVC zK3{Nif>h1t)Z{MzRaTjbL``+Uaeu{BL)iHe>k1zZt6mQo+&Mhl%Lna^H`UhrDxGj0 zDpkQLe3Re-9``7I&JCXnLVsrxcc|ZyXo<{TU##}-Q~UzI8^u~_MYCwa(g*eJuJhl& z{lYg6nwZVGt!FTu{qAI7#ktvHhXhw{8u9@y1k~$9yQnrO)-FpE!?1$&U6DVD0N7{C zo+(3B>sY7}-Hr1u4yST+?wg1zHe{ylJd`6VmNZlSjOjfVS?9G%CvPl$?wx1uyez}S z$0bk^_B$L1B@vcmic_pnQO1J+sU#J!XND;Di_cP#uf7b^LW(Kd`Z@Y~f+#CaHlI4U z2lq>ci4KEh_yD$L96vU}?{lKav1;r60^{Q-sqC6*hX%geH#!pNw|~E&wwY;tinc*yp(sw~*Qy?v z?9Jt0T^G>-5t%iT+Y8}g{dR|@0-wf+`>ouAvq_fTI$s#Y4wS6-BhW^_c?|Ui7!aOe zoF2OhQDAL^5h~SwS7e!$9atGgQe_HB3+Xv~Sv$*a>ud@BLj^;5oFm&fR+DUV6?iM#x(UN!%r!|Mqtv>B+D7$T7sj6^N4cL^UfoNj zAL#KEKf4GQa{nRPbx#ZK`d9tZ8vxNW9#NXTWqeXlvxyc1~huchiL?)?Vi`xJL} zA2(1qmfX)?@AEF+bIdG`7tMOaL)5{3P^hM*j1hT5E3~6Goy<~i(vVdX)-(DfNvHI} zJW;@`3eHY}DQ-h?(?BLOmAS&ol-FF%@E5>_P@(~oeq!8ai>*P)J)jvldS33J?~abN zF-AmO#vJSD|Fn!N8iQ#1bCAus!??0nE*iZ0x)sx5N7_qSpix+rF$J>Depo7M=&*|G zlBeMtnOq2+^0cNiAQ=TTR@EEZH9OAa#H{SvjwH|*;cI*_-z5|AKKfm64qEVvr(&EF%p^&*4@m&`dgnhW=>4G73=?MXWB=T5L zNJ4}VMRX^6tYfL$DrivVCAaS>rpp=l30L9dro*oa?wL6Eg4h#5;f&*RbEHOphTkxq z&i&%2c$|?Ko(UPCXLwA*(uMTXr>(YjU|VJ(!dZ`u5A}`*F6~g|7EGE^&oaY~ZRf4+ z?0uJ*LPP*rYLi1AvVs^Q9ie*0K;0k%B4{UYmze_FF z=N|L#2q7Vn@$I;13gUaN;+ETeyiXrc3C2f1`raMtbP8YORs4&C z9e(QE!mk=??qas`@dlcm7rJ}<=5}6*bk+&ot+VReYE7P*p2;nMlREz^cT!ApE-f>U%>DQv^ za@UFMWG#A(X9~EtnL7_`q_#BOKvk5&VHi!&RCb_nij(YOYOG87rJT{VT*jDP`E5Pz zt|?1zQo;P($ABXJBeNd+vBqOHy(V|p0j3xZZn1bb`6K6Fhzd%}0vQT};|oHhuJd8G z89Y8kF{5O`sSAsNj%EZXGrbhI)m)jPzbdrBvkYfUt~Lg+6v3Yz%(6A;hJ61)cIttv ztpa~L5`0{6&E8slDbv;yJ@%2zyuHgWUtQ2wXw+ngS-DbFDHPBg3d(CMb%@nH$g3`w zJD^z$v+v1Ck68UU!>2pI@DT#FkXBF6XPQyRmbIHokNwxyw=Cb=MI;GyNirV?e}K;N zZD(+<40BO6rH}udo+Y`@ZQcv$DVoaW#9swLF-`HA67 zQLfqJPkaH0R;?+7^((T@;>RVl?mR1=gfH8-1AVa7Xgb@$TouvqYph$um`lWuoFSRr zXv)o>lQjZxPyL{!vyp#sM(o+(L)Ff|3_r3CN5FwKMq&q%11uE@YL8vE5tzz}?r5+& zZMDsNdAi*@)i-(<{@k&AR%`2Pm9uPCntLSj+qS*)@Xr}!8`KbcdQ}sxN%69;kQR}4 z=+`j!miSy5a*@~PFsk33O-gawM()5#Q*sUiRXjJ*y$C^L^c79kM!qARaDN z8gh{@D;_v2*82M{(Yj8SrXB|}LJvGmdmI0AA>O zxG@H5)I@Fs))3Xcd0$-BC{xDREbrE+LJnWJDwiC#hYyyn?Z}VvEr&%C37O_4?^-PU z)?dbj-!ku(*I#ex<1B%Dd*Ob#EwRc(02S~D@>YpC36R%XQ>D&9Sx@t_1Jn*?3N4pU z4$SbsZ{6X~A2zI!_Bi@DFC*F)(%+AH& zMq+1j`Uws;Oy7N((FzB*GMpt(ML5?aP_8j_WDcM6}Wn5ldvkF*?(m z(S>#Fsn|F9g1n-AO|To1OM_k zv+G5Ihj9k|lNa138Yy4T!_yQKjXI7;JIX_%jUHO7o?5p+AhW%neJxr-l?#3xn@ifF zZxITj`LgmmK_)SJ^{PrF1sXrQt;cGSF^G`p&;Mnz$U46?lbf`ix@pP4Q*OtX2AHE<*Z0A0$uOK*X08XYOuYQLgBEZ(N z1HEdw6(*#as&^#A%QUmUC#;aRQ6YXEZrG2Z5y_TE4_s3rOH6cXgMv3YOea=jh=f3m zt(93a86%O+io}65!QFyB&n2bwtIfn$(aXg655m>V1(E5^T2$;Uf0GjB-R*e>BMH7* zi#)n!F0T}dZWB)q5!U8xl`rF5$RnO^IvYWu{oH}xg;UgBCtBKa)FL&wX^J|BCI%#hen4KXfU;KK;R+^-f?!etx> zS0R2^vseT8InnQ*RMvgu4MR1-Z%t5?y+|~0ci_STQB8x62>V<&3}Jg(CruzU?UQF0 zJVgRGS_UGeq%D#0(^^ejg<-gr*-M8Zq`vlSRn+#Cb`;#Q6~+4Lm5bu6sD56ld1$Y_ zyrPFPAJBGf^n58{7VRv-U~G$DLRlfspo9=z+LJ z+jGJ-=Ub*+qAkOEG|k0jTBm5IL5P9x%TB!feK68Im)#67g!iW*=j|6=O0l!bBkf(0 zI8$Xk?xgNtYA$P@dny0$H%gxy3zKz$3GweZc^;8ziA;R+;)`DtDWX=l4WIcJ0DnF# zGP|#6z6G8Ql(LEJ`hdqJ9;0~%FiE}P07#)K#WB@psg`0#dX09Z9fvD6!4JGE94S=$ z86~588bqYT^iNT20l<8`$^@U=6v?Nl3EpSmEOEc`r@$-$=g(euw zVHf4sDT!e$vZ9q7f^;qHECH7f#qc#|9-_geGFZY9(Wn;ugFT2t8P1QFYt#U~6}GJK zePGRDQa?zDNWtjNc73*qEA}Iqvc%k?huKCzU2*rfWpdFAy~`3eCrQ%Xeo+kkK}?}N zEl()6pM1Y@1v>MZmw(Re5&JFJSI!LFZkBW%@7&Li{%-uq)4yC|msP(VT5nmIJ<4XHdkn{C66hce4O`XKiUQPBlx<1Q z&bXvDdlnqtdX31PIpmQcv zhQlxTZ21xL|6c5e3!!$id#Vt%W0>Pz4|p-4{s8S~uFxv4`zZhXz4MX>Ho3WCNf4!wF&`lJUJm)y^UfP%|emDh#784QOr}rg(%o*%o)AMgTR*7qzc+~?5knhGf86)<09A3X z@4~EXGI>!mhQDQdm(|Z-uvDLs7TqrK(#Px%_YN7M=%qhj)fb6X=uk*6$5gNuJ-yFX zXr|I9$&6MdqWu!uWC&9e7~>sf#^eC>kyPx8wn z!e@tz$YuOsnP$z>OIqY)xP4P@`oKu?X3-`3&IpMJ9JmU}pVWv8A-S#-LP{|`Gr!NP zikfj+@oT0h*3p}S-_;cVQhBDA4+7*=g>L*6I-|*%esDKS1$_<>=!fNsNXA%{VsifW<-9%bacBk5Cx1Wc z^t#gLUWw+Fv9fX5a1{!&Pb?7>6rwc*!xD**WtAS+!uhONNu@PrN`zqzeqo9WH>LVG z#tfU6T5ZQjZjwO0#p+vM`05vPd}TxplU+% z^HkRP=b_10&aZm&(SJk4*VGr}-)yM6dPzfLe!P`QcWbw{MLN^B`Cp2>^i-Y8kG?IR zr3(oA6HE^@ZX8>td}j9&nC!>+9SHm9W6Xe>&}k*^q?+CsZiewJcO#sgsLVerdj6tE zrWZ2m>*KC1F3S~2iXzm?po3l&kacw4HFowN^Ld^I_`8E(Df0JL-P(_RbRIRn2_`BW zQ1Zj4+zJ~a-pXv-%_iGaTU&M%4MFLsrtuEQmUcjwprVd;CHcy3 zbHUvDJoY=8p?F|qUspf72C`IZANvfP`vt>4tyY{$U3z8M^kCiT{BooUBb(`stgV6XMR*V_|u?8~i=VtmyWc=Pg) zVa?k0YO&rNILj_#Gp6OI4=FhwAd@#OMAHV0nynpGw$E1F2tz?rwC-ouag^?(DgDl? z?6WLiOU4Qb-^flIhRl4b%TCGHF$`dJ!uJ*oft?D~9S;H_} zr7}7w;PbVw@W$-iOqGzYY21qV#r0jkHP*rkx#LVI#oPJ4+e8mcPHo^#c@nmCLS;O8 zYm_D7|XvOv|HsCjuC%TKW)ctjlKEb+oeh#MpiF1fhmdk$2 zz)0pyeAxFZ3E?xd)G$jZf!(>_lOkh^(+(pm&%qCr}v4+@AQRI2u&gMkSCIYH_TlS8P?=#4(>?AL&6kdT*S7%*604f zj#n(g8sV)+CYbY|TNEaZpe0v|xn|z|h=ZUyvlORLl`#KS52Hc}XZbAx=B#C=xgTyH zurTM(fs>mRHPiZO3kqBw2xu3g4Oky+eVp3&mOw`Xzr+2ljJ;o;-hkjH9OZbIwsh>vIInINHDH6%@5%UruI zT&=u%le<@ah)t;dn{i3CnKv?(el?2ymN5C(#j5uq0j``QvJmh(s!N+jOll!(;6~W} zR(koDQ9?-K=o`#1i%NQONdD(wM6IZz^_s^}6-kDi(w90WrhjZFs@yC)E20_>`bgmu zHpDx@C;A*XICT;~wcj*AmjOHaW4P6VuKrV(d@T|(kM58;otCaRUMf25se1%|Pjk?; zAucj0-74U2!ERo+eVz!>1sh{&?o4%wenGBnjY*kIw<-u8V4Ll)Q*F^U^)CI-hk0Y2 zO$ZAH(Kn$_PmjSoNfjRj=^=@#yg(W58r5cF#&TpWV+V)Lrp9N-JnJiJOix992!BTS zqX97su>fE?lR$)|-0>Vd23P0?3Je$Gk`TXIll;)@%X${9=W&wlP%+PEF}4JYDUX~% zo%^)1_a&Ayzy?Akn*nJvHgh0mU zSNvT4^73CEaf@d&qKTN`Y8uw&)o+h+nK6$0P0$6h!d)MdukRT~=NWbPMayKLmhF=Z z7>lbpiFu07hg_UxS5=DVm~+X#A!pnrsq*h%>J^ehM-aHtpOC#Jqd3j3!McW(m>MVJ20Yui)JgfjgX(-E{emW#%({#v z;lxc8;mN`Rc+zBz5--Ec9oKoOo%$W^x5Qevu8m&Ex*9n(DbJ7m-anEOyt_-gL^>iH zqWg$ytb2rW?E8Dh1eulS`nCioqu-j%d8k(e5>1>rt)tI^ z(*;aQM(HHGhqw<^VZ>jC`Y$66@O43EZovS)=P~#pVn;?N(TK0OA$7>zB9jbp*{5qd zI93!vZgC<+ds1Yyxlh&A78{M=Z7iGGm-OFugF@9(qTJR2x;C6`*E1?tZNl5mli(~7 zf*wtVej-{}HKvdUajxO^v%1sqQCd$ObK!{}H*U*Bs7Q3*k#{&YIk5Gb+bqc3ntRJ* z;H7?icbXr5h@>_1TIV28!9}CrX6w6+I{MTz&bRGt>cM7WCLT;=>B6%I3g4;6ro|nx zXspWBh?zq)dR6;WqOmU@%Zjlr#gG(H9!DBm@v2Lf;%1*^ic!_P73Jf961|F<|NV#t zukM4mrvH39M#YWsK+9SmB!n~f;>FY(&k!Ezcy9q#zm-WQ zC2II_PfEJaJks(P`tn;CiEMe&;ZM6CSAOZK{)|iNo4hA!S9-tCrJ~x3Eqy0H-neIt z>sM{BzwwLJUcuW&%G-ubtDSLTbVYfK4m(Fz(-Y-?)gLyQM~2gEyw;kJv-6!k2w<~g znrq+o>Gm2crDGpP33-ll+u_0?jJyrLybXGww9tenw`F3t$TtK1?R{^eWgd?Rp<5AK z6}QUT=FN@=gn0I|v=3@*TqF@;znv4g{-PzI%DzF#>UJKb&zSCSRAnx`z~M@}pICFt|7JJ2+s+gjuECPgpq1`X!13F)V(jO;+}XPsMP)l66UQ z^CQ!aHy~JyDJqD<>Pgj-S_qvVMHR&KV`)q+tvAICZ zg=sp^M$`!kPQg8OIKxS&6)GY`eNGJCkTpl0^ilQwmuO~+JmNf;Xn&Fh*N>#nXVCHf zZms0BZJI9Iq2Wzd3Q6DM*QVBxl7yWpVy(III9JsDj4A$9U5wsIdt9a8)>Ph5mg*i> z)3M&T-rFqjKG_IBU$DFo+6|*Q_o`)qPgbpg<=Ky)Gsn%3K}1LvA(*B)7jRa38OrrR z4 zQYCikHR<-peW??4%QIlcZb3-_(M1aB{eA#GzP0s^-pv=+tXlqw*f7a%Z;57BZB2;6 zY4+YL#Qm!ZrP0NjOnu?glf_#a9)`F^bs-Dd2fX9+e8C^1nm2zOJn;2rvU#p$-_2=9 z<06fHOk2Af-z&9ueDt{^e_ApK=dXNn7>y}%jEO$Q{$a7*VPKc86+Dx_H~)a4TJwd@ z?|Us~`M1>BS%Xd3Qw?tp0S%l-oV_1!=prWwAa+ zb@BoStQ3gqaq`PN%fh(RDr=SR!`zn@86*Fchr4(T?W;EhR9FJPIjSLE?QR5GKdSd_+7DT!RV}N%Se%`j zs5Ntq+B`q=1Kc& z3WIiYt5}(?N6ClE>H`Q&zMz`o+vsKZVRdiibvQ6!RSkt%W|SxQVN6E;jQNLTR})Wv zz;FI)_38PQT^B6LK?iJ+gt)bgQjH~W@Wn`yg;yGi41!Sg7mc-FHSa|Ta;n?p55Le6 z_i%mHJYc^j82p+T4aUSskwRR+1c(6%Xx*zkp1{S^53luD8;uqMVJfimQ6;EbUE}i) z!zW+&HSfHWmYcvWxC$To{~8-FIFTj10&f39=IJ|tTw=EhdEL6Ta;|?e{^A@#JfkDv z+`u^^f>de~+t;he=~CBwq->XL%IR(7%SV_wuYcJL>=UFRi-Z#&P#fVNl(=g!z}g6U z9}>$dOz6yhyv*|GBF}?4#0-3Hx}kWQQr9+Zv;fxivi+XGwkQMpt0LMyGpLa%=D0MS z`)vsOl101GedyKONBaORMHbCRIMOWhJno>{Up3ouqhc5xNfA_(S<8b8%O7)RyGOTF z#yV1MTymiS4daM5)Gk%BWPQ?;knY(rkpYfMWqh-`ahk9dhict9W?*I01N+koA?!GD z(A?Heno-VapnE{(xaqkFqM8&G$ob(oE7#^Img#w~GwY+dLoPBI(+irvv-Zk1sKBbW zRfu$cz2Tb&YC1aZuzc+&{yQP$H=>!tP2|#$(k;uFlG{u{Z%?`H`*gocbYerRMy#YR zzXrFJnZ^vIn@PBx*a68L$VFcU8qvUmp6_N*-34lZGpg4g@%thmoeS$JNF+qX%oJQSw|%*FlO5b77*A(>*wm`o^Yxv) zOAUq*lFi8)Qp}00%z3>os;Z~1u7?AL_o4(I)W!iw(k|wxcDcn7bn^k&<$3Pw9@b5v zU98K$?8bm=h4sfVn2W2{iJy9x6iXuUP7K^`gvs%?0BK)oYvw|w$4<|Rf*0=B4RlsI z&Etyp`#zFq0L-3q=4_|$``TT4323_cya;gSgLi5>Y0|k;hYwjBZRS^bPj*Fjx#E>9 zh&nfB7{8T?(z*(HVs%~hU43%TChIDm+=qPN{#_818grJM*GkK_V$8lR8Bk-y08RZA zc=?@=|Ef)g2^tCUP<@+b+PlASUXLWV+>NWmCw2Zx`wpCA<&3tg?^;_62Q<=)iH}JG zZUTnlzIj40IT;)GAdIF3(0k`o8;S>5BqpcjII(5zW;PPSE#|yH zA%dTie{3_k)S?IG?#N-EMmwKJU9&~zj!V>iZ72}u?v8yNT>)M3IvtlOo?0X%UFc{P zCtv#3+KQYFICrBtIt1h4>I!-Vg;7zqyB6XOadYqc7$!EySFuG(ef0_l5A0h%gE>4g z9V!KyyU{NJIM*B#JK5-rvw<&)28HMOMq9671kU|s?HwtG++k;ywa{`~orInN)Gz$^ z6#~IAC}^#5#d!){|AaTYdihE<<@dF-JY?v1<1B})ZEHoGhzFKdJxx~Xc;C6HS4D&X6cMUWJ`59fgMz|K9SP_z-*;8x8-nvpAcu?oY3#FeQzOMYfL1jOmw9Tjy~Sq&>_+WbI@VNr0@7Ba92gmg<1}v zyw5II=!N-g+SC-XrQ6fqCc7_>ELSUQ`qK3l0pNkzIr$TmjZsO8143QsYV)^`cpJA&SnW>PY@2DIf9#hmAOv%EVC6Q@xoO=a zd(t_=8%K3)bA+{u&16+wP`kG~7FuY!eP4}S*SI!iWJ(e3qmCO?lzYvOW>?Ppg7$MY zk`QEy9wykjDg7KcZSb zb5ES8t_ApS!DD|(mga+HognPE-4uJqGqfn-4Q15(NL%NLGF)>6qN#Hm!88ZFQCd>8 zU}_RNlp&&{dOTmIHnq{SE^;J&P6326C3fw1MPNfZp3X;t&jO%w$e5m(m3vAOLd&8w zMW&X!BX=y$3n(_6W*oWgfFo|1>+8Wz@~GknCmb^eu5&ZZaTzLc<>O)k5P^3Yk3R?+Gw5MRV3t#6cR{%yOMr%oJ*h zR$!)UqmFQ%)6*{dT^PAz`osyB?ylt+f`cPdW{k=hV1j|GsC=KHRkdw*Y&LH`g6vs&h zwCu0J<2=i-zXx3rz0^!tulem1TA=K(np4rBMzmc6*? zn@GoC!k}#SOBBqirjDBm=FxNcu2CrN58e#5PKRowPc?oEes?}QSfJ^P;7mz&17GUS z8ix&hyBUHYJo=`14tsCTWnH5|_dhW6tz~m{oeDm7oFR4=T_prFeZ?k7e-c^Kofu;%_HRMSO|TOF#$)XbNq9`r20Swx7T;Dt5|gnZ z&hxcj_|jL!bp_`$&^T*5#$E9ZyP3B-KTT8D-C35kTp=Fops z>^n%{AdsPmg*!Uavs+Wlj7WGiTPF=*4!?5lyIc5MtG;6*89lz4yaGO^ycvy-+}w7A z%BYNG3u6oG)f814*PMLnaxhBjTz&$ge5i66=)&%D$r*W4AA@98wBLS~0Y==)616=z z7vY8FD9NMyT9DXb3nA#QBRESrRi?9o$0dG*7L8n@gj=JZNgn&k;dNNr81ai0_I*NV zDfVQ_V4rwb%mt2p?|KlxSDf!Qg6D!g*4-CN;a2Wk0;Cl~uu}3KVu-K+ zY{mN6TSFn=33xyADqr%us{(A?>rRYutvFH8WculV*a%Ui`C;kQHV8r@q>PI*vjTfJ z6wNf>cPT#>qx=%0?@B+@f1wOK3$-m4-id1o9lccL)Oc^43T!Twe>X(;N!z3;g88Bj z#=x@iZA3bTikn0O!Kbo^^~ebSGl)4t+o@rq;z?07sWN_9_v!|H!^)g--0dvt-R~QR z@v;!-dzT}Fbz%Gl7B=p~u2`Yd2I;G79k6i|+56(>fUSrLeIuv*OGKR>xVA3L4Mp=5 zF<(YnQP{LinSQ9ydZ+X9olY~X|9Nof&{GU!S4ztVcv#zMV6luX{zUyn;Z{8BtF&Z6 zaebAOZDzft-aaS)r!StHoaN11CfZ+01f$&lOm=KrcQgISZ<^BtY2H@|-(ZZ@o;9ma zCNdH|gSivjw4W26bvNTHOU#V~OOpH>6D@;hFeF%B6FjGkj)t{E3C&mZi9MLPm-9OZ z$tms9x#ki>my~H%s9g2qS5X8?$+QmU`OovvZ4Ifd(8Lr&S?|d*nnA;|R#quoM&2_?twsUNcmy>+g44UGPI8i;!9lNL=>k`X-N_HEXz;T_TC&q8E~#G$kmk$m{lAEIfh;nX2Yo0sD#cH(b`9tb0iJ*RJ#L73{x_ckm4M3r5z+-lmK zNBD2FP`UFx%KAB#;5$Ow&~GWJn{FqQPQON4oz#q+T1Fr@QF8~V<8%fkkJ6JvlIB1i zmxR|{=vKC|#0N*#Wbs}^L5&o39asD8CJUOJJl1zZ~<*cz&K2VF$ro|)#B#ByeBk;)@E3RM=;z9 zmEC74ICYI_Hlj?!O(vN^)wlmD0vR F{{;m>xhDVs literal 0 HcmV?d00001 diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs index c470e9fb..6ea45831 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs @@ -1,69 +1,137 @@ module Tendermint.SDK.Test.AuthTreeStoreSpec where -import Control.Lens (iso) -import Data.Bifunctor (first) -import Data.ByteString (ByteString) -import qualified Data.Serialize as Serialize -import Data.String.Conversions (cs) -import Polysemy (runM) -import Polysemy.Error (runError) -import Tendermint.SDK.AuthTreeStore (AuthTree, eval, initAuthTree) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Errors (AppError (..), - SDKError (InternalError), - throwSDKError) -import Tendermint.SDK.Store (IsKey (..), RawKey (..), - StoreKey (..), delete, get, put, - rawStoreBeginTransaction, - rawStoreRollback, - withTransaction) +import Control.Lens (iso) +import Control.Monad (void) +import Control.Monad.IO.Class (liftIO) +import Data.Bifunctor (first) +import Data.ByteString (ByteString) +import qualified Data.Serialize as Serialize +import Data.String.Conversions (cs) +import Polysemy (Embed, Sem, runM) +import Polysemy.Error (Error, runError) +import Polysemy.Reader (Reader, runReader) +import Polysemy.Resource (Resource, + resourceToIO) +import Polysemy.Tagged (Tagged) +import Tendermint.SDK.BaseApp.Errors (AppError (..), SDKError (InternalError), + throwSDKError) +import Tendermint.SDK.BaseApp.Store (ConnectionScope (..), + IsKey (..), + MergeScopes, + RawKey (..), + RawStore, + StoreKey (..), + applyScope, + beginBlock, + commitBlock, + delete, get, + mergeScopes, put, + withSandbox, + withTransaction) +import Tendermint.SDK.BaseApp.Store.AuthTreeStore (AuthTreeGetter (..), + AuthTreeState, + evalMergeScopes, + evalTagged, + initAuthTreeState) +import Tendermint.SDK.Codec (HasCodec (..)) import Test.Hspec spec :: Spec spec = beforeAll beforeAction $ describe "AuthTreeStore" $ do + it "can fail to query an empty AuthTreeStore" $ \driver -> do - Right mv <- runM . runError . eval driver $ get storeKey IntStoreKey + Right mv <- runAuthTree driver $ applyScope @'Query $ + get storeKey IntStoreKey mv `shouldBe` Nothing + it "can set a value and query the value" $ \driver -> do - Right mv <- runM . runError . eval driver $ do + Right mv <- runAuthTree driver $ applyScope @'Consensus $ do put storeKey IntStoreKey (IntStore 1) get storeKey IntStoreKey mv `shouldBe` Just (IntStore 1) + it "can make changes and roll back" $ \driver -> do - Right mv <- runM . runError . eval driver $ do - rawStoreBeginTransaction - put storeKey IntStoreKey (IntStore 5) - get storeKey IntStoreKey - mv `shouldBe` Just (IntStore 5) + Right mv'' <- runAuthTree driver $ applyScope @'Mempool $ do + put storeKey IntStoreKey (IntStore 1) + withSandbox $ do + put storeKey IntStoreKey (IntStore 5) + mv <- get storeKey IntStoreKey + liftIO (mv `shouldBe` Just (IntStore 5)) - Right mv' <- runM . runError . eval driver $ do - delete storeKey IntStoreKey - get storeKey IntStoreKey - mv' `shouldBe` Nothing + delete storeKey IntStoreKey + mv' <- get storeKey IntStoreKey - Right mv'' <- runM . runError . eval driver $ do - rawStoreRollback + liftIO (mv' `shouldBe` Nothing) get storeKey IntStoreKey mv'' `shouldBe` Just (IntStore 1) + it "can roll back if an error occurs during a transaction" $ \driver -> do - Left apperr <- runM . runError . eval driver . withTransaction True $ do - put storeKey IntStoreKey (IntStore 5) - throwSDKError InternalError + Left apperr <- runAuthTree driver $ applyScope @'Consensus $ do + put storeKey IntStoreKey (IntStore 1) + withTransaction $ do + put storeKey IntStoreKey (IntStore 6) + throwSDKError InternalError appErrorCode apperr `shouldBe` 1 - Right mv <- runM . runError . eval driver $ get storeKey IntStoreKey + Right mv <- runAuthTree driver $ applyScope @'Consensus $ + get storeKey IntStoreKey mv `shouldBe` Just (IntStore 1) + it "can make changes with a transaction" $ \driver -> do - Right mv <- runM . runError . eval driver . withTransaction True $ do + Right mv <- runAuthTree driver . applyScope @'Consensus . withTransaction $ do put storeKey IntStoreKey (IntStore 5) get storeKey IntStoreKey mv `shouldBe` Just (IntStore 5) + it "can merge the scopes" $ \driver -> do + -- set all to be initially the same value + void $ runAuthTree driver . applyScope @'Query $ + put storeKey IntStoreKey (IntStore 0) + void $ runAuthTree driver . applyScope @'Mempool $ + put storeKey IntStoreKey (IntStore 0) + void $ runAuthTree driver . applyScope @'Consensus $ + put storeKey IntStoreKey (IntStore 0) + + -- see what happens with a sandboxed checktx + void $ runAuthTree driver . applyScope @'Mempool $ + withSandbox $ put storeKey IntStoreKey (IntStore 1) + + void $ runAuthTree driver . applyScope @'Query $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) + void $ runAuthTree driver . applyScope @'Mempool $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) + void $ runAuthTree driver . applyScope @'Consensus $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) + + + void $ runAuthTree driver . applyScope @'Consensus $ do + beginBlock + withTransaction $ put storeKey IntStoreKey (IntStore 1) + commitBlock + + void $ runAuthTree driver . applyScope @'Query $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) + void $ runAuthTree driver . applyScope @'Mempool $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) + void $ runAuthTree driver . applyScope @'Consensus $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) -beforeAction :: IO AuthTree -beforeAction = initAuthTree + void $ runAuthTree driver . applyScope @'Consensus $ mergeScopes -newtype IntStore = IntStore Int deriving (Eq, Show, Serialize.Serialize) + void $ runAuthTree driver . applyScope @'Query $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) + void $ runAuthTree driver . applyScope @'Mempool $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) + void $ runAuthTree driver . applyScope @'Consensus $ + get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) + + + + +beforeAction :: IO AuthTreeState +beforeAction = initAuthTreeState + +newtype IntStore = IntStore Int deriving (Eq, Show, Num, Serialize.Serialize) data IntStoreKey = IntStoreKey @@ -82,3 +150,22 @@ instance IsKey IntStoreKey "int_store" where storeKey :: StoreKey "int_store" storeKey = StoreKey "int_store" + +runAuthTree + :: AuthTreeGetter s + => AuthTreeState + -> Sem [ Tagged s RawStore + , MergeScopes + , Reader AuthTreeState + , Error AppError + , Resource + , Embed IO + ] a + -> IO (Either AppError a) +runAuthTree driver = + runM . + resourceToIO . + runError . + runReader driver . + evalMergeScopes . + evalTagged diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs index c0841c53..b4f1f77d 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs @@ -1,8 +1,9 @@ module Tendermint.SDK.Test.EventSpec where -import qualified Data.Aeson as A -import GHC.Generics (Generic) -import Tendermint.SDK.Events (FromEvent (..), ToEvent (..), makeEvent) +import qualified Data.Aeson as A +import GHC.Generics (Generic) +import Tendermint.SDK.BaseApp.Events (FromEvent (..), ToEvent (..), + makeEvent) import Test.Hspec spec :: Spec diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs index dd3c8095..a0b7b28b 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs @@ -2,14 +2,14 @@ module Tendermint.SDK.Test.GasSpec where -import Control.Monad.IO.Class (MonadIO (..)) -import Data.Either (isRight) -import Data.Int (Int64) -import qualified Data.IORef as Ref +import Control.Monad.IO.Class (MonadIO (..)) +import Data.Either (isRight) +import Data.Int (Int64) +import qualified Data.IORef as Ref import Polysemy -import Polysemy.Error (Error, runError) -import Tendermint.SDK.Errors (AppError (..)) -import qualified Tendermint.SDK.Gas as G +import Polysemy.Error (Error, runError) +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import qualified Tendermint.SDK.BaseApp.Gas as G import Test.Hspec data Dog m a where diff --git a/stack.yaml b/stack.yaml index 1640e212..44c480b0 100644 --- a/stack.yaml +++ b/stack.yaml @@ -26,7 +26,8 @@ extra-deps: commit: 3f6dd6f612cf2eba3c05798926ff924b0d5ab4fa - git: https://github.com/awakesecurity/proto3-wire commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 - - polysemy-1.2.0.0 + - polysemy-1.2.3.0 + - polysemy-zoo-0.6.0.0 explicit-setup-deps: hs-abci-server: true diff --git a/stack.yaml.lock b/stack.yaml.lock index e379ced0..327e9dd5 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -82,12 +82,19 @@ packages: git: https://github.com/awakesecurity/proto3-wire commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 - completed: - hackage: polysemy-1.2.0.0@sha256:9313315be0746936c65bc02a5b9bc2abd3bd02bcef9329db272a991939e5b037,5776 + hackage: polysemy-1.2.3.0@sha256:d9cfa7942940c7c6d07d1f26ae70c4f1170f9bd6c331bdbe586e810fafc25f17,5878 pantry-tree: - size: 3622 - sha256: d7b017cc60394fd87330c577e8c8e48e0fcff3f72cb6c518a20a7fd5cfc8ddab + size: 3625 + sha256: a54b1b565848944e37a5533bd91e91ecb7cdfa21294ba599c13d015d354c4f39 original: - hackage: polysemy-1.2.0.0 + hackage: polysemy-1.2.3.0 +- completed: + hackage: polysemy-zoo-0.6.0.0@sha256:44595a96a37b9e33edb87c9f7ff79f8d10f6453d826bc9881b00d8988b69729a,3852 + pantry-tree: + size: 3012 + sha256: 9a8ddbf6c0a5ed2e254202c2990aae99dc4a66fa24949622a123c48795ec6547 + original: + hackage: polysemy-zoo-0.6.0.0 snapshots: - completed: size: 524786 From 129f5402e262c2ded1e7f088e80b932dbed904ea Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 10 Dec 2019 07:51:25 -0800 Subject: [PATCH 20/70] Delete wreq.png --- hs-abci-sdk/src/wreq.png | Bin 15524 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 hs-abci-sdk/src/wreq.png diff --git a/hs-abci-sdk/src/wreq.png b/hs-abci-sdk/src/wreq.png deleted file mode 100644 index 6a5ccff20b1379b53ff063536004b556cc469024..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15524 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 F{{;m>xhDVs From 7b6143871b2b89889fc1a7bc6c7fbd555d3de833 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Thu, 12 Dec 2019 09:38:40 -0800 Subject: [PATCH 21/70] remove cereal, make auth a real module (#147) * remove cereal, make auth a real module * add ceral back to simple storage * remove comment --- hs-abci-examples/nameservice/package.yaml | 1 - .../Nameservice/Modules/Nameservice/Query.hs | 3 +- .../Nameservice/Modules/Nameservice/Types.hs | 2 +- .../test/Nameservice/Test/E2ESpec.hs | 11 +- hs-abci-examples/simple-storage/package.yaml | 1 - .../Modules/SimpleStorage/Types.hs | 1 + .../simple-storage/src/SimpleStorage/Types.hs | 1 - hs-abci-sdk/package.yaml | 6 +- hs-abci-sdk/protos/modules/auth.proto | 12 ++ .../Tendermint/SDK/Application/Handlers.hs | 5 +- .../src/Tendermint/SDK/Application/Module.hs | 27 +++- .../src/Tendermint/SDK/BaseApp/Errors.hs | 7 +- hs-abci-sdk/src/Tendermint/SDK/Crypto.hs | 15 +- .../src/Tendermint/SDK/Modules/Auth.hs | 133 +++++------------- .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 33 +++++ .../src/Tendermint/SDK/Modules/Auth/Query.hs | 23 +++ .../src/Tendermint/SDK/Modules/Auth/Types.hs | 68 +++++++++ .../src/Tendermint/SDK/Types/Message.hs | 3 + .../src/Tendermint/SDK/Types/Transaction.hs | 5 + .../test/Tendermint/SDK/Test/CryptoSpec.hs | 12 +- 20 files changed, 238 insertions(+), 131 deletions(-) delete mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs create mode 100644 hs-abci-sdk/protos/modules/auth.proto create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 30db5e5a..a7fea215 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -50,7 +50,6 @@ dependencies: - aeson-casing - base >= 4.7 && < 5 - bytestring -- cereal - data-default-class - errors - exceptions diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index 3c00ac49..d76936d0 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -5,7 +5,6 @@ import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Name, Whois) import Polysemy (Members, Sem) import Polysemy.Error (Error) -import Servant.API ((:>)) import qualified Tendermint.SDK.BaseApp as BaseApp -------------------------------------------------------------------------------- @@ -14,7 +13,7 @@ import qualified Tendermint.SDK.BaseApp as BaseApp type NameserviceContents = '[(Name, Whois)] -type Api = "nameservice" :> BaseApp.QueryApi NameserviceContents +type Api = BaseApp.QueryApi NameserviceContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 90bbac3e..b8c9d3ea 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -52,7 +52,7 @@ instance BaseApp.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) instance BaseApp.IsKey Name NameserviceModule where - type Value Name NameserviceModule = Whois + type Value Name NameserviceModule = Whois instance BaseApp.Queryable Whois where type Name Whois = "whois" diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 3faf5018..f44b8949 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,20 +1,22 @@ module Nameservice.Test.E2ESpec where import Control.Lens ((^.)) -import Crypto.Secp256k1 (SecKey, derivePubKey, +import Crypto.Secp256k1 (CompactRecSig (..), + SecKey, derivePubKey, exportCompactRecSig, secKey) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString, snoc) import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BL +import Data.ByteString.Short (fromShort) import Data.Default.Class (def) import Data.Either (partitionEithers) import Data.Maybe (fromJust) import Data.Proxy -import qualified Data.Serialize as Serialize import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text) @@ -291,7 +293,7 @@ mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned , rawTransactionSignature = "" } sig = signRawTransaction algProxy privateKey unsigned - sign rt = rt { rawTransactionSignature = Serialize.encode $ exportCompactRecSig sig } + sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } data User = User { userPrivKey :: SecKey @@ -324,3 +326,6 @@ apiP = Proxy (getBalance :<|> getWhois) = genClient (Proxy :: Proxy RPC.TendermintM) apiP def + +encodeCompactRecSig :: CompactRecSig -> ByteString +encodeCompactRecSig (CompactRecSig r s v) = snoc (fromShort r <> fromShort s) v diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index ee5a77d8..817aeb6b 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -86,7 +86,6 @@ library: - SimpleStorage.Modules.SimpleStorage.Keeper - SimpleStorage.Modules.SimpleStorage.Router - SimpleStorage.Modules.SimpleStorage.Query - - SimpleStorage.Types generated-exposed-modules: - Proto.SimpleStorage.Messages diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs index 55502363..6d52ba79 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -8,6 +8,7 @@ import Data.ByteArray (convert) import Data.ByteString (ByteString) import Data.Int (Int32) import qualified Data.Serialize as Serialize +import qualified Data.Serialize.Text () import Data.String.Conversions (cs) import GHC.Generics (Generic) import qualified Tendermint.SDK.BaseApp as BaseApp diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs deleted file mode 100644 index 11d223bc..00000000 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Types.hs +++ /dev/null @@ -1 +0,0 @@ -module SimpleStorage.Types where diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index e9459e88..ca25d8a2 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -52,8 +52,6 @@ dependencies: - avl-auth - base >= 4.7 && < 5 - bytestring -- cereal -- cereal-text - containers - cryptonite - data-default-class @@ -128,6 +126,8 @@ library: - Tendermint.SDK.Types.TxResult generated-exposed-modules: + - Proto.Modules.Auth + - Proto.Modules.Auth_Fields - Proto.Types.Transaction - Proto.Types.Transaction_Fields @@ -150,12 +150,12 @@ tests: - -with-rtsopts=-N dependencies: + - cereal - generic-arbitrary - hs-abci-server - hs-abci-sdk - hspec - hspec-core - hspec-discover - - cereal - QuickCheck - quickcheck-instances diff --git a/hs-abci-sdk/protos/modules/auth.proto b/hs-abci-sdk/protos/modules/auth.proto new file mode 100644 index 00000000..be8369f9 --- /dev/null +++ b/hs-abci-sdk/protos/modules/auth.proto @@ -0,0 +1,12 @@ +syntax = "proto3"; +package Auth; + +message Coin { + string denomination = 1; + uint64 amount = 2; +} + +message Account { + repeated Coin coins = 1; + uint64 nonce = 2; +} \ No newline at end of file diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index 788fcc1b..3ecce151 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -32,7 +32,6 @@ import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) import qualified Tendermint.SDK.BaseApp.Store as Store import Tendermint.SDK.Crypto (RecoverableSignatureSchema, SignatureSchema (..)) -import Tendermint.SDK.Modules.Auth (AuthError) import Tendermint.SDK.Types.TxResult (TxResult, checkTxTxResult, deliverTxTxResult, @@ -82,7 +81,7 @@ data HandlersContext alg ms r core = HandlersContext -- Common function between checkTx and deliverTx makeHandlers :: forall alg ms r core. - Member (Error AuthError) r + Member (Error AppError) r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => M.TxRouter ms r @@ -156,7 +155,7 @@ makeHandlers HandlersContext{..} = makeApp :: forall alg ms r core. - Member (Error AuthError) r + Member (Error AppError) r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => M.TxRouter ms r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 5a1c0036..8255a1f1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -6,6 +6,7 @@ module Tendermint.SDK.Application.Module , queryRouter , TxRouter , txRouter + , voidRouter ) where import Crypto.Hash (Digest) @@ -14,18 +15,18 @@ import Data.ByteString (ByteString) import Data.Proxy import Data.String.Conversions (cs) import Data.Validation (Validation (..)) +import Data.Void import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) import Polysemy (EffectRow, Member, Sem) -import Polysemy.Error (Error, throw) +import Polysemy.Error (Error) import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), +import Tendermint.SDK.BaseApp (AppError, SDKError (..), throwSDKError) import qualified Tendermint.SDK.BaseApp.Query as Q import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Crypto (RecoverableSignatureSchema, SignatureSchema (..)) -import Tendermint.SDK.Modules.Auth (AuthError (..)) import Tendermint.SDK.Types.Message (Msg (..), ValidateMessage (..), formatMessageSemanticError) @@ -66,8 +67,8 @@ instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api r ': m' ': txRouter :: forall alg ms r . - Member (Error AuthError) r - => RecoverableSignatureSchema alg + RecoverableSignatureSchema alg + => Member (Error AppError) r => Message alg ~ Digest SHA256 => TxRouter ms r => Proxy alg @@ -77,7 +78,7 @@ txRouter txRouter (p :: Proxy alg) ms bs = let etx = decode bs >>= parseTx p in case etx of - Left errMsg -> throw $ TransactionParseError errMsg + Left errMsg -> throwSDKError $ ParseError ("Transaction ParseError: " <> errMsg) Right tx -> routeTx ms tx class TxRouter ms r where @@ -87,6 +88,11 @@ instance (Member (Error AppError) r) => TxRouter '[] r where routeTx NilModules Tx{txRoute} = throwSDKError $ UnmatchedRoute txRoute +instance (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void api r ': ms) r where + routeTx (ConsModule _ rest) tx@Tx{txRoute} + | symbolVal (Proxy :: Proxy name) == cs txRoute = throwSDKError $ UnmatchedRoute txRoute + | otherwise = routeTx rest tx + instance (Member (Error AppError) r, TxRouter ms r, HasCodec msg, ValidateMessage msg, KnownSymbol name) => TxRouter (Module name msg api r ': ms) r where routeTx (ConsModule m rest) tx@Tx{..} | symbolVal (Proxy :: Proxy name) == cs txRoute = do @@ -101,3 +107,12 @@ instance (Member (Error AppError) r, TxRouter ms r, HasCodec msg, ValidateMessag Success _ -> pure () moduleRouter m tx' | otherwise = routeTx rest tx + +voidRouter + :: forall a r. + RoutedTx Void + -> Sem r a +voidRouter (RoutedTx tx) = + let Tx{txMsg} = tx + Msg{msgData} = txMsg + in pure $ absurd msgData diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index 97ac827b..7a23a383 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -94,7 +94,7 @@ data SDKError = -- ^ The name of the route that failed to match. | OutOfGasException | MessageValidation [Text] - -- ^ Message validation errors. + | SignatureRecoveryError Text -- | As of right now it's not expected that one can recover from an 'SDKError', -- | so we are throwing them as 'AppError's directly. @@ -134,3 +134,8 @@ instance IsAppError SDKError where , appErrorCodespace = "sdk" , appErrorMessage = "Message failed validation: " <> intercalate "\n" errors } + makeAppError (SignatureRecoveryError msg) = AppError + { appErrorCode = 6 + , appErrorCodespace = "sdk" + , appErrorMessage = "Signature Recovery Error: " <> msg + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs b/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs index 97c05ac4..40461f20 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs @@ -6,7 +6,7 @@ module Tendermint.SDK.Crypto , Secp256k1 ) where -import Control.Error (hush, note) +import Control.Error (note) import Crypto.Hash (Digest, hashWith) import Crypto.Hash.Algorithms (Keccak_256 (..), SHA256) @@ -14,9 +14,9 @@ import qualified Crypto.Secp256k1 as Secp256k1 import Data.ByteArray (convert) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteString as B +import qualified Data.ByteString.Short as Short import Data.Maybe (fromMaybe) import Data.Proxy -import qualified Data.Serialize as Serialize import Data.Text (Text) import qualified Network.ABCI.Types.Messages.FieldTypes as FT import Tendermint.SDK.Types.Address (Address, @@ -77,9 +77,14 @@ instance RecoverableSignatureSchema Secp256k1 where signRecoverableMessage _ priv dig = Secp256k1.signRecMsg priv (msgFromSHA256 dig) recover _ sig dig = Secp256k1.recover sig (msgFromSHA256 dig) - -- NOTE: I think the use of Data.Serialize is harmless here, because it basically - -- just peels off bytes - makeRecoverableSignature _ bs = Secp256k1.importCompactRecSig =<< hush (Serialize.decode bs) + makeRecoverableSignature _ bs = + let (r,rest) = B.splitAt 32 bs + (s,v) = B.splitAt 32 rest + in if B.length r /= 32 || B.length s /= 32 || B.length v /= 1 + then Nothing + else Secp256k1.importCompactRecSig $ + Secp256k1.CompactRecSig (Short.toShort r) (Short.toShort s) (B.head v) + parsePubKey :: SignatureSchema alg diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index b45a63e9..2b63e0d7 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -1,101 +1,32 @@ -{-# LANGUAGE TemplateHaskell #-} - -module Tendermint.SDK.Modules.Auth where - -import Data.Bifunctor (first) -import Data.Proxy -import qualified Data.Serialize as Serialize -import Data.Serialize.Text () -import Data.String.Conversions (cs) -import Data.Text (Text) -import Data.Word -import GHC.Generics (Generic) -import GHC.TypeLits (symbolVal) -import Polysemy -import Polysemy.Error (Error, mapError) -import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..), - IsKey (..), Queryable (..), - RawStore, StoreKey (..), get, - put) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address) - --------------------------------------------------------------------------------- - -data AuthModule -type AuthModuleSym = "auth" - --------------------------------------------------------------------------------- - -data Coin = Coin - { coinDenomination :: Text - , coinAmount :: Word64 - } deriving Generic - -instance Serialize.Serialize Coin - -data Account = Account - { coins :: [Coin] - , nonce :: Word64 - } deriving Generic - -instance Serialize.Serialize Account - -instance HasCodec Account where - encode = Serialize.encode - decode = first cs . Serialize.decode - -instance IsKey Address AuthModule where - type Value Address AuthModule = Account - -instance Queryable Account where - type Name Account = "account" - --------------------------------------------------------------------------------- - -data AuthError = - RecoveryError Text - | TransactionParseError Text - -instance IsAppError AuthError where - makeAppError (RecoveryError msg) = AppError - { appErrorCode = 1 - , appErrorCodespace = cs . symbolVal $ (Proxy :: Proxy AuthModuleSym) - , appErrorMessage = "Signature Recovery Error: " <> msg - } - makeAppError (TransactionParseError msg) = AppError - { appErrorCode = 2 - , appErrorCodespace = cs . symbolVal $ (Proxy :: Proxy AuthModuleSym) - , appErrorMessage = msg - } - --------------------------------------------------------------------------------- - -data Accounts m a where - PutAccount :: Address -> Account -> Accounts m () - GetAccount :: Address -> Accounts m (Maybe Account) - -makeSem ''Accounts - -type AuthEffs = [Accounts, Error AuthError] - -storeKey :: StoreKey AuthModule -storeKey = StoreKey "auth" - -eval - :: Members [RawStore, Error AppError] r - => Sem (Accounts ': Error AuthError ': r) a - -> Sem r a -eval = mapError makeAppError . evalAuth - where - evalAuth - :: Members [RawStore, Error AppError] r - => Sem (Accounts ': r) a - -> Sem r a - evalAuth = - interpret (\case - GetAccount addr -> - get storeKey addr - PutAccount addr acnt -> - put storeKey addr acnt - ) +module Tendermint.SDK.Modules.Auth + ( authModule + + , AuthEffs + , Accounts + , getAccount + , putAccount + , eval + + , Api + , server + + , module Tendermint.SDK.Modules.Auth.Types + ) where + +import Data.Void +import Polysemy (Members) +import Tendermint.SDK.Application.Module (Module (..), voidRouter) +import Tendermint.SDK.BaseApp (BaseAppEffs) +import Tendermint.SDK.Modules.Auth.Keeper +import Tendermint.SDK.Modules.Auth.Query +import Tendermint.SDK.Modules.Auth.Types + +type AuthM r = Module AuthModule Void Api r + +authModule + :: Members BaseAppEffs r + => AuthM r +authModule = Module + { moduleRouter = voidRouter + , moduleQueryServer = server + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs new file mode 100644 index 00000000..1af9a87a --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.Modules.Auth.Keeper where + +import Polysemy +import Polysemy.Error (Error) +import Tendermint.SDK.BaseApp (AppError, RawStore, + StoreKey (..), get, put) +import Tendermint.SDK.Modules.Auth.Types +import Tendermint.SDK.Types.Address (Address) + +data Accounts m a where + PutAccount :: Address -> Account -> Accounts m () + GetAccount :: Address -> Accounts m (Maybe Account) + +makeSem ''Accounts + +type AuthEffs = '[Accounts] + +storeKey :: StoreKey AuthModule +storeKey = StoreKey "auth" + +eval + :: Members [RawStore, Error AppError] r + => Sem (Accounts : r) a + -> Sem r a +eval = + interpret (\case + GetAccount addr -> + get storeKey addr + PutAccount addr acnt -> + put storeKey addr acnt + ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs new file mode 100644 index 00000000..9acb6a88 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs @@ -0,0 +1,23 @@ +module Tendermint.SDK.Modules.Auth.Query where + +import Data.Proxy +import Polysemy (Members, Sem) +import Polysemy.Error (Error) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Modules.Auth.Keeper (storeKey) +import Tendermint.SDK.Modules.Auth.Types (Account) +import Tendermint.SDK.Types.Address (Address) + +-------------------------------------------------------------------------------- +-- | Query API +-------------------------------------------------------------------------------- + +type AuthContents = '[(Address, Account)] + +type Api = BaseApp.QueryApi AuthContents + +server + :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + => BaseApp.RouteT Api (Sem r) +server = + BaseApp.storeQueryHandlers (Proxy :: Proxy AuthContents) storeKey (Proxy :: Proxy (Sem r)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs new file mode 100644 index 00000000..0e310347 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -0,0 +1,68 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Tendermint.SDK.Modules.Auth.Types where + +import Control.Lens (Wrapped (..), from, iso, view, + (&), (.~), (^.), (^..), + _Unwrapped') +import Data.Bifunctor (bimap) +import qualified Data.ProtoLens as P +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word +import GHC.Generics (Generic) +import qualified Proto.Modules.Auth as A +import qualified Proto.Modules.Auth_Fields as A +import Tendermint.SDK.BaseApp (IsKey (..), Queryable (..)) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) + +type AuthModule = "auth" + +data Coin = Coin + { coinDenomination :: Text + , coinAmount :: Word64 + } deriving Generic + +instance Wrapped Coin where + type Unwrapped Coin = A.Coin + + _Wrapped' = iso t f + where + t Coin {..} = + P.defMessage + & A.denomination .~ coinDenomination + & A.amount .~ coinAmount + f message = Coin + { coinDenomination = message ^. A.denomination + , coinAmount = message ^. A.amount + } + +data Account = Account + { accountCoins :: [Coin] + , accountNonce :: Word64 + } deriving Generic + +instance Wrapped Account where + type Unwrapped Account = A.Account + + _Wrapped' = iso t f + where + t Account {..} = + P.defMessage + & A.coins .~ accountCoins ^.. traverse . _Wrapped' + & A.nonce .~ accountNonce + f message = Account + { accountCoins = message ^.. A.coins. traverse . _Unwrapped' + , accountNonce = message ^. A.nonce + } + +instance HasCodec Account where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + +instance IsKey Address AuthModule where + type Value Address AuthModule = Account + +instance Queryable Account where + type Name Account = "account" diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs index 7e77db98..a80127f1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs @@ -13,6 +13,9 @@ data Msg msg = Msg , msgData :: msg } +instance Functor Msg where + fmap f msg@Msg{msgData} = msg {msgData = f msgData} + -- | This is a general error type, primarily accomodating protobuf messages being parsed -- | by either the [proto3-wire](https://hackage.haskell.org/package/proto3-wire) -- | or the [proto-lens](https://hackage.haskell.org/package/proto-lens) libraries. diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index fe357210..b1baacd9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -30,6 +30,9 @@ data Tx alg msg = Tx , txSigner :: PubKey alg } +instance Functor (Tx alg) where + fmap f tx@Tx{txMsg} = tx {txMsg = fmap f txMsg} + -------------------------------------------------------------------------------- -- TODO: figure out what the actual standards are for these things, if there @@ -108,3 +111,5 @@ parseTx p rawTx@RawTransaction{..} = do data RoutedTx msg where RoutedTx :: Tx alg msg -> RoutedTx msg +instance Functor RoutedTx where + fmap f (RoutedTx tx) = RoutedTx $ fmap f tx diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs index 2d6360bf..c40d2208 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs @@ -1,11 +1,13 @@ module Tendermint.SDK.Test.CryptoSpec where -import Crypto.Secp256k1 (SecKey, derivePubKey, +import Crypto.Secp256k1 (CompactRecSig (..), SecKey, + derivePubKey, exportCompactRecSig, secKey) import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString, snoc) +import Data.ByteString.Short (fromShort) import Data.Maybe (fromJust) import Data.Proxy -import qualified Data.Serialize as Serialize import Data.String (fromString) import Tendermint.SDK.Crypto (Secp256k1) import Tendermint.SDK.Types.Transaction @@ -20,7 +22,8 @@ spec = describe "Crypto Tests" $ do , rawTransactionRoute= "dog" } signature = signRawTransaction algProxy privateKey rawTxWithoutSig - rawTxWithSig = rawTxWithoutSig {rawTransactionSignature = Serialize.encode $ exportCompactRecSig signature} + rawTxWithSig = rawTxWithoutSig {rawTransactionSignature = + encodeCompactSig $ exportCompactRecSig signature} eTx = parseTx algProxy rawTxWithSig Tx{..} = case eTx of Left errMsg -> error $ show errMsg @@ -33,3 +36,6 @@ privateKey = fromJust . secKey . Hex.toBytes . fromString $ algProxy :: Proxy Secp256k1 algProxy = Proxy + +encodeCompactSig :: CompactRecSig -> ByteString +encodeCompactSig (CompactRecSig r s v) = snoc (fromShort r <> fromShort s) v From a3115ff33a05bc34b69df1ce6278a880c655abbe Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Thu, 12 Dec 2019 11:38:08 -0800 Subject: [PATCH 22/70] Separate tx checker (#148) * separate module methods for deliver and checktx * fix nameservice check response codes * stylish --- .../src/Nameservice/Modules/Nameservice.hs | 6 ++- .../src/Nameservice/Modules/Token.hs | 6 ++- .../test/Nameservice/Test/E2ESpec.hs | 18 ++++--- .../SimpleStorage/Modules/SimpleStorage.hs | 6 ++- hs-abci-sdk/src/Tendermint/SDK/Application.hs | 1 + .../Tendermint/SDK/Application/Handlers.hs | 6 +-- .../src/Tendermint/SDK/Application/Module.hs | 48 ++++++++++++------- .../src/Tendermint/SDK/Modules/Auth.hs | 3 +- 8 files changed, 62 insertions(+), 32 deletions(-) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index bce0670d..6002c52f 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -43,7 +43,8 @@ import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) -import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.Application (Module (..), + defaultTxChecker) import Tendermint.SDK.BaseApp (BaseAppEffs) type NameserviceM r = Module "nameservice" NameserviceMessage Api r @@ -54,6 +55,7 @@ nameserviceModule => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module - { moduleRouter = router + { moduleTxDeliverer = router + , moduleTxChecker = defaultTxChecker , moduleQueryServer = server } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 4b87de77..4469d997 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -40,7 +40,8 @@ import Nameservice.Modules.Token.Query import Nameservice.Modules.Token.Router import Nameservice.Modules.Token.Types import Polysemy (Members) -import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.Application (Module (..), + defaultTxChecker) import Tendermint.SDK.BaseApp (BaseAppEffs) import Tendermint.SDK.Types.Address (Address) @@ -51,6 +52,7 @@ tokenModule => Members TokenEffs r => TokenM r tokenModule = Module - { moduleRouter = router + { moduleTxDeliverer = router + , moduleTxChecker = defaultTxChecker , moduleQueryServer = server } diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index f44b8949..f24f16cd 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -118,8 +118,7 @@ spec = do -- try to set a name without being the owner let msg = TypedMessage "SetName" (encode $ SetName satoshi addr2 "goodbye to a world") rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg - checkResp <- getCheckTxResponse rawTx - ensureCheckResponseCode checkResp 2 + ensureCheckAndDeliverResponseCodes (0,2) rawTx it "Can buy an existing name (success 0)" $ do let oldVal = "goodbye to a world" @@ -165,8 +164,7 @@ spec = do -- try to buy at a lower price let msg = TypedMessage "BuyName" (encode $ BuyName 100 satoshi "hello (again) world" addr1) rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg - checkResp <- getCheckTxResponse rawTx - ensureCheckResponseCode checkResp 1 + ensureCheckAndDeliverResponseCodes (0,1) rawTx it "Can delete names (success 0)" $ do let msg = TypedMessage "DeleteName" (encode $ DeleteName addr2 satoshi) @@ -186,8 +184,7 @@ spec = do it "Can fail a transfer (failure 1)" $ do let msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 2000) rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg - checkResp <- getCheckTxResponse rawTx - ensureCheckResponseCode checkResp 1 + ensureCheckAndDeliverResponseCodes (0,1) rawTx it "Can transfer (success 0)" $ do let senderBeforeQueryReq = defaultQueryWithData addr2 @@ -248,6 +245,15 @@ getDeliverTxResponse rawTx = do fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ RPC.broadcastTxCommit txReq +ensureCheckAndDeliverResponseCodes :: (Word32, Word32) -> RawTransaction -> IO () +ensureCheckAndDeliverResponseCodes codes rawTx = do + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + resp <- runRPC $ RPC.broadcastTxCommit txReq + let checkResp = RPC.resultBroadcastTxCommitCheckTx resp + deliverResp = RPC.resultBroadcastTxCommitDeliverTx resp + codes `shouldBe` (checkResp ^. Response._checkTxCode, deliverResp ^. Response._deliverTxCode) + + -- get the logged events from a deliver response, deliverTxEvents :: FromEvent e => Response.DeliverTx -> Text -> IO ([Text],[e]) deliverTxEvents deliverResp eventName = do diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index 2dfab3bc..1bdd7371 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -15,7 +15,8 @@ import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Query (Api, server) import SimpleStorage.Modules.SimpleStorage.Router (router) import SimpleStorage.Modules.SimpleStorage.Types -import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.Application (Module (..), + defaultTxChecker) import qualified Tendermint.SDK.BaseApp as BaseApp type SimpleStorageM r = Module "simple_storage" SimpleStorageMessage Api r @@ -25,6 +26,7 @@ simpleStorageModule => Members BaseApp.BaseAppEffs r => SimpleStorageM r simpleStorageModule = Module - { moduleRouter = router + { moduleTxDeliverer = router + , moduleTxChecker = defaultTxChecker , moduleQueryServer = server } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application.hs b/hs-abci-sdk/src/Tendermint/SDK/Application.hs index 8d878f19..c939e35d 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application.hs @@ -1,6 +1,7 @@ module Tendermint.SDK.Application ( Modules(..) , Module(..) + , defaultTxChecker , HandlersContext(..) , createIOApp , makeApp diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index 3ecce151..04b75c77 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -92,7 +92,7 @@ makeHandlers -> Handlers core makeHandlers HandlersContext{..} = let - txRouter = M.txRouter signatureAlgP modules + txRouter context = M.txRouter signatureAlgP context modules queryRouter = compileToBaseApp . M.queryRouter modules query (RequestQuery q) = Store.applyScope $ @@ -110,7 +110,7 @@ makeHandlers HandlersContext{..} = checkTx (RequestCheckTx _checkTx) = Store.applyScope $ catch (do - txResult <- transactionHandler txRouter $ + txResult <- transactionHandler (txRouter M.CheckTxContext) $ _checkTx ^. Req._checkTxTx . to Base64.toBytes return $ ResponseCheckTx $ def & checkTxTxResult .~ txResult ) @@ -121,7 +121,7 @@ makeHandlers HandlersContext{..} = deliverTx (RequestDeliverTx _deliverTx) = Store.applyScope $ catch @AppError (do - txResult <- transactionHandler txRouter $ + txResult <- transactionHandler (txRouter M.DeliverTxContext) $ _deliverTx ^. Req._deliverTxTx . to Base64.toBytes return $ ResponseDeliverTx $ def & deliverTxTxResult .~ txResult ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 8255a1f1..e86c6c59 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -1,9 +1,11 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.Application.Module ( Module(..) + , defaultTxChecker , Modules(..) , QueryRouter(Api) , queryRouter + , TxRouteContext(..) , TxRouter , txRouter , voidRouter @@ -14,7 +16,7 @@ import Crypto.Hash.Algorithms (SHA256) import Data.ByteString (ByteString) import Data.Proxy import Data.String.Conversions (cs) -import Data.Validation (Validation (..)) +import qualified Data.Validation as V import Data.Void import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) @@ -34,10 +36,22 @@ import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..), parseTx) data Module (name :: Symbol) msg (api :: *) (r :: EffectRow) = Module - { moduleRouter :: RoutedTx msg -> Sem r () + { moduleTxDeliverer :: RoutedTx msg -> Sem r () + , moduleTxChecker :: RoutedTx msg -> Sem r () , moduleQueryServer :: Q.RouteT api (Sem r) } +defaultTxChecker + :: Member (Error AppError) r + => ValidateMessage msg + => RoutedTx msg + -> Sem r () +defaultTxChecker (RoutedTx Tx{txMsg}) = + case validateMessage txMsg of + V.Failure err -> + throwSDKError . MessageValidation . map formatMessageSemanticError $ err + V.Success _ -> pure () + data Modules (ms :: [*]) r where NilModules :: Modules '[] r ConsModule :: Module name msg api r -> Modules ms r -> Modules (Module name msg api r ': ms) r @@ -65,6 +79,9 @@ instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api r ': m' ': -------------------------------------------------------------------------------- +data TxRouteContext = + CheckTxContext | DeliverTxContext + txRouter :: forall alg ms r . RecoverableSignatureSchema alg @@ -72,41 +89,40 @@ txRouter => Message alg ~ Digest SHA256 => TxRouter ms r => Proxy alg + -> TxRouteContext -> Modules ms r -> ByteString -> Sem r () -txRouter (p :: Proxy alg) ms bs = +txRouter (p :: Proxy alg) routeContext ms bs = let etx = decode bs >>= parseTx p in case etx of Left errMsg -> throwSDKError $ ParseError ("Transaction ParseError: " <> errMsg) - Right tx -> routeTx ms tx + Right tx -> routeTx routeContext ms tx class TxRouter ms r where - routeTx :: forall alg. Modules ms r -> Tx alg ByteString -> Sem r () + routeTx :: forall alg. TxRouteContext -> Modules ms r -> Tx alg ByteString -> Sem r () instance (Member (Error AppError) r) => TxRouter '[] r where - routeTx NilModules Tx{txRoute} = + routeTx _ NilModules Tx{txRoute} = throwSDKError $ UnmatchedRoute txRoute instance (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void api r ': ms) r where - routeTx (ConsModule _ rest) tx@Tx{txRoute} + routeTx routeContext (ConsModule _ rest) tx@Tx{txRoute} | symbolVal (Proxy :: Proxy name) == cs txRoute = throwSDKError $ UnmatchedRoute txRoute - | otherwise = routeTx rest tx + | otherwise = routeTx routeContext rest tx -instance (Member (Error AppError) r, TxRouter ms r, HasCodec msg, ValidateMessage msg, KnownSymbol name) => TxRouter (Module name msg api r ': ms) r where - routeTx (ConsModule m rest) tx@Tx{..} +instance (Member (Error AppError) r, TxRouter ms r, HasCodec msg, KnownSymbol name) => TxRouter (Module name msg api r ': ms) r where + routeTx routeContext (ConsModule m rest) tx@Tx{..} | symbolVal (Proxy :: Proxy name) == cs txRoute = do msg <- case decode $ msgData txMsg of Left err -> throwSDKError $ ParseError err Right (msg :: msg) -> return msg let msg' = txMsg {msgData = msg} tx' = RoutedTx $ tx {txMsg = msg'} - case validateMessage msg' of - Failure err -> - throwSDKError . MessageValidation . map formatMessageSemanticError $ err - Success _ -> pure () - moduleRouter m tx' - | otherwise = routeTx rest tx + case routeContext of + CheckTxContext -> moduleTxChecker m tx' + DeliverTxContext -> moduleTxDeliverer m tx' + | otherwise = routeTx routeContext rest tx voidRouter :: forall a r. diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 2b63e0d7..1a730587 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -27,6 +27,7 @@ authModule :: Members BaseAppEffs r => AuthM r authModule = Module - { moduleRouter = voidRouter + { moduleTxDeliverer = voidRouter + , moduleTxChecker = voidRouter , moduleQueryServer = server } From c7aaa87a1884fe292d3159f1dc2817e34f2b7ca1 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Fri, 13 Dec 2019 10:41:22 -0800 Subject: [PATCH 23/70] apps compile with generated eval function (#149) * apps compile with generated eval function * better syntax * added voidModuleMessages * syntax error --- .../src/Nameservice/Application.hs | 24 ++++++---- .../src/Nameservice/Modules/Nameservice.hs | 4 +- .../src/Nameservice/Modules/Token.hs | 3 +- .../src/SimpleStorage/Application.hs | 27 ++++++----- .../SimpleStorage/Modules/SimpleStorage.hs | 12 ++--- .../Modules/SimpleStorage/Keeper.hs | 3 ++ .../Tendermint/SDK/Application/Handlers.hs | 29 ++++++------ .../src/Tendermint/SDK/Application/Module.hs | 47 +++++++++++++++---- .../src/Tendermint/SDK/Modules/Auth.hs | 6 ++- 9 files changed, 98 insertions(+), 57 deletions(-) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 7032224a..e1cf79f9 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -9,9 +9,9 @@ module Nameservice.Application import Data.Proxy import qualified Nameservice.Modules.Nameservice as N import qualified Nameservice.Modules.Token as T -import Polysemy (Sem) import Tendermint.SDK.Application (HandlersContext (..), Modules (..)) +import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp as BaseApp import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL import Tendermint.SDK.Crypto (Secp256k1) @@ -30,23 +30,27 @@ makeAppConfig logCfg = do -------------------------------------------------------------------------------- type EffR = - N.NameserviceEffs BaseApp.:& - T.TokenEffs BaseApp.:& - A.AuthEffs BaseApp.:& + N.NameserviceEffs :& + T.TokenEffs :& + A.AuthEffs :& BaseApp.BaseApp BaseApp.CoreEffs -type NameserviceModules = '[T.TokenM EffR, N.NameserviceM EffR] +type NameserviceModules = + '[ N.NameserviceM EffR + , T.TokenM EffR + , A.AuthM EffR + ] handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR BaseApp.CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules - , compileToBaseApp = compileNameserviceToBaseApp , compileToCore = BaseApp.compileScopedEff } where nameserviceModules :: Modules NameserviceModules EffR - nameserviceModules = ConsModule T.tokenModule $ ConsModule N.nameserviceModule NilModules - - compileNameserviceToBaseApp :: Sem EffR a -> Sem (BaseApp.BaseApp BaseApp.CoreEffs) a - compileNameserviceToBaseApp = A.eval . T.eval . N.eval + nameserviceModules = + ConsModule N.nameserviceModule $ + ConsModule T.tokenModule $ + ConsModule A.authModule $ + NilModules diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index 6002c52f..6541eaf8 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -47,7 +47,8 @@ import Tendermint.SDK.Application (Module (..), defaultTxChecker) import Tendermint.SDK.BaseApp (BaseAppEffs) -type NameserviceM r = Module "nameservice" NameserviceMessage Api r +type NameserviceM r = + Module "nameservice" NameserviceMessage Api NameserviceEffs r nameserviceModule :: Members BaseAppEffs r @@ -58,4 +59,5 @@ nameserviceModule = Module { moduleTxDeliverer = router , moduleTxChecker = defaultTxChecker , moduleQueryServer = server + , moduleEval = eval } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index 4469d997..a51fb3fc 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -45,7 +45,7 @@ import Tendermint.SDK.Application (Module (..), import Tendermint.SDK.BaseApp (BaseAppEffs) import Tendermint.SDK.Types.Address (Address) -type TokenM r = Module "token" TokenMessage Api r +type TokenM r = Module "token" TokenMessage Api TokenEffs r tokenModule :: Members BaseAppEffs r @@ -55,4 +55,5 @@ tokenModule = Module { moduleTxDeliverer = router , moduleTxChecker = defaultTxChecker , moduleQueryServer = server + , moduleEval = eval } diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index 7ad38a79..97cfe33a 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -1,15 +1,15 @@ module SimpleStorage.Application ( AppConfig(..) , makeAppConfig - , SimpelStorageEffs + , EffR , handlersContext ) where import Data.Proxy -import Polysemy (Sem) import SimpleStorage.Modules.SimpleStorage as SimpleStorage import Tendermint.SDK.Application (HandlersContext (..), Modules (..)) +import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp as BaseApp import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL import Tendermint.SDK.Crypto (Secp256k1) @@ -27,22 +27,25 @@ makeAppConfig logCfg = do -------------------------------------------------------------------------------- -type SimpelStorageEffs = - SimpleStorage.SimpleStorage ': A.AuthEffs BaseApp.:& BaseApp.BaseApp BaseApp.CoreEffs +type EffR = + SimpleStorage.SimpleStorageEffs :& + A.AuthEffs :& + BaseApp.BaseApp BaseApp.CoreEffs type SimpleStorageModules = - '[SimpleStorage.SimpleStorageM SimpelStorageEffs] + '[ SimpleStorage.SimpleStorageM EffR + , A.AuthM EffR + ] -handlersContext :: HandlersContext Secp256k1 SimpleStorageModules SimpelStorageEffs BaseApp.CoreEffs +handlersContext :: HandlersContext Secp256k1 SimpleStorageModules EffR BaseApp.CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = simpleStorageModules - , compileToBaseApp = compileSimpleStorageToBaseApp , compileToCore = BaseApp.compileScopedEff } where - simpleStorageModules :: Modules SimpleStorageModules SimpelStorageEffs - simpleStorageModules = ConsModule SimpleStorage.simpleStorageModule NilModules - - compileSimpleStorageToBaseApp :: Sem SimpelStorageEffs a -> Sem (BaseApp.BaseApp BaseApp.CoreEffs) a - compileSimpleStorageToBaseApp = A.eval . SimpleStorage.eval + simpleStorageModules :: Modules SimpleStorageModules EffR + simpleStorageModules = + ConsModule SimpleStorage.simpleStorageModule $ + ConsModule A.authModule $ + NilModules diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index 1bdd7371..b284a43b 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -1,16 +1,14 @@ module SimpleStorage.Modules.SimpleStorage - ( SimpleStorage - , SimpleStorageM + ( SimpleStorageM , Api , simpleStorageModule - , eval + , module SimpleStorage.Modules.SimpleStorage.Keeper , module SimpleStorage.Modules.SimpleStorage.Message , module SimpleStorage.Modules.SimpleStorage.Types ) where import Polysemy (Member, Members) -import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, - eval) +import SimpleStorage.Modules.SimpleStorage.Keeper hiding (storeKey) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Query (Api, server) import SimpleStorage.Modules.SimpleStorage.Router (router) @@ -19,7 +17,8 @@ import Tendermint.SDK.Application (Module (..), defaultTxChecker) import qualified Tendermint.SDK.BaseApp as BaseApp -type SimpleStorageM r = Module "simple_storage" SimpleStorageMessage Api r +type SimpleStorageM r = + Module "simple_storage" SimpleStorageMessage Api SimpleStorageEffs r simpleStorageModule :: Member SimpleStorage r @@ -29,4 +28,5 @@ simpleStorageModule = Module { moduleTxDeliverer = router , moduleTxChecker = defaultTxChecker , moduleQueryServer = server + , moduleEval = eval } diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs index edbec565..30465740 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -1,6 +1,7 @@ {-# LANGUAGE TemplateHaskell #-} module SimpleStorage.Modules.SimpleStorage.Keeper ( SimpleStorage + , SimpleStorageEffs , storeKey , putCount , getCount @@ -26,6 +27,8 @@ data SimpleStorage m a where makeSem ''SimpleStorage +type SimpleStorageEffs = '[SimpleStorage] + eval :: forall r. Members '[BaseApp.RawStore, Output BaseApp.Event, Error BaseApp.AppError] r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index 04b75c77..696ebf06 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -8,7 +8,6 @@ import Control.Lens (to, (&), (.~), (^.)) import Crypto.Hash (Digest) import Crypto.Hash.Algorithms (SHA256) import qualified Data.ByteArray.Base64String as Base64 -import Data.ByteString (ByteString) import Data.Default.Class (Default (..)) import Data.Proxy import Network.ABCI.Server.App (App (..), @@ -32,8 +31,7 @@ import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) import qualified Tendermint.SDK.BaseApp.Store as Store import Tendermint.SDK.Crypto (RecoverableSignatureSchema, SignatureSchema (..)) -import Tendermint.SDK.Types.TxResult (TxResult, - checkTxTxResult, +import Tendermint.SDK.Types.TxResult (checkTxTxResult, deliverTxTxResult, txResultEvents) @@ -72,10 +70,9 @@ defaultHandlers = Handlers defaultHandler = const $ pure def data HandlersContext alg ms r core = HandlersContext - { signatureAlgP :: Proxy alg - , modules :: M.Modules ms r - , compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a - , compileToCore :: forall a. BA.ScopedEff core a -> Sem core a + { signatureAlgP :: Proxy alg + , modules :: M.Modules ms r + , compileToCore :: forall a. BA.ScopedEff core a -> Sem core a } -- Common function between checkTx and deliverTx @@ -88,13 +85,21 @@ makeHandlers => M.QueryRouter ms r => HasRouter (M.Api ms) => Members CoreEffs core + => M.Eval ms core + => M.Effs ms core ~ r => HandlersContext alg ms r core -> Handlers core makeHandlers HandlersContext{..} = let + compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a + compileToBaseApp = M.eval modules txRouter context = M.txRouter signatureAlgP context modules queryRouter = compileToBaseApp . M.queryRouter modules + transactionHandler router bs = do + events <- withEventBuffer . compileToBaseApp $ router bs + pure $ def & txResultEvents .~ events + query (RequestQuery q) = Store.applyScope $ catch (do @@ -144,14 +149,6 @@ makeHandlers HandlersContext{..} = , deliverTx = deliverTx , commit = commit } - where - transactionHandler - :: (ByteString -> Sem r ()) - -> ByteString - -> Sem (BA.BaseApp core) TxResult - transactionHandler txRouter bs = do - events <- withEventBuffer . compileToBaseApp $ txRouter bs - pure $ def & txResultEvents .~ events makeApp :: forall alg ms r core. @@ -162,6 +159,8 @@ makeApp => M.QueryRouter ms r => HasRouter (M.Api ms) => Members CoreEffs core + => M.Eval ms core + => M.Effs ms core ~ r => HandlersContext alg ms r core -> App (Sem core) makeApp handlersContext@HandlersContext{compileToCore} = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index e86c6c59..4b7f7d3b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -1,6 +1,7 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.Application.Module ( Module(..) + , voidModuleMessages , defaultTxChecker , Modules(..) , QueryRouter(Api) @@ -9,6 +10,7 @@ module Tendermint.SDK.Application.Module , TxRouter , txRouter , voidRouter + , Eval(..) ) where import Crypto.Hash (Digest) @@ -20,10 +22,12 @@ import qualified Data.Validation as V import Data.Void import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) -import Polysemy (EffectRow, Member, Sem) +import Polysemy (EffectRow, Member, Members, + Sem) import Polysemy.Error (Error) import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp (AppError, SDKError (..), +import Tendermint.SDK.BaseApp ((:&), AppError, BaseApp, + BaseAppEffs, SDKError (..), throwSDKError) import qualified Tendermint.SDK.BaseApp.Query as Q import Tendermint.SDK.Codec (HasCodec (..)) @@ -35,12 +39,19 @@ import Tendermint.SDK.Types.Message (Msg (..), import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..), parseTx) -data Module (name :: Symbol) msg (api :: *) (r :: EffectRow) = Module +data Module (name :: Symbol) msg (api :: *) (s :: EffectRow) (r :: EffectRow) = Module { moduleTxDeliverer :: RoutedTx msg -> Sem r () , moduleTxChecker :: RoutedTx msg -> Sem r () , moduleQueryServer :: Q.RouteT api (Sem r) + , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } +voidModuleMessages :: Module name msg api s r -> Module name Void api s r +voidModuleMessages m = + m { moduleTxDeliverer = voidRouter + , moduleTxChecker = voidRouter + } + defaultTxChecker :: Member (Error AppError) r => ValidateMessage msg @@ -54,7 +65,7 @@ defaultTxChecker (RoutedTx Tx{txMsg}) = data Modules (ms :: [*]) r where NilModules :: Modules '[] r - ConsModule :: Module name msg api r -> Modules ms r -> Modules (Module name msg api r ': ms) r + ConsModule :: Module name msg api s r -> Modules ms r -> Modules (Module name msg api s r ': ms) r -------------------------------------------------------------------------------- @@ -69,12 +80,12 @@ class QueryRouter ms r where type Api ms :: * routeQuery :: Modules ms r -> Q.RouteT (Api ms) (Sem r) -instance QueryRouter (Module name msg api r ': '[]) r where - type Api (Module name msg api r ': '[]) = name :> api +instance QueryRouter '[Module name msg api s r] r where + type Api '[Module name msg api s r] = name :> api routeQuery (ConsModule m NilModules) = moduleQueryServer m -instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api r ': m' ': ms) r where - type Api (Module name msg api r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) +instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api s r ': m' ': ms) r where + type Api (Module name msg api s r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) routeQuery (ConsModule m rest) = moduleQueryServer m :<|> routeQuery rest -------------------------------------------------------------------------------- @@ -106,12 +117,12 @@ instance (Member (Error AppError) r) => TxRouter '[] r where routeTx _ NilModules Tx{txRoute} = throwSDKError $ UnmatchedRoute txRoute -instance (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void api r ': ms) r where +instance {-# OVERLAPPING #-} (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void api s r ': ms) r where routeTx routeContext (ConsModule _ rest) tx@Tx{txRoute} | symbolVal (Proxy :: Proxy name) == cs txRoute = throwSDKError $ UnmatchedRoute txRoute | otherwise = routeTx routeContext rest tx -instance (Member (Error AppError) r, TxRouter ms r, HasCodec msg, KnownSymbol name) => TxRouter (Module name msg api r ': ms) r where +instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCodec msg, KnownSymbol name) => TxRouter (Module name msg api s r ': ms) r where routeTx routeContext (ConsModule m rest) tx@Tx{..} | symbolVal (Proxy :: Proxy name) == cs txRoute = do msg <- case decode $ msgData txMsg of @@ -132,3 +143,19 @@ voidRouter (RoutedTx tx) = let Tx{txMsg} = tx Msg{msgData} = txMsg in pure $ absurd msgData + +-------------------------------------------------------------------------------- + +class Eval ms core where + type Effs ms core :: EffectRow + eval :: Modules ms r + -> forall a. Sem (Effs ms core) a + -> Sem (BaseApp core) a + +instance Eval '[Module name msg api s r] core where + type Effs '[Module name msg api s r] core = s :& BaseApp core + eval (ConsModule m NilModules) = moduleEval m + +instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name msg api s r ': m' ': ms) core where + type Effs (Module name msg api s r ': m' ': ms) core = s :& (Effs (m': ms)) core + eval (ConsModule m rest) = eval rest . moduleEval m diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 1a730587..60f0b2bf 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -1,5 +1,6 @@ module Tendermint.SDK.Modules.Auth - ( authModule + ( AuthM + , authModule , AuthEffs , Accounts @@ -21,7 +22,7 @@ import Tendermint.SDK.Modules.Auth.Keeper import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types -type AuthM r = Module AuthModule Void Api r +type AuthM r = Module AuthModule Void Api AuthEffs r authModule :: Members BaseAppEffs r @@ -30,4 +31,5 @@ authModule = Module { moduleTxDeliverer = voidRouter , moduleTxChecker = voidRouter , moduleQueryServer = server + , moduleEval = eval } From 98c56bf6a64a7d6c52b90ef385c0276aa2cad96a Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Wed, 18 Dec 2019 16:02:34 -0500 Subject: [PATCH 24/70] impl metrics effect (#144) * intial metrics setup * mvp for logging stdout with metrics * builds * add katip-datadog + metric logger * add metrics to nameservice * Metrics effect stub * add time to sdk * wip write metrics tests * wip add withTimer tests * remove katip from metrics * remove severity and msgtype * reword count example * wip integrate prometheus * mvar madness * Add prometheus counter inc test * Use concurrent registry * wip impl histogram summand * impl histogram summand + tests * check registry in time test * Add histogram bucket test * add metrics to nameservice * some cleanup * clean up * add very basic env var reading * fmap and stylish * Add IsString instances for metric names * add metrics to baseapp + use in nameservice * fix import * remove unnecessary import * add default histogram buckets * add datadog-agent to docker-compose * Simplify metrics effect interface; fix off by one with incCounter * add no-op interpreter and use MetricsConfig * simplify runMetricsServer callsite * add env vars for stats and dd api key * some review changes * move defaults to makefile * move stats port before export --- Makefile | 8 +- hs-abci-examples/nameservice/app/Main.hs | 29 ++- .../nameservice/docker-compose.yaml | 16 ++ .../src/Nameservice/Application.hs | 25 +-- .../Nameservice/Modules/Nameservice/Router.hs | 15 +- .../nameservice/src/Nameservice/Server.hs | 15 +- hs-abci-examples/simple-storage/app/Main.hs | 1 - .../src/SimpleStorage/Application.hs | 2 +- hs-abci-extra/package.yaml | 3 + .../ABCI/Server/Middleware/MetricsLogger.hs | 152 +++++++++++++ .../ABCI/Server/Middleware/RequestLogger.hs | 1 + hs-abci-sdk/package.yaml | 5 + hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 10 +- .../src/Tendermint/SDK/BaseApp/BaseApp.hs | 4 + .../src/Tendermint/SDK/BaseApp/CoreEff.hs | 15 +- .../src/Tendermint/SDK/BaseApp/Metrics.hs | 33 +++ .../SDK/BaseApp/Metrics/Prometheus.hs | 204 ++++++++++++++++++ .../test/Tendermint/SDK/Test/MetricsSpec.hs | 70 ++++++ stack.yaml | 2 + stack.yaml.lock | 14 ++ 20 files changed, 586 insertions(+), 38 deletions(-) create mode 100644 hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs diff --git a/Makefile b/Makefile index d9cc6098..3d1b6328 100644 --- a/Makefile +++ b/Makefile @@ -1,11 +1,13 @@ -help: ## Ask for help! - @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +STATS_PORT ?= 9200 export # This is useful for copying example app binaries built on a linux machine rather than building in docker SIMPLE_STORAGE_BINARY := $(shell stack exec -- which simple-storage) +help: ## Ask for help! + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' + ##################### # Linting and Styling ##################### @@ -59,6 +61,8 @@ deploy-simple-storage-local: install ## run the simple storage locally stack exec simple-storage deploy-nameservice-local: install ## run the nameservice locally + DD_API_KEY=$(DD_API_KEY) \ + STATS_PORT=$(STATS_PORT) \ stack exec nameservice test-kv-store: install ## Run the test suite for the client interface diff --git a/hs-abci-examples/nameservice/app/Main.hs b/hs-abci-examples/nameservice/app/Main.hs index b906fffd..93de182e 100644 --- a/hs-abci-examples/nameservice/app/Main.hs +++ b/hs-abci-examples/nameservice/app/Main.hs @@ -1,19 +1,30 @@ module Main where -import Control.Exception (bracket) -import qualified Katip as K -import Nameservice.Application (makeAppConfig) -import Nameservice.Server (makeAndServeApplication) -import System.IO (stdout) -import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), - mkLogConfig) - +import Control.Exception (bracket) +import Data.Text (pack) +import qualified Katip as K +import Nameservice.Application (makeAppConfig) +import Nameservice.Server (makeAndServeApplication) +import System.Environment (lookupEnv) +import System.IO (stdout) +import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), + mkLogConfig) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (MetricsConfig (..), + mkMetricsConfig) +import qualified Text.Read as T main :: IO () main = do + metCfg <- mkMetricsConfig + mApiKey <- lookupEnv "DD_API_KEY" + mMetricsPort <- lookupEnv "STATS_PORT" logCfg <- mkLogConfig "dev" "nameservice" handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) K.V2 let mkLogEnv = K.registerScribe "stdout" handleScribe K.defaultScribeSettings (_logEnv logCfg) bracket mkLogEnv K.closeScribes $ \le -> do - cfg <- makeAppConfig logCfg {_logEnv = le} + cfg <- makeAppConfig + metCfg { metricsAPIKey = pack <$> mApiKey + , metricsPort = T.read <$> mMetricsPort + } + logCfg {_logEnv = le} makeAndServeApplication cfg diff --git a/hs-abci-examples/nameservice/docker-compose.yaml b/hs-abci-examples/nameservice/docker-compose.yaml index 65b2b811..b7f572be 100644 --- a/hs-abci-examples/nameservice/docker-compose.yaml +++ b/hs-abci-examples/nameservice/docker-compose.yaml @@ -21,9 +21,25 @@ services: context: ../../. dockerfile: Dockerfile image: hs-abci:test + environment: + - DD_API_KEY=${DD_API_KEY} + - STATS_PORT=9200 restart: always entrypoint: /usr/local/bin/nameservice expose: - "26658" + - "9200" + datadog: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /proc/:/host/proc/:ro + - /sys/fs/cgroup:/host/sys/fs/cgroup:ro + labels: + com.datadoghq.ad.check_names: '["openmetrics"]' + com.datadoghq.ad.init_configs: '["{}"]' + com.datadoghq.ad.instances: '["{\"prometheus_url\":\"http://%%host%%:9200/metrics \",\"namespace\":\"nameservice\",\"metrics\":[\"count_buy\" \"count_set\" \"count_delete\" \"histogram_buy*\" \"histogram_set*\" \"histogram_delete*\"]}"]' volumes: tendermint-storage: diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index e1cf79f9..5b0dac88 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -7,23 +7,24 @@ module Nameservice.Application ) where import Data.Proxy -import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T -import Tendermint.SDK.Application (HandlersContext (..), - Modules (..)) -import Tendermint.SDK.BaseApp ((:&)) -import qualified Tendermint.SDK.BaseApp as BaseApp -import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import Tendermint.SDK.Crypto (Secp256k1) -import qualified Tendermint.SDK.Modules.Auth as A +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import Tendermint.SDK.Application (HandlersContext (..), + Modules (..)) +import Tendermint.SDK.BaseApp ((:&)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus +import Tendermint.SDK.Crypto (Secp256k1) +import qualified Tendermint.SDK.Modules.Auth as A data AppConfig = AppConfig { baseAppContext :: BaseApp.Context } -makeAppConfig :: KL.LogConfig -> IO AppConfig -makeAppConfig logCfg = do - c <- BaseApp.makeContext logCfg +makeAppConfig :: Prometheus.MetricsConfig -> KL.LogConfig -> IO AppConfig +makeAppConfig metCfg logCfg = do + c <- BaseApp.makeContext (Just metCfg) logCfg pure $ AppConfig { baseAppContext = c } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 0d503fcb..8b575f1e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -6,7 +6,8 @@ import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members, Sem) -import Tendermint.SDK.BaseApp (BaseAppEffs) +import Tendermint.SDK.BaseApp (BaseAppEffs, + incCount, withTimer) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) @@ -20,6 +21,12 @@ router router (RoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of - NSetName msg -> setName msg - NBuyName msg -> buyName msg - NDeleteName msg -> deleteName msg + NSetName msg -> do + incCount "count_set" + withTimer "histogram_set" $ setName msg + NBuyName msg -> do + incCount "count_buy" + withTimer "histogram_buy" $ buyName msg + NDeleteName msg -> do + incCount "count_delete" + withTimer "histogram_delete" $ deleteName msg diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index 6dbafa75..5449ddf8 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -6,29 +6,38 @@ import Nameservice.Application (AppConfig (..), handlersContext) import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) +import qualified Network.ABCI.Server.Middleware.MetricsLogger as MetLogger import qualified Network.ABCI.Server.Middleware.RequestLogger as ReqLogger import qualified Network.ABCI.Server.Middleware.ResponseLogger as ResLogger import Polysemy (Sem) import Tendermint.SDK.Application (createIOApp, makeApp) -import Tendermint.SDK.BaseApp (CoreEffs, +import Tendermint.SDK.BaseApp (Context (..), + CoreEffs, runCoreEffs) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (MetricsConfig (..), + runMetricsServer) makeAndServeApplication :: AppConfig -> IO () -makeAndServeApplication cfg = do +makeAndServeApplication AppConfig{..} = do putStrLn "Starting ABCI application..." + runMetricsServer metCfg let nat :: forall a. Sem CoreEffs a -> IO a - nat = runCoreEffs $ baseAppContext cfg + nat = runCoreEffs baseAppContext application = createIOApp nat $ makeApp handlersContext serveApp =<< hookInMiddleware application where + metCfg = contextMetricsConfig baseAppContext + apiKey = (fmap MetLogger.ApiKey . metricsAPIKey) =<< metCfg mkMiddleware :: IO (Middleware IO) mkMiddleware = do reqLogger <- ReqLogger.mkLogStdoutDev resLogger <- ResLogger.mkLogStdoutDev + metLogger <- MetLogger.mkMetricsLogDatadog apiKey pure . appEndo . fold $ [ Endo reqLogger , Endo resLogger + , Endo metLogger ] hookInMiddleware _app = do middleware <- mkMiddleware diff --git a/hs-abci-examples/simple-storage/app/Main.hs b/hs-abci-examples/simple-storage/app/Main.hs index 0dfd1190..4ef4fd0e 100644 --- a/hs-abci-examples/simple-storage/app/Main.hs +++ b/hs-abci-examples/simple-storage/app/Main.hs @@ -8,7 +8,6 @@ import System.IO (stdout) import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), mkLogConfig) - main :: IO () main = do logCfg <- mkLogConfig "dev" "simple-storage" diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index 97cfe33a..86fcfd1c 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -21,7 +21,7 @@ data AppConfig = AppConfig makeAppConfig :: KL.LogConfig -> IO AppConfig makeAppConfig logCfg = do - c <- BaseApp.makeContext logCfg + c <- BaseApp.makeContext Nothing logCfg pure $ AppConfig { baseAppContext = c } diff --git a/hs-abci-extra/package.yaml b/hs-abci-extra/package.yaml index 047a7de1..aede9b77 100644 --- a/hs-abci-extra/package.yaml +++ b/hs-abci-extra/package.yaml @@ -22,6 +22,7 @@ description: Please see the README on GitHub at = 4.7 && < 5 - katip +- katip-datadog - hs-abci-server - aeson - aeson-casing @@ -30,6 +31,8 @@ dependencies: - text - lens - bytestring +- time +- containers default-extensions: - NamedFieldPuns diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs new file mode 100644 index 00000000..3a67db70 --- /dev/null +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs @@ -0,0 +1,152 @@ +module Network.ABCI.Server.Middleware.MetricsLogger + ( -- * Basic stdout logging + mkMetricsLogStdout + , mkMetricsLogDatadog + , mkMetricsLogDatadogLocal + -- * Custom Loggers + , mkMetricsLogger + , mkMetricsLoggerM + -- * ApiKey + , ApiKey(..) + ) where +import Control.Concurrent.MVar (MVar, modifyMVar, newMVar) +import Control.Monad.IO.Class (MonadIO, liftIO) +import qualified Data.Aeson as A +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as Map +import Data.Text (Text) +import Data.Time (NominalDiffTime, diffUTCTime, + getCurrentTime) +import Katip +import Katip.Scribes.Datadog.TCP +import Network.ABCI.Server.App (App (..), MessageType (..), + Middleware, Request (..), + msgTypeKey) +import System.IO (stdout) + +--------------------------------------------------------------------------- +-- mkMetricsLogStdout +--------------------------------------------------------------------------- +-- | Creates a production Metrics logger as middleware for ABCI Server. +-- Uses lowest possible verbosity, however, with the current minimal metrics +-- verbosity has no effect on which metrics logged. +mkMetricsLogStdout :: (MonadIO m) => m (Middleware m) +mkMetricsLogStdout = liftIO $ do + handleScribe <- mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V0 + le <- registerScribe "stdout" handleScribe defaultScribeSettings + =<< initLogEnv "ABCI" "production" + let ns = "Server" + mvarReqC <- newMVar Map.empty + pure $ mkMetricsLogger mvarReqC le ns + +--------------------------------------------------------------------------- +-- mkMetricsLogDatadog +--------------------------------------------------------------------------- + +mkMetricsLogDatadogLocal :: (MonadIO m) => Integer -> m (Middleware m) +mkMetricsLogDatadogLocal port = liftIO $ do + datadogScribeSettings <- mkDatadogScribeSettings (localAgentConnectionParams (fromInteger port)) NoAuthLocal + scribe <- mkDatadogScribe datadogScribeSettings (permitItem InfoS) V0 + le <- registerScribe "datadog" scribe defaultScribeSettings + =<< initLogEnv "ABCI" "production" + let ns = "Server" + mvarReqC <- newMVar Map.empty + pure $ mkMetricsLogger mvarReqC le ns + +newtype ApiKey = ApiKey Text +mkMetricsLogDatadog :: (MonadIO m) => Maybe ApiKey -> m (Middleware m) +mkMetricsLogDatadog mApiKey = + case mApiKey of + Nothing -> pure id + Just (ApiKey key) -> liftIO $ do + let apiKey = APIKey key + datadogScribeSettings <- mkDatadogScribeSettings directAPIConnectionParams (DirectAuth apiKey) + scribe <- mkDatadogScribe datadogScribeSettings (permitItem InfoS) V0 + le <- registerScribe "datadog" scribe defaultScribeSettings + =<< initLogEnv "ABCI" "production" + let ns = "Server" + mvarReqC <- newMVar Map.empty + pure $ mkMetricsLogger mvarReqC le ns + +--------------------------------------------------------------------------- +-- mkRequestLogger +--------------------------------------------------------------------------- +-- | Request logger middleware for ABCI metrics with custom 'Katip.LogEnv' +-- and 'Katip.Namespace'. +mkMetricsLogger :: (MonadIO m) => MVar (Map OrderedMessageType Integer) -> LogEnv -> Namespace -> Middleware m +mkMetricsLogger mvarMap le ns (App app) = App $ \ req -> do + startTime <- liftIO getCurrentTime + res <- app req + endTime <- liftIO getCurrentTime + metrics <- liftIO $ modifyMVar mvarMap $ \metMap -> + let mt = requestToMessageType req + count = maybe 1 (+1) (metMap Map.!? mt) + metrics = Metrics mt count (diffUTCTime endTime startTime) + newMetMap = Map.insert mt count metMap + in pure (newMetMap, metrics) + runKatipContextT le () ns $ logMetrics metrics + pure res + +--------------------------------------------------------------------------- +-- mkMetricsLoggerM +--------------------------------------------------------------------------- +-- | Metrics logger middleware for ABCI server already within the KatipContext. +-- Great for `App m` with a `KatipContext` instance. +mkMetricsLoggerM :: (KatipContext m) => MVar (Map OrderedMessageType Integer) -> Middleware m +mkMetricsLoggerM mvarMap (App app) = App $ \ req -> do + startTime <- liftIO getCurrentTime + res <- app req + endTime <- liftIO getCurrentTime + metrics <- liftIO $ modifyMVar mvarMap $ \metMap -> + let mt = requestToMessageType req + count = maybe 1 (+1) (metMap Map.!? mt) + metrics = Metrics mt count (diffUTCTime endTime startTime) + newMetMap = Map.insert mt count metMap + in pure (newMetMap, metrics) + logMetrics metrics + pure res + +--------------------------------------------------------------------------- +-- Common +--------------------------------------------------------------------------- +-- | Metrics logger function. +logMetrics :: (KatipContext m) => Metrics -> m () +logMetrics metrics = katipAddContext metrics $ logFM InfoS "" + + +data Metrics = Metrics + { metricsMessageType :: OrderedMessageType + , metricsMessageCount :: Integer + , metricsResponseTime :: NominalDiffTime + } +instance A.ToJSON Metrics where + toJSON Metrics{..} = A.object + [ "message_type" A..= A.toJSON ((msgTypeKey $ unOrderedMessageType metricsMessageType) :: String) + , "message_count" A..= A.toJSON metricsMessageCount + , "response_time" A..= A.toJSON metricsResponseTime + ] + +instance ToObject Metrics +instance LogItem Metrics where + payloadKeys _ _ = AllKeys + +newtype OrderedMessageType = OrderedMessageType {unOrderedMessageType :: MessageType} + +instance Eq OrderedMessageType where + (==) (OrderedMessageType a) (OrderedMessageType b) = msgTypeKey a == msgTypeKey b + +instance Ord OrderedMessageType where + (<=) (OrderedMessageType a) (OrderedMessageType b) = msgTypeKey a <= msgTypeKey b + +requestToMessageType :: Request (t :: MessageType) -> OrderedMessageType +requestToMessageType (RequestEcho _) = OrderedMessageType MTEcho +requestToMessageType (RequestInfo _) = OrderedMessageType MTInfo +requestToMessageType (RequestSetOption _) = OrderedMessageType MTSetOption +requestToMessageType (RequestQuery _) = OrderedMessageType MTQuery +requestToMessageType (RequestCheckTx _) = OrderedMessageType MTCheckTx +requestToMessageType (RequestFlush _) = OrderedMessageType MTFlush +requestToMessageType (RequestInitChain _) = OrderedMessageType MTInitChain +requestToMessageType (RequestBeginBlock _) = OrderedMessageType MTBeginBlock +requestToMessageType (RequestDeliverTx _) = OrderedMessageType MTDeliverTx +requestToMessageType (RequestEndBlock _) = OrderedMessageType MTEndBlock +requestToMessageType (RequestCommit _) = OrderedMessageType MTCommit diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs index dc1ad784..8738960c 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs @@ -6,6 +6,7 @@ module Network.ABCI.Server.Middleware.RequestLogger , mkRequestLogger , mkRequestLoggerM ) where + import Control.Monad.IO.Class (MonadIO, liftIO) import qualified Data.Aeson as A import Katip diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index ca25d8a2..934257bf 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -70,6 +70,7 @@ dependencies: - polysemy-plugin - polysemy-zoo - profunctors +- prometheus - proto-lens - proto-lens-runtime - proto3-wire @@ -79,6 +80,7 @@ dependencies: - stm - string-conversions - text +- time - uri-bytestring - validation @@ -106,6 +108,8 @@ library: - Tendermint.SDK.BaseApp.Gas - Tendermint.SDK.BaseApp.Logger - Tendermint.SDK.BaseApp.Logger.Katip + - Tendermint.SDK.BaseApp.Metrics + - Tendermint.SDK.BaseApp.Metrics.Prometheus - Tendermint.SDK.BaseApp.Query - Tendermint.SDK.BaseApp.Query.Class - Tendermint.SDK.BaseApp.Query.Client @@ -140,6 +144,7 @@ tests: - Tendermint.SDK.Test.CryptoSpec - Tendermint.SDK.Test.EventSpec - Tendermint.SDK.Test.GasSpec + - Tendermint.SDK.Test.MetricsSpec ghc-options: - -fplugin=Polysemy.Plugin diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index c5497aa4..e0ecd314 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -9,7 +9,7 @@ module Tendermint.SDK.BaseApp -- * CoreEff , CoreEffs - , Context + , Context(..) , makeContext , runCoreEffs @@ -42,6 +42,13 @@ module Tendermint.SDK.BaseApp , Tendermint.SDK.BaseApp.Logger.log , Severity(..) + -- * Metrics + , Metrics + , incCount + , withTimer + , CountName(..) + , HistogramName(..) + -- * Query , Queryable(..) , FromQueryData(..) @@ -57,5 +64,6 @@ import Tendermint.SDK.BaseApp.Errors import Tendermint.SDK.BaseApp.Events import Tendermint.SDK.BaseApp.Gas import Tendermint.SDK.BaseApp.Logger +import Tendermint.SDK.BaseApp.Metrics import Tendermint.SDK.BaseApp.Query import Tendermint.SDK.BaseApp.Store diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs index fce6c0df..4e7d8967 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs @@ -21,6 +21,8 @@ import Tendermint.SDK.BaseApp.Events (Event, evalWithBuffer) import Tendermint.SDK.BaseApp.Logger (Logger) import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.BaseApp.Metrics (Metrics) +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus import Tendermint.SDK.BaseApp.Store (ApplyScope, ConnectionScope (..), RawStore, ResolveScope (..)) @@ -32,6 +34,7 @@ import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT type BaseAppEffs = [ RawStore , Output Event + , Metrics , Logger , Resource , Error AppError @@ -56,6 +59,7 @@ compileToCoreEffs action = do eRes <- runError . resourceToIO . KL.evalKatip . + Prometheus.evalWithMetrics . evalWithBuffer . resolveScope $ action either (liftIO . throwIO) return eRes diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs index 5a7b5bcf..6629b800 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs @@ -17,6 +17,7 @@ import Polysemy.Reader (Reader, asks, import Tendermint.SDK.BaseApp.Events (EventBuffer, newEventBuffer) import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus import Tendermint.SDK.BaseApp.Store (MergeScopes, ResolveScope (..)) import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT @@ -27,6 +28,7 @@ type CoreEffs = '[ Reader EventBuffer , MergeScopes , Reader KL.LogConfig + , Reader (Maybe Prometheus.MetricsConfig) , Reader AT.AuthTreeState , Embed IO ] @@ -43,17 +45,19 @@ instance (Members CoreEffs r) => K.KatipContext (Sem r) where -- | 'Context' is the environment required to run 'CoreEffs' to 'IO' data Context = Context - { contextLogConfig :: KL.LogConfig - , contextEventBuffer :: EventBuffer - , contextAuthTree :: AT.AuthTreeState + { contextLogConfig :: KL.LogConfig + , contextMetricsConfig :: Maybe Prometheus.MetricsConfig + , contextEventBuffer :: EventBuffer + , contextAuthTree :: AT.AuthTreeState } -makeContext :: KL.LogConfig -> IO Context -makeContext logCfg = do +makeContext :: Maybe Prometheus.MetricsConfig -> KL.LogConfig -> IO Context +makeContext metCfg logCfg = do authTreeState <- AT.initAuthTreeState eb <- newEventBuffer pure $ Context { contextLogConfig = logCfg + , contextMetricsConfig = metCfg , contextEventBuffer = eb , contextAuthTree = authTreeState } @@ -68,6 +72,7 @@ runCoreEffs runCoreEffs Context{..} = runM . runReader contextAuthTree . + runReader contextMetricsConfig . runReader contextLogConfig . AT.evalMergeScopes . runReader contextEventBuffer diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs new file mode 100644 index 00000000..91ef3f98 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs @@ -0,0 +1,33 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.BaseApp.Metrics where + +import Data.String (IsString (..)) +import Data.Text (Text) +import Polysemy + +data CountName = CountName + { countName :: Text + , countLabels :: [(Text, Text)] + } deriving (Eq, Ord) + +instance IsString CountName where + fromString s = CountName (fromString s) mempty + +data HistogramName = HistogramName + { histogramName :: Text + , histogramLabels :: [(Text, Text)] + , histogramBuckets :: [Double] + } deriving (Eq, Ord) + +instance IsString HistogramName where + fromString s = HistogramName (fromString s) mempty defaultBuckets + where defaultBuckets = [0.0001, 0.001, 0.01, 0.1, 0.25, 0.5, 0.75, 1, 10, 100] + +data Metrics m a where + -- | Increments the count of a specific message + IncCount :: CountName -> Metrics m () + -- | Times an action and records it in a histogram + WithTimer :: HistogramName -> m a -> Metrics m () + +makeSem ''Metrics diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs new file mode 100644 index 00000000..09e3cc13 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -0,0 +1,204 @@ +module Tendermint.SDK.BaseApp.Metrics.Prometheus where + +import Control.Arrow ((***)) +import Control.Concurrent (forkIO) +import Control.Concurrent.MVar (MVar, + modifyMVar_, + newMVar) +import Control.Monad.IO.Class (MonadIO, liftIO) +import Data.Map.Strict (Map, insert) +import qualified Data.Map.Strict as Map +import Data.String (IsString, + fromString) +import Data.Text (Text) +import qualified Data.Text as Text +import Data.Time (diffUTCTime, + getCurrentTime) +import Polysemy (Embed, Member, + Sem, interpretH, + pureT, raise, + runT) +import Polysemy.Reader (Reader (..), + ask) +import qualified System.Metrics.Prometheus.Concurrent.Registry as Registry +import qualified System.Metrics.Prometheus.Http.Scrape as Http +import qualified System.Metrics.Prometheus.Metric.Counter as Counter +import qualified System.Metrics.Prometheus.Metric.Histogram as Histogram +import qualified System.Metrics.Prometheus.MetricId as MetricId +import Tendermint.SDK.BaseApp.Metrics (CountName (..), HistogramName (..), + Metrics (..)) + +-------------------------------------------------------------------------------- +-- eval +-------------------------------------------------------------------------------- + +evalWithMetrics + :: Member (Embed IO) r + => Member (Reader (Maybe MetricsConfig)) r + => Sem (Metrics ': r) a + -> Sem r a +evalWithMetrics action = do + mCfg <- ask + case mCfg of + Nothing -> evalNothing action + Just cfg -> evalMetrics (metricsState cfg) action + +evalNothing + :: Sem (Metrics ': r) a + -> Sem r a +evalNothing = do + interpretH (\case + IncCount _ -> pureT () + WithTimer _ _ -> pureT () + ) + +evalMetrics + :: Member (Embed IO) r + => MetricsState + -> Sem (Metrics ': r) a + -> Sem r a +evalMetrics state@MetricsState{..} = do + interpretH (\case + -- | Increments existing count; if it doesn't exist, creates a new + -- | counter and increments it. + IncCount ctrName -> do + let c@MetricIdentifier{..} = countToIdentifier ctrName + cid = metricIdStorable c + cMetricIdName = MetricId.Name metricIdName + liftIO $ modifyMVar_ metricsCounters $ \counterMap -> + case Map.lookup cid counterMap of + Nothing -> do + newCtr <- liftIO $ + Registry.registerCounter cMetricIdName metricIdLabels metricsRegistry + let newCounterMap = insert cid newCtr counterMap + liftIO $ Counter.inc newCtr + pure newCounterMap + Just ctr -> do + liftIO $ Counter.inc ctr + pure counterMap + pureT () + + -- | Updates a histogram with the time it takes to do an action + -- | If histogram doesn't exist, creates a new one and observes it. + WithTimer histName action -> do + start <- liftIO $ getCurrentTime + a <- runT action + end <- liftIO $ getCurrentTime + let time = fromRational . (* 1000.0) . toRational $ (end `diffUTCTime` start) + observeHistogram state histName time + _ <- raise $ evalMetrics state a + pureT () + ) + +-- | Updates a histogram with an observed value +observeHistogram :: MonadIO m => MetricsState -> HistogramName -> Double -> m () +observeHistogram MetricsState{..} histName val = liftIO $ do + let h@MetricIdentifier{..} = histogramToIdentifier histName + hid = metricIdStorable h + hMetricIdName = MetricId.Name metricIdName + modifyMVar_ metricsHistograms $ \histMap -> + case Map.lookup hid histMap of + Nothing -> do + newHist <- + Registry.registerHistogram hMetricIdName metricIdLabels metricIdHistoBuckets metricsRegistry + let newHistMap = insert hid newHist histMap + Histogram.observe val newHist + pure $ newHistMap + Just hist -> do + Histogram.observe val hist + pure histMap + +-------------------------------------------------------------------------------- +-- indexes +-------------------------------------------------------------------------------- + +countToIdentifier :: CountName -> MetricIdentifier +countToIdentifier (CountName name labels) = MetricIdentifier + { metricIdName = fixMetricName name + , metricIdLabels = MetricId.fromList labels + , metricIdHistoBuckets = [] + } + +histogramToIdentifier :: HistogramName -> MetricIdentifier +histogramToIdentifier (HistogramName name labels buckets) = MetricIdentifier + { metricIdName = fixMetricName name + , metricIdLabels = MetricId.fromList labels + , metricIdHistoBuckets = buckets + } + +-- | Prometheus registry index key +mkPrometheusMetricId :: MetricIdentifier -> MetricId.MetricId +mkPrometheusMetricId MetricIdentifier{..} = + MetricId.MetricId (MetricId.Name metricIdName) metricIdLabels + +-- | Index key for storing metrics +metricIdStorable :: MetricIdentifier -> (Text, MetricId.Labels) +metricIdStorable c = (fixMetricName $ metricIdName c, fixMetricLabels $ metricIdLabels c) + where fixMetricLabels = + MetricId.fromList . + map (fixMetricName *** fixMetricName) . + MetricId.toList + +fixMetricName :: Text -> Text +fixMetricName = Text.map fixer + where fixer c = if c `elem` validChars then c else '_' + validChars = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_" + +-- | Core metrics state +type MetricsMap a = Map (Text, MetricId.Labels) a +data MetricsState = MetricsState + { metricsRegistry :: Registry.Registry + , metricsCounters :: MVar (MetricsMap Counter.Counter) + , metricsHistograms :: MVar (MetricsMap Histogram.Histogram) + } + +-- | Core metrics config +type Port = Int +data MetricsConfig = MetricsConfig + { metricsState :: MetricsState + , metricsPort :: Maybe Port + , metricsAPIKey :: Maybe Text + } + +-- | Intermediary prometheus registry index key +data MetricIdentifier = MetricIdentifier + { metricIdName :: Text + , metricIdLabels :: MetricId.Labels + , metricIdHistoBuckets :: [Double] + } + +instance IsString MetricIdentifier where + fromString s = MetricIdentifier (fromString s) mempty mempty + +-------------------------------------------------------------------------------- +-- setup +-------------------------------------------------------------------------------- + +emptyState :: IO MetricsState +emptyState = do + counters <- newMVar Map.empty + histos <- newMVar Map.empty + registry <- Registry.new + return $ MetricsState registry counters histos + +mkMetricsConfig :: IO MetricsConfig +mkMetricsConfig = do + state <- emptyState + return $ MetricsConfig state Nothing Nothing + +runMetricsServer + :: MonadIO m + => Maybe MetricsConfig + -> m () +runMetricsServer mMetCfg = liftIO $ do + case mMetCfg of + Nothing -> return () + Just metCfg -> do + let MetricsConfig{..} = metCfg + MetricsState{..} = metricsState + case metricsPort of + Nothing -> return () + Just port -> do + _ <- forkIO $ + Http.serveHttpTextMetrics port ["metrics"] (Registry.sample metricsRegistry) + return () diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs new file mode 100644 index 00000000..f89ae482 --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs @@ -0,0 +1,70 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.Test.MetricsSpec where + +import Control.Concurrent.MVar (readMVar) +import Data.Map.Strict ((!)) +import qualified Data.Map.Strict as Map +import Polysemy +import qualified System.Metrics.Prometheus.Concurrent.Registry as Registry +import qualified System.Metrics.Prometheus.Metric as Metric +import qualified System.Metrics.Prometheus.Metric.Counter as Counter +import qualified System.Metrics.Prometheus.Metric.Histogram as Histogram +import qualified System.Metrics.Prometheus.Registry as RSample +import Tendermint.SDK.BaseApp.Metrics +import Tendermint.SDK.BaseApp.Metrics.Prometheus +import Test.Hspec + +data Fox m a where + Shine :: Fox m () + +makeSem ''Fox + +evalFox :: Sem (Fox ': r) a -> Sem r a +evalFox = interpret $ \case + Shine -> pure () + +eval + :: MetricsState + -> Sem [Fox, Metrics, Embed IO] a + -> IO a +eval s = runM . evalMetrics s . evalFox + +spec :: Spec +spec = describe "Metrics tests" $ do + let countName = "testCount" + c = countToIdentifier countName + cid = metricIdStorable c + cMetricId = mkPrometheusMetricId c + it "Can make a new count and increment it" $ do + state@MetricsState{..} <- emptyState + -- Creates new count and sets it to 1 + _ <- eval state $ incCount countName + newCtrIndex <- readMVar metricsCounters + newCtrValue <- Counter.sample $ newCtrIndex ! cid + Counter.unCounterSample newCtrValue `shouldBe` 1 + -- register should contain new counter metric + newRegistrySample <- Registry.sample metricsRegistry + let registryMap = RSample.unRegistrySample newRegistrySample + (Metric.CounterMetricSample registryCtrSample) = registryMap ! cMetricId + Counter.unCounterSample registryCtrSample `shouldBe` 1 + -- increment it again + _ <- eval state $ incCount countName + incCtrIndex <- readMVar metricsCounters + incCtrValue <- Counter.sample $ incCtrIndex ! cid + Counter.unCounterSample incCtrValue `shouldBe` 2 + + let buckettedHistName = "bucketted" + buckettedH = histogramToIdentifier buckettedHistName + buckettedHMetricId = mkPrometheusMetricId buckettedH + it "Can measure action response times with default buckets" $ do + state@MetricsState{..} <- emptyState + -- time an action + _ <- eval state $ withTimer buckettedHistName shine + -- check registry hist buckets + newRegistrySample <- Registry.sample metricsRegistry + let registryMap = RSample.unRegistrySample newRegistrySample + (Metric.HistogramMetricSample registryHistSample) = registryMap ! buckettedHMetricId + histBuckets = Histogram.histBuckets registryHistSample + Map.elems histBuckets `shouldSatisfy` atLeastOneUpdated + where atLeastOneUpdated = foldr (\b acc -> acc || (b /= 0.0)) False diff --git a/stack.yaml b/stack.yaml index 44c480b0..318c6995 100644 --- a/stack.yaml +++ b/stack.yaml @@ -20,6 +20,8 @@ extra-deps: - lens-labels-0.3.0.1 - proto-lens-0.5.0.0 - proto-lens-protoc-0.5.0.0 + - katip-datadog-0.1.0.0 + - prometheus-2.1.3 - git: https://github.com/oscoin/avl-auth commit: dfc468845a82cdd7d759943b20853999bc026505 - git: https://github.com/awakesecurity/proto3-suite diff --git a/stack.yaml.lock b/stack.yaml.lock index 327e9dd5..750365a8 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -39,6 +39,20 @@ packages: sha256: 30942e172639508c45742e4b0cb6bf3493bee24b291b6cd4c1ad0785f1e9057c original: hackage: proto-lens-protoc-0.5.0.0 +- completed: + hackage: katip-datadog-0.1.0.0@sha256:4e72dca402b953bd34b7a744ad23eb90600a420adef67192c9702073564f1cae,1885 + pantry-tree: + size: 415 + sha256: c3d816dcd90b113a246520c8069d3278ee77eacf0f45dd0eb77eb2f7c53d5b3b + original: + hackage: katip-datadog-0.1.0.0 +- completed: + hackage: prometheus-2.1.3@sha256:4fdf8602f7c74367cda182cf71dab108f78a86993b428bf96b61dd6c519b6f22,4296 + pantry-tree: + size: 1559 + sha256: a8d0a0150abddf0d7b673fcb19ff2ef235d4005161826e1538ad802394c9fc0e + original: + hackage: prometheus-2.1.3 - completed: cabal-file: size: 1872 From 7d42dc571bcfba2a0cec4554473e061a4323b5f2 Mon Sep 17 00:00:00 2001 From: Charles Crain Date: Fri, 20 Dec 2019 10:28:26 -0800 Subject: [PATCH 25/70] Charles/iavl client (#153) * added proto files and compiles generated protobuf types * testing setup, hash rpc call * grpc calls complete * make travis use iavl docker image * Update .travis.yml * passing tests and travis Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- .travis.yml | 4 + Makefile | 7 +- hs-iavl-client/Setup.hs | 2 + hs-iavl-client/package.yaml | 97 ++ .../protos/google/api/annotations.proto | 31 + hs-iavl-client/protos/google/api/http.proto | 376 ++++++++ .../protos/google/protobuf/descriptor.proto | 882 ++++++++++++++++++ .../protos/google/protobuf/empty.proto | 52 ++ hs-iavl-client/protos/iavl/api.proto | 274 ++++++ hs-iavl-client/src/Database/IAVL/RPCCall.hs | 150 +++ .../test/Database/IAVL/RPCCallSpec.hs | 185 ++++ hs-iavl-client/test/Spec.hs | 2 + stack.yaml | 5 + stack.yaml.lock | 28 + 14 files changed, 2094 insertions(+), 1 deletion(-) create mode 100644 hs-iavl-client/Setup.hs create mode 100644 hs-iavl-client/package.yaml create mode 100644 hs-iavl-client/protos/google/api/annotations.proto create mode 100644 hs-iavl-client/protos/google/api/http.proto create mode 100644 hs-iavl-client/protos/google/protobuf/descriptor.proto create mode 100644 hs-iavl-client/protos/google/protobuf/empty.proto create mode 100644 hs-iavl-client/protos/iavl/api.proto create mode 100644 hs-iavl-client/src/Database/IAVL/RPCCall.hs create mode 100644 hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs create mode 100644 hs-iavl-client/test/Spec.hs diff --git a/.travis.yml b/.travis.yml index 7a843f17..dea67e46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -41,6 +41,10 @@ jobs: - echo "This stage is currently disabled as Nix doesn't seem to play well with libsecp256k1" #- curl https://nixos.org/nix/install | sh #- source $HOME/.nix-profile/etc/profile.d/nix.sh && stack --nix test hs-abci-types hs-abci-server hs-abci-sdk + - name: "Test IAVL Client" + script: + - docker run -p 8090:8090 -p 8091:8091 -d foamspace/iavl:latest /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + - make test-iavl-client - name: "Test Tendermint Client" script: - docker-compose -f hs-tendermint-client/docker-compose.yaml -p test-hs-tendermint-client up -d diff --git a/Makefile b/Makefile index 3d1b6328..1d3b5b80 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,8 @@ hlint: ## Run hlint on all haskell projects hs-abci-extra \ hs-abci-sdk \ hs-abci-examples/simple-storage \ - hs-abci-examples/nameservice + hs-abci-examples/nameservice \ + hs-iavl-client stylish: ## Run stylish-haskell over all haskell projects find ./hs-abci-types \ @@ -27,6 +28,7 @@ stylish: ## Run stylish-haskell over all haskell projects ./hs-abci-examples \ ./hs-abci-sdk \ ./hs-abci-server \ + ./hs-iavl-client \ -name "*.hs" | xargs stack exec stylish-haskell -- -c ./.stylish_haskell.yaml -i ################### @@ -46,6 +48,9 @@ install: ## Runs stack install to compile library and counter example app test-libraries: install ## Run the haskell test suite for all haskell libraries stack test hs-abci-types hs-abci-server hs-abci-sdk +test-iavl-client: ## test the iavl client library basic operation (requires grpc service running on port 8090) + stack test hs-iavl-client + ##################### # Example Application diff --git a/hs-iavl-client/Setup.hs b/hs-iavl-client/Setup.hs new file mode 100644 index 00000000..c81784fa --- /dev/null +++ b/hs-iavl-client/Setup.hs @@ -0,0 +1,2 @@ +import Data.ProtoLens.Setup +main = defaultMainGeneratingProtos "protos" diff --git a/hs-iavl-client/package.yaml b/hs-iavl-client/package.yaml new file mode 100644 index 00000000..06e08a34 --- /dev/null +++ b/hs-iavl-client/package.yaml @@ -0,0 +1,97 @@ +name: hs-iavl-client +version: 0.1.0.0 +github: "f-o-a-m/hs-abci/hs-iavl-client" +license: Apache +author: "Martin Allen" +maintainer: "martin@foam.space" +copyright: "2019 Martin Allen" + +extra-source-files: +- protos/**/*.proto + +description: Please see the README on GitHub at + +custom-setup: + dependencies: + - base + - Cabal + - proto-lens-setup + +default-extensions: +- NamedFieldPuns +- RecordWildCards +- FlexibleContexts +- DeriveGeneric +- LambdaCase +- TypeFamilies +- GADTs +- GeneralizedNewtypeDeriving +- DataKinds +- PolyKinds +- RankNTypes +- DataKinds +- ScopedTypeVariables +- FlexibleInstances +- OverloadedStrings +- MultiParamTypeClasses +- FunctionalDependencies + + +dependencies: +- base >= 4.7 && < 5 +- aeson +- aeson-casing +- base16-bytestring +- bytestring +- data-default-class +- lens +- memory +- proto-lens +- proto-lens-runtime +- string-conversions +- text +- template-haskell +- time +- http2-client +- http2-client-grpc + +library: + source-dirs: src + ghc-options: + - -Werror + - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-uni-patterns + - -Wredundant-constraints + exposed-modules: + - Database.IAVL.RPCCall + generated-exposed-modules: + - Proto.Iavl.Api + - Proto.Iavl.Api_Fields + - Proto.Google.Api.Http + - Proto.Google.Protobuf.Empty + - Proto.Google.Api.Annotations + +tests: + hs-iavl-client-test: + main: Spec.hs + source-dirs: test + ghc-options: + - -Werror + - -Wall + - -threaded + - -rtsopts + - -with-rtsopts=-N + + dependencies: + - generic-arbitrary + - hs-iavl-client + - hspec + - hspec-core + - hspec-discover + - binary + - lens-labels + - proto-lens-arbitrary + - QuickCheck + - quickcheck-instances diff --git a/hs-iavl-client/protos/google/api/annotations.proto b/hs-iavl-client/protos/google/api/annotations.proto new file mode 100644 index 00000000..85c361b4 --- /dev/null +++ b/hs-iavl-client/protos/google/api/annotations.proto @@ -0,0 +1,31 @@ +// Copyright (c) 2015, Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +syntax = "proto3"; + +package google.api; + +import "google/api/http.proto"; +import "google/protobuf/descriptor.proto"; + +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "AnnotationsProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +extend google.protobuf.MethodOptions { + // See `HttpRule`. + HttpRule http = 72295728; +} diff --git a/hs-iavl-client/protos/google/api/http.proto b/hs-iavl-client/protos/google/api/http.proto new file mode 100644 index 00000000..b2977f51 --- /dev/null +++ b/hs-iavl-client/protos/google/api/http.proto @@ -0,0 +1,376 @@ +// Copyright 2019 Google LLC. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +syntax = "proto3"; + +package google.api; + +option cc_enable_arenas = true; +option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; +option java_multiple_files = true; +option java_outer_classname = "HttpProto"; +option java_package = "com.google.api"; +option objc_class_prefix = "GAPI"; + +// Defines the HTTP configuration for an API service. It contains a list of +// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method +// to one or more HTTP REST API methods. +message Http { + // A list of HTTP configuration rules that apply to individual API methods. + // + // **NOTE:** All service configuration rules follow "last one wins" order. + repeated HttpRule rules = 1; + + // When set to true, URL path parameters will be fully URI-decoded except in + // cases of single segment matches in reserved expansion, where "%2F" will be + // left encoded. + // + // The default behavior is to not decode RFC 6570 reserved characters in multi + // segment matches. + bool fully_decode_reserved_expansion = 2; +} + +// # gRPC Transcoding +// +// gRPC Transcoding is a feature for mapping between a gRPC method and one or +// more HTTP REST endpoints. It allows developers to build a single API service +// that supports both gRPC APIs and REST APIs. Many systems, including [Google +// APIs](https://github.com/googleapis/googleapis), +// [Cloud Endpoints](https://cloud.google.com/endpoints), [gRPC +// Gateway](https://github.com/grpc-ecosystem/grpc-gateway), +// and [Envoy](https://github.com/envoyproxy/envoy) proxy support this feature +// and use it for large scale production services. +// +// `HttpRule` defines the schema of the gRPC/REST mapping. The mapping specifies +// how different portions of the gRPC request message are mapped to the URL +// path, URL query parameters, and HTTP request body. It also controls how the +// gRPC response message is mapped to the HTTP response body. `HttpRule` is +// typically specified as an `google.api.http` annotation on the gRPC method. +// +// Each mapping specifies a URL path template and an HTTP method. The path +// template may refer to one or more fields in the gRPC request message, as long +// as each field is a non-repeated field with a primitive (non-message) type. +// The path template controls how fields of the request message are mapped to +// the URL path. +// +// Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/{name=messages/*}" +// }; +// } +// } +// message GetMessageRequest { +// string name = 1; // Mapped to URL path. +// } +// message Message { +// string text = 1; // The resource content. +// } +// +// This enables an HTTP REST to gRPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(name: "messages/123456")` +// +// Any fields in the request message which are not bound by the path template +// automatically become HTTP query parameters if there is no HTTP request body. +// For example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get:"/v1/messages/{message_id}" +// }; +// } +// } +// message GetMessageRequest { +// message SubMessage { +// string subfield = 1; +// } +// string message_id = 1; // Mapped to URL path. +// int64 revision = 2; // Mapped to URL query parameter `revision`. +// SubMessage sub = 3; // Mapped to URL query parameter `sub.subfield`. +// } +// +// This enables a HTTP JSON to RPC mapping as below: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | +// `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: +// "foo"))` +// +// Note that fields which are mapped to URL query parameters must have a +// primitive type or a repeated primitive type or a non-repeated message type. +// In the case of a repeated type, the parameter can be repeated in the URL +// as `...?param=A¶m=B`. In the case of a message type, each field of the +// message is mapped to a separate parameter, such as +// `...?foo.a=A&foo.b=B&foo.c=C`. +// +// For HTTP methods that allow a request body, the `body` field +// specifies the mapping. Consider a REST update method on the +// message resource collection: +// +// service Messaging { +// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "message" +// }; +// } +// } +// message UpdateMessageRequest { +// string message_id = 1; // mapped to the URL +// Message message = 2; // mapped to the body +// } +// +// The following HTTP JSON to RPC mapping is enabled, where the +// representation of the JSON in the request body is determined by +// protos JSON encoding: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" message { text: "Hi!" })` +// +// The special name `*` can be used in the body mapping to define that +// every field not bound by the path template should be mapped to the +// request body. This enables the following alternative definition of +// the update method: +// +// service Messaging { +// rpc UpdateMessage(Message) returns (Message) { +// option (google.api.http) = { +// patch: "/v1/messages/{message_id}" +// body: "*" +// }; +// } +// } +// message Message { +// string message_id = 1; +// string text = 2; +// } +// +// +// The following HTTP JSON to RPC mapping is enabled: +// +// HTTP | gRPC +// -----|----- +// `PATCH /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: +// "123456" text: "Hi!")` +// +// Note that when using `*` in the body mapping, it is not possible to +// have HTTP parameters, as all fields not bound by the path end in +// the body. This makes this option more rarely used in practice when +// defining REST APIs. The common usage of `*` is in custom methods +// which don't use the URL at all for transferring data. +// +// It is possible to define multiple HTTP methods for one RPC by using +// the `additional_bindings` option. Example: +// +// service Messaging { +// rpc GetMessage(GetMessageRequest) returns (Message) { +// option (google.api.http) = { +// get: "/v1/messages/{message_id}" +// additional_bindings { +// get: "/v1/users/{user_id}/messages/{message_id}" +// } +// }; +// } +// } +// message GetMessageRequest { +// string message_id = 1; +// string user_id = 2; +// } +// +// This enables the following two alternative HTTP JSON to RPC mappings: +// +// HTTP | gRPC +// -----|----- +// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` +// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: +// "123456")` +// +// ## Rules for HTTP mapping +// +// 1. Leaf request fields (recursive expansion nested messages in the request +// message) are classified into three categories: +// - Fields referred by the path template. They are passed via the URL path. +// - Fields referred by the [HttpRule.body][google.api.HttpRule.body]. They are passed via the HTTP +// request body. +// - All other fields are passed via the URL query parameters, and the +// parameter name is the field path in the request message. A repeated +// field can be represented as multiple query parameters under the same +// name. +// 2. If [HttpRule.body][google.api.HttpRule.body] is "*", there is no URL query parameter, all fields +// are passed via URL path and HTTP request body. +// 3. If [HttpRule.body][google.api.HttpRule.body] is omitted, there is no HTTP request body, all +// fields are passed via URL path and URL query parameters. +// +// ### Path template syntax +// +// Template = "/" Segments [ Verb ] ; +// Segments = Segment { "/" Segment } ; +// Segment = "*" | "**" | LITERAL | Variable ; +// Variable = "{" FieldPath [ "=" Segments ] "}" ; +// FieldPath = IDENT { "." IDENT } ; +// Verb = ":" LITERAL ; +// +// The syntax `*` matches a single URL path segment. The syntax `**` matches +// zero or more URL path segments, which must be the last part of the URL path +// except the `Verb`. +// +// The syntax `Variable` matches part of the URL path as specified by its +// template. A variable template must not contain other variables. If a variable +// matches a single path segment, its template may be omitted, e.g. `{var}` +// is equivalent to `{var=*}`. +// +// The syntax `LITERAL` matches literal text in the URL path. If the `LITERAL` +// contains any reserved character, such characters should be percent-encoded +// before the matching. +// +// If a variable contains exactly one path segment, such as `"{var}"` or +// `"{var=*}"`, when such a variable is expanded into a URL path on the client +// side, all characters except `[-_.~0-9a-zA-Z]` are percent-encoded. The +// server side does the reverse decoding. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{var}`. +// +// If a variable contains multiple path segments, such as `"{var=foo/*}"` +// or `"{var=**}"`, when such a variable is expanded into a URL path on the +// client side, all characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. +// The server side does the reverse decoding, except "%2F" and "%2f" are left +// unchanged. Such variables show up in the +// [Discovery +// Document](https://developers.google.com/discovery/v1/reference/apis) as +// `{+var}`. +// +// ## Using gRPC API Service Configuration +// +// gRPC API Service Configuration (service config) is a configuration language +// for configuring a gRPC service to become a user-facing product. The +// service config is simply the YAML representation of the `google.api.Service` +// proto message. +// +// As an alternative to annotating your proto file, you can configure gRPC +// transcoding in your service config YAML files. You do this by specifying a +// `HttpRule` that maps the gRPC method to a REST endpoint, achieving the same +// effect as the proto annotation. This can be particularly useful if you +// have a proto that is reused in multiple services. Note that any transcoding +// specified in the service config will override any matching transcoding +// configuration in the proto. +// +// Example: +// +// http: +// rules: +// # Selects a gRPC method and applies HttpRule to it. +// - selector: example.v1.Messaging.GetMessage +// get: /v1/messages/{message_id}/{sub.subfield} +// +// ## Special notes +// +// When gRPC Transcoding is used to map a gRPC to JSON REST endpoints, the +// proto to JSON conversion must follow the [proto3 +// specification](https://developers.google.com/protocol-buffers/docs/proto3#json). +// +// While the single segment variable follows the semantics of +// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 Simple String +// Expansion, the multi segment variable **does not** follow RFC 6570 Section +// 3.2.3 Reserved Expansion. The reason is that the Reserved Expansion +// does not expand special characters like `?` and `#`, which would lead +// to invalid URLs. As the result, gRPC Transcoding uses a custom encoding +// for multi segment variables. +// +// The path variables **must not** refer to any repeated or mapped field, +// because client libraries are not capable of handling such variable expansion. +// +// The path variables **must not** capture the leading "/" character. The reason +// is that the most common use case "{var}" does not capture the leading "/" +// character. For consistency, all path variables must share the same behavior. +// +// Repeated message fields must not be mapped to URL query parameters, because +// no client library can support such complicated mapping. +// +// If an API needs to use a JSON array for request or response body, it can map +// the request or response body to a repeated field. However, some gRPC +// Transcoding implementations may not support this feature. +message HttpRule { + // Selects a method to which this rule applies. + // + // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. + string selector = 1; + + // Determines the URL pattern is matched by this rules. This pattern can be + // used with any of the {get|put|post|delete|patch} methods. A custom method + // can be defined using the 'custom' field. + oneof pattern { + // Maps to HTTP GET. Used for listing and getting information about + // resources. + string get = 2; + + // Maps to HTTP PUT. Used for replacing a resource. + string put = 3; + + // Maps to HTTP POST. Used for creating a resource or performing an action. + string post = 4; + + // Maps to HTTP DELETE. Used for deleting a resource. + string delete = 5; + + // Maps to HTTP PATCH. Used for updating a resource. + string patch = 6; + + // The custom pattern is used for specifying an HTTP method that is not + // included in the `pattern` field, such as HEAD, or "*" to leave the + // HTTP method unspecified for this rule. The wild-card rule is useful + // for services that provide content to Web (HTML) clients. + CustomHttpPattern custom = 8; + } + + // The name of the request field whose value is mapped to the HTTP request + // body, or `*` for mapping all request fields not captured by the path + // pattern to the HTTP body, or omitted for not having any HTTP request body. + // + // NOTE: the referred field must be present at the top-level of the request + // message type. + string body = 7; + + // Optional. The name of the response field whose value is mapped to the HTTP + // response body. When omitted, the entire response message will be used + // as the HTTP response body. + // + // NOTE: The referred field must be present at the top-level of the response + // message type. + string response_body = 12; + + // Additional HTTP bindings for the selector. Nested bindings must + // not contain an `additional_bindings` field themselves (that is, + // the nesting may only be one level deep). + repeated HttpRule additional_bindings = 11; +} + +// A custom pattern is used for defining custom HTTP verb. +message CustomHttpPattern { + // The name of this custom HTTP verb. + string kind = 1; + + // The path matched by this custom verb. + string path = 2; +} diff --git a/hs-iavl-client/protos/google/protobuf/descriptor.proto b/hs-iavl-client/protos/google/protobuf/descriptor.proto new file mode 100644 index 00000000..8c1273db --- /dev/null +++ b/hs-iavl-client/protos/google/protobuf/descriptor.proto @@ -0,0 +1,882 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Author: kenton@google.com (Kenton Varda) +// Based on original Protocol Buffers design by +// Sanjay Ghemawat, Jeff Dean, and others. +// +// The messages in this file describe the definitions found in .proto files. +// A valid .proto file can be translated directly to a FileDescriptorProto +// without any other information (e.g. without reading its imports). + + +syntax = "proto2"; + +package google.protobuf; +option go_package = "github.com/golang/protobuf/protoc-gen-go/descriptor;descriptor"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "DescriptorProtos"; +option csharp_namespace = "Google.Protobuf.Reflection"; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// descriptor.proto must be optimized for speed because reflection-based +// algorithms don't work during bootstrapping. +option optimize_for = SPEED; + +// The protocol compiler can output a FileDescriptorSet containing the .proto +// files it parses. +message FileDescriptorSet { + repeated FileDescriptorProto file = 1; +} + +// Describes a complete .proto file. +message FileDescriptorProto { + optional string name = 1; // file name, relative to root of source tree + optional string package = 2; // e.g. "foo", "foo.bar", etc. + + // Names of files imported by this file. + repeated string dependency = 3; + // Indexes of the public imported files in the dependency list above. + repeated int32 public_dependency = 10; + // Indexes of the weak imported files in the dependency list. + // For Google-internal migration only. Do not use. + repeated int32 weak_dependency = 11; + + // All top-level definitions in this file. + repeated DescriptorProto message_type = 4; + repeated EnumDescriptorProto enum_type = 5; + repeated ServiceDescriptorProto service = 6; + repeated FieldDescriptorProto extension = 7; + + optional FileOptions options = 8; + + // This field contains optional information about the original source code. + // You may safely remove this entire field without harming runtime + // functionality of the descriptors -- the information is needed only by + // development tools. + optional SourceCodeInfo source_code_info = 9; + + // The syntax of the proto file. + // The supported values are "proto2" and "proto3". + optional string syntax = 12; +} + +// Describes a message type. +message DescriptorProto { + optional string name = 1; + + repeated FieldDescriptorProto field = 2; + repeated FieldDescriptorProto extension = 6; + + repeated DescriptorProto nested_type = 3; + repeated EnumDescriptorProto enum_type = 4; + + message ExtensionRange { + optional int32 start = 1; + optional int32 end = 2; + + optional ExtensionRangeOptions options = 3; + } + repeated ExtensionRange extension_range = 5; + + repeated OneofDescriptorProto oneof_decl = 8; + + optional MessageOptions options = 7; + + // Range of reserved tag numbers. Reserved tag numbers may not be used by + // fields or extension ranges in the same message. Reserved ranges may + // not overlap. + message ReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Exclusive. + } + repeated ReservedRange reserved_range = 9; + // Reserved field names, which may not be used by fields in the same message. + // A given name may only be reserved once. + repeated string reserved_name = 10; +} + +message ExtensionRangeOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +// Describes a field within a message. +message FieldDescriptorProto { + enum Type { + // 0 is reserved for errors. + // Order is weird for historical reasons. + TYPE_DOUBLE = 1; + TYPE_FLOAT = 2; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if + // negative values are likely. + TYPE_INT64 = 3; + TYPE_UINT64 = 4; + // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if + // negative values are likely. + TYPE_INT32 = 5; + TYPE_FIXED64 = 6; + TYPE_FIXED32 = 7; + TYPE_BOOL = 8; + TYPE_STRING = 9; + // Tag-delimited aggregate. + // Group type is deprecated and not supported in proto3. However, Proto3 + // implementations should still be able to parse the group wire format and + // treat group fields as unknown fields. + TYPE_GROUP = 10; + TYPE_MESSAGE = 11; // Length-delimited aggregate. + + // New in version 2. + TYPE_BYTES = 12; + TYPE_UINT32 = 13; + TYPE_ENUM = 14; + TYPE_SFIXED32 = 15; + TYPE_SFIXED64 = 16; + TYPE_SINT32 = 17; // Uses ZigZag encoding. + TYPE_SINT64 = 18; // Uses ZigZag encoding. + }; + + enum Label { + // 0 is reserved for errors + LABEL_OPTIONAL = 1; + LABEL_REQUIRED = 2; + LABEL_REPEATED = 3; + }; + + optional string name = 1; + optional int32 number = 3; + optional Label label = 4; + + // If type_name is set, this need not be set. If both this and type_name + // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. + optional Type type = 5; + + // For message and enum types, this is the name of the type. If the name + // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping + // rules are used to find the type (i.e. first the nested types within this + // message are searched, then within the parent, on up to the root + // namespace). + optional string type_name = 6; + + // For extensions, this is the name of the type being extended. It is + // resolved in the same manner as type_name. + optional string extendee = 2; + + // For numeric types, contains the original text representation of the value. + // For booleans, "true" or "false". + // For strings, contains the default text contents (not escaped in any way). + // For bytes, contains the C escaped value. All bytes >= 128 are escaped. + // TODO(kenton): Base-64 encode? + optional string default_value = 7; + + // If set, gives the index of a oneof in the containing type's oneof_decl + // list. This field is a member of that oneof. + optional int32 oneof_index = 9; + + // JSON name of this field. The value is set by protocol compiler. If the + // user has set a "json_name" option on this field, that option's value + // will be used. Otherwise, it's deduced from the field's name by converting + // it to camelCase. + optional string json_name = 10; + + optional FieldOptions options = 8; +} + +// Describes a oneof. +message OneofDescriptorProto { + optional string name = 1; + optional OneofOptions options = 2; +} + +// Describes an enum type. +message EnumDescriptorProto { + optional string name = 1; + + repeated EnumValueDescriptorProto value = 2; + + optional EnumOptions options = 3; + + // Range of reserved numeric values. Reserved values may not be used by + // entries in the same enum. Reserved ranges may not overlap. + // + // Note that this is distinct from DescriptorProto.ReservedRange in that it + // is inclusive such that it can appropriately represent the entire int32 + // domain. + message EnumReservedRange { + optional int32 start = 1; // Inclusive. + optional int32 end = 2; // Inclusive. + } + + // Range of reserved numeric values. Reserved numeric values may not be used + // by enum values in the same enum declaration. Reserved ranges may not + // overlap. + repeated EnumReservedRange reserved_range = 4; + + // Reserved enum value names, which may not be reused. A given name may only + // be reserved once. + repeated string reserved_name = 5; +} + +// Describes a value within an enum. +message EnumValueDescriptorProto { + optional string name = 1; + optional int32 number = 2; + + optional EnumValueOptions options = 3; +} + +// Describes a service. +message ServiceDescriptorProto { + optional string name = 1; + repeated MethodDescriptorProto method = 2; + + optional ServiceOptions options = 3; +} + +// Describes a method of a service. +message MethodDescriptorProto { + optional string name = 1; + + // Input and output type names. These are resolved in the same way as + // FieldDescriptorProto.type_name, but must refer to a message type. + optional string input_type = 2; + optional string output_type = 3; + + optional MethodOptions options = 4; + + // Identifies if client streams multiple client messages + optional bool client_streaming = 5 [default=false]; + // Identifies if server streams multiple server messages + optional bool server_streaming = 6 [default=false]; +} + + +// =================================================================== +// Options + +// Each of the definitions above may have "options" attached. These are +// just annotations which may cause code to be generated slightly differently +// or may contain hints for code that manipulates protocol messages. +// +// Clients may define custom options as extensions of the *Options messages. +// These extensions may not yet be known at parsing time, so the parser cannot +// store the values in them. Instead it stores them in a field in the *Options +// message called uninterpreted_option. This field must have the same name +// across all *Options messages. We then use this field to populate the +// extensions when we build a descriptor, at which point all protos have been +// parsed and so all extensions are known. +// +// Extension numbers for custom options may be chosen as follows: +// * For options which will only be used within a single application or +// organization, or for experimental options, use field numbers 50000 +// through 99999. It is up to you to ensure that you do not use the +// same number for multiple options. +// * For options which will be published and used publicly by multiple +// independent entities, e-mail protobuf-global-extension-registry@google.com +// to reserve extension numbers. Simply provide your project name (e.g. +// Objective-C plugin) and your project website (if available) -- there's no +// need to explain how you intend to use them. Usually you only need one +// extension number. You can declare multiple options with only one extension +// number by putting them in a sub-message. See the Custom Options section of +// the docs for examples: +// https://developers.google.com/protocol-buffers/docs/proto#options +// If this turns out to be popular, a web service will be set up +// to automatically assign option numbers. + + +message FileOptions { + + // Sets the Java package where classes generated from this .proto will be + // placed. By default, the proto package is used, but this is often + // inappropriate because proto packages do not normally start with backwards + // domain names. + optional string java_package = 1; + + + // If set, all the classes from the .proto file are wrapped in a single + // outer class with the given name. This applies to both Proto1 + // (equivalent to the old "--one_java_file" option) and Proto2 (where + // a .proto always translates to a single class, but you may want to + // explicitly choose the class name). + optional string java_outer_classname = 8; + + // If set true, then the Java code generator will generate a separate .java + // file for each top-level message, enum, and service defined in the .proto + // file. Thus, these types will *not* be nested inside the outer class + // named by java_outer_classname. However, the outer class will still be + // generated to contain the file's getDescriptor() method as well as any + // top-level extensions defined in the file. + optional bool java_multiple_files = 10 [default=false]; + + // This option does nothing. + optional bool java_generate_equals_and_hash = 20 [deprecated=true]; + + // If set true, then the Java2 code generator will generate code that + // throws an exception whenever an attempt is made to assign a non-UTF-8 + // byte sequence to a string field. + // Message reflection will do the same. + // However, an extension field still accepts non-UTF-8 byte sequences. + // This option has no effect on when used with the lite runtime. + optional bool java_string_check_utf8 = 27 [default=false]; + + + // Generated classes can be optimized for speed or code size. + enum OptimizeMode { + SPEED = 1; // Generate complete code for parsing, serialization, + // etc. + CODE_SIZE = 2; // Use ReflectionOps to implement these methods. + LITE_RUNTIME = 3; // Generate code using MessageLite and the lite runtime. + } + optional OptimizeMode optimize_for = 9 [default=SPEED]; + + // Sets the Go package where structs generated from this .proto will be + // placed. If omitted, the Go package will be derived from the following: + // - The basename of the package import path, if provided. + // - Otherwise, the package statement in the .proto file, if present. + // - Otherwise, the basename of the .proto file, without extension. + optional string go_package = 11; + + + + // Should generic services be generated in each language? "Generic" services + // are not specific to any particular RPC system. They are generated by the + // main code generators in each language (without additional plugins). + // Generic services were the only kind of service generation supported by + // early versions of google.protobuf. + // + // Generic services are now considered deprecated in favor of using plugins + // that generate code specific to your particular RPC system. Therefore, + // these default to false. Old code which depends on generic services should + // explicitly set them to true. + optional bool cc_generic_services = 16 [default=false]; + optional bool java_generic_services = 17 [default=false]; + optional bool py_generic_services = 18 [default=false]; + optional bool php_generic_services = 42 [default=false]; + + // Is this file deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for everything in the file, or it will be completely ignored; in the very + // least, this is a formalization for deprecating files. + optional bool deprecated = 23 [default=false]; + + // Enables the use of arenas for the proto messages in this file. This applies + // only to generated classes for C++. + optional bool cc_enable_arenas = 31 [default=false]; + + + // Sets the objective c class prefix which is prepended to all objective c + // generated classes from this .proto. There is no default. + optional string objc_class_prefix = 36; + + // Namespace for generated classes; defaults to the package. + optional string csharp_namespace = 37; + + // By default Swift generators will take the proto package and CamelCase it + // replacing '.' with underscore and use that to prefix the types/symbols + // defined. When this options is provided, they will use this value instead + // to prefix the types/symbols defined. + optional string swift_prefix = 39; + + // Sets the php class prefix which is prepended to all php generated classes + // from this .proto. Default is empty. + optional string php_class_prefix = 40; + + // Use this option to change the namespace of php generated classes. Default + // is empty. When this option is empty, the package name will be used for + // determining the namespace. + optional string php_namespace = 41; + + // Use this option to change the namespace of php generated metadata classes. + // Default is empty. When this option is empty, the proto file name will be used + // for determining the namespace. + optional string php_metadata_namespace = 44; + + // Use this option to change the package of ruby generated classes. Default + // is empty. When this option is not set, the package name will be used for + // determining the ruby package. + optional string ruby_package = 45; + + // The parser stores options it doesn't recognize here. + // See the documentation for the "Options" section above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. + // See the documentation for the "Options" section above. + extensions 1000 to max; + + reserved 38; +} + +message MessageOptions { + // Set true to use the old proto1 MessageSet wire format for extensions. + // This is provided for backwards-compatibility with the MessageSet wire + // format. You should not use this for any other reason: It's less + // efficient, has fewer features, and is more complicated. + // + // The message must be defined exactly as follows: + // message Foo { + // option message_set_wire_format = true; + // extensions 4 to max; + // } + // Note that the message cannot have any defined fields; MessageSets only + // have extensions. + // + // All extensions of your type must be singular messages; e.g. they cannot + // be int32s, enums, or repeated messages. + // + // Because this is an option, the above two restrictions are not enforced by + // the protocol compiler. + optional bool message_set_wire_format = 1 [default=false]; + + // Disables the generation of the standard "descriptor()" accessor, which can + // conflict with a field of the same name. This is meant to make migration + // from proto1 easier; new code should avoid fields named "descriptor". + optional bool no_standard_descriptor_accessor = 2 [default=false]; + + // Is this message deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the message, or it will be completely ignored; in the very least, + // this is a formalization for deprecating messages. + optional bool deprecated = 3 [default=false]; + + // Whether the message is an automatically generated map entry type for the + // maps field. + // + // For maps fields: + // map map_field = 1; + // The parsed descriptor looks like: + // message MapFieldEntry { + // option map_entry = true; + // optional KeyType key = 1; + // optional ValueType value = 2; + // } + // repeated MapFieldEntry map_field = 1; + // + // Implementations may choose not to generate the map_entry=true message, but + // use a native map in the target language to hold the keys and values. + // The reflection APIs in such implementations still need to work as + // if the field is a repeated message field. + // + // NOTE: Do not set the option in .proto files. Always use the maps syntax + // instead. The option should only be implicitly set by the proto compiler + // parser. + optional bool map_entry = 7; + + reserved 8; // javalite_serializable + reserved 9; // javanano_as_lite + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message FieldOptions { + // The ctype option instructs the C++ code generator to use a different + // representation of the field than it normally would. See the specific + // options below. This option is not yet implemented in the open source + // release -- sorry, we'll try to include it in a future version! + optional CType ctype = 1 [default = STRING]; + enum CType { + // Default mode. + STRING = 0; + + CORD = 1; + + STRING_PIECE = 2; + } + // The packed option can be enabled for repeated primitive fields to enable + // a more efficient representation on the wire. Rather than repeatedly + // writing the tag and type for each element, the entire array is encoded as + // a single length-delimited blob. In proto3, only explicit setting it to + // false will avoid using packed encoding. + optional bool packed = 2; + + // The jstype option determines the JavaScript type used for values of the + // field. The option is permitted only for 64 bit integral and fixed types + // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING + // is represented as JavaScript string, which avoids loss of precision that + // can happen when a large value is converted to a floating point JavaScript. + // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to + // use the JavaScript "number" type. The behavior of the default option + // JS_NORMAL is implementation dependent. + // + // This option is an enum to permit additional types to be added, e.g. + // goog.math.Integer. + optional JSType jstype = 6 [default = JS_NORMAL]; + enum JSType { + // Use the default type. + JS_NORMAL = 0; + + // Use JavaScript strings. + JS_STRING = 1; + + // Use JavaScript numbers. + JS_NUMBER = 2; + } + + // Should this field be parsed lazily? Lazy applies only to message-type + // fields. It means that when the outer message is initially parsed, the + // inner message's contents will not be parsed but instead stored in encoded + // form. The inner message will actually be parsed when it is first accessed. + // + // This is only a hint. Implementations are free to choose whether to use + // eager or lazy parsing regardless of the value of this option. However, + // setting this option true suggests that the protocol author believes that + // using lazy parsing on this field is worth the additional bookkeeping + // overhead typically needed to implement it. + // + // This option does not affect the public interface of any generated code; + // all method signatures remain the same. Furthermore, thread-safety of the + // interface is not affected by this option; const methods remain safe to + // call from multiple threads concurrently, while non-const methods continue + // to require exclusive access. + // + // + // Note that implementations may choose not to check required fields within + // a lazy sub-message. That is, calling IsInitialized() on the outer message + // may return true even if the inner message has missing required fields. + // This is necessary because otherwise the inner message would have to be + // parsed in order to perform the check, defeating the purpose of lazy + // parsing. An implementation which chooses not to check required fields + // must be consistent about it. That is, for any particular sub-message, the + // implementation must either *always* check its required fields, or *never* + // check its required fields, regardless of whether or not the message has + // been parsed. + optional bool lazy = 5 [default=false]; + + // Is this field deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for accessors, or it will be completely ignored; in the very least, this + // is a formalization for deprecating fields. + optional bool deprecated = 3 [default=false]; + + // For Google-internal migration only. Do not use. + optional bool weak = 10 [default=false]; + + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; + + reserved 4; // removed jtype +} + +message OneofOptions { + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumOptions { + + // Set this option to true to allow mapping different tag names to the same + // value. + optional bool allow_alias = 2; + + // Is this enum deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum, or it will be completely ignored; in the very least, this + // is a formalization for deprecating enums. + optional bool deprecated = 3 [default=false]; + + reserved 5; // javanano_as_lite + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message EnumValueOptions { + // Is this enum value deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the enum value, or it will be completely ignored; in the very least, + // this is a formalization for deprecating enum values. + optional bool deprecated = 1 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message ServiceOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this service deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the service, or it will be completely ignored; in the very least, + // this is a formalization for deprecating services. + optional bool deprecated = 33 [default=false]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + +message MethodOptions { + + // Note: Field numbers 1 through 32 are reserved for Google's internal RPC + // framework. We apologize for hoarding these numbers to ourselves, but + // we were already using them long before we decided to release Protocol + // Buffers. + + // Is this method deprecated? + // Depending on the target platform, this can emit Deprecated annotations + // for the method, or it will be completely ignored; in the very least, + // this is a formalization for deprecating methods. + optional bool deprecated = 33 [default=false]; + + // Is this method side-effect-free (or safe in HTTP parlance), or idempotent, + // or neither? HTTP based RPC implementation may choose GET verb for safe + // methods, and PUT verb for idempotent methods instead of the default POST. + enum IdempotencyLevel { + IDEMPOTENCY_UNKNOWN = 0; + NO_SIDE_EFFECTS = 1; // implies idempotent + IDEMPOTENT = 2; // idempotent, but may have side effects + } + optional IdempotencyLevel idempotency_level = + 34 [default=IDEMPOTENCY_UNKNOWN]; + + // The parser stores options it doesn't recognize here. See above. + repeated UninterpretedOption uninterpreted_option = 999; + + // Clients can define custom options in extensions of this message. See above. + extensions 1000 to max; +} + + +// A message representing a option the parser does not recognize. This only +// appears in options protos created by the compiler::Parser class. +// DescriptorPool resolves these when building Descriptor objects. Therefore, +// options protos in descriptor objects (e.g. returned by Descriptor::options(), +// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions +// in them. +message UninterpretedOption { + // The name of the uninterpreted option. Each string represents a segment in + // a dot-separated name. is_extension is true iff a segment represents an + // extension (denoted with parentheses in options specs in .proto files). + // E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents + // "foo.(bar.baz).qux". + message NamePart { + required string name_part = 1; + required bool is_extension = 2; + } + repeated NamePart name = 2; + + // The value of the uninterpreted option, in whatever type the tokenizer + // identified it as during parsing. Exactly one of these should be set. + optional string identifier_value = 3; + optional uint64 positive_int_value = 4; + optional int64 negative_int_value = 5; + optional double double_value = 6; + optional bytes string_value = 7; + optional string aggregate_value = 8; +} + +// =================================================================== +// Optional source code info + +// Encapsulates information about the original source file from which a +// FileDescriptorProto was generated. +message SourceCodeInfo { + // A Location identifies a piece of source code in a .proto file which + // corresponds to a particular definition. This information is intended + // to be useful to IDEs, code indexers, documentation generators, and similar + // tools. + // + // For example, say we have a file like: + // message Foo { + // optional string foo = 1; + // } + // Let's look at just the field definition: + // optional string foo = 1; + // ^ ^^ ^^ ^ ^^^ + // a bc de f ghi + // We have the following locations: + // span path represents + // [a,i) [ 4, 0, 2, 0 ] The whole field definition. + // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). + // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). + // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). + // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). + // + // Notes: + // - A location may refer to a repeated field itself (i.e. not to any + // particular index within it). This is used whenever a set of elements are + // logically enclosed in a single code segment. For example, an entire + // extend block (possibly containing multiple extension definitions) will + // have an outer location whose path refers to the "extensions" repeated + // field without an index. + // - Multiple locations may have the same path. This happens when a single + // logical declaration is spread out across multiple places. The most + // obvious example is the "extend" block again -- there may be multiple + // extend blocks in the same scope, each of which will have the same path. + // - A location's span is not always a subset of its parent's span. For + // example, the "extendee" of an extension declaration appears at the + // beginning of the "extend" block and is shared by all extensions within + // the block. + // - Just because a location's span is a subset of some other location's span + // does not mean that it is a descendant. For example, a "group" defines + // both a type and a field in a single declaration. Thus, the locations + // corresponding to the type and field and their components will overlap. + // - Code which tries to interpret locations should probably be designed to + // ignore those that it doesn't understand, as more types of locations could + // be recorded in the future. + repeated Location location = 1; + message Location { + // Identifies which part of the FileDescriptorProto was defined at this + // location. + // + // Each element is a field number or an index. They form a path from + // the root FileDescriptorProto to the place where the definition. For + // example, this path: + // [ 4, 3, 2, 7, 1 ] + // refers to: + // file.message_type(3) // 4, 3 + // .field(7) // 2, 7 + // .name() // 1 + // This is because FileDescriptorProto.message_type has field number 4: + // repeated DescriptorProto message_type = 4; + // and DescriptorProto.field has field number 2: + // repeated FieldDescriptorProto field = 2; + // and FieldDescriptorProto.name has field number 1: + // optional string name = 1; + // + // Thus, the above path gives the location of a field name. If we removed + // the last element: + // [ 4, 3, 2, 7 ] + // this path refers to the whole field declaration (from the beginning + // of the label to the terminating semicolon). + repeated int32 path = 1 [packed=true]; + + // Always has exactly three or four elements: start line, start column, + // end line (optional, otherwise assumed same as start line), end column. + // These are packed into a single field for efficiency. Note that line + // and column numbers are zero-based -- typically you will want to add + // 1 to each before displaying to a user. + repeated int32 span = 2 [packed=true]; + + // If this SourceCodeInfo represents a complete declaration, these are any + // comments appearing before and after the declaration which appear to be + // attached to the declaration. + // + // A series of line comments appearing on consecutive lines, with no other + // tokens appearing on those lines, will be treated as a single comment. + // + // leading_detached_comments will keep paragraphs of comments that appear + // before (but not connected to) the current element. Each paragraph, + // separated by empty lines, will be one comment element in the repeated + // field. + // + // Only the comment content is provided; comment markers (e.g. //) are + // stripped out. For block comments, leading whitespace and an asterisk + // will be stripped from the beginning of each line other than the first. + // Newlines are included in the output. + // + // Examples: + // + // optional int32 foo = 1; // Comment attached to foo. + // // Comment attached to bar. + // optional int32 bar = 2; + // + // optional string baz = 3; + // // Comment attached to baz. + // // Another line attached to baz. + // + // // Comment attached to qux. + // // + // // Another line attached to qux. + // optional double qux = 4; + // + // // Detached comment for corge. This is not leading or trailing comments + // // to qux or corge because there are blank lines separating it from + // // both. + // + // // Detached comment for corge paragraph 2. + // + // optional string corge = 5; + // /* Block comment attached + // * to corge. Leading asterisks + // * will be removed. */ + // /* Block comment attached to + // * grault. */ + // optional int32 grault = 6; + // + // // ignored detached comments. + optional string leading_comments = 3; + optional string trailing_comments = 4; + repeated string leading_detached_comments = 6; + } +} + +// Describes the relationship between generated code and its original source +// file. A GeneratedCodeInfo message is associated with only one generated +// source file, but may contain references to different source .proto files. +message GeneratedCodeInfo { + // An Annotation connects some span of text in generated code to an element + // of its generating .proto file. + repeated Annotation annotation = 1; + message Annotation { + // Identifies the element in the original source .proto file. This field + // is formatted the same as SourceCodeInfo.Location.path. + repeated int32 path = 1 [packed=true]; + + // Identifies the filesystem path to the original source .proto. + optional string source_file = 2; + + // Identifies the starting offset in bytes in the generated code + // that relates to the identified object. + optional int32 begin = 3; + + // Identifies the ending offset in bytes in the generated code that + // relates to the identified offset. The end offset should be one past + // the last relevant byte (so the length of the text = end - begin). + optional int32 end = 4; + } +} diff --git a/hs-iavl-client/protos/google/protobuf/empty.proto b/hs-iavl-client/protos/google/protobuf/empty.proto new file mode 100644 index 00000000..03cacd23 --- /dev/null +++ b/hs-iavl-client/protos/google/protobuf/empty.proto @@ -0,0 +1,52 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +syntax = "proto3"; + +package google.protobuf; + +option csharp_namespace = "Google.Protobuf.WellKnownTypes"; +option go_package = "github.com/golang/protobuf/ptypes/empty"; +option java_package = "com.google.protobuf"; +option java_outer_classname = "EmptyProto"; +option java_multiple_files = true; +option objc_class_prefix = "GPB"; +option cc_enable_arenas = true; + +// A generic empty message that you can re-use to avoid defining duplicated +// empty messages in your APIs. A typical example is to use it as the request +// or the response type of an API method. For instance: +// +// service Foo { +// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); +// } +// +// The JSON representation for `Empty` is empty JSON object `{}`. +message Empty {} diff --git a/hs-iavl-client/protos/iavl/api.proto b/hs-iavl-client/protos/iavl/api.proto new file mode 100644 index 00000000..2daec57d --- /dev/null +++ b/hs-iavl-client/protos/iavl/api.proto @@ -0,0 +1,274 @@ +syntax = "proto3"; +package proto; + +import "google/protobuf/empty.proto"; +import "google/api/annotations.proto"; + +// ---------------------------------------------------------------------------- +// gRPC service +// ---------------------------------------------------------------------------- + +// IAVLService defines the gRPC service API contract for the IAVL tree. +service IAVLService { + // Has returns a result containing a boolean on whether or not the IAVL tree + // has a given key at a specific tree version. + rpc Has(HasRequest) returns (HasResponse) { + option (google.api.http) = { + get: "/v1/tree/{version}/has" + }; + } + + // Get returns a result containing the IAVL tree version and value for a given + // key based on the current state (version) of the tree. + rpc Get(GetRequest) returns (GetResponse) { + option (google.api.http) = { + get: "/v1/tree/get" + }; + } + + // GetWithProof returns a result containing the IAVL tree version and value for + // a given key based on the current state (version) of the tree including a + // verifiable Merkle proof. + rpc GetWithProof(GetRequest) returns (GetWithProofResponse) { + option (google.api.http) = { + get: "/v1/tree/get_with_proof" + }; + } + + // GetVersioned returns a result containing the IAVL tree version and value + // for a given key at a specific tree version. + rpc GetVersioned(GetVersionedRequest) returns (GetResponse) { + option (google.api.http) = { + get: "/v1/tree/{version}/get_versioned" + }; + } + + // GetVersionedWithProof returns a result containing the IAVL tree version and + // value for a given key at a specific tree version including a verifiable Merkle + // proof. + rpc GetVersionedWithProof(GetVersionedRequest) returns (GetWithProofResponse) { + option (google.api.http) = { + get: "/v1/tree/{version}/get_versioned_with_proof" + }; + } + + // Set returns a result after inserting a key/value pair into the IAVL tree + // based on the current state (version) of the tree. + rpc Set(SetRequest) returns (SetResponse) { + option (google.api.http) = { + post: "/v1/tree/set" + body: "*" + }; + } + + // Remove returns a result after removing a key/value pair from the IAVL tree + // based on the current state (version) of the tree. + rpc Remove(RemoveRequest) returns (RemoveResponse) { + option (google.api.http) = { + post: "/v1/tree/remove" + body: "*" + }; + } + + // SaveVersion saves a new IAVL tree version to the DB based on the current + // state (version) of the tree. It returns a result containing the hash and + // new version number. + rpc SaveVersion(google.protobuf.Empty) returns (SaveVersionResponse) { + option (google.api.http) = { + post: "/v1/tree/save_version" + body: "*" + }; + } + + // DeleteVersion deletes an IAVL tree version from the DB. The version can then + // no longer be accessed. It returns a result containing the version and root + // hash of the versioned tree that was deleted. + rpc DeleteVersion(DeleteVersionRequest) returns (DeleteVersionResponse) { + option (google.api.http) = { + post: "/v1/tree/delete_version" + body: "*" + }; + } + + // Version returns the IAVL tree version based on the current state. + rpc Version(google.protobuf.Empty) returns (VersionResponse) { + option (google.api.http) = { + get: "/v1/tree/version" + }; + } + + // Hash returns the IAVL tree root hash based on the current state. + rpc Hash(google.protobuf.Empty) returns (HashResponse) { + option (google.api.http) = { + get: "/v1/tree/hash" + }; + } + + // VersionExists returns a result containing a boolean on whether or not a given + // version exists in the IAVL tree. + rpc VersionExists(VersionExistsRequest) returns (VersionExistsResponse) { + option (google.api.http) = { + get: "/v1/tree/version_exists" + }; + } + + // Verify verifies an IAVL range proof returning an error if the proof is + // invalid. + rpc Verify(VerifyRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/v1/tree/range_proof/verify" + }; + } + + // VerifyItem verifies if a given key/value pair in an IAVL range proof returning + // an error if the proof or key is invalid. + rpc VerifyItem(VerifyItemRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/v1/tree/range_proof/verify_item" + }; + } + + // VerifyAbsence verifies the absence of a given key in an IAVL range proof + // returning an error if the proof or key is invalid. + rpc VerifyAbsence(VerifyAbsenceRequest) returns (google.protobuf.Empty) { + option (google.api.http) = { + get: "/v1/tree/range_proof/verify_absence" + }; + } + + // Rollback resets the working tree to the latest saved version, discarding + // any unsaved modifications. + rpc Rollback(google.protobuf.Empty) returns (google.protobuf.Empty) { + option (google.api.http) = { + post: "/v1/tree/rollback" + body: "*" + }; + } +} + +// ---------------------------------------------------------------------------- +// Request types +// ---------------------------------------------------------------------------- + +message HasRequest { + int64 version = 1; + bytes key = 2; +} + +message GetRequest { + bytes key = 1; +} + +message GetVersionedRequest { + int64 version = 1; + bytes key = 2; +} + +message SetRequest { + bytes key = 1; + bytes value = 2; +} + +message RemoveRequest { + bytes key = 1; +} + +message DeleteVersionRequest { + int64 version = 1; +} + +message VersionExistsRequest { + int64 version = 1; +} + +message VerifyRequest { + bytes root_hash = 1; + RangeProof proof = 2; +} + +message VerifyItemRequest { + bytes root_hash = 1; + RangeProof proof = 2; + bytes key = 3; + bytes value = 4; +} + +message VerifyAbsenceRequest { + bytes root_hash = 1; + RangeProof proof = 2; + bytes key = 3; +} + +// ---------------------------------------------------------------------------- +// Response types +// ---------------------------------------------------------------------------- + +message HasResponse { + bool result = 1; +} + +message GetResponse { + int64 index = 1; + bytes value = 2; +} + +message SetResponse { + bool result = 1; +} + +message RemoveResponse { + bytes value = 1; + bool removed = 2; +} + +message SaveVersionResponse { + bytes root_hash = 1; + int64 version = 2; +} + +message DeleteVersionResponse{ + bytes root_hash = 1; + int64 version = 2; +} + +message VersionResponse { + int64 version = 1; +} + +message HashResponse { + bytes root_hash = 1; +} + +message VersionExistsResponse { + bool result = 1; +} + +message GetWithProofResponse { + bytes value = 1; + RangeProof proof = 2; +} + +message ProofInnerNode { + int32 height = 1; + int64 size = 2; + int64 version = 3; + bytes left = 4; + bytes right = 5; +} + +message ProofLeafNode { + bytes key = 1; + bytes value_hash = 2; + int64 version = 3; +} + +message PathToLeaf { + repeated ProofInnerNode nodes = 1; +} + +message RangeProof { + bytes key = 1; + PathToLeaf left_path = 2; + repeated PathToLeaf inner_nodes = 3; + repeated ProofLeafNode leaves = 4; +} diff --git a/hs-iavl-client/src/Database/IAVL/RPCCall.hs b/hs-iavl-client/src/Database/IAVL/RPCCall.hs new file mode 100644 index 00000000..0c7c64ea --- /dev/null +++ b/hs-iavl-client/src/Database/IAVL/RPCCall.hs @@ -0,0 +1,150 @@ +module Database.IAVL.RPCCall where + +import Data.ProtoLens.Message (defMessage) +import Network.GRPC.Client (RPC (..), RawReply) +import Network.GRPC.Client.Helpers (GrpcClient, rawUnary) +import Network.HTTP2.Client (ClientIO, TooMuchConcurrency) + +import qualified Proto.Google.Protobuf.Empty as PT (Empty) +import qualified Proto.Iavl.Api as Api + + +-------------------------------------------------------------------------------- +-- | get +-------------------------------------------------------------------------------- +get + :: GrpcClient + -> Api.GetRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.GetResponse)) +get = rawUnary (RPC :: RPC Api.IAVLService "get") + +-------------------------------------------------------------------------------- +-- | getVersioned +-------------------------------------------------------------------------------- +getVersioned + :: GrpcClient + -> Api.GetVersionedRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.GetResponse)) +getVersioned = rawUnary (RPC :: RPC Api.IAVLService "getVersioned") + +-------------------------------------------------------------------------------- +-- | getVersionededWithProof +-------------------------------------------------------------------------------- +getVersionedWithProof + :: GrpcClient + -> Api.GetVersionedRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.GetWithProofResponse)) +getVersionedWithProof = rawUnary (RPC :: RPC Api.IAVLService "getVersionedWithProof") + +-------------------------------------------------------------------------------- +-- | getWithProof +-------------------------------------------------------------------------------- +getWithProof + :: GrpcClient + -> Api.GetRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.GetWithProofResponse)) +getWithProof = rawUnary (RPC :: RPC Api.IAVLService "getWithProof") + +-------------------------------------------------------------------------------- +-- | set +-------------------------------------------------------------------------------- +set + :: GrpcClient + -> Api.SetRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.SetResponse)) +set = rawUnary (RPC :: RPC Api.IAVLService "set") + +-------------------------------------------------------------------------------- +-- | remove +-------------------------------------------------------------------------------- +remove + :: GrpcClient + -> Api.RemoveRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.RemoveResponse)) +remove = rawUnary (RPC :: RPC Api.IAVLService "remove") + +-------------------------------------------------------------------------------- +-- | saveVersion +-------------------------------------------------------------------------------- +saveVersion + :: GrpcClient + -> ClientIO (Either TooMuchConcurrency (RawReply Api.SaveVersionResponse)) +saveVersion gc = rawUnary (RPC :: RPC Api.IAVLService "saveVersion") gc defMessage + +-------------------------------------------------------------------------------- +-- | deleteVersion +-------------------------------------------------------------------------------- +deleteVersion + :: GrpcClient + -> Api.DeleteVersionRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.DeleteVersionResponse)) +deleteVersion = rawUnary (RPC :: RPC Api.IAVLService "deleteVersion") + +-------------------------------------------------------------------------------- +-- | version +-------------------------------------------------------------------------------- +version + :: GrpcClient + -> ClientIO (Either TooMuchConcurrency (RawReply Api.VersionResponse)) +version gc = rawUnary (RPC :: RPC Api.IAVLService "version") gc defMessage + +-------------------------------------------------------------------------------- +-- | hash +-------------------------------------------------------------------------------- +hash + :: GrpcClient + -> ClientIO (Either TooMuchConcurrency (RawReply Api.HashResponse)) +hash gc = rawUnary (RPC :: RPC Api.IAVLService "hash") gc defMessage + +-------------------------------------------------------------------------------- +-- | versionExists +-------------------------------------------------------------------------------- +versionExists + :: GrpcClient + -> Api.VersionExistsRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.VersionExistsResponse)) +versionExists = rawUnary (RPC :: RPC Api.IAVLService "versionExists") + +-------------------------------------------------------------------------------- +-- | verify +-------------------------------------------------------------------------------- +verify + :: GrpcClient + -> Api.VerifyRequest + -> ClientIO (Either TooMuchConcurrency (RawReply PT.Empty)) +verify = rawUnary (RPC :: RPC Api.IAVLService "verify") + +-------------------------------------------------------------------------------- +-- | verifyItem +-------------------------------------------------------------------------------- +verifyItem + :: GrpcClient + -> Api.VerifyItemRequest + -> ClientIO (Either TooMuchConcurrency (RawReply PT.Empty)) +verifyItem = rawUnary (RPC :: RPC Api.IAVLService "verifyItem") + +-------------------------------------------------------------------------------- +-- | verifyAbsence +-------------------------------------------------------------------------------- +verifyAbsence + :: GrpcClient + -> Api.VerifyAbsenceRequest + -> ClientIO (Either TooMuchConcurrency (RawReply PT.Empty)) +verifyAbsence = rawUnary (RPC :: RPC Api.IAVLService "verifyAbsence") + +-------------------------------------------------------------------------------- +-- | rollback +-------------------------------------------------------------------------------- +rollback + :: GrpcClient + -> ClientIO (Either TooMuchConcurrency (RawReply PT.Empty)) +rollback gc = rawUnary (RPC :: RPC Api.IAVLService "rollback") gc defMessage + +-------------------------------------------------------------------------------- +-- | has +-------------------------------------------------------------------------------- +has + :: GrpcClient + -> Api.HasRequest + -> ClientIO (Either TooMuchConcurrency (RawReply Api.HasResponse)) +has = rawUnary (RPC :: RPC Api.IAVLService "has") diff --git a/hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs b/hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs new file mode 100644 index 00000000..e31a2645 --- /dev/null +++ b/hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs @@ -0,0 +1,185 @@ + +module Database.IAVL.RPCCallSpec where + +import Control.Lens ((&), (.~), (^.)) +import Control.Monad (void) +import Data.ProtoLens.Message (defMessage) +import Database.IAVL.RPCCall +import Network.GRPC.Client (RawReply, uncompressed) +import Network.GRPC.Client.Helpers (GrpcClient, GrpcClientConfig (..), + grpcClientConfigSimple, + setupGrpcClient) +import Network.HTTP2.Client (ClientIO, TooMuchConcurrency, + runClientIO) +import qualified Proto.Iavl.Api_Fields as Api +import Test.Hspec + +spec :: Spec +spec = beforeAll initGrpcClient $ do + let testKey = "test-key" + testValue = "test-value" + rootWithTestKey = "\209)\148\US[p\231 do + res <- runGrpc $ hash gc + res ^. Api.rootHash `shouldBe` "" + + it "should call `set` RPC method and get false as result since it does not already exist" $ \gc -> do + let setReq = defMessage & Api.key .~ testKey + & Api.value .~ testValue + res <- runGrpc $ set gc setReq + res ^. Api.result `shouldBe` False + + it "should call `get` RPC method and get the expected value" $ \gc -> do + let getReq = defMessage & Api.key .~ testKey + res <- runGrpc $ get gc getReq + res ^. Api.value `shouldBe` testValue + + it "should call `saveVersion` RPC method and get the latest hash" $ \gc -> do + _ <- runGrpc $ saveVersion gc + res <- runGrpc $ hash gc + res ^. Api.rootHash `shouldBe` rootWithTestKey + + it "should call `getWithProof` RPC method and get value from earlier version" $ \gc -> do + let getReq = defMessage & Api.key .~ testKey + getRes <- runGrpc $ getWithProof gc getReq + getRes ^. Api.value `shouldBe` testValue + + it "should call `getVersioned` RPC method and get value from earlier version" $ \gc -> do + let newVal = "new-value" + setReq = defMessage & Api.key .~ testKey + & Api.value .~ newVal + res <- runGrpc $ set gc setReq + res ^. Api.result `shouldBe` True + _ <- runGrpc $ saveVersion gc + + let getReq = defMessage & Api.key .~ testKey + & Api.version .~ 1 + getRes <- runGrpc $ getVersioned gc getReq + getRes ^. Api.value `shouldBe` testValue + + it "should call `getVersionedWithProof` RPC method and get value from earlier version" $ \gc -> do + let newVal = "new-value-2" + setReq = defMessage & Api.key .~ testKey + & Api.value .~ newVal + res <- runGrpc $ set gc setReq + res ^. Api.result `shouldBe` True + _ <- runGrpc $ saveVersion gc + + let getReq = defMessage & Api.key .~ testKey + & Api.version .~ 1 + getRes <- runGrpc $ getVersionedWithProof gc getReq + getRes ^. Api.value `shouldBe` testValue + + it "should call `remove` RPC method" $ \gc -> do + let key = "key-to-remove" + value = "value-to-remove" + setReq = defMessage & Api.key .~ key + & Api.value .~ value + res <- runGrpc $ set gc setReq + res ^. Api.result `shouldBe` False + _ <- runGrpc $ saveVersion gc + + let removeReq = defMessage & Api.key .~ key + removeRes <- runGrpc $ remove gc removeReq + removeRes ^. Api.value `shouldBe` value + + let getReq = defMessage & Api.key .~ key + getRes <- runGrpc $ get gc getReq + getRes ^. Api.value `shouldBe` "" + + it "should call `verify` RPC method" $ \gc -> do + let getReq = defMessage & Api.key .~ testKey + & Api.version .~ 1 + getRes <- runGrpc $ getVersionedWithProof gc getReq + + let verifyReq = defMessage & Api.rootHash .~ rootWithTestKey + & Api.proof .~ (getRes ^. Api.proof) + void . runGrpc $ verify gc verifyReq + + it "should call `verifyItem` RPC method" $ \gc -> do + let getReq = defMessage & Api.key .~ testKey + & Api.version .~ 1 + getRes <- runGrpc $ getVersionedWithProof gc getReq + + let verifyReq = defMessage & Api.rootHash .~ rootWithTestKey + & Api.proof .~ (getRes ^. Api.proof) + & Api.key .~ testKey + & Api.value .~ testValue + void . runGrpc $ verifyItem gc verifyReq + + it "should call `verifyAbsence` RPC method" $ \gc -> do + let getReq = defMessage & Api.key .~ testKey + & Api.version .~ 1 + getRes <- runGrpc $ getVersionedWithProof gc getReq + + let verifyAbReq = defMessage & Api.rootHash .~ rootWithTestKey + & Api.proof .~ (getRes ^. Api.proof) + & Api.key .~ "non-existing key" + void . runGrpc $ verifyAbsence gc verifyAbReq + + it "should call `versionExists` RPC method on existing version" $ \gc -> do + let verExistsReq = defMessage & Api.version .~ 1 + verExistsRes <- runGrpc $ versionExists gc verExistsReq + verExistsRes ^. Api.result `shouldBe` True + + it "should call `versionExists` RPC method on non-existing version" $ \gc -> do + let verExistsReq = defMessage & Api.version .~ 25 + verExistsRes <- runGrpc $ versionExists gc verExistsReq + verExistsRes ^. Api.result `shouldBe` False + + it "should call `has` RPC method" $ \gc -> do + let hasReq = defMessage & Api.key .~ testKey + & Api.version .~ 1 + hasRes <- runGrpc $ has gc hasReq + hasRes ^. Api.result `shouldBe` True + + it "should call `has` RPC method and fail" $ \gc -> do + let hasReq = defMessage & Api.key .~ "non-existing-key" + & Api.version .~ 1 + hasRes <- runGrpc $ has gc hasReq + hasRes ^. Api.result `shouldBe` False + + it "should call `deleteVersion` RPC method and get False for non-existing version" $ \gc -> do + let delVerReq = defMessage & Api.version .~ 1 + let verExistsReq = defMessage & Api.version .~ 1 + delVerRes <- runGrpc $ deleteVersion gc delVerReq + verExistsRes <- runGrpc $ versionExists gc verExistsReq + delVerRes ^. Api.rootHash `shouldBe` rootWithTestKey + verExistsRes ^. Api.result `shouldBe` False + + it "should call `rollback` RPC method" $ \gc -> do + let key = "key-with-rollback" + value = "value-with-rollback" + setReq = defMessage & Api.key .~ key + & Api.value .~ value + res <- runGrpc $ set gc setReq + res ^. Api.result `shouldBe` False + + let getReq = defMessage & Api.key .~ key + getRes <- runGrpc $ get gc getReq + getRes ^. Api.value `shouldBe` value + + _ <- runGrpc $ rollback gc + + getRes' <- runGrpc $ get gc getReq + getRes' ^. Api.value `shouldBe` "" + +initGrpcClient :: IO GrpcClient +initGrpcClient = + let grpcClient = grpcClientConfigSimple "localhost" 8090 False + in runClientIO (setupGrpcClient (grpcClient{_grpcClientConfigCompression=uncompressed})) >>= \case + Right gc -> pure gc + _ -> error "Error creating GrpcClient" + +runGrpc :: ClientIO (Either TooMuchConcurrency (RawReply a)) -> IO a +runGrpc f = runClientIO f >>= \case + Right (Right (Right (_, _, Right res))) -> pure res + Right (Right (Right (_, _, Left err))) -> error ("Error running grpc call: " <> show err) + Right (Right (Left err)) -> error ("Error running grpc call: " <> show err) + Right (Left err) -> error ("Error running grpc call: " <> show err) + Left err -> error ("Error running grpc call: " <> show err) + + + diff --git a/hs-iavl-client/test/Spec.hs b/hs-iavl-client/test/Spec.hs new file mode 100644 index 00000000..fcb16768 --- /dev/null +++ b/hs-iavl-client/test/Spec.hs @@ -0,0 +1,2 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} +{-# OPTIONS_GHC -fno-warn-missing-import-lists #-} diff --git a/stack.yaml b/stack.yaml index 318c6995..6371dd36 100644 --- a/stack.yaml +++ b/stack.yaml @@ -13,6 +13,7 @@ packages: - ./hs-abci-sdk - ./hs-abci-examples/simple-storage - ./hs-abci-examples/nameservice +- ./hs-iavl-client extra-deps: - proto-lens-runtime-0.5.0.0 @@ -30,6 +31,10 @@ extra-deps: commit: 23015cf6363d1962fde6bdff0de111f7ec59ab75 - polysemy-1.2.3.0 - polysemy-zoo-0.6.0.0 + - http2-client-0.9.0.0 + - http2-grpc-types-0.4.0.0 + - git: https://github.com/lucasdicioccio/http2-client-grpc + commit: 6a1aacfc18e312ef57552133f13dd1024c178706 explicit-setup-deps: hs-abci-server: true diff --git a/stack.yaml.lock b/stack.yaml.lock index 750365a8..39b26e2c 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -109,6 +109,34 @@ packages: sha256: 9a8ddbf6c0a5ed2e254202c2990aae99dc4a66fa24949622a123c48795ec6547 original: hackage: polysemy-zoo-0.6.0.0 +- completed: + hackage: http2-client-0.9.0.0@sha256:b8885c89adcc8b9d4ebb9abf6ae0ac6336e3fdf947a2b1f2b95c4e2c8c4acf01,2685 + pantry-tree: + size: 853 + sha256: d7a1be66eb14e84cfedd87e8363e406abb244bb74b742e8f0141efce27545008 + original: + hackage: http2-client-0.9.0.0 +- completed: + hackage: http2-grpc-types-0.4.0.0@sha256:ffb02152397186dbc925358498b5c005b982ef54f191f4465f9c7947afd7f9d4,1354 + pantry-tree: + size: 405 + sha256: e5025945fee56509538efe6377dd1930189be2bf908bbce428555aa5efab51ff + original: + hackage: http2-grpc-types-0.4.0.0 +- completed: + cabal-file: + size: 1910 + sha256: 414ea9a90a92d1e0e96b3a9892c856c86a606f5464706963422fdb235cf84d13 + name: http2-client-grpc + version: 0.7.0.1 + git: https://github.com/lucasdicioccio/http2-client-grpc + pantry-tree: + size: 625 + sha256: 92a9ff8a51c40678a839a92919add1744ce1da786de01747baaf02679052ea4e + commit: 6a1aacfc18e312ef57552133f13dd1024c178706 + original: + git: https://github.com/lucasdicioccio/http2-client-grpc + commit: 6a1aacfc18e312ef57552133f13dd1024c178706 snapshots: - completed: size: 524786 From b7fba13e3210aba0427e9906c208b80edac28afc Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Fri, 20 Dec 2019 14:31:47 -0500 Subject: [PATCH 26/70] Walkthrough (#154) * added markdown setup * Readme sym link * try link * try to link * reverse file exts * changed modules constructor * added tutorial first step * message tutorial compiles * reorder imports * reorder imports * added query lhs file * added module lhs file * update package yaml to alphabetical * added application lhs file * added note about tutorial in intro * hlint * toc * toc * fix tox * toc * fix toc * toc * only hlint .hs files * Toc * toc * added separate tutorial job * Update Message.md * Update Module.md * Update Types.md * simplify file structure * ilyas PR suggestions * foundations overview link * Update Overview.md * Walkthrough suggestions (#159) * Update README.md Suggested link in the readme to the walkthrough * Update README.md * Update BaseApp.md * Update BaseApp.md * Update Modules.md * Update Modules.md * Update Types.md * Update Message.md * Update Keeper.md * Update Query.md * Update Module.md * Update Application.md * Renamed Nameservice to NameserviceKeeper to be consistent with tutorial * Update Message.md * fix carl's comments Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> Co-authored-by: Kristoffer Josefsson --- .travis.yml | 4 + Makefile | 8 +- README.md | 2 + hs-abci-examples/nameservice/package.yaml | 15 + .../src/Nameservice/Application.hs | 8 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 20 +- .../Nameservice/Modules/Nameservice/Types.hs | 6 +- .../tutorial/Foundations/BaseApp.md | 30 ++ .../tutorial/Foundations/Modules.md | 45 +++ .../tutorial/Foundations/Overview.md | 19 ++ .../nameservice/tutorial/README.lhs | 1 + .../nameservice/tutorial/README.md | 85 ++++++ .../Tutorial/Nameservice/Application.lhs | 1 + .../Tutorial/Nameservice/Application.md | 95 +++++++ .../tutorial/Tutorial/Nameservice/Keeper.lhs | 1 + .../tutorial/Tutorial/Nameservice/Keeper.md | 120 ++++++++ .../tutorial/Tutorial/Nameservice/Message.lhs | 1 + .../tutorial/Tutorial/Nameservice/Message.md | 205 ++++++++++++++ .../tutorial/Tutorial/Nameservice/Module.lhs | 1 + .../tutorial/Tutorial/Nameservice/Module.md | 69 +++++ .../tutorial/Tutorial/Nameservice/Overview.md | 25 ++ .../tutorial/Tutorial/Nameservice/Query.lhs | 1 + .../tutorial/Tutorial/Nameservice/Query.md | 41 +++ .../tutorial/Tutorial/Nameservice/Types.lhs | 1 + .../tutorial/Tutorial/Nameservice/Types.md | 262 ++++++++++++++++++ .../src/SimpleStorage/Application.hs | 6 +- .../src/Tendermint/SDK/Application/Module.hs | 16 +- 27 files changed, 1059 insertions(+), 29 deletions(-) create mode 100644 hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md create mode 100644 hs-abci-examples/nameservice/tutorial/Foundations/Modules.md create mode 100644 hs-abci-examples/nameservice/tutorial/Foundations/Overview.md create mode 120000 hs-abci-examples/nameservice/tutorial/README.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/README.md create mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md create mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md create mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md create mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md create mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md create mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md diff --git a/.travis.yml b/.travis.yml index dea67e46..0d74e7a5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -62,6 +62,10 @@ jobs: - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml -p test-hs-abci-examples-nameservice-e2e up -d - make test-nameservice - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local + - name: "Test Nameservice Tutorial" + script: + - travis_wait 120 stack --skip-ghc-check install markdown-unlit-0.5.0 + - make test-tutorial - name: "Ensure that code matches style guidelines" # When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty if: branch = master diff --git a/Makefile b/Makefile index 1d3b5b80..242833e2 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ help: ## Ask for help! ##################### hlint: ## Run hlint on all haskell projects - stack exec hlint -- -h .hlint.yaml hs-abci-server \ + stack exec hlint -- -e hs -h .hlint.yaml hs-abci-server \ hs-tendermint-client \ hs-abci-extra \ hs-abci-sdk \ @@ -77,7 +77,11 @@ test-simple-storage: install ## Run the test suite for the simple-storage exampl stack test simple-storage test-nameservice: install ## Run the test suite for the nameservice example application - stack test nameservice + stack test nameservice:nameservice-test + +test-tutorial: install ## Make sure the tutorial builds + stack test nameservice:tutorial + ##################### diff --git a/README.md b/README.md index 87b92175..a9df83e7 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,8 @@ This is the official repository for the Haskell implementation of the ABCI serve SDK for developing applications backed by the Tendermint replication engine. You can read more about Tendermint and the ABCI specs in their [documentation](https://tendermint.com/docs/spec/abci/). +To understand how to build a simple application using this library, see the literate haskell [walkthrough](https://github.com/f-o-a-m/hs-abci/tree/walkthrough/hs-abci-examples/nameservice/tutorial). + ## Build ### Prerequisites diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index a7fea215..ca4db45f 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -86,6 +86,7 @@ library: - -Wredundant-constraints exposed-modules: - Nameservice.Application + - Nameservice.Aeson - Nameservice.Server - Nameservice.Modules.TypedMessage - Nameservice.Modules.Nameservice @@ -124,6 +125,20 @@ executables: - nameservice tests: + tutorial: + main: README.lhs + source-dirs: tutorial + other-modules: + - Tutorial.Nameservice.Application + - Tutorial.Nameservice.Keeper + - Tutorial.Nameservice.Message + - Tutorial.Nameservice.Module + - Tutorial.Nameservice.Query + - Tutorial.Nameservice.Types + ghc-options: -Wall -pgmL markdown-unlit + dependencies: + - hs-abci-sdk + - nameservice nameservice-test: main: Spec.hs source-dirs: test diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 5b0dac88..6cccd077 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -51,7 +51,7 @@ handlersContext = HandlersContext where nameserviceModules :: Modules NameserviceModules EffR nameserviceModules = - ConsModule N.nameserviceModule $ - ConsModule T.tokenModule $ - ConsModule A.authModule $ - NilModules + N.nameserviceModule + :+ T.tokenModule + :+ A.authModule + :+ NilModules diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index df9ad693..a7e8e10e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -16,27 +16,27 @@ import Polysemy.Error (Error, mapError, import Polysemy.Output (Output) import qualified Tendermint.SDK.BaseApp as BaseApp -data Nameservice m a where - PutWhois :: Name -> Whois -> Nameservice m () - GetWhois :: Name -> Nameservice m (Maybe Whois) - DeleteWhois :: Name -> Nameservice m () +data NameserviceKeeper m a where + PutWhois :: Name -> Whois -> NameserviceKeeper m () + GetWhois :: Name -> NameserviceKeeper m (Maybe Whois) + DeleteWhois :: Name -> NameserviceKeeper m () -makeSem ''Nameservice +makeSem ''NameserviceKeeper -type NameserviceEffs = '[Nameservice, Error NameserviceError] +type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] -storeKey :: BaseApp.StoreKey NameserviceModule -storeKey = BaseApp.StoreKey . cs . symbolVal $ (Proxy :: Proxy NameserviceModule) +storeKey :: BaseApp.StoreKey NameserviceModuleName +storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @ NameserviceModuleName eval :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => forall a. Sem (Nameservice ': Error NameserviceError ': r) a + => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a -> Sem r a eval = mapError BaseApp.makeAppError . evalNameservice where evalNameservice :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => Sem (Nameservice ': r) a -> Sem r a + => Sem (NameserviceKeeper ': r) a -> Sem r a evalNameservice = interpret (\case GetWhois name -> diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index b8c9d3ea..f64a8ad5 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -22,7 +22,7 @@ import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- -type NameserviceModule = "nameservice" +type NameserviceModuleName = "nameservice" -------------------------------------------------------------------------------- @@ -51,8 +51,8 @@ instance HasCodec Whois where instance BaseApp.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) -instance BaseApp.IsKey Name NameserviceModule where - type Value Name NameserviceModule = Whois +instance BaseApp.IsKey Name NameserviceModuleName where + type Value Name NameserviceModuleName = Whois instance BaseApp.Queryable Whois where type Name Whois = "whois" diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md b/hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md new file mode 100644 index 00000000..6c321ad7 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md @@ -0,0 +1,30 @@ +# BaseApp + +`BaseApp` is the set of effects that the SDK operates with and are freely available +for an application developer to make use of in any part of their application code. It is expected +(in fact required) that any application code can be rewritten in terms of the `BaseApp` +effects. Let's look at the `BaseApp` type: + +~~~ haskell ignore +type BaseAppEffs = + [ RawStore + , Output Event + , Logger + , Resource + , Error AppError + ] +~~~ + +These effects are: + +1. `RawStore` - allows for basic storage operations, e.g. get, put, delete, prove etc. +2. `Output Event` - allows for emitting events in the course of transaction processing. +3. `Logger` - allows for console loging with log levels. +4. `Resource` - allows for bracketing and resource management in the presence of exeptions. +5. `Error AppError` -- allows for errors of type `AppError` to be thrown or caught. + +`BaseApp` acts as an intermediate effect system for specifying applications, it does not make any assumptions about how these effects will be interpreted at runtime. For example, `RawStore` could eventualy be interpeted by any persistent or in-memory storage capable of handling the commands `Put`, `Get` etc. + +Most of the work in writing modules involves plugging into `BaseApp` at various points. For example, your module can create custom errors to throw or catch, but you must tell the SDK how to translate this custom error into an `AppError`. Likewise your module can define custom events to log during transaction execution, but you must describe to the SDK how to translate these custom events types into the type `Event`. + +[Next: Modules](Modules.md) diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md b/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md new file mode 100644 index 00000000..c52a5689 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md @@ -0,0 +1,45 @@ +# Modules + +## Definition + +A `Module` has a very specific meaning in the context of this SDK. A `Module` is something between a library and a small state machine. It is built on top of the `BaseApp` abstraction in the sense that all `Module`s must be explicitly interpeted in terms of `BaseApp` in order to compile the application. The full type definition is + +~~~ haskell ignore +data Module (name :: Symbol) msg (api :: *) (s :: EffectRow) (r :: EffectRow) = Module + { moduleTxDeliverer :: RoutedTx msg -> Sem r () + , moduleTxChecker :: RoutedTx msg -> Sem r () + , moduleQueryServer :: RouteT api (Sem r) + , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a + } +~~~ + +where the type parameters + +- `name` is the name of the module, e.g. `"bank"`. +- `msg` is the type of the incoming messages the module must handle. +- `api` is the query api for querying state in the url format (more on this later). +- `s` is the set of effects introduced by this module. +- `r` is the global set of effects that this module will run in when part of a larger application (more on this later). + +Below that line we see the fields for the `Module` data type, where + + - `moduleTxDeliverer` specifies how the module processes transactions in order to update the application state during `deliverTx` messages. + - `moduleTxChecker` is used during `checkTx` messages to check if a transaction in the mempool is a valid transaction. + - `moduleQueryServer` is responsible for handling queries for application state from the `query` message. + - `moduleEval` is the natural transformation that specifies how to interpet the `Module` in terms of `BaseApp`. + +Note that in the event that a `Module` is _abstract_, meaning it doesn't have any messages to respond to, then we have `msg ~ Void`. + +## Composition + +`Module`s are meant to be composed to create larger applications. We will see examples of this with the `Nameservice` application. The way to do this is easy, as the `Modules` data type allows you to simply combine them in a heterogeneous list: + +~~~ haskell ignore +data Modules (ms :: [*]) r where + NilModules :: Modules '[] r + (:+) :: Module name msg api s r -> Modules ms r -> Modules (Module name msg api s r ': ms) r +~~~ + +When you are ready to create your application, you simply specify a value of type `Modules` and some other configuration data, and the SDK will create an `App` for you. + +[Next: Types](../Tutorial/Nameservice/Types.md) diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/Overview.md b/hs-abci-examples/nameservice/tutorial/Foundations/Overview.md new file mode 100644 index 00000000..145c9432 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Foundations/Overview.md @@ -0,0 +1,19 @@ +# Overview + +The SDK relies heavily on two abstractions to facilitate application development, effects systems and Modules. + +## Effects Systems + +The effects system is backed by a library called `polysemy` which we mentioned in the introduction. An application basically has three layers of effects + +1. **Application level effects**: These are introduced by the application developer in order to customize application behavior. +2. **BaseApp effects**: These are effects into which you must interpret your application in order for it to be runnable by the SDK. +3. **Core effects**: These are largely internal and used to interpet the BaseApp effects to `IO`. There are a few different core options available in the SDK, but the more advanced developer might wish to use their own. + +The tutorial explains the multiple points at which you can hook your application specific effects and types into the SDK. + +## Modules + +The core building block of an application is a Module. There are some modules that ship with the SDK and make up a kind of standard library. These modules are of general utility, like dealing with things like authentication or tokens, and are considered to be safe. + +The most useful part of the SDK is that you are free to define your own modules, or depend on other third party modules outside the SDK. Since they all have the same type, they all easily fit into larger applications as standalone components or dependencies. diff --git a/hs-abci-examples/nameservice/tutorial/README.lhs b/hs-abci-examples/nameservice/tutorial/README.lhs new file mode 120000 index 00000000..42061c01 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/README.lhs @@ -0,0 +1 @@ +README.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/README.md b/hs-abci-examples/nameservice/tutorial/README.md new file mode 100644 index 00000000..1ee84be8 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/README.md @@ -0,0 +1,85 @@ +## Introduction + +We're going to build an example application that mirrors the `golang` [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) example application called [Nameservice](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). There is also a tutorial for that application which you can find [here](https://tutorials.cosmos.network/nameservice/tutorial/00-intro.html) for comparison. + +## Contents +1. [Introduction](README.md) + - [Application Specification](README.md#application-specification) + - [How to Read this Tutorial](README.md#how-to-read-this-tutorial) + - [Tutorial Goals](README.md#tutorial-goals) +2. Foundations + - [Overview](Foundations/Overview.md) + - [BaseApp](Foundations/BaseApp.md) + - [Modules](Foundations/Modules.md) + 1. [Definition](Foundations/Modules.md#definition) + 2. [Composition](Foundations/Modules.md#composition) +3. Nameservice + - [Overview](Tutorial/Nameservice/Overview.md) + - [Types](Tutorial/Nameservice/Types.md) + 1. [Using A Typed Key Value Store](Tutorial/Nameservice/Types.md#using-a-typed-key-value-store) + 2. [Tutorial.Nameservice.Types](Tutorial/Nameservice/Types.md#tutorialnameservicetypes) + - [Message](Tutorial/Nameservice/Message.md) + 1. [Message Types](Tutorial/Nameservice/Message.md#message-types) + 2. [Tutorial.Nameservice.Message](Tutorial/Nameservice/Message.md#tutorialnameservicemessage) + - [Keeper](Tutorial/Nameservice/Keeper.md) + 1. [Definition](Tutorial/Nameservice/Keeper.md#definition) + 2. [Tutorial.Nameservice.Keeper](Tutorial/Nameservice/Keeper.md#tutorialnameservicekeeper) + - [Query](Tutorial/Nameservice/Query.md) + 1. [Definition](Tutorial/Nameservice/Query.md#definition) + 2. [Tutorial.Nameservice.Query](Tutorial/Nameservice/Query.md#tutorialnameservicequery) + - [Module](Tutorial/Nameservice/Module.md) + 1. [Tutorial.Nameservice.Module](Tutorial/Nameservice/Module.md#tutorialnameservicemodule) + - [Application](Tutorial/Nameservice/Application.md) + 1. [From Modules To App](Tutorial/Nameservice/Application.md#from-modules-to-app) + 2. [Tutorial.Nameservice.Application](Tutorial/Nameservice/Application.md#tutorialnameserviceapplication) + + +## Application Specification +The Nameservice application is a simple marketplace for a name resolution service. Let us say that a `Name` resolves to type called `Whois` where + +~~~ haskell ignore +data Whois = Whois + { whoisValue :: Text + , whoisOwner :: Address + , whoisPrice :: Amount + } +~~~ + +This means that users can buy and sell entries in a shared mapping of type `Name -> Whois` where: +1. An unclaimed `Name` can be bought by a user and set to an arbitrary value. +2. Existing `(Name, Whois)` pairs can be updated by their owner or sold to a new owner for the price. +3. Existing `(Name, Whois)` pairs can be deleted by their owner and the owner receives a refund for the purchase price. + +The application consists of three modules: +1. `Auth` - Manages accounts for users, things like nonces and token balances. +2. `Token` - Allows users manage their tokens, things like transfering or burning. +3. `Nameservice` - Controls the shared `Name -> Value` mapping described above. + +## How to Read this Tutorial + +This tutorial is largely written as a literate haskell file to simulate developing the Nameservice app from scratch. The file structure is similar to the actual app. We will partially develop a haskell module corresponding to what you find in the app, but possibly not the whole thing. Thus whenever we depend on a haskell module in the tutorial, rather than importing from the tutorial itself we will import from the app. + +The benefit of this is that we don't have to develop the entire application in this tutorial. Any breaking changes in the app will (hopefully) break the tutorial and so if you can read this, the tutorial is correct. + +## Tutorial Goals +The goal of this tutorial is to explain how the Nameservice app is constructed using the `hs-abci-sdk` package. Nameservice is a relatively simple but still non-trivial application. +If you would like to start with something simpler, you can view the tutorial for the [simple-storage](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/simple-storage) example application. + +This tutorial should teach you: +1. How to construct application specific modules. +2. How to enable a module to receive application specific transactions. +3. How to compose modules and wire up an application. +4. How to add event logging, console logging, and other effects to module. +4. How to use the type system to control the capabilities of a module. + +The SDK makes heavy use of the effects system brought to haskell by the [polysemy](https://hackage.haskell.org/package/polysemy-1.2.3.0) library. We're not going to explain how this library works here, there are several existing tutorials that do this already. Suffice it to say that polysemy encourages the application developer to develop modules that have well defined roles and scopes, and to prohibit certain modules from interfering with the roles and scopes of other modules unless explicitly allowed by the type system. + +It is also allows the application developer to construct modules without much regard for how they will plug into the SDK, leaving that job to the SDK itself. + +(This tutorial is integrated as a literate haskell file, meaning that the following is necessary to ensure it compiles.) +~~~ haskell +main :: IO () +main = pure () +~~~ + +[Next: BaseApp](Foundations/Overview.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs new file mode 120000 index 00000000..cdd5b2f1 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs @@ -0,0 +1 @@ +Application.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md new file mode 100644 index 00000000..325572e6 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md @@ -0,0 +1,95 @@ +# Application + +## From Modules to App + +The `App` type in `Network.ABCI.Server` is defined as + +~~~ haskell ignore +newtype App m = App + { unApp :: forall (t :: MessageType). Request t -> m (Response t) } +~~~ + +and ultimately our configuration of modules must be converted to this format. This is probably the most important part of the SDK, to provide the bridge between the list of modules - a heterogeneous list of type `Modules` - and the actual application. The type that provides the input for this bridge is `HandlersContext`: + +~~~ haskell ignore +data HandlersContext alg ms r core = HandlersContext + { signatureAlgP :: Proxy alg + , modules :: M.Modules ms r + , compileToCore :: forall a. ScopedEff core a -> Sem core a + } +~~~ + +where +- `alg` is the signature schema you would like to use for authentication (e.g. Secp256k1) +- `ms` is the type level list of modules +- `r` is the global effects list for the application +- `core` is the set of core effects that are used to interpet `BaseApp` to `IO`. + +We should say a few words on this `compileToCore` field. The application developer has access to any effects in `BaseApp`, +but `BaseApp` itself still needs to be interpreted in order to run the application. In other words, `BaseApp` is still just a +list of free effects. The set of effects capable of interpreting `BaseApp` is called `core`, and while the developer is free to provide any `core` they want, we have a standard set of them in the SDK - e.g. in memory, production, etc. + +The `ScopedEff` type is more complicated and not relevant to the discussion of application development. Long story short, tendermint core requests three connections to the application's state - `Consensus`, `Mempool` and `Query`. The `ScopedEff` type is used to abstract this concern away from the developer, and as long as you are using one of the `core` effects provided in the SDK you don't need to worry about it. + +## Tutorial.Nameservice.Application + +~~~ haskell +module Tutorial.Nameservice.Application where + +import Data.Proxy +import Nameservice.Modules.Nameservice (nameserviceModule, NameserviceM, NameserviceEffs) +import Nameservice.Modules.Token (tokenModule, TokenM, TokenEffs) +import Network.ABCI.Server.App (App) +import Polysemy (Sem) +import Tendermint.SDK.Modules.Auth (authModule, AuthEffs, AuthM) +import Tendermint.SDK.Application (Modules(..), HandlersContext(..), makeApp) +import Tendermint.SDK.BaseApp (BaseApp, CoreEffs, (:&), compileScopedEff) +import Tendermint.SDK.Crypto (Secp256k1) +~~~ + +This is the part of the application where the effects list must be given a monomorphic type. There is also a requirement +that the `Modules` type for the application be given the same _order_ as the effects introducted. This ordering problem is due +to the fact that type level lists are used to represent the effects in `polysemy`, and the order matters there. Still, it's only a small annoyance. + + +~~~ haskell +type EffR = + NameserviceEffs :& + TokenEffs :& + AuthEffs :& + BaseApp CoreEffs + +type NameserviceModules = + '[ NameserviceM EffR + , TokenM EffR + , AuthM EffR + ] +~~~ + +Notice that we've specified `EffR` as the effects list for each of the modules to run in, which trivially satisfies the constraints on each module at the definition site, since it is simply the union of all effects. + +We're now ready to define the `HandlersContext` for our application: + +~~~ haskell +handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR CoreEffs +handlersContext = HandlersContext + { signatureAlgP = Proxy @Secp256k1 + , modules = nameserviceModules + , compileToCore = compileScopedEff + } + where + nameserviceModules :: Modules NameserviceModules EffR + nameserviceModules = + nameserviceModule + :+ tokenModule + :+ authModule + :+ NilModules +~~~ + +Finally we're able to define our application that runs in the `CoreEffs` context defined in the SDK: + + +~~~ haskell +app :: App (Sem CoreEffs) +app = makeApp handlersContext +~~~ diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs new file mode 120000 index 00000000..9e71f64f --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs @@ -0,0 +1 @@ +Keeper.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md new file mode 100644 index 00000000..ad163891 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md @@ -0,0 +1,120 @@ +# Keeper + +## Definition + +"Keeper" is a word taken from the cosmos-sdk, it's basically the interface that the module exposes to the other modules in the application. For example, in the Nameservice app, the Nameservice keeper exposes functions to `buy`/`sell`/`delete` entries in the mapping. Likewise, the Nameservice keeper depends on the keeper from the `bank` module in order to transfer tokens when executing those methods. A keeper might also indicate what kinds of exceptions are able to be caught and thrown from the module. For example, calling `transfer` while buying a `Name` might throw an `InsufficientFunds` exception, which the Namerservice module can chose whether to catch or not. + +## Tutorial.Nameservice.Keeper + +~~~ haskell +{-# LANGUAGE TemplateHaskell #-} +module Tutorial.Nameservice.Keeper where + +import Data.Proxy +import Data.String.Conversions (cs) +import GHC.TypeLits (symbolVal) +import Polysemy (Sem, Members, makeSem, interpret) +import Polysemy.Error (Error, throw, mapError) +import Polysemy.Output (Output) +import Nameservice.Modules.Nameservice.Messages (DeleteName(..)) +import Nameservice.Modules.Nameservice.Types (Whois(..), Name, NameDeleted(..), NameserviceModuleName, NameserviceError(..)) +import Nameservice.Modules.Token (Token, mint) +import qualified Tendermint.SDK.BaseApp as BA +~~~ + +Generally a keeper is defined by a set of effects that the module introduces and depends on. In the case of Nameservice, we introduce the custom `Nameservice` effect: + + +~~~ haskell +data NameserviceKeeper m a where + PutWhois :: Name -> Whois -> NameserviceKeeper m () + GetWhois :: Name -> NameserviceKeeper m (Maybe Whois) + DeleteWhois :: Name -> NameserviceKeeper m () + +makeSem ''NameserviceKeeper + +type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] +~~~ + +where `makeSem` is from polysemy, it uses template Haskell to create the helper functions `putWhoIs`, `getWhois`, `deleteWhois`: + +~~~ haskell ignore +putWhois :: forall r. Member NameserviceKeeper r => Name -> Whois -> Sem r () +getWhois :: forall r. Member NameserviceKeeper r => Name -> Sem r (Maybe Whois) +deleteWhois :: forall r. Member NameserviceKeeper r => Name -> Sem r () +~~~ + +We can then write the top level function for example for deleting a name: + +~~~ haskell +deleteName + :: Members [Token, Output BA.Event] r + => Members [NameserviceKeeper, Error NameserviceError] r + => DeleteName + -> Sem r () +deleteName DeleteName{..} = do + mWhois <- getWhois deleteNameName + case mWhois of + Nothing -> throw $ InvalidDelete "Can't remove unassigned name." + Just Whois{..} -> + if whoisOwner /= deleteNameOwner + then throw $ InvalidDelete "Deleter must be the owner." + else do + mint deleteNameOwner whoisPrice + deleteWhois deleteNameName + BA.emit NameDeleted + { nameDeletedName = deleteNameName + } +~~~ + +The control flow should be pretty clear: +1. Check that the name is actually registered, if not throw an error. +2. Check that the name is registered to the person trying to delete it, if not throw an error. +3. Refund the tokens locked in the name to the owner. +4. Delete the entry from the database. +5. Emit an event that the name has been deleted. + +Taking a look at the class constraints, we see + +~~~ haskell ignore +(Members NameserviceEffs, Members [Token, Output Event] r) +~~~ + +- The `NameserviceKeeper` effect is required because the function may manipulate the modules database with `deleteName`. +- The `Error NameserviceError` effect is required because the function may throw an error. +- The `Token` effect is required because the function will mint coins. +- The `Output Event` effect is required because the function may emit a `NameDeleted` event. + +### Evaluating Module Effects + +Like we said before, all modules must ultimately compile to the set of effects belonging to `BaseApp`. For effects interpreted to `RawStore`, this means that you will need to define something called a `StoreKey`. + + +A `StoreKey` is effectively a namespacing inside the database, and is unique for a given module. In theory it could be any `ByteString`, but the natural definition in the case of Nameservice is would be something like + +~~~ haskell +storeKey :: BA.StoreKey NameserviceModuleName +storeKey = BA.StoreKey . cs . symbolVal $ (Proxy @NameserviceModuleName) +~~~ + +With this `storeKey` it is possible to write the `eval` function to resolve the effects defined in Nameservice, namely the `NameserviceKeeper` effect and `Error NameserviceError`: + +~~~ haskell +eval + :: Members [BA.RawStore, Error BA.AppError] r + => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a + -> Sem r a +eval = mapError BA.makeAppError . evalNameservice + where + evalNameservice + :: Members [BA.RawStore, Error BA.AppError] r + => Sem (NameserviceKeeper ': r) a -> Sem r a + evalNameservice = + interpret (\case + GetWhois name -> BA.get storeKey name + PutWhois name whois -> BA.put storeKey name whois + DeleteWhois name -> BA.delete storeKey name + ) +~~~ + +[Next: Query](Query.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs new file mode 120000 index 00000000..c1b3cdd7 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs @@ -0,0 +1 @@ +Message.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md new file mode 100644 index 00000000..8cd0aa37 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md @@ -0,0 +1,205 @@ +# Message + +## Message Types + +The `Message` module is ultimately a small state machine used for processing messages. Each module must define what messages it accepts, if any. Like many other types found in the SDK, this message class must implement the `HasCodec` class. We recommend using a protobuf serialization format for messages using either the `proto3-suite` or `proto-lens` libraries, though in theory you could use anything (e.g. `JSON`). + +### `proto3-suite` +The advantages of using the `proto3-suite` library are that it has support for generics and that you can generate a `.proto` file from your haskell code for export to other applications. This is particularly useful when prototyping or when you have control over the message specification. +The disadvantage is that `proto3-suite` doesn't act as a `protoc` plugin, and instead uses it's own protobuf parser. This means that you do not have access to the full protobuf specs when parsing `.proto` files. + +### `proto-lens` +The advantages of using `proto-lens` are that it can parse and generate types for pretty much any `.proto` file. +The disadvantage is that the generated code is a bit strange, and may require you to create wrapper types to avoid depending directly on the generated code. An additional disadvantage is that you cannot generate `.proto` files from haskell code. + +All in all, neither is really difficult to work with, and depending on what stage you're at in development you might chose one over the other. + +## Tutorial.Nameservice.Message + +~~~ haskell +module Tutorial.Nameservice.Message where + +import Data.Bifunctor (first) +import Data.Foldable (sequenceA_) +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) +import Nameservice.Modules.Nameservice.Types (Name(..)) +import Nameservice.Modules.Token (Amount) +import Nameservice.Modules.TypedMessage (TypedMessage(..)) +import Proto3.Suite (Named, Message, fromByteString, toLazyByteString) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (Msg(..), ValidateMessage(..), + isAuthorCheck, nonEmptyCheck, + coerceProto3Error, formatMessageParseError) +import Tendermint.SDK.Codec (HasCodec(..)) +~~~ + +### Message Definitions + +For the puroposes of the tutorial, we will use the `proto3-suite` for the message codecs: + + +~~~ haskell +data SetName = SetName + { setNameName :: Name + , setNameOwner :: Address + , setNameValue :: Text + } deriving (Eq, Show, Generic) + +instance Message SetName +instance Named SetName + +instance HasCodec SetName where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +data DeleteName = DeleteName + { deleteNameOwner :: Address + , deleteNameName :: Name + } deriving (Eq, Show, Generic) + +instance Message DeleteName +instance Named DeleteName + +instance HasCodec DeleteName where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + +data BuyName = BuyName + { buyNameBid :: Amount + , buyNameName :: Name + , buyNameValue :: Text + , buyNameBuyer :: Address + } deriving (Eq, Show, Generic) + +instance Message BuyName +instance Named BuyName + +instance HasCodec BuyName where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +~~~ + +We want a sum type that covers all possible messages the module can receive. As `protobuf` is a schemaless format, parsing is sometimes ambiguous if two types are the same up to field names, or one is a subset of the other. For this reason we defined a type called `TypedMessage`: + +~~~ haskell ignore +data TypedMessage = TypedMessage + { typedMessageType :: Text + , typedMessageContents :: BS.ByteString + } deriving (Eq, Show, Generic) + +instance Message TypedMessage +instance Named TypedMessage + +instance HasCodec TypedMessage where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +~~~ + +This allows us to disambiguated messages based on the `type` field, so that for example we can distinguish `DeleteName` from a submessage of `BuyName`. With that out of the way, we can define the module level (sum) message type: + +~~~ haskell +data NameserviceMessage = + NSetName SetName + | NBuyName BuyName + | NDeleteName DeleteName + deriving (Eq, Show, Generic) + +instance HasCodec NameserviceMessage where + decode bs = do + TypedMessage{..} <- decode bs + case typedMessageType of + "SetName" -> NSetName <$> decode typedMessageContents + "DeleteName" -> NDeleteName <$> decode typedMessageContents + "BuyName" -> NBuyName <$> decode typedMessageContents + _ -> Left . cs $ "Unknown Nameservice message type " ++ cs typedMessageType + encode = \case + NSetName msg -> encode msg + NBuyName msg -> encode msg + NDeleteName msg -> encode msg +~~~ + +## Message Validation + +Message validation is an important part of the transaction life cycle. When a `checkTx` message comes in, Tendermint is asking whether a transaction bytestring from the mempool is potentially runnable. At the very least this means that + +1. The transaction parses to a known message +2. The message passes basic signature authentication, if any is required. +3. The message author has enough funds for the gas costs, if any. +4. The message can be successfully routed to a module without handling. + +On top of this you might wish to ensure other static properties of the message, such as that the author of the message is the owner of the funds being transfered. For this we have a `ValidateMessage` class: + +~~~ haskell ignore +data MessageSemanticError = + PermissionError Text + | InvalidFieldError Text + | OtherSemanticError Text + +class ValidateMessage msg where + validateMessage :: Msg msg -> Validation [MessageSemanticError] () +~~~ + +We're using the applicative functor [`Data.Validation.Validation`](https://hackage.haskell.org/package/validation-1.1/docs/Data-Validation.html#t:Validation) to perform valdiation because it is capable of reporting all errors at once, rather than the first that occurs as in ther case with something like `Either`. + +Here's what the `isAuthor` check looks like, that was described above: + +~~~ haskell ignore +isAuthorCheck + :: Text + -> Msg msg + -> (msg -> Address) + -> V.Validation [MessageSemanticError] () +isAuthorCheck fieldName Msg{msgAuthor, msgData} getAuthor + | getAuthor msgData /= msgAuthor = + _Failure # [PermissionError $ fieldName <> " must be message author."] + | otherwise = Success () +~~~ + +It is also possible to run dynamic checks on the transaction, i.e. checks that need to query state in order to succeed or fail. We will say more on this later. + +Here are the validation instances for our message types, which use some of the combinators defined in the SDK + +~~~ haskell +instance ValidateMessage SetName where + validateMessage msg@Msg{..} = + let SetName{setNameName, setNameValue} = msgData + Name name = setNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , nonEmptyCheck "Value" setNameValue + , isAuthorCheck "Owner" msg setNameOwner + ] + +instance ValidateMessage DeleteName where + validateMessage msg@Msg{..} = + let DeleteName{deleteNameName} = msgData + Name name = deleteNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , isAuthorCheck "Owner" msg deleteNameOwner + ] + +instance ValidateMessage BuyName where + validateMessage msg@Msg{..} = + let BuyName{buyNameName, buyNameValue} = msgData + Name name = buyNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , nonEmptyCheck "Value" buyNameValue + , isAuthorCheck "Owner" msg buyNameBuyer + ] +~~~ + +Finally we can define a `ValidateMessage` instance for our top level message type by dispatching on the message type: + +~~~ haskell +instance ValidateMessage NameserviceMessage where + validateMessage m@Msg{msgData} = case msgData of + NBuyName msg -> validateMessage m {msgData = msg} + NSetName msg -> validateMessage m {msgData = msg} + NDeleteName msg -> validateMessage m {msgData = msg} +~~~ + +[Next: Keeper](Keeper.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs new file mode 120000 index 00000000..a2722172 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs @@ -0,0 +1 @@ +Module.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md new file mode 100644 index 00000000..85c180e5 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md @@ -0,0 +1,69 @@ +# Module + +## Tutorial.Nameservice.Module + +At this point we can collect the relevant pieces to form the Nameservice module: + +~~~ haskell +module Tutorial.Nameservice.Module where + +import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, eval) +import Nameservice.Modules.Nameservice.Messages (NameserviceMessage) +import Nameservice.Modules.Nameservice.Query (Api, server) +import Nameservice.Modules.Nameservice.Router (router) +import Nameservice.Modules.Nameservice.Types (NameserviceModuleName) +import Nameservice.Modules.Token (TokenEffs) +import Polysemy (Members) +import Tendermint.SDK.Application (Module (..), + defaultTxChecker) +import Tendermint.SDK.BaseApp (BaseAppEffs) + +-- a convenient type alias +type NameserviceM r = + Module NameserviceModuleName NameserviceMessage Api NameserviceEffs r + +nameserviceModule + :: Members BaseAppEffs r + => Members TokenEffs r + => Members NameserviceEffs r + => NameserviceM r +nameserviceModule = Module + { moduleTxDeliverer = router + , moduleTxChecker = defaultTxChecker + , moduleQueryServer = server + , moduleEval = eval + } +~~~ + +We are using `defaultTxChecker` as our transaction checker, which is a static message validator defined as + +~~~ haskell ignore +defaultTxChecker + :: Member (Error AppError) r + => ValidateMessage msg + => RoutedTx msg + -> Sem r () +defaultTxChecker (RoutedTx Tx{txMsg}) = + case validateMessage txMsg of + V.Failure err -> + throwSDKError . MessageValidation . map formatMessageSemanticError $ err + V.Success _ -> pure () +~~~ + +This means that we are only doing static validation, meaning that we're not interested in checking message validitity against the database. This is reflected in the return type for the checker `Sem r ()`. If you want to add custom checking, you may write a custom checker for your module. + +Note the constraints on the module's effects `r`: + +~~~ haskell ignore +... + :: Members BaseAppEffs r + => Members TokenEffs r + => Members NameserviceEffs r +... +~~~ + +This is saying that we can run this module in any context for which `r` has the effects from `BaseApp`, `Token`, and `Nameservice`. This is how we explicitly declare module dependencies, by using the constraint system. + +Other than that, there is nothing really to note. We are just collecting the pieces we have already defined in one place. + +[Next: Application](Application.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md new file mode 100644 index 00000000..c2925ec7 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md @@ -0,0 +1,25 @@ +# Overview + +This section is where we sketch the definition of the Nameservice module and application. It's to everyones benefit if module structures follow a similar file heirachy as the Nameservice module, or any module found in the SDK. In the case of Nameservice this roughly translates to + +``` +├── Nameservice +  │   ├── Keeper.hs +  │   ├── Messages.hs +  │   ├── Query.hs +  │   ├── Router.hs +  │   └── Types.hs +  ├── Nameservice.hs + +``` + +The contents of these modules are roughly as follows: + +- `Nameservice.Types` - Core types and instances for the module, including events, custom errors, database types. +- `Nameservice.Keeper` - Defines the module's effect system, it's database operations (if any), core utility. +- `Nameservice.Message` - Defines the message types that the module must process (if any) and their validation instances. +- `Nameservice.Query` - Defines the query server for handling state queries from clients. +- `Nameservice.Router` - Defines the transaction router for the module. +- `Namervice` Defines the module itself and re-exports any types or utils necessary for using this module as a dependency. + +The reason why we suggest this is that each of these haskell modules is buiding up one of the core components of our defition of a module, and it provides a nice logical split between these pieces. \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs new file mode 120000 index 00000000..dc7a9d6f --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs @@ -0,0 +1 @@ +Query.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md new file mode 100644 index 00000000..63f16fef --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md @@ -0,0 +1,41 @@ +# Query + +## Tutorial.Nameservice.Query + +~~~ haskell +module Tutorial.Nameservice.Query where + +import Data.Proxy +import Nameservice.Modules.Nameservice.Keeper (storeKey) +import Nameservice.Modules.Nameservice.Types (Whois, Name) +import Polysemy (Sem, Members) +import Polysemy.Error (Error) +import Tendermint.SDK.BaseApp (RawStore, AppError, RouteT, QueryApi, storeQueryHandlers) +~~~ + +The way to query application state is via the `query` message which uses a `url` like format. The SDK tries to abstract as much of this away as possible. For example, if you want to only serve state that you have registered with the store via the `IsKey` class, then things are very easy. If you need to make joins to serve requests, we support this as well and it's not hard, but we will skip this for now. + +In the case we just want to serve data we have registered with the `IsKey` class, we simply need to declare some types + +```haskell +type NameserviceContents = '[(Name, Whois)] + +type Api = QueryApi NameserviceContents +``` + +- `NameserviceContents` is simply a type level list of the key value pairs you wish to serve. In this case there is only `Name -> Whois` +- `Api` is the list of leaves of valid url's for this module. When the type family `QueryApi` is applied, it will construct the leaves from the key value pairs based on the `IsKey` class. In this case you end up with only `"/whois"` endpoint, which accepts the `Name` in the `data` field of the `query` message encoded via the `HasCodec` class. + +To serve all the data registered with the `IsKey` class, we can use the `storeQueryHandlers` function, supplying a proxy for the store contents, the `storeKey` and a proxy for the effects used in serving requests. In this case because we are serving only types registered with the store, we will need to assume the `RawStore` and `Error AppError` effects. + +~~~ haskell +server + :: Members [RawStore, Error AppError] r + => RouteT Api (Sem r) +server = + storeQueryHandlers (Proxy @NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) +~~~ + +Here `RouteT` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely copy-pasted from that codebase. + +[Next: Module](Module.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs new file mode 120000 index 00000000..62d28166 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs @@ -0,0 +1 @@ +Types.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md new file mode 100644 index 00000000..603cad91 --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md @@ -0,0 +1,262 @@ +# Types + +The `Types` module is used to define the basic types that the module will make use of. This includes things like custom error types, event types, database types, etc. + +## Using A Typed Key Value Store +It is important to note that the database modeled by the `RawStore` effect (in the `BaseApp` type) is just a key value store for raw `ByteString`s. This means you can _think_ of `RawStore` as + +~~~ haskell ignore +type RawStore = Map ByteString ByteString +~~~ + +although the definition of `RawStore` is different than the above. + +The interface we give is actually a typed key value store. This means that within the scope of a module `m`, for any key type `k`, there is only one possible value type `v` associated with `k`. + +For example, a user's balance in the `Token` module, might be modeled by a mapping + +~~~ haskell ignore +balance :: Tendermint.SDK.Types.Address -> Integer +~~~ + +(We'll properly introduce the module `Token` later in the walkthrough.) + +This means that in the scope of the `Token` module, the database utlity `get` function applied to a value of type `Address` will result in a value of type `Integer`. If the `Token` module would like to store another mapping whose keys have type `Tendermint.SDK.Types.Address`, you must use a newtype instead. Otherwise you will get a compiler error. + +At the same time, you are free to define another mapping from `k -> v'` in the scope of a different module. For example, you can have both the `balance` mapping described above, as well a mapping + +~~~ haskell ignore +owner :: Tendermint.SDK.Types.Address -> Account +~~~ +in the `Auth` module. + +## Tutorial.Nameservice.Types + +Let's look at the example in `Nameservice.Types`. + +~~~ haskell +module Tutorial.Nameservice.Types where + +import Control.Lens (iso) +import qualified Data.Aeson as A +import Data.Bifunctor (first) +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) +import GHC.TypeLits (symbolVal) +import Nameservice.Aeson (defaultNameserviceOptions) +import Nameservice.Modules.Token (Amount) +import Proto3.Suite (Message, fromByteString, toLazyByteString) +import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.Codec (HasCodec(..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (coerceProto3Error, formatMessageParseError) +~~~ + +### Storage types + +Remember the `Nameservice` module is responsible for maintaining a marketplace around a mapping `Name -> Whois`. Let us define the types for the marketplace mapping as + +~~~ haskell +newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON) + +data Whois = Whois + { whoisValue :: Text + , whoisOwner :: Address + , whoisPrice :: Amount + } deriving (Eq, Show, Generic) +~~~ + +The way that we register `Name` as a key in the store is by using the `RawKey` typeclass + +~~~ haskell ignore +class RawKey k where + rawKey :: Iso' k ByteString +~~~ + +This class gives us a way to convert back and forth from a key to its encoding as a `ByteString`. In our case we implement + +~~~ haskell +-- here cs resolves to Data.Text.Encoding.encodeUtf8, Data.Text.Encoding.decodeUtf8 respectively +instance BA.RawKey Name where + rawKey = iso (\(Name n) -> cs n) (Name . cs) +~~~ + +In order to register `Whois` as a storage type, we must implement the `HasCodec` typeclass + +~~~ haskell ignore +class HasCodec a where + encode :: a -> ByteString + decode :: ByteString -> Either Text a +~~~ + +This class is used everywhere in the SDK as the binary codec class for things like storage items, messages, transaction formats etc. It's agnostic to the actual serialization format, you can use `JSON`, `CBOR`, `Protobuf`, etc. Throughout the SDK we typically use `protobuf` as it is powerful in addition to the fact that there's decent support for this in Haskell either through the `proto3-suite` package or the `proto-lens` package. + +So we can implement a `HasCodec` instance for `Whois` + +~~~ haskell +-- Message is a class from proto3-suite that defines protobuf codecs generically. +instance Message Whois + +instance HasCodec Whois where + encode = cs . toLazyByteString + decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +~~~ + +Finally we can register `(Name, Whois)` with the module's store with the `IsKey` class, which tells how to associate a key type with a value type within the scope of given module, where the scope is represented by the modules name as a type level string. There is an optional prefixing function for the key in this context in order to avoid collisions in the database. This would be useful for example if you were using multiple newtyped `Address` types as keys in the same module. + +~~~ haskell ignore +class RawKey k => IsKey k ns where + type Value k ns = a | a -> ns k + prefixWith :: Proxy k -> Proxy ns -> BS.ByteString + + default prefixWith :: Proxy k -> Proxy ns -> BS.ByteString + prefixWith _ _ = "" +~~~ + +For the case of the `Name -> Whois` mapping, the `IsKey` instance looked like looks like this: + +~~~ haskell +type NameserviceModuleName = "nameservice" + +instance BA.IsKey Name NameserviceModuleName where + type Value Name NameserviceModuleName = Whois +~~~ + +At is point, you can use the database operations exported by `Tendermint.SDK.BaseApp.Store` such as `put`/`set`/`delete` for key value pairs of type `(Name, Whois)`. + +### Query Types + +The [`cosmos-sdk`](https://github.com/cosmos/cosmos-sdk) assumes that you use `url` formatted queries with some possible query params. For example, to query a `Whois` value based on a `Name`, you might submit a `query` message with the route `nameservice/whois` and supply a value of type `Name` to specify as the `data` field. Our SDK makes the same assumption for compatability reasons. + +In order to register the `Whois` type with the query service, you must implement the `Queryable` typeclass: + +~~~ haskell ignore +class Queryable a where + type Name a :: Symbol + encodeQueryResult :: a -> Base64String + decodeQueryResult :: Base64String -> Either Text a + + default encodeQueryResult :: HasCodec a => a -> Base64String + encodeQueryResult = fromBytes . encode + + default decodeQueryResult :: HasCodec a => Base64String -> Either Text a + decodeQueryResult = decode . toByte +~~~ + +What this means is that you need to supply codecs for the type to query, with the default using the `HasCodec` class. You also need to name the type, as this will match the leaf of the `url` used for querying. So for example, in the Nameservice app we have + +~~~ haskell +instance BA.Queryable Whois where + type Name Whois = "whois" +~~~ + +since `Whois` already implements the `HasCodec` class. + +### Error Types + +You might want to define a module specific error type that has a `throw`/`catch` interface. This error type should be accessible by any other dependent modules, and any uncaught error should eventually be converted into some kind of generic application error understandable by Tendermint. + +There is a simple way to do this using the `IsAppError` typeclass + +~~~ haskell ignore +data AppError = AppError + { appErrorCode :: Word32 + , appErrorCodespace :: Text + , appErrorMessage :: Text + } deriving Show + +class IsAppError e where + makeAppError :: e -> AppError +~~~ + +The fields for `AppError` correspond to tendermint message fields for messages that support error return types, such as `checkTx`, `deliverTx`, and `query`. Typically we use the module name as the codespace, like in the definition of `NamespaceError`: + +~~~ haskell +data NameserviceError = + InsufficientBid Text + | UnauthorizedSet Text + | InvalidDelete Text + +instance BA.IsAppError NameserviceError where + -- remember 'symbolVal (Proxy @NameserviceModuleName)' resolves to "nameservice" + makeAppError (InsufficientBid msg) = + BA.AppError + { appErrorCode = 1 + , appErrorCodespace = cs $ symbolVal (Proxy @NameserviceModuleName) + , appErrorMessage = msg + } + makeAppError (UnauthorizedSet msg) = + BA.AppError + { appErrorCode = 2 + , appErrorCodespace = cs $ symbolVal (Proxy @NameserviceModuleName) + , appErrorMessage = msg + } + makeAppError (InvalidDelete msg) = + BA.AppError + { appErrorCode = 3 + , appErrorCodespace = cs $ symbolVal (Proxy @NameserviceModuleName) + , appErrorMessage = msg + } +~~~ + +### Event Types +Tendermint has the capability to report event logs for transactions in the responses for both `checkTx` and `deliverTx` messages. The basic event type can be found in `Network.ABCI.Types.MessageFields`, it is simply a named key value mapping between `Bytestring`s: + +~~~ haskell ignore +data Event = Event + { eventType :: Text + -- ^ Type of Event + , eventAttributes :: [KVPair] + -- ^ Event attributes + } + +data KVPair = KVPair + { kVPairKey :: Base64String + -- ^ key + , kVPairValue :: Base64String + -- ^ value + } +~~~ + +Similar to the custom error messages, you can define custom events at the module level as long as they implement the `ToEvent` class to translate them to this standard type: + +~~~ haskell ignore +class ToEvent e where + makeEventType :: Proxy e -> String + makeEventData :: e -> [(BS.ByteString, BS.ByteString)] + + default makeEventData :: A.ToJSON e => e -> [(BS.ByteString, BS.ByteString)] + makeEventData e = case A.toJSON e of + A.Object obj -> bimap cs (cs . A.encode) <$> toList obj + _ -> mempty +~~~ + +As you can see, there is a default instance for those types which have a `JSON` representation as an `Object`. The reason that we chose a `JSON` default instance is simply because of support for generics, but this isn't set in stone. + +In the case of `Nameservice`, here is an example of a custom event: + +~~~ haskell +data NameClaimed = NameClaimed + { nameClaimedOwner :: Address + , nameClaimedName :: Name + , nameClaimedValue :: Text + , nameClaimedBid :: Amount + } deriving (Eq, Show, Generic) + +-- 'defaultNameserviceOptions' is used to remove the record accessor prefix. +nameClaimedAesonOptions :: A.Options +nameClaimedAesonOptions = defaultNameserviceOptions "nameClaimed" + +instance A.ToJSON NameClaimed where + toJSON = A.genericToJSON nameClaimedAesonOptions + +instance A.FromJSON NameClaimed where + parseJSON = A.genericParseJSON nameClaimedAesonOptions + +instance BA.ToEvent NameClaimed where + makeEventType _ = "NameClaimed" +~~~ + +[Next: Message](Message.md) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index 86fcfd1c..c700a1ae 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -46,6 +46,6 @@ handlersContext = HandlersContext where simpleStorageModules :: Modules SimpleStorageModules EffR simpleStorageModules = - ConsModule SimpleStorage.simpleStorageModule $ - ConsModule A.authModule $ - NilModules + SimpleStorage.simpleStorageModule + :+ A.authModule + :+ NilModules diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 4b7f7d3b..93a5a58b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -65,7 +65,9 @@ defaultTxChecker (RoutedTx Tx{txMsg}) = data Modules (ms :: [*]) r where NilModules :: Modules '[] r - ConsModule :: Module name msg api s r -> Modules ms r -> Modules (Module name msg api s r ': ms) r + (:+) :: Module name msg api s r -> Modules ms r -> Modules (Module name msg api s r ': ms) r + +infixr 5 :+ -------------------------------------------------------------------------------- @@ -82,11 +84,11 @@ class QueryRouter ms r where instance QueryRouter '[Module name msg api s r] r where type Api '[Module name msg api s r] = name :> api - routeQuery (ConsModule m NilModules) = moduleQueryServer m + routeQuery (m :+ NilModules) = moduleQueryServer m instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api s r ': m' ': ms) r where type Api (Module name msg api s r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) - routeQuery (ConsModule m rest) = moduleQueryServer m :<|> routeQuery rest + routeQuery (m :+ rest) = moduleQueryServer m :<|> routeQuery rest -------------------------------------------------------------------------------- @@ -118,12 +120,12 @@ instance (Member (Error AppError) r) => TxRouter '[] r where throwSDKError $ UnmatchedRoute txRoute instance {-# OVERLAPPING #-} (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void api s r ': ms) r where - routeTx routeContext (ConsModule _ rest) tx@Tx{txRoute} + routeTx routeContext (_ :+ rest) tx@Tx{txRoute} | symbolVal (Proxy :: Proxy name) == cs txRoute = throwSDKError $ UnmatchedRoute txRoute | otherwise = routeTx routeContext rest tx instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCodec msg, KnownSymbol name) => TxRouter (Module name msg api s r ': ms) r where - routeTx routeContext (ConsModule m rest) tx@Tx{..} + routeTx routeContext (m :+ rest) tx@Tx{..} | symbolVal (Proxy :: Proxy name) == cs txRoute = do msg <- case decode $ msgData txMsg of Left err -> throwSDKError $ ParseError err @@ -154,8 +156,8 @@ class Eval ms core where instance Eval '[Module name msg api s r] core where type Effs '[Module name msg api s r] core = s :& BaseApp core - eval (ConsModule m NilModules) = moduleEval m + eval (m :+ NilModules) = moduleEval m instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name msg api s r ': m' ': ms) core where type Effs (Module name msg api s r ': m' ': ms) core = s :& (Effs (m': ms)) core - eval (ConsModule m rest) = eval rest . moduleEval m + eval (m :+ rest) = eval rest . moduleEval m From e4101666316a22fb1c69ec461ce56814eda55445 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Sun, 22 Dec 2019 11:10:54 -0500 Subject: [PATCH 27/70] Es scribe logger (#160) * add katip-es deps * wip es scribe/logger * add metrics instructions * toggle response/request logs to ES or console * add some comments and rename app logger middleware for clarity * some code cleanup * wip * wip almost builds * builds * config verbosity levels * clean up * wip: lens configs refactor * proper resource cleanup * properly name protogen main * updated simple storage * added env vars to Makefile * tests compile * trim middleware * stylish * try to fix hlint Co-authored-by: Carl Factora --- Makefile | 6 + hs-abci-examples/nameservice/README.md | 22 ++ hs-abci-examples/nameservice/app/Main.hs | 43 ++-- hs-abci-examples/nameservice/package.yaml | 8 +- hs-abci-examples/nameservice/protogen/Main.hs | 73 +++++- .../nameservice/protogen/Protogen.hs | 72 ------ .../src/Nameservice/Application.hs | 33 +-- .../nameservice/src/Nameservice/Config.hs | 109 +++++++++ .../nameservice/src/Nameservice/Server.hs | 48 ++-- hs-abci-examples/simple-storage/app/Main.hs | 26 ++- hs-abci-examples/simple-storage/package.yaml | 7 + .../src/SimpleStorage/Application.hs | 16 +- .../src/SimpleStorage/Config.hs | 108 +++++++++ .../src/SimpleStorage/Server.hs | 45 ++-- .../ABCI/Server/Middleware/MetricsLogger.hs | 82 ++----- .../ABCI/Server/Middleware/RequestLogger.hs | 41 +--- .../ABCI/Server/Middleware/ResponseLogger.hs | 5 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 4 + .../src/Tendermint/SDK/BaseApp/CoreEff.hs | 61 +++-- .../src/Tendermint/SDK/BaseApp/Events.hs | 3 + .../src/Tendermint/SDK/BaseApp/Logger.hs | 1 + .../Tendermint/SDK/BaseApp/Logger/Katip.hs | 34 ++- .../SDK/BaseApp/Metrics/Prometheus.hs | 220 ++++++++++-------- stack.yaml | 7 + stack.yaml.lock | 49 ++++ 25 files changed, 694 insertions(+), 429 deletions(-) delete mode 100644 hs-abci-examples/nameservice/protogen/Protogen.hs create mode 100644 hs-abci-examples/nameservice/src/Nameservice/Config.hs create mode 100644 hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs diff --git a/Makefile b/Makefile index 242833e2..bd228335 100644 --- a/Makefile +++ b/Makefile @@ -63,9 +63,15 @@ deploy-nameservice-docker: install ## run the nameservice docker network docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml up --build deploy-simple-storage-local: install ## run the simple storage locally + ES_HOST=$(ES_HOST) \ + ES_PORT=$(ES_PORT) \ + DD_API_KEY=$(DD_API_KEY) \ + STATS_PORT=$(STATS_PORT) \ stack exec simple-storage deploy-nameservice-local: install ## run the nameservice locally + ES_HOST=$(ES_HOST) \ + ES_PORT=$(ES_PORT) \ DD_API_KEY=$(DD_API_KEY) \ STATS_PORT=$(STATS_PORT) \ stack exec nameservice diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index e40d5f34..363571ab 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -1 +1,23 @@ # nameservice + +## Metrics + +Run `nameservice` via the `Makefile`: + +``` +make deploy-nameservice-local +``` + +Along with running `nameservice`, this also starts a Prometheus] metrics server. +By default, the metrics server runs on `localhost:9200`. To use a different port, +set the `STATS_PORT` environment variable to the desired port value. + +To see these metrics in Datadog, follow the +[Prometheus host config instructions](https://docs.datadoghq.com/getting_started/integrations/prometheus/?tab=host#pagetitle) +to configure a local Datadog agent to scrape the endpoint. + +Alternatively, use the `docker-compose` command: + +``` +make deploy-nameservice-docker +``` diff --git a/hs-abci-examples/nameservice/app/Main.hs b/hs-abci-examples/nameservice/app/Main.hs index 93de182e..dca0944d 100644 --- a/hs-abci-examples/nameservice/app/Main.hs +++ b/hs-abci-examples/nameservice/app/Main.hs @@ -1,30 +1,21 @@ module Main where -import Control.Exception (bracket) -import Data.Text (pack) -import qualified Katip as K -import Nameservice.Application (makeAppConfig) -import Nameservice.Server (makeAndServeApplication) -import System.Environment (lookupEnv) -import System.IO (stdout) -import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), - mkLogConfig) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (MetricsConfig (..), - mkMetricsConfig) -import qualified Text.Read as T +import Control.Concurrent (killThread) +import Control.Exception (bracket) +import Control.Lens ((^.)) +import Data.IORef (readIORef) +import qualified Katip as K +import Nameservice.Config (baseAppContext, + makeAppConfig, + prometheusServerThreadId) +import Nameservice.Server (makeAndServeApplication) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL main :: IO () -main = do - metCfg <- mkMetricsConfig - mApiKey <- lookupEnv "DD_API_KEY" - mMetricsPort <- lookupEnv "STATS_PORT" - logCfg <- mkLogConfig "dev" "nameservice" - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) K.V2 - let mkLogEnv = K.registerScribe "stdout" handleScribe K.defaultScribeSettings (_logEnv logCfg) - bracket mkLogEnv K.closeScribes $ \le -> do - cfg <- makeAppConfig - metCfg { metricsAPIKey = pack <$> mApiKey - , metricsPort = T.read <$> mMetricsPort - } - logCfg {_logEnv = le} - makeAndServeApplication cfg +main = + let close cfg = do + _ <- K.closeScribes (cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv) + prometheusThreadId <- readIORef $ cfg ^. prometheusServerThreadId + maybe (pure ()) killThread prometheusThreadId + in bracket makeAppConfig close makeAndServeApplication diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index ca4db45f..3fc3ee9e 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -49,7 +49,9 @@ dependencies: - aeson - aeson-casing - base >= 4.7 && < 5 +- bloodhound - bytestring +- containers - data-default-class - errors - exceptions @@ -58,6 +60,10 @@ dependencies: - hs-abci-sdk - hs-abci-server - hs-abci-types +- http-client +- katip +- katip-datadog +- katip-elasticsearch - lens - polysemy - polysemy-plugin @@ -67,7 +73,6 @@ dependencies: - proto-lens-runtime - proto3-suite - proto3-wire -- katip - servant - string-conversions - text @@ -86,6 +91,7 @@ library: - -Wredundant-constraints exposed-modules: - Nameservice.Application + - Nameservice.Config - Nameservice.Aeson - Nameservice.Server - Nameservice.Modules.TypedMessage diff --git a/hs-abci-examples/nameservice/protogen/Main.hs b/hs-abci-examples/nameservice/protogen/Main.hs index bc49cd89..5b9a3b73 100644 --- a/hs-abci-examples/nameservice/protogen/Main.hs +++ b/hs-abci-examples/nameservice/protogen/Main.hs @@ -1,4 +1,75 @@ +{-# LANGUAGE MagicHash #-} + +-- @NOTE: ^ possibly the only language extension needed from tutorial +-- Seems to be the only requirement for generating the .proto string + module Main where +import Data.Aeson.Casing (snakeCase) +import qualified Data.ByteString.Lazy as BL +import GHC.Exts (Proxy#, proxy#) +import Nameservice.Modules.Nameservice.Messages (BuyName (..), + DeleteName (..), + SetName (..)) +import Nameservice.Modules.Nameservice.Types (Name (..), + Whois (..)) +import Nameservice.Modules.Token (Amount (..)) +import Proto3.Suite (DotProtoDefinition, + Message, + fromByteString, + message, + packageFromDefs) +import Proto3.Suite.DotProto as DotProto +import Proto3.Suite.DotProto.Rendering (RenderingOptions, + defRenderingOptions) +import qualified Proto3.Wire.Decode as Decode +import Proto3.Wire.Types (FieldNumber (..)) +import Tendermint.SDK.Types.Address (Address (..)) +import qualified Text.PrettyPrint as PP + main :: IO () -main = putStrLn "Gen some protos" +main = putStrLn messagesProtoFile + +-------------------------------------------------------------------------------- +-- test +-------------------------------------------------------------------------------- + +testSN :: SetName +testSN = SetName (Name "satoshi") (Address "01") "cool cats" + +testBN :: BuyName +testBN = BuyName (Amount 999) (Name "satoshi") "cool cats" (Address "01") + +testDN :: DeleteName +testDN = DeleteName (Address "01") (Name "satoshi") + +-- @NOTE: for some reason, encoding results in a lazy bytestring +-- while the provided decoder only accepts a strict bytestring +fromLazyByteString :: Message a => BL.ByteString -> Either Decode.ParseError a +fromLazyByteString = fromByteString . BL.toStrict + +-------------------------------------------------------------------------------- +-- Requires magic hash extension +-------------------------------------------------------------------------------- + +stripPrefixName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc +stripPrefixName (Single typeName) (Single fieldName) _ = + let prefixLen = length typeName + fieldName' = Single . snakeCase . drop prefixLen $ fieldName + in pPrint fieldName' +-- @NOTE: we don't yet need for other identifiers +stripPrefixName _ _ _ = error "stripPrefixName unused case" + +msgStripPrefixOptions :: RenderingOptions +msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } + +messagesProtoFile :: String +messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" + ([ message (proxy# :: Proxy# SetName) + , message (proxy# :: Proxy# BuyName) + , message (proxy# :: Proxy# DeleteName) + ] :: [DotProtoDefinition]) + +whoisProtoFile :: String +whoisProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" + ([ message (proxy# :: Proxy# Whois) ] :: [DotProtoDefinition]) diff --git a/hs-abci-examples/nameservice/protogen/Protogen.hs b/hs-abci-examples/nameservice/protogen/Protogen.hs deleted file mode 100644 index 3138919c..00000000 --- a/hs-abci-examples/nameservice/protogen/Protogen.hs +++ /dev/null @@ -1,72 +0,0 @@ -{-# LANGUAGE MagicHash #-} - --- @NOTE: ^ possibly the only language extension needed from tutorial --- Seems to be the only requirement for generating the .proto string - -module Protogen where - -import Data.Aeson.Casing (snakeCase) -import qualified Data.ByteString.Lazy as BL -import GHC.Exts (Proxy#, proxy#) -import Nameservice.Modules.Nameservice.Messages (BuyName (..), - DeleteName (..), - SetName (..)) -import Nameservice.Modules.Nameservice.Types (Name (..), - Whois (..)) -import Nameservice.Modules.Token (Amount (..)) -import Proto3.Suite (DotProtoDefinition, - Message, - fromByteString, - message, - packageFromDefs) -import Proto3.Suite.DotProto as DotProto -import Proto3.Suite.DotProto.Rendering (RenderingOptions, - defRenderingOptions) -import qualified Proto3.Wire.Decode as Decode -import Proto3.Wire.Types (FieldNumber (..)) -import Tendermint.SDK.Types.Address (Address (..)) -import qualified Text.PrettyPrint as PP - --------------------------------------------------------------------------------- --- test --------------------------------------------------------------------------------- - -testSN :: SetName -testSN = SetName (Name "satoshi") (Address "01") "cool cats" - -testBN :: BuyName -testBN = BuyName (Amount 999) (Name "satoshi") "cool cats" (Address "01") - -testDN :: DeleteName -testDN = DeleteName (Address "01") (Name "satoshi") - --- @NOTE: for some reason, encoding results in a lazy bytestring --- while the provided decoder only accepts a strict bytestring -fromLazyByteString :: Message a => BL.ByteString -> Either Decode.ParseError a -fromLazyByteString = fromByteString . BL.toStrict - --------------------------------------------------------------------------------- --- Requires magic hash extension --------------------------------------------------------------------------------- - -stripPrefixName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc -stripPrefixName (Single typeName) (Single fieldName) _ = - let prefixLen = length typeName - fieldName' = Single . snakeCase . drop prefixLen $ fieldName - in pPrint fieldName' --- @NOTE: we don't yet need for other identifiers -stripPrefixName _ _ _ = error "stripPrefixName unused case" - -msgStripPrefixOptions :: RenderingOptions -msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } - -messagesProtoFile :: String -messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" - ([ message (proxy# :: Proxy# SetName) - , message (proxy# :: Proxy# BuyName) - , message (proxy# :: Proxy# DeleteName) - ] :: [DotProtoDefinition]) - -whoisProtoFile :: String -whoisProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" - ([ message (proxy# :: Proxy# Whois) ] :: [DotProtoDefinition]) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 6cccd077..934309da 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -1,34 +1,19 @@ module Nameservice.Application - ( AppConfig(..) - , makeAppConfig - , EffR + ( EffR , NameserviceModules , handlersContext ) where import Data.Proxy -import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T -import Tendermint.SDK.Application (HandlersContext (..), - Modules (..)) -import Tendermint.SDK.BaseApp ((:&)) -import qualified Tendermint.SDK.BaseApp as BaseApp -import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus -import Tendermint.SDK.Crypto (Secp256k1) -import qualified Tendermint.SDK.Modules.Auth as A +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import Tendermint.SDK.Application (HandlersContext (..), + Modules (..)) +import Tendermint.SDK.BaseApp ((:&)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Crypto (Secp256k1) +import qualified Tendermint.SDK.Modules.Auth as A -data AppConfig = AppConfig - { baseAppContext :: BaseApp.Context - } - -makeAppConfig :: Prometheus.MetricsConfig -> KL.LogConfig -> IO AppConfig -makeAppConfig metCfg logCfg = do - c <- BaseApp.makeContext (Just metCfg) logCfg - pure $ AppConfig { baseAppContext = c - } - --------------------------------------------------------------------------------- type EffR = N.NameserviceEffs :& diff --git a/hs-abci-examples/nameservice/src/Nameservice/Config.hs b/hs-abci-examples/nameservice/src/Nameservice/Config.hs new file mode 100644 index 00000000..217a585c --- /dev/null +++ b/hs-abci-examples/nameservice/src/Nameservice/Config.hs @@ -0,0 +1,109 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Nameservice.Config + ( AppConfig(..) + , baseAppContext + , prometheusServerThreadId + , serverMetricsMap + , makeAppConfig + ) where + +import Control.Arrow (Kleisli (..), + returnA, (>>>)) +import Control.Concurrent (ThreadId) +import Control.Concurrent.MVar (MVar, newMVar) +import Control.Error (MaybeT (..), + runMaybeT) +import Control.Lens (makeLenses, (&), + (.~), (^.), (^?), + _Just) +import Data.IORef (IORef, newIORef) +import Data.Map.Strict (Map, empty) +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Database.V5.Bloodhound as BH +import qualified Katip as K +import qualified Katip.Scribes.Datadog.TCP as DD +import qualified Katip.Scribes.ElasticSearch as ES +import Network.ABCI.Server.Middleware.MetricsLogger (OrderedMessageType) +import qualified Network.HTTP.Client as Client +import System.Environment +import System.IO (stdout) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P +import Text.Read (read) + + + +data AppConfig = AppConfig + { _baseAppContext :: BaseApp.Context + , _prometheusServerThreadId :: IORef (Maybe ThreadId) + , _serverMetricsMap :: MVar (Map OrderedMessageType Integer) + } +makeLenses ''AppConfig + +makeAppConfig :: IO AppConfig +makeAppConfig = do + prometheusEnv <- runMaybeT $ do + prometheusPort <- read <$> MaybeT (lookupEnv "STATS_PORT") + ddApiKey <- cs <$> MaybeT (lookupEnv "DD_API_KEY") + pure $ P.MetricsScrapingConfig prometheusPort ddApiKey + metricsMap <- newMVar empty + c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") prometheusEnv + prometheusServer <- newIORef Nothing + addScribesToLogEnv $ + AppConfig { _baseAppContext = c + , _prometheusServerThreadId = prometheusServer + , _serverMetricsMap = metricsMap + } + +addScribesToLogEnv :: AppConfig -> IO AppConfig +addScribesToLogEnv cfg = do + consoleCfg <- makeConsoleLoggingConfig + let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv + mdatadogApiKey = cfg ^? baseAppContext . BaseApp.contextPrometheusEnv . + _Just . P.envMetricsScrapingConfig . P.dataDogApiKey + addScribes = runKleisli $ + makeKatipScribe consoleCfg + >>> maybe returnA makeMetricsScribe mdatadogApiKey + scribesLogEnv <- addScribes initialLogEnv + pure $ cfg & + baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv + +-------------------------------------------------------------------------------- + +data KatipConfig = ES {host :: String, port :: String} | Console + +makeConsoleLoggingConfig :: IO KatipConfig +makeConsoleLoggingConfig = do + mEsConfig <- runMaybeT $ + ES <$> (MaybeT $ lookupEnv "ES_HOST") <*> (MaybeT $ lookupEnv "ES_PORT") + pure $ fromMaybe Console mEsConfig + +-- makes a log environment for console logs / ES logs +makeKatipScribe :: KatipConfig -> Kleisli IO K.LogEnv K.LogEnv +makeKatipScribe kcfg = Kleisli $ \le -> + let verbosity = K.V0 + in case kcfg of + Console -> do + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) verbosity + K.registerScribe "stdout" handleScribe K.defaultScribeSettings le + ES {host, port} -> do + mgr <- Client.newManager Client.defaultManagerSettings + let serverAddress = "http://" <> host <> ":" <> port + bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr + esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "nameservice") + (BH.MappingName "application-logs") (K.permitItem K.DebugS) verbosity + K.registerScribe "es" esScribe K.defaultScribeSettings le + +-------------------------------------------------------------------------------- + +-- makes a log environment for metrics logs +makeMetricsScribe :: Text -> Kleisli IO K.LogEnv K.LogEnv +makeMetricsScribe key = Kleisli $ \le -> do + let apiKey = DD.APIKey key + datadogScribeSettings <- DD.mkDatadogScribeSettings DD.directAPIConnectionParams (DD.DirectAuth apiKey) + scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem K.InfoS) K.V0 + K.registerScribe "datadog" scribe K.defaultScribeSettings le diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index 5449ddf8..ff71d897 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -1,44 +1,38 @@ module Nameservice.Server (makeAndServeApplication) where import Data.Foldable (fold) +import Data.IORef (writeIORef) import Data.Monoid (Endo (..)) -import Nameservice.Application (AppConfig (..), - handlersContext) +import Nameservice.Application (handlersContext) +import Nameservice.Config (AppConfig (..)) import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.MetricsLogger as MetLogger -import qualified Network.ABCI.Server.Middleware.RequestLogger as ReqLogger -import qualified Network.ABCI.Server.Middleware.ResponseLogger as ResLogger +import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met +import qualified Network.ABCI.Server.Middleware.RequestLogger as Req +import qualified Network.ABCI.Server.Middleware.ResponseLogger as Res import Polysemy (Sem) import Tendermint.SDK.Application (createIOApp, makeApp) import Tendermint.SDK.BaseApp (Context (..), CoreEffs, runCoreEffs) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (MetricsConfig (..), - runMetricsServer) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication AppConfig{..} = do putStrLn "Starting ABCI application..." - runMetricsServer metCfg + case _contextPrometheusEnv _baseAppContext of + Nothing -> pure () + Just prometheusEnv -> do + prometheusThreadId <- forkMetricsServer prometheusEnv + writeIORef _prometheusServerThreadId (Just prometheusThreadId) let nat :: forall a. Sem CoreEffs a -> IO a - nat = runCoreEffs baseAppContext - application = createIOApp nat $ makeApp handlersContext - serveApp =<< hookInMiddleware application - where - metCfg = contextMetricsConfig baseAppContext - apiKey = (fmap MetLogger.ApiKey . metricsAPIKey) =<< metCfg - mkMiddleware :: IO (Middleware IO) - mkMiddleware = do - reqLogger <- ReqLogger.mkLogStdoutDev - resLogger <- ResLogger.mkLogStdoutDev - metLogger <- MetLogger.mkMetricsLogDatadog apiKey - pure . appEndo . fold $ - [ Endo reqLogger - , Endo resLogger - , Endo metLogger - ] - hookInMiddleware _app = do - middleware <- mkMiddleware - pure $ middleware _app + nat = runCoreEffs _baseAppContext + application = makeApp handlersContext + middleware :: Middleware (Sem CoreEffs) + middleware = appEndo . fold $ + [ Endo Req.mkRequestLoggerM + , Endo Res.mkResponseLoggerM + , Endo $ Met.mkMetricsLoggerM _serverMetricsMap + ] + serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-examples/simple-storage/app/Main.hs b/hs-abci-examples/simple-storage/app/Main.hs index 4ef4fd0e..e44b170d 100644 --- a/hs-abci-examples/simple-storage/app/Main.hs +++ b/hs-abci-examples/simple-storage/app/Main.hs @@ -1,18 +1,22 @@ module Main where +import Control.Concurrent (killThread) import Control.Exception (bracket) +import Control.Lens ((^.)) +import Data.IORef (readIORef) import qualified Katip as K -import SimpleStorage.Application (makeAppConfig) +import SimpleStorage.Config (baseAppContext, + makeAppConfig, + prometheusServerThreadId) import SimpleStorage.Server (makeAndServeApplication) -import System.IO (stdout) -import Tendermint.SDK.BaseApp.Logger.Katip (LogConfig (..), - mkLogConfig) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL main :: IO () -main = do - logCfg <- mkLogConfig "dev" "simple-storage" - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) K.V2 - let mkLogEnv = K.registerScribe "stdout" handleScribe K.defaultScribeSettings (_logEnv logCfg) - bracket mkLogEnv K.closeScribes $ \le -> do - cfg <- makeAppConfig logCfg {_logEnv = le} - makeAndServeApplication cfg +main = + let close cfg = do + _ <- K.closeScribes (cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv) + prometheusThreadId <- readIORef $ cfg ^. prometheusServerThreadId + maybe (pure ()) killThread prometheusThreadId + in bracket makeAppConfig close makeAndServeApplication + diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index 817aeb6b..ff9d5267 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -41,17 +41,23 @@ default-extensions: dependencies: - aeson - base >= 4.7 && < 5 +- bloodhound - bytestring - cereal - cereal-text +- containers - cryptonite - errors - exceptions +- katip +- katip-datadog +- katip-elasticsearch - hs-abci-extra - hs-abci-server - hs-abci-types - hs-abci-extra - hs-abci-sdk +- http-client - lens - memory - mtl @@ -80,6 +86,7 @@ library: exposed-modules: - SimpleStorage.Server - SimpleStorage.Application + - SimpleStorage.Config - SimpleStorage.Modules.SimpleStorage - SimpleStorage.Modules.SimpleStorage.Types - SimpleStorage.Modules.SimpleStorage.Message diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index c700a1ae..c378f6dc 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -1,7 +1,6 @@ module SimpleStorage.Application - ( AppConfig(..) - , makeAppConfig - , EffR + ( EffR + , SimpleStorageModules , handlersContext ) where @@ -11,20 +10,9 @@ import Tendermint.SDK.Application (HandlersContext (..), Modules (..)) import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp as BaseApp -import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A -data AppConfig = AppConfig - { baseAppContext :: BaseApp.Context - } - -makeAppConfig :: KL.LogConfig -> IO AppConfig -makeAppConfig logCfg = do - c <- BaseApp.makeContext Nothing logCfg - pure $ AppConfig { baseAppContext = c - } - -------------------------------------------------------------------------------- type EffR = diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs new file mode 100644 index 00000000..f83bf4cc --- /dev/null +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs @@ -0,0 +1,108 @@ +{-# LANGUAGE TemplateHaskell #-} + +module SimpleStorage.Config + ( AppConfig(..) + , baseAppContext + , prometheusServerThreadId + , serverMetricsMap + , makeAppConfig + ) where + +import Control.Arrow (Kleisli (..), + returnA, (>>>)) +import Control.Concurrent (ThreadId) +import Control.Concurrent.MVar (MVar, newMVar) +import Control.Error (MaybeT (..), + runMaybeT) +import Control.Lens (makeLenses, (&), + (.~), (^.), (^?), + _Just) +import Data.IORef (IORef, newIORef) +import Data.Map.Strict (Map, empty) +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Database.V5.Bloodhound as BH +import qualified Katip as K +import qualified Katip.Scribes.Datadog.TCP as DD +import qualified Katip.Scribes.ElasticSearch as ES +import Network.ABCI.Server.Middleware.MetricsLogger (OrderedMessageType) +import qualified Network.HTTP.Client as Client +import System.Environment +import System.IO (stdout) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P +import Text.Read (read) + + +data AppConfig = AppConfig + { _baseAppContext :: BaseApp.Context + , _prometheusServerThreadId :: IORef (Maybe ThreadId) + , _serverMetricsMap :: MVar (Map OrderedMessageType Integer) + } +makeLenses ''AppConfig + +makeAppConfig :: IO AppConfig +makeAppConfig = do + prometheusEnv <- runMaybeT $ do + prometheusPort <- read <$> MaybeT (lookupEnv "STATS_PORT") + ddApiKey <- cs <$> MaybeT (lookupEnv "DD_API_KEY") + pure $ P.MetricsScrapingConfig prometheusPort ddApiKey + metricsMap <- newMVar empty + c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") prometheusEnv + prometheusServer <- newIORef Nothing + addScribesToLogEnv $ + AppConfig { _baseAppContext = c + , _prometheusServerThreadId = prometheusServer + , _serverMetricsMap = metricsMap + } + +addScribesToLogEnv :: AppConfig -> IO AppConfig +addScribesToLogEnv cfg = do + consoleCfg <- makeConsoleLoggingConfig + let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv + mdatadogApiKey = cfg ^? baseAppContext . BaseApp.contextPrometheusEnv . + _Just . P.envMetricsScrapingConfig . P.dataDogApiKey + addScribes = runKleisli $ + makeKatipScribe consoleCfg + >>> maybe returnA makeMetricsScribe mdatadogApiKey + scribesLogEnv <- addScribes initialLogEnv + pure $ cfg & + baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv + +-------------------------------------------------------------------------------- + +data KatipConfig = ES {host :: String, port :: String} | Console + +makeConsoleLoggingConfig :: IO KatipConfig +makeConsoleLoggingConfig = do + mEsConfig <- runMaybeT $ + ES <$> (MaybeT $ lookupEnv "ES_HOST") <*> (MaybeT $ lookupEnv "ES_PORT") + pure $ fromMaybe Console mEsConfig + +-- makes a log environment for console logs / ES logs +makeKatipScribe :: KatipConfig -> Kleisli IO K.LogEnv K.LogEnv +makeKatipScribe kcfg = Kleisli $ \le -> + let verbosity = K.V0 + in case kcfg of + Console -> do + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) verbosity + K.registerScribe "stdout" handleScribe K.defaultScribeSettings le + ES {host, port} -> do + mgr <- Client.newManager Client.defaultManagerSettings + let serverAddress = "http://" <> host <> ":" <> port + bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr + esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "nameservice") + (BH.MappingName "application-logs") (K.permitItem K.DebugS) verbosity + K.registerScribe "es" esScribe K.defaultScribeSettings le + +-------------------------------------------------------------------------------- + +-- makes a log environment for metrics logs +makeMetricsScribe :: Text -> Kleisli IO K.LogEnv K.LogEnv +makeMetricsScribe key = Kleisli $ \le -> do + let apiKey = DD.APIKey key + datadogScribeSettings <- DD.mkDatadogScribeSettings DD.directAPIConnectionParams (DD.DirectAuth apiKey) + scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem K.InfoS) K.V0 + K.registerScribe "datadog" scribe K.defaultScribeSettings le diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs index bac6db60..15ba555b 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs @@ -1,35 +1,38 @@ module SimpleStorage.Server (makeAndServeApplication) where import Data.Foldable (fold) +import Data.IORef (writeIORef) import Data.Monoid (Endo (..)) import Network.ABCI.Server (serveApp) import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.RequestLogger as ReqLogger -import qualified Network.ABCI.Server.Middleware.ResponseLogger as ResLogger +import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met +import qualified Network.ABCI.Server.Middleware.RequestLogger as Req +import qualified Network.ABCI.Server.Middleware.ResponseLogger as Res import Polysemy (Sem) -import SimpleStorage.Application (AppConfig (..), - handlersContext) +import SimpleStorage.Application (handlersContext) +import SimpleStorage.Config (AppConfig (..)) import Tendermint.SDK.Application (createIOApp, makeApp) -import Tendermint.SDK.BaseApp (CoreEffs, +import Tendermint.SDK.BaseApp (Context (..), + CoreEffs, runCoreEffs) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) makeAndServeApplication :: AppConfig -> IO () -makeAndServeApplication cfg = do +makeAndServeApplication AppConfig{..} = do putStrLn "Starting ABCI application..." + case _contextPrometheusEnv _baseAppContext of + Nothing -> pure () + Just prometheusEnv -> do + prometheusThreadId <- forkMetricsServer prometheusEnv + writeIORef _prometheusServerThreadId (Just prometheusThreadId) let nat :: forall a. Sem CoreEffs a -> IO a - nat = runCoreEffs $ baseAppContext cfg - application = createIOApp nat $ makeApp handlersContext - serveApp =<< hookInMiddleware application - where - mkMiddleware :: IO (Middleware IO) - mkMiddleware = do - reqLogger <- ReqLogger.mkLogStdoutDev - resLogger <- ResLogger.mkLogStdoutDev - pure . appEndo . fold $ - [ Endo reqLogger - , Endo resLogger - ] - hookInMiddleware _app = do - middleware <- mkMiddleware - pure $ middleware _app + nat = runCoreEffs _baseAppContext + application = makeApp handlersContext + middleware :: Middleware (Sem CoreEffs) + middleware = appEndo . fold $ + [ Endo Req.mkRequestLoggerM + , Endo Res.mkResponseLoggerM + , Endo $ Met.mkMetricsLoggerM _serverMetricsMap + ] + serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs index 3a67db70..573616c2 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs @@ -1,75 +1,22 @@ module Network.ABCI.Server.Middleware.MetricsLogger - ( -- * Basic stdout logging - mkMetricsLogStdout - , mkMetricsLogDatadog - , mkMetricsLogDatadogLocal - -- * Custom Loggers - , mkMetricsLogger + ( -- * Custom Loggers + mkMetricsLogger , mkMetricsLoggerM - -- * ApiKey - , ApiKey(..) + , OrderedMessageType(..) ) where -import Control.Concurrent.MVar (MVar, modifyMVar, newMVar) -import Control.Monad.IO.Class (MonadIO, liftIO) -import qualified Data.Aeson as A -import Data.Map.Strict (Map) -import qualified Data.Map.Strict as Map -import Data.Text (Text) -import Data.Time (NominalDiffTime, diffUTCTime, - getCurrentTime) +import Control.Concurrent.MVar (MVar, modifyMVar) +import Control.Monad.IO.Class (MonadIO, liftIO) +import qualified Data.Aeson as A +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as Map +import Data.Time (NominalDiffTime, diffUTCTime, + getCurrentTime) import Katip -import Katip.Scribes.Datadog.TCP -import Network.ABCI.Server.App (App (..), MessageType (..), - Middleware, Request (..), - msgTypeKey) -import System.IO (stdout) +import Network.ABCI.Server.App (App (..), MessageType (..), + Middleware, Request (..), msgTypeKey) --------------------------------------------------------------------------- --- mkMetricsLogStdout ---------------------------------------------------------------------------- --- | Creates a production Metrics logger as middleware for ABCI Server. --- Uses lowest possible verbosity, however, with the current minimal metrics --- verbosity has no effect on which metrics logged. -mkMetricsLogStdout :: (MonadIO m) => m (Middleware m) -mkMetricsLogStdout = liftIO $ do - handleScribe <- mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V0 - le <- registerScribe "stdout" handleScribe defaultScribeSettings - =<< initLogEnv "ABCI" "production" - let ns = "Server" - mvarReqC <- newMVar Map.empty - pure $ mkMetricsLogger mvarReqC le ns - ---------------------------------------------------------------------------- --- mkMetricsLogDatadog ---------------------------------------------------------------------------- - -mkMetricsLogDatadogLocal :: (MonadIO m) => Integer -> m (Middleware m) -mkMetricsLogDatadogLocal port = liftIO $ do - datadogScribeSettings <- mkDatadogScribeSettings (localAgentConnectionParams (fromInteger port)) NoAuthLocal - scribe <- mkDatadogScribe datadogScribeSettings (permitItem InfoS) V0 - le <- registerScribe "datadog" scribe defaultScribeSettings - =<< initLogEnv "ABCI" "production" - let ns = "Server" - mvarReqC <- newMVar Map.empty - pure $ mkMetricsLogger mvarReqC le ns - -newtype ApiKey = ApiKey Text -mkMetricsLogDatadog :: (MonadIO m) => Maybe ApiKey -> m (Middleware m) -mkMetricsLogDatadog mApiKey = - case mApiKey of - Nothing -> pure id - Just (ApiKey key) -> liftIO $ do - let apiKey = APIKey key - datadogScribeSettings <- mkDatadogScribeSettings directAPIConnectionParams (DirectAuth apiKey) - scribe <- mkDatadogScribe datadogScribeSettings (permitItem InfoS) V0 - le <- registerScribe "datadog" scribe defaultScribeSettings - =<< initLogEnv "ABCI" "production" - let ns = "Server" - mvarReqC <- newMVar Map.empty - pure $ mkMetricsLogger mvarReqC le ns - ---------------------------------------------------------------------------- --- mkRequestLogger +-- mkMetricsLogger --------------------------------------------------------------------------- -- | Request logger middleware for ABCI metrics with custom 'Katip.LogEnv' -- and 'Katip.Namespace'. @@ -103,7 +50,8 @@ mkMetricsLoggerM mvarMap (App app) = App $ \ req -> do metrics = Metrics mt count (diffUTCTime endTime startTime) newMetMap = Map.insert mt count metMap in pure (newMetMap, metrics) - logMetrics metrics + localKatipNamespace (<> "server") $ + logMetrics metrics pure res --------------------------------------------------------------------------- diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs index 8738960c..cada162f 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs @@ -1,18 +1,15 @@ module Network.ABCI.Server.Middleware.RequestLogger - ( -- * Basic stdout logging - mkLogStdout - , mkLogStdoutDev - -- * Custom Loggers - , mkRequestLogger + ( -- * Custom Loggers + mkRequestLogger , mkRequestLoggerM ) where -import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.IO.Class (MonadIO) import qualified Data.Aeson as A import Katip import Network.ABCI.Server.App (App (..), MessageType, Middleware, Request (..)) -import System.IO (stdout) + --------------------------------------------------------------------------- -- Types --------------------------------------------------------------------------- @@ -25,34 +22,8 @@ instance ToObject (Loggable (Request (t :: MessageType))) where _ -> error "Contract violation: `toJSON` of any `Request t` must result with json object" instance LogItem (Loggable (Request (t :: MessageType))) where - payloadKeys V0 _ = SomeKeys ["type"] - payloadKeys _ _ = AllKeys - ---------------------------------------------------------------------------- --- mkLogStdout --------------------------------------------------------------------------- --- | Creates a production request logger as middleware for ABCI requests. --- Uses lowest possible verbosity. -mkLogStdout :: (MonadIO m) => m (Middleware m) -mkLogStdout = do - handleScribe <- liftIO $ mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V0 - le <- liftIO (registerScribe "stdout" handleScribe defaultScribeSettings - =<< initLogEnv "ABCI" "production") - let ns = "Server" - pure $ mkRequestLogger le ns - ---------------------------------------------------------------------------- --- mkLogStdoutDev --------------------------------------------------------------------------- --- | Creates a request logger as middleware for ABCI requests. --- Uses highest possible verbosity. -mkLogStdoutDev :: (MonadIO m) => m (Middleware m) -mkLogStdoutDev = do - handleScribe <- liftIO $ mkHandleScribe ColorIfTerminal stdout (permitItem DebugS) V3 - le <- liftIO (registerScribe "stdout" handleScribe defaultScribeSettings - =<< initLogEnv "ABCI" "development") - let ns = "Server" - pure $ mkRequestLogger le ns + payloadKeys V3 _ = AllKeys + payloadKeys _ _ = SomeKeys ["type"] --------------------------------------------------------------------------- -- mkRequestLogger diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs index 420e2618..8176ab9a 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs @@ -12,6 +12,7 @@ import Katip import Network.ABCI.Server.App (App (..), MessageType, Middleware, Response (..)) import System.IO (stdout) + --------------------------------------------------------------------------- -- Types --------------------------------------------------------------------------- @@ -24,8 +25,8 @@ instance ToObject (Loggable (Response (t :: MessageType))) where _ -> error "Contract violation: `toJSON` of any `Response t` must result with json object" instance LogItem (Loggable (Response (t :: MessageType))) where - payloadKeys V0 _ = SomeKeys ["type"] - payloadKeys _ _ = AllKeys + payloadKeys V3 _ = AllKeys + payloadKeys _ _ = SomeKeys ["type"] --------------------------------------------------------------------------- -- mkLogStdout diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index e0ecd314..2a12ffda 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -10,6 +10,10 @@ module Tendermint.SDK.BaseApp -- * CoreEff , CoreEffs , Context(..) + , contextLogConfig + , contextPrometheusEnv + , contextEventBuffer + , contextAuthTree , makeContext , runCoreEffs diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs index 6629b800..48cae792 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs @@ -1,14 +1,21 @@ +{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE UndecidableInstances #-} {-# OPTIONS_GHC -fno-warn-orphans #-} module Tendermint.SDK.BaseApp.CoreEff ( CoreEffs , Context(..) + , contextLogConfig + , contextPrometheusEnv + , contextEventBuffer + , contextAuthTree , makeContext , runCoreEffs ) where -import Control.Lens (over, view) +import Control.Lens (makeLenses, over, + view) +import Data.Text (Text) import qualified Katip as K import Polysemy (Embed, Members, Sem, runM) @@ -17,7 +24,7 @@ import Polysemy.Reader (Reader, asks, import Tendermint.SDK.BaseApp.Events (EventBuffer, newEventBuffer) import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P import Tendermint.SDK.BaseApp.Store (MergeScopes, ResolveScope (..)) import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT @@ -28,7 +35,7 @@ type CoreEffs = '[ Reader EventBuffer , MergeScopes , Reader KL.LogConfig - , Reader (Maybe Prometheus.MetricsConfig) + , Reader (Maybe P.PrometheusEnv) , Reader AT.AuthTreeState , Embed IO ] @@ -45,22 +52,42 @@ instance (Members CoreEffs r) => K.KatipContext (Sem r) where -- | 'Context' is the environment required to run 'CoreEffs' to 'IO' data Context = Context - { contextLogConfig :: KL.LogConfig - , contextMetricsConfig :: Maybe Prometheus.MetricsConfig - , contextEventBuffer :: EventBuffer - , contextAuthTree :: AT.AuthTreeState + { _contextLogConfig :: KL.LogConfig + , _contextPrometheusEnv :: Maybe P.PrometheusEnv + , _contextEventBuffer :: EventBuffer + , _contextAuthTree :: AT.AuthTreeState } -makeContext :: Maybe Prometheus.MetricsConfig -> KL.LogConfig -> IO Context -makeContext metCfg logCfg = do +makeLenses ''Context + +makeContext + :: KL.InitialLogNamespace + -> Maybe P.MetricsScrapingConfig + -> IO Context +makeContext KL.InitialLogNamespace{..} scrapingCfg = do + metCfg <- case scrapingCfg of + Nothing -> pure Nothing + Just scfg -> P.emptyState >>= \es -> + pure . Just $ P.PrometheusEnv es scfg authTreeState <- AT.initAuthTreeState eb <- newEventBuffer + logCfg <- mkLogConfig _initialLogEnvironment _initialLogProcessName pure $ Context - { contextLogConfig = logCfg - , contextMetricsConfig = metCfg - , contextEventBuffer = eb - , contextAuthTree = authTreeState + { _contextLogConfig = logCfg + , _contextPrometheusEnv = metCfg + , _contextEventBuffer = eb + , _contextAuthTree = authTreeState } + where + mkLogConfig :: Text -> Text -> IO KL.LogConfig + mkLogConfig env pName = do + let mkLogEnv = K.initLogEnv (K.Namespace [pName]) (K.Environment env) + le <- mkLogEnv + return $ KL.LogConfig + { _logNamespace = mempty + , _logContext = mempty + , _logEnv = le + } instance (Members CoreEffs r, AT.AuthTreeGetter s) => ResolveScope s r where resolveScope = AT.evalTagged @@ -71,8 +98,8 @@ runCoreEffs -> forall a. Sem CoreEffs a -> IO a runCoreEffs Context{..} = runM . - runReader contextAuthTree . - runReader contextMetricsConfig . - runReader contextLogConfig . + runReader _contextAuthTree . + runReader _contextPrometheusEnv . + runReader _contextLogConfig . AT.evalMergeScopes . - runReader contextEventBuffer + runReader _contextEventBuffer diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index 80b9de94..956308c9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -47,6 +47,9 @@ class ToEvent e where A.Object obj -> bimap cs (cs . A.encode) <$> toList obj _ -> mempty +-- data LoggableEvent a = LoggableEvent a +-- logEvent :: + -- | A class that can parse event log items in the deliverTx response. Primarily -- | useful for client applications and testing. class ToEvent e => FromEvent e where diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs index 4c549973..5c14d96b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs @@ -14,5 +14,6 @@ data Severity = Debug | Info | Warning | Error | Exception deriving (Eq, Ord) -- | Effect allowing for console logging. data Logger m a where Log :: Severity -> Text -> Logger m () + -- AddContext :: Object -> m a -> Logger m a makeSem ''Logger diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs index c7e0b01e..9af1321d 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs @@ -1,5 +1,17 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.BaseApp.Logger.Katip where + +module Tendermint.SDK.BaseApp.Logger.Katip + ( -- setup and config + LogConfig(..) + , logNamespace + , logContext + , logEnv + , InitialLogNamespace(..) + , initialLogEnvironment + , initialLogProcessName + -- eval + , evalKatip + ) where import Control.Lens.TH (makeLenses) import Data.String (fromString) @@ -16,16 +28,12 @@ data LogConfig = LogConfig } makeLenses ''LogConfig -mkLogConfig :: Text -> Text -> IO LogConfig -mkLogConfig environment processName = do - le <- mkLogEnv - return $ LogConfig - { _logNamespace = mempty - , _logContext = mempty - , _logEnv = le - } - where - mkLogEnv = K.initLogEnv (K.Namespace [processName]) (K.Environment environment) +data InitialLogNamespace = InitialLogNamespace + { _initialLogEnvironment :: Text + , _initialLogProcessName :: Text + } + +makeLenses ''InitialLogNamespace evalKatip :: forall r a. @@ -34,7 +42,9 @@ evalKatip -> Sem r a evalKatip = do interpret (\case - Log severity msg -> K.logFM (coerceSeverity severity) (fromString . cs $ msg) + Log severity msg -> + K.localKatipNamespace (<> "application") $ + K.logFM (coerceSeverity severity) (fromString . cs $ msg) ) where coerceSeverity :: Severity -> K.Severity diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs index 09e3cc13..ee155085 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -1,10 +1,34 @@ -module Tendermint.SDK.BaseApp.Metrics.Prometheus where +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.BaseApp.Metrics.Prometheus + ( -- config and setup + MetricsScrapingConfig(..) + , prometheusPort + , dataDogApiKey + , MetricsState(..) + , PrometheusEnv(..) + , envMetricsState + , envMetricsScrapingConfig + , emptyState + , forkMetricsServer + -- utils + , mkPrometheusMetricId + , metricIdStorable + , countToIdentifier + , histogramToIdentifier + -- eval + , evalWithMetrics + , evalNothing + , evalMetrics + ) where import Control.Arrow ((***)) -import Control.Concurrent (forkIO) +import Control.Concurrent (ThreadId, + forkIO) import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar) +import Control.Lens (makeLenses) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.Map.Strict (Map, insert) import qualified Data.Map.Strict as Map @@ -27,6 +51,99 @@ import qualified System.Metrics.Prometheus.Metric.Histogram as Histogram import qualified System.Metrics.Prometheus.MetricId as MetricId import Tendermint.SDK.BaseApp.Metrics (CountName (..), HistogramName (..), Metrics (..)) +-------------------------------------------------------------------------------- +-- Metrics Types +-------------------------------------------------------------------------------- + +-- | Core metrics state +type MetricsMap a = Map (Text, MetricId.Labels) a + +data MetricsState = MetricsState + { metricsRegistry :: Registry.Registry + , metricsCounters :: MVar (MetricsMap Counter.Counter) + , metricsHistograms :: MVar (MetricsMap Histogram.Histogram) + } + +-- | Intermediary prometheus registry index key +data MetricIdentifier = MetricIdentifier + { metricIdName :: Text + , metricIdLabels :: MetricId.Labels + , metricIdHistoBuckets :: [Double] + } + +instance IsString MetricIdentifier where + fromString s = MetricIdentifier (fromString s) mempty mempty + +fixMetricName :: Text -> Text +fixMetricName = Text.map fixer + where fixer c = if c `elem` validChars then c else '_' + validChars = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_" + +-- indexes + +countToIdentifier :: CountName -> MetricIdentifier +countToIdentifier (CountName name labels) = MetricIdentifier + { metricIdName = fixMetricName name + , metricIdLabels = MetricId.fromList labels + , metricIdHistoBuckets = [] + } + +histogramToIdentifier :: HistogramName -> MetricIdentifier +histogramToIdentifier (HistogramName name labels buckets) = MetricIdentifier + { metricIdName = fixMetricName name + , metricIdLabels = MetricId.fromList labels + , metricIdHistoBuckets = buckets + } + +-- | Prometheus registry index key +mkPrometheusMetricId :: MetricIdentifier -> MetricId.MetricId +mkPrometheusMetricId MetricIdentifier{..} = + MetricId.MetricId (MetricId.Name metricIdName) metricIdLabels + +-- | Index key for storing metrics +metricIdStorable :: MetricIdentifier -> (Text, MetricId.Labels) +metricIdStorable c = (fixMetricName $ metricIdName c, fixMetricLabels $ metricIdLabels c) + where fixMetricLabels = + MetricId.fromList . + map (fixMetricName *** fixMetricName) . + MetricId.toList + + +-------------------------------------------------------------------------------- +-- Config +-------------------------------------------------------------------------------- + +-- | Core metrics config +data MetricsScrapingConfig = MetricsScrapingConfig + { _prometheusPort :: Int + , _dataDogApiKey :: Text + } + +makeLenses ''MetricsScrapingConfig + +data PrometheusEnv = PrometheusEnv + { _envMetricsState :: MetricsState + , _envMetricsScrapingConfig :: MetricsScrapingConfig + } + +makeLenses ''PrometheusEnv + +emptyState :: IO MetricsState +emptyState = do + counters <- newMVar Map.empty + histos <- newMVar Map.empty + registry <- Registry.new + return $ MetricsState registry counters histos + +forkMetricsServer + :: MonadIO m + => PrometheusEnv + -> m ThreadId +forkMetricsServer metCfg = liftIO $ + let PrometheusEnv{..} = metCfg + port = _prometheusPort $ _envMetricsScrapingConfig + MetricsState{..} = _envMetricsState + in forkIO $ Http.serveHttpTextMetrics port ["metrics"] (Registry.sample metricsRegistry) -------------------------------------------------------------------------------- -- eval @@ -34,14 +151,14 @@ import Tendermint.SDK.BaseApp.Metrics (CountName (..), evalWithMetrics :: Member (Embed IO) r - => Member (Reader (Maybe MetricsConfig)) r + => Member (Reader (Maybe PrometheusEnv)) r => Sem (Metrics ': r) a -> Sem r a evalWithMetrics action = do mCfg <- ask case mCfg of Nothing -> evalNothing action - Just cfg -> evalMetrics (metricsState cfg) action + Just cfg -> evalMetrics (_envMetricsState cfg) action evalNothing :: Sem (Metrics ': r) a @@ -107,98 +224,3 @@ observeHistogram MetricsState{..} histName val = liftIO $ do Just hist -> do Histogram.observe val hist pure histMap - --------------------------------------------------------------------------------- --- indexes --------------------------------------------------------------------------------- - -countToIdentifier :: CountName -> MetricIdentifier -countToIdentifier (CountName name labels) = MetricIdentifier - { metricIdName = fixMetricName name - , metricIdLabels = MetricId.fromList labels - , metricIdHistoBuckets = [] - } - -histogramToIdentifier :: HistogramName -> MetricIdentifier -histogramToIdentifier (HistogramName name labels buckets) = MetricIdentifier - { metricIdName = fixMetricName name - , metricIdLabels = MetricId.fromList labels - , metricIdHistoBuckets = buckets - } - --- | Prometheus registry index key -mkPrometheusMetricId :: MetricIdentifier -> MetricId.MetricId -mkPrometheusMetricId MetricIdentifier{..} = - MetricId.MetricId (MetricId.Name metricIdName) metricIdLabels - --- | Index key for storing metrics -metricIdStorable :: MetricIdentifier -> (Text, MetricId.Labels) -metricIdStorable c = (fixMetricName $ metricIdName c, fixMetricLabels $ metricIdLabels c) - where fixMetricLabels = - MetricId.fromList . - map (fixMetricName *** fixMetricName) . - MetricId.toList - -fixMetricName :: Text -> Text -fixMetricName = Text.map fixer - where fixer c = if c `elem` validChars then c else '_' - validChars = ['a'..'z'] ++ ['A'..'Z'] ++ ['0'..'9'] ++ "_" - --- | Core metrics state -type MetricsMap a = Map (Text, MetricId.Labels) a -data MetricsState = MetricsState - { metricsRegistry :: Registry.Registry - , metricsCounters :: MVar (MetricsMap Counter.Counter) - , metricsHistograms :: MVar (MetricsMap Histogram.Histogram) - } - --- | Core metrics config -type Port = Int -data MetricsConfig = MetricsConfig - { metricsState :: MetricsState - , metricsPort :: Maybe Port - , metricsAPIKey :: Maybe Text - } - --- | Intermediary prometheus registry index key -data MetricIdentifier = MetricIdentifier - { metricIdName :: Text - , metricIdLabels :: MetricId.Labels - , metricIdHistoBuckets :: [Double] - } - -instance IsString MetricIdentifier where - fromString s = MetricIdentifier (fromString s) mempty mempty - --------------------------------------------------------------------------------- --- setup --------------------------------------------------------------------------------- - -emptyState :: IO MetricsState -emptyState = do - counters <- newMVar Map.empty - histos <- newMVar Map.empty - registry <- Registry.new - return $ MetricsState registry counters histos - -mkMetricsConfig :: IO MetricsConfig -mkMetricsConfig = do - state <- emptyState - return $ MetricsConfig state Nothing Nothing - -runMetricsServer - :: MonadIO m - => Maybe MetricsConfig - -> m () -runMetricsServer mMetCfg = liftIO $ do - case mMetCfg of - Nothing -> return () - Just metCfg -> do - let MetricsConfig{..} = metCfg - MetricsState{..} = metricsState - case metricsPort of - Nothing -> return () - Just port -> do - _ <- forkIO $ - Http.serveHttpTextMetrics port ["metrics"] (Registry.sample metricsRegistry) - return () diff --git a/stack.yaml b/stack.yaml index 6371dd36..1e248a75 100644 --- a/stack.yaml +++ b/stack.yaml @@ -21,6 +21,13 @@ extra-deps: - lens-labels-0.3.0.1 - proto-lens-0.5.0.0 - proto-lens-protoc-0.5.0.0 + - containers-0.5.11.0@sha256:28ad7337057442f75bc689315ab4ec7bdf5e6b2c39668f306672cecd82c02798,16685 + - http-client-0.5.14@sha256:4880b27d6741e331454a1d4c887d96ce3d7d625322c8433983a4b1cd08538577,5348 + - binary-0.8.7.0@sha256:ae3e6cca723ac55c54bbb3fa771bcf18142bc727afd57818e66d6ee6c8044f12,7705 + - text-1.2.4.0@sha256:8c24450feb8e3bbb7ea3e17af24ef57e85db077c4bf53e5bcc345b283d1b1d5b,10081 + - katip-elasticsearch-0.6.0.0@sha256:be8513ce611db989c63c9f836af99699767d4fc3a9cb0fd81fcbae4d1f2ed7ee,2746 + - bloodhound-0.16.0.0@sha256:b7be3a83e7b914fbe80a9b9de29009ad60cff072f5f8d4af4ee64de8e6406d32,5508 + - hpc-0.6.0.3@sha256:de3f7982345d315f1d7713df38b4f2cf09bd274f7d64dffec0cf2a0d9c8aab19,1185 - katip-datadog-0.1.0.0 - prometheus-2.1.3 - git: https://github.com/oscoin/avl-auth diff --git a/stack.yaml.lock b/stack.yaml.lock index 39b26e2c..beccf8ec 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -39,6 +39,55 @@ packages: sha256: 30942e172639508c45742e4b0cb6bf3493bee24b291b6cd4c1ad0785f1e9057c original: hackage: proto-lens-protoc-0.5.0.0 +- completed: + hackage: containers-0.5.11.0@sha256:28ad7337057442f75bc689315ab4ec7bdf5e6b2c39668f306672cecd82c02798,16685 + pantry-tree: + size: 4849 + sha256: faa4e75922a28f7cfe9920c1d7ab3866b792cefcd29bf79f54cfe3b6b5f57cbf + original: + hackage: containers-0.5.11.0@sha256:28ad7337057442f75bc689315ab4ec7bdf5e6b2c39668f306672cecd82c02798,16685 +- completed: + hackage: http-client-0.5.14@sha256:4880b27d6741e331454a1d4c887d96ce3d7d625322c8433983a4b1cd08538577,5348 + pantry-tree: + size: 2457 + sha256: 02bcffba9cad572fefb4640f5fc9be68e770b32ab73efcac649db20290994c6d + original: + hackage: http-client-0.5.14@sha256:4880b27d6741e331454a1d4c887d96ce3d7d625322c8433983a4b1cd08538577,5348 +- completed: + hackage: binary-0.8.7.0@sha256:ae3e6cca723ac55c54bbb3fa771bcf18142bc727afd57818e66d6ee6c8044f12,7705 + pantry-tree: + size: 1976 + sha256: 35e44b6d3ccf0d56fc5407dc3f0895e74696a66da189afbd65973c95743f5e25 + original: + hackage: binary-0.8.7.0@sha256:ae3e6cca723ac55c54bbb3fa771bcf18142bc727afd57818e66d6ee6c8044f12,7705 +- completed: + hackage: text-1.2.4.0@sha256:8c24450feb8e3bbb7ea3e17af24ef57e85db077c4bf53e5bcc345b283d1b1d5b,10081 + pantry-tree: + size: 7457 + sha256: 3437b0a73ce2ae1a81aa8b3438d41a85981c00894cdbee0d6d6d6873046a5d5d + original: + hackage: text-1.2.4.0@sha256:8c24450feb8e3bbb7ea3e17af24ef57e85db077c4bf53e5bcc345b283d1b1d5b,10081 +- completed: + hackage: katip-elasticsearch-0.6.0.0@sha256:be8513ce611db989c63c9f836af99699767d4fc3a9cb0fd81fcbae4d1f2ed7ee,2746 + pantry-tree: + size: 679 + sha256: 907421eb58249f6bed58f4e94f00627b383e53fd0ea0737050c1b1f7ab9fee44 + original: + hackage: katip-elasticsearch-0.6.0.0@sha256:be8513ce611db989c63c9f836af99699767d4fc3a9cb0fd81fcbae4d1f2ed7ee,2746 +- completed: + hackage: bloodhound-0.16.0.0@sha256:b7be3a83e7b914fbe80a9b9de29009ad60cff072f5f8d4af4ee64de8e6406d32,5508 + pantry-tree: + size: 4812 + sha256: 7f21ce00e92f7fd24a91dd19a82aab38f62047c1b93f2cc070481760b41a4d37 + original: + hackage: bloodhound-0.16.0.0@sha256:b7be3a83e7b914fbe80a9b9de29009ad60cff072f5f8d4af4ee64de8e6406d32,5508 +- completed: + hackage: hpc-0.6.0.3@sha256:de3f7982345d315f1d7713df38b4f2cf09bd274f7d64dffec0cf2a0d9c8aab19,1185 + pantry-tree: + size: 432 + sha256: 4686c367eb25eb4d32d66bd4c080d6caa2b5e78c73beea3993db690137e1d6cb + original: + hackage: hpc-0.6.0.3@sha256:de3f7982345d315f1d7713df38b4f2cf09bd274f7d64dffec0cf2a0d9c8aab19,1185 - completed: hackage: katip-datadog-0.1.0.0@sha256:4e72dca402b953bd34b7a744ad23eb90600a420adef67192c9702073564f1cae,1885 pantry-tree: From a9df7236622cae01edfbee1cc8927ee403531edf Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 24 Dec 2019 05:48:02 -0500 Subject: [PATCH 28/70] Weeding (#164) * iavl, tendermint client * types * server * extra * sdk * simple storage * nameservice * wip * iavl, tendermint-client, types * Extra * sdk * nameservice * added weeder to travis for master * stylish * make command --- .travis.yml | 4 +- .weeder.yaml | 132 ++++++++++++++++++ Makefile | 3 + hs-abci-examples/nameservice/package.yaml | 93 +++++++----- hs-abci-examples/nameservice/protogen/Main.hs | 72 +--------- .../nameservice/protogen/Protogen.hs | 55 ++++++++ .../test/Nameservice/Test/E2ESpec.hs | 2 +- hs-abci-examples/simple-storage/package.yaml | 73 +++++----- .../test/SimpleStorage/Test/E2ESpec.hs | 22 +-- hs-abci-extra/package.yaml | 18 +-- hs-abci-sdk/package.yaml | 89 ++++++------ .../src/Tendermint/SDK/Modules/Auth.hs | 2 +- .../src/Tendermint/SDK/Modules/Auth/Query.hs | 5 +- .../Tendermint/SDK/Test/AuthTreeStoreSpec.hs | 2 +- .../test/Tendermint/SDK/Test/CryptoSpec.hs | 2 +- .../test/Tendermint/SDK/Test/EventSpec.hs | 2 +- .../test/Tendermint/SDK/Test/GasSpec.hs | 2 +- .../test/Tendermint/SDK/Test/MetricsSpec.hs | 2 +- hs-abci-server/package.yaml | 40 +++--- .../test/Network/ABCI/Test/Server/AppSpec.hs | 2 +- hs-abci-types/package.yaml | 39 +++--- .../src/Data/Time/Calendar/Private.hs | 43 +----- hs-abci-types/src/Data/Time/Orphans.hs | 4 +- .../Network/ABCI/Test/Types/MessagesSpec.hs | 9 +- hs-iavl-client/package.yaml | 38 ++--- .../src/Database/IAVL/{RPCCall.hs => RPC.hs} | 2 +- .../IAVL/{RPCCallSpec.hs => RPCSpec.hs} | 5 +- .../kv-test/KVStore/Test/KVSpec.hs | 2 +- hs-tendermint-client/package.yaml | 25 ++-- .../src/Network/Tendermint/Client.hs | 1 + stack.yaml | 2 + 31 files changed, 436 insertions(+), 356 deletions(-) create mode 100644 .weeder.yaml create mode 100644 hs-abci-examples/nameservice/protogen/Protogen.hs rename hs-iavl-client/src/Database/IAVL/{RPCCall.hs => RPC.hs} (99%) rename hs-iavl-client/test/Database/IAVL/{RPCCallSpec.hs => RPCSpec.hs} (99%) diff --git a/.travis.yml b/.travis.yml index 0d74e7a5..378124df 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,5 +70,7 @@ jobs: # When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty if: branch = master script: - - travis_wait 120 stack --skip-ghc-check install hlint-2.1.26 stylish-haskell-0.9.4.3 + - travis_wait 120 stack --skip-ghc-check install hlint-2.1.26 stylish-haskell-0.9.4.3 weeder-1.0.8 - make stylish && git diff-index --quiet HEAD + - make hlint + - make weeder diff --git a/.weeder.yaml b/.weeder.yaml new file mode 100644 index 00000000..67136eea --- /dev/null +++ b/.weeder.yaml @@ -0,0 +1,132 @@ +- package: + - name: hs-abci-server + - section: + - name: test:hs-abci-server-test + - message: + - name: Redundant build-depends entry + - depends: hspec-discover + +- package: + - name: hs-abci-types + - section: + - name: library + - message: + - name: Missing other-modules entry + - module: + - Proto.Types + - Proto.Types_Fields + - Proto.Vendored.Google.Protobuf.Timestamp + - Proto.Vendored.Google.Protobuf.Timestamp_Fields + - Proto.Vendored.Tendermint.Tendermint.Crypto.Merkle.Merkle + - Proto.Vendored.Tendermint.Tendermint.Crypto.Merkle.Merkle_Fields + - Proto.Vendored.Tendermint.Tendermint.Libs.Common.Types + - Proto.Vendored.Tendermint.Tendermint.Libs.Common.Types_Fields + - message: + - name: Module not compiled + - module: + - Proto.Types + - Proto.Types_Fields + - Proto.Vendored.Gogo.Protobuf.Gogoproto.Gogo + - Proto.Vendored.Gogo.Protobuf.Gogoproto.Gogo_Fields + - Proto.Vendored.Google.Protobuf.Timestamp + - Proto.Vendored.Google.Protobuf.Timestamp_Fields + - Proto.Vendored.Tendermint.Tendermint.Crypto.Merkle.Merkle + - Proto.Vendored.Tendermint.Tendermint.Crypto.Merkle.Merkle_Fields + - Proto.Vendored.Tendermint.Tendermint.Libs.Common.Types + - Proto.Vendored.Tendermint.Tendermint.Libs.Common.Types_Fields + - message: + - name: Redundant build-depends entry + - depends: proto-lens-runtime + - section: + - name: test:hs-abci-types-test + - message: + - name: Redundant build-depends entry + - depends: hspec-discover + +- package: + - name: hs-iavl-client + - section: + - name: test:hs-iavl-client-test + - message: + - name: Redundant build-depends entry + - depends: hspec-discover + - section: + - name: library + - message: + - name: Missing other-modules entry + - module: + - Proto.Google.Protobuf.Empty + - Proto.Iavl.Api + - message: + - name: Module not compiled + - module: + - Proto.Google.Api.Annotations + - Proto.Google.Api.Http + - Proto.Google.Protobuf.Empty + - Proto.Iavl.Api + - Proto.Iavl.Api_Fields + - message: + - name: Redundant build-depends entry + - depends: proto-lens-runtime + +- package: + - name: hs-abci-sdk + - section: + - name: library + - message: + - name: Missing other-modules entry + - module: + - Proto.Modules.Auth + - Proto.Modules.Auth_Fields + - Proto.Types.Transaction + - Proto.Types.Transaction_Fields + - message: + - name: Module not compiled + - module: + - Proto.Modules.Auth + - Proto.Modules.Auth_Fields + - Proto.Types.Transaction + - Proto.Types.Transaction_Fields + - message: + - name: Redundant build-depends entry + - depends: + - polysemy-plugin + - proto-lens-runtime + - section: + - name: test:hs-abci-sdk-test + - message: + - name: Redundant build-depends entry + - depends: + - hspec-discover + - polysemy-plugin + +- package: + - name: simple-storage + - section: + - name: library + - message: + - name: Missing other-modules entry + - module: + - Proto.SimpleStorage.Messages + - Proto.SimpleStorage.Messages_Fields + - message: + - name: Module not compiled + - module: + - Proto.SimpleStorage.Messages + - Proto.SimpleStorage.Messages_Fields + - message: + - name: Redundant build-depends entry + - depends: + - polysemy-plugin + - proto-lens-runtime + + +- package: + - name: nameservice + - section: + - name: library + - message: + - name: Redundant build-depends entry + - depends: polysemy-plugin + - section: + - name: test:tutorial diff --git a/Makefile b/Makefile index bd228335..fff5f91a 100644 --- a/Makefile +++ b/Makefile @@ -12,6 +12,9 @@ help: ## Ask for help! # Linting and Styling ##################### +weeder: ## look for unused packages and functions + weeder . --build + hlint: ## Run hlint on all haskell projects stack exec hlint -- -e hs -h .hlint.yaml hs-abci-server \ hs-tendermint-client \ diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 3fc3ee9e..c6ebedb7 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -45,41 +45,32 @@ default-extensions: - StandaloneDeriving - ConstraintKinds -dependencies: -- aeson -- aeson-casing -- base >= 4.7 && < 5 -- bloodhound -- bytestring -- containers -- data-default-class -- errors -- exceptions -- either -- hs-abci-extra -- hs-abci-sdk -- hs-abci-server -- hs-abci-types -- http-client -- katip -- katip-datadog -- katip-elasticsearch -- lens -- polysemy -- polysemy-plugin -- polysemy-zoo -- pretty -- proto-lens -- proto-lens-runtime -- proto3-suite -- proto3-wire -- servant -- string-conversions -- text -- validation - library: source-dirs: src + dependencies: + - aeson + - aeson-casing + - base >= 4.7 && < 5 + - bloodhound + - bytestring + - containers + - errors + - hs-abci-extra + - hs-abci-server + - hs-abci-sdk + - hs-abci-types + - http-client + - katip + - katip-datadog + - katip-elasticsearch + - lens + - polysemy + - polysemy-plugin + - proto3-suite + - proto3-wire + - string-conversions + - text + - validation ghc-options: - -fplugin=Polysemy.Plugin - -Werror @@ -116,8 +107,11 @@ executables: - -threaded - -rtsopts - -with-rtsopts=-N - - -fplugin=Polysemy.Plugin dependencies: + - base + - hs-abci-sdk + - katip + - lens - nameservice gen-protos-exe: @@ -128,7 +122,14 @@ executables: - -rtsopts - -with-rtsopts=-N dependencies: + - aeson-casing + - base + - bytestring + - hs-abci-sdk - nameservice + - pretty + - proto3-suite + - proto3-wire tests: tutorial: @@ -143,8 +144,17 @@ tests: - Tutorial.Nameservice.Types ghc-options: -Wall -pgmL markdown-unlit dependencies: + - aeson + - base - hs-abci-sdk + - hs-abci-server + - lens - nameservice + - polysemy + - polysemy-plugin + - proto3-suite + - string-conversions + - text nameservice-test: main: Spec.hs source-dirs: test @@ -155,8 +165,19 @@ tests: - -rtsopts - -with-rtsopts=-N dependencies: - - nameservice + - aeson - aeson-pretty + - base >= 4.7 && < 5 + - bytestring + - data-default-class + - hs-abci-sdk + - hs-abci-types + - hs-tendermint-client - hspec + - lens + - nameservice + - proto3-suite - secp256k1-haskell - - hs-tendermint-client + - servant + - string-conversions + - text diff --git a/hs-abci-examples/nameservice/protogen/Main.hs b/hs-abci-examples/nameservice/protogen/Main.hs index 5b9a3b73..be58d77c 100644 --- a/hs-abci-examples/nameservice/protogen/Main.hs +++ b/hs-abci-examples/nameservice/protogen/Main.hs @@ -1,75 +1,11 @@ -{-# LANGUAGE MagicHash #-} - -- @NOTE: ^ possibly the only language extension needed from tutorial -- Seems to be the only requirement for generating the .proto string module Main where -import Data.Aeson.Casing (snakeCase) -import qualified Data.ByteString.Lazy as BL -import GHC.Exts (Proxy#, proxy#) -import Nameservice.Modules.Nameservice.Messages (BuyName (..), - DeleteName (..), - SetName (..)) -import Nameservice.Modules.Nameservice.Types (Name (..), - Whois (..)) -import Nameservice.Modules.Token (Amount (..)) -import Proto3.Suite (DotProtoDefinition, - Message, - fromByteString, - message, - packageFromDefs) -import Proto3.Suite.DotProto as DotProto -import Proto3.Suite.DotProto.Rendering (RenderingOptions, - defRenderingOptions) -import qualified Proto3.Wire.Decode as Decode -import Proto3.Wire.Types (FieldNumber (..)) -import Tendermint.SDK.Types.Address (Address (..)) -import qualified Text.PrettyPrint as PP +import Protogen (messagesProtoFile, whoisProtoFile) main :: IO () -main = putStrLn messagesProtoFile - --------------------------------------------------------------------------------- --- test --------------------------------------------------------------------------------- - -testSN :: SetName -testSN = SetName (Name "satoshi") (Address "01") "cool cats" - -testBN :: BuyName -testBN = BuyName (Amount 999) (Name "satoshi") "cool cats" (Address "01") - -testDN :: DeleteName -testDN = DeleteName (Address "01") (Name "satoshi") - --- @NOTE: for some reason, encoding results in a lazy bytestring --- while the provided decoder only accepts a strict bytestring -fromLazyByteString :: Message a => BL.ByteString -> Either Decode.ParseError a -fromLazyByteString = fromByteString . BL.toStrict - --------------------------------------------------------------------------------- --- Requires magic hash extension --------------------------------------------------------------------------------- - -stripPrefixName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc -stripPrefixName (Single typeName) (Single fieldName) _ = - let prefixLen = length typeName - fieldName' = Single . snakeCase . drop prefixLen $ fieldName - in pPrint fieldName' --- @NOTE: we don't yet need for other identifiers -stripPrefixName _ _ _ = error "stripPrefixName unused case" - -msgStripPrefixOptions :: RenderingOptions -msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } - -messagesProtoFile :: String -messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" - ([ message (proxy# :: Proxy# SetName) - , message (proxy# :: Proxy# BuyName) - , message (proxy# :: Proxy# DeleteName) - ] :: [DotProtoDefinition]) - -whoisProtoFile :: String -whoisProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" - ([ message (proxy# :: Proxy# Whois) ] :: [DotProtoDefinition]) +main = do + putStrLn messagesProtoFile + putStrLn whoisProtoFile diff --git a/hs-abci-examples/nameservice/protogen/Protogen.hs b/hs-abci-examples/nameservice/protogen/Protogen.hs new file mode 100644 index 00000000..589b0381 --- /dev/null +++ b/hs-abci-examples/nameservice/protogen/Protogen.hs @@ -0,0 +1,55 @@ +{-# LANGUAGE MagicHash #-} + +-- @NOTE: ^ possibly the only language extension needed from tutorial +-- Seems to be the only requirement for generating the .proto string + +module Protogen (messagesProtoFile, whoisProtoFile) where + +import Data.Aeson.Casing (snakeCase) +import qualified Data.ByteString.Lazy as BL +import GHC.Exts (Proxy#, proxy#) +import Nameservice.Modules.Nameservice.Messages (BuyName (..), + DeleteName (..), + SetName (..)) +import Nameservice.Modules.Nameservice.Types (Name (..), + Whois (..)) +import Nameservice.Modules.Token (Amount (..)) +import Proto3.Suite (DotProtoDefinition, + Message, + fromByteString, + message, + packageFromDefs) +import Proto3.Suite.DotProto as DotProto +import Proto3.Suite.DotProto.Rendering (RenderingOptions, + defRenderingOptions) +import qualified Proto3.Wire.Decode as Decode +import Proto3.Wire.Types (FieldNumber (..)) +import Tendermint.SDK.Types.Address (Address (..)) +import qualified Text.PrettyPrint as PP + + +-------------------------------------------------------------------------------- +-- Requires magic hash extension +-------------------------------------------------------------------------------- + +stripPrefixName :: DotProtoIdentifier -> DotProtoIdentifier -> FieldNumber -> PP.Doc +stripPrefixName (Single typeName) (Single fieldName) _ = + let prefixLen = length typeName + fieldName' = Single . snakeCase . drop prefixLen $ fieldName + in pPrint fieldName' +-- @NOTE: we don't yet need for other identifiers +stripPrefixName _ _ _ = error "stripPrefixName unused case" + +msgStripPrefixOptions :: RenderingOptions +msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } + +messagesProtoFile :: String +messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" + ([ message (proxy# :: Proxy# SetName) + , message (proxy# :: Proxy# BuyName) + , message (proxy# :: Proxy# DeleteName) + ] :: [DotProtoDefinition]) + +whoisProtoFile :: String +whoisProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" + ([ message (proxy# :: Proxy# Whois) ] :: [DotProtoDefinition]) diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index f24f16cd..e697e9eb 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,4 +1,4 @@ -module Nameservice.Test.E2ESpec where +module Nameservice.Test.E2ESpec (spec) where import Control.Lens ((^.)) import Crypto.Secp256k1 (CompactRecSig (..), diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index ff9d5267..722466ed 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -37,39 +37,6 @@ default-extensions: - StandaloneDeriving - ConstraintKinds - -dependencies: -- aeson -- base >= 4.7 && < 5 -- bloodhound -- bytestring -- cereal -- cereal-text -- containers -- cryptonite -- errors -- exceptions -- katip -- katip-datadog -- katip-elasticsearch -- hs-abci-extra -- hs-abci-server -- hs-abci-types -- hs-abci-extra -- hs-abci-sdk -- http-client -- lens -- memory -- mtl -- polysemy -- polysemy-plugin -- proto-lens -- proto-lens-runtime -- servant -- string-conversions -- text -- validation - custom-setup: dependencies: - base @@ -83,6 +50,34 @@ library: - -fplugin=Polysemy.Plugin - -Werror - -Wall + dependencies: + - aeson + - base >= 4.7 && < 5 + - bloodhound + - bytestring + - cereal + - cereal-text + - containers + - cryptonite + - errors + - hs-abci-extra + - hs-abci-server + - hs-abci-types + - hs-abci-extra + - hs-abci-sdk + - http-client + - katip + - katip-datadog + - katip-elasticsearch + - lens + - memory + - polysemy + - polysemy-plugin + - proto-lens + - proto-lens-runtime + - string-conversions + - text + - validation exposed-modules: - SimpleStorage.Server - SimpleStorage.Application @@ -112,7 +107,10 @@ executables: - -Wincomplete-uni-patterns - -Wredundant-constraints dependencies: + - base >= 4.7 && < 5 - katip + - hs-abci-sdk + - lens - simple-storage tests: @@ -130,10 +128,17 @@ tests: dependencies: - aeson - aeson-pretty + - base >= 4.7 && < 5 + - bytestring + - cereal - data-default-class + - hs-abci-sdk - simple-storage - hs-abci-types - hs-tendermint-client - hspec - - QuickCheck + - lens + - mtl - secp256k1-haskell + - servant + - string-conversions diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 91c9244f..a1a752f4 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -1,18 +1,16 @@ -module SimpleStorage.Test.E2ESpec where +module SimpleStorage.Test.E2ESpec (spec) where import Control.Lens ((^.)) import Control.Monad.Reader (ReaderT) -import Crypto.Secp256k1 (SecKey, derivePubKey, +import Crypto.Secp256k1 (SecKey, exportCompactRecSig, secKey) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) -import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString) import Data.Default.Class (def) -import Data.Int (Int32) import Data.Maybe (fromJust) import Data.Proxy import qualified Data.Serialize as Serial @@ -28,9 +26,7 @@ import Tendermint.SDK.BaseApp.Query.Client (ClientResponse (..), HasClient (..), RunClient (..)) import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (Secp256k1, - addressFromPubKey) -import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.SDK.Crypto (Secp256k1) import Tendermint.SDK.Types.Transaction (RawTransaction (..), signRawTransaction) import Test.Hspec @@ -72,13 +68,6 @@ spec = do ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq foundCount `shouldBe` SS.Count 4 - -encodeCount :: Int32 -> Base64String -encodeCount = Base64.fromBytes . Serial.encode - -decodeCount :: Base64String -> Int32 -decodeCount = (\(Right a) -> a) . Serial.decode . Base64.toBytes - runRPC :: forall a. RPC.TendermintM a -> IO a runRPC = RPC.runTendermintM rpcConfig where @@ -125,7 +114,6 @@ mkSignedRawTransaction privateKey msg = sign unsigned data User = User { userPrivKey :: SecKey - , userAddress :: Address } user1 :: User @@ -134,9 +122,7 @@ user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e03 makeUser :: String -> User makeUser privKeyStr = let privateKey = fromJust . secKey . Hex.toBytes . fromString $ privKeyStr - pubKey = derivePubKey privateKey - address = addressFromPubKey (Proxy @Secp256k1) pubKey - in User privateKey address + in User privateKey algProxy :: Proxy Secp256k1 algProxy = Proxy diff --git a/hs-abci-extra/package.yaml b/hs-abci-extra/package.yaml index aede9b77..2e054a7e 100644 --- a/hs-abci-extra/package.yaml +++ b/hs-abci-extra/package.yaml @@ -21,18 +21,6 @@ description: Please see the README on GitHub at = 4.7 && < 5 -- katip -- katip-datadog -- hs-abci-server -- aeson -- aeson-casing -- unordered-containers -- memory -- text -- lens -- bytestring -- time -- containers default-extensions: - NamedFieldPuns @@ -51,6 +39,12 @@ default-extensions: library: source-dirs: src + dependencies: + - aeson + - containers + - katip + - hs-abci-server + - time ghc-options: - -Werror - -Wall diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 934257bf..184c42b3 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -44,49 +44,42 @@ default-extensions: - ConstraintKinds - PackageImports - -dependencies: -- aeson -- aeson-casing -- async -- avl-auth -- base >= 4.7 && < 5 -- bytestring -- containers -- cryptonite -- data-default-class -- errors -- exceptions -- hs-abci-types -- hs-abci-server -- hs-tendermint-client -- http-types -- katip -- lens -- memory -- mmorph -- mtl -- polysemy -- polysemy-plugin -- polysemy-zoo -- profunctors -- prometheus -- proto-lens -- proto-lens-runtime -- proto3-wire -- proto3-suite -- secp256k1-haskell -- servant -- stm -- string-conversions -- text -- time -- uri-bytestring -- validation - library: source-dirs: - src + dependencies: + - aeson + - aeson-casing + - avl-auth + - base >= 4.7 && < 5 + - bytestring + - containers + - cryptonite + - data-default-class + - errors + - hs-abci-server + - hs-abci-types + - hs-tendermint-client + - http-types + - katip + - lens + - memory + - mmorph + - mtl + - polysemy + - polysemy-plugin + - polysemy-zoo + - prometheus + - proto-lens + - proto-lens-runtime + - proto3-wire + - secp256k1-haskell + - servant + - stm + - string-conversions + - text + - time + - validation ghc-options: - -fplugin=Polysemy.Plugin - -Werror @@ -155,12 +148,20 @@ tests: - -with-rtsopts=-N dependencies: + - aeson + - base >= 4.7 && < 5 + - bytestring + - containers + - lens - cereal - - generic-arbitrary - - hs-abci-server - hs-abci-sdk + - hs-abci-types - hspec - hspec-core - hspec-discover - - QuickCheck - - quickcheck-instances + - polysemy + - polysemy-plugin + - polysemy-zoo + - prometheus + - secp256k1-haskell + - string-conversions diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 60f0b2bf..19fbe10e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -3,7 +3,7 @@ module Tendermint.SDK.Modules.Auth , authModule , AuthEffs - , Accounts + , Accounts(..) , getAccount , putAccount , eval diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs index 9acb6a88..7d62df95 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs @@ -1,4 +1,7 @@ -module Tendermint.SDK.Modules.Auth.Query where +module Tendermint.SDK.Modules.Auth.Query + ( Api + , server + ) where import Data.Proxy import Polysemy (Members, Sem) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs index 6ea45831..4634a9ee 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Test.AuthTreeStoreSpec where +module Tendermint.SDK.Test.AuthTreeStoreSpec (spec) where import Control.Lens (iso) import Control.Monad (void) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs index c40d2208..c0245140 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Test.CryptoSpec where +module Tendermint.SDK.Test.CryptoSpec (spec) where import Crypto.Secp256k1 (CompactRecSig (..), SecKey, derivePubKey, diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs index b4f1f77d..f5d7cb7d 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs @@ -1,4 +1,4 @@ -module Tendermint.SDK.Test.EventSpec where +module Tendermint.SDK.Test.EventSpec (spec) where import qualified Data.Aeson as A import GHC.Generics (Generic) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs index a0b7b28b..3d892412 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs @@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Test.GasSpec where +module Tendermint.SDK.Test.GasSpec (spec) where import Control.Monad.IO.Class (MonadIO (..)) import Data.Either (isRight) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs index f89ae482..d6036b30 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs @@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Test.MetricsSpec where +module Tendermint.SDK.Test.MetricsSpec (spec) where import Control.Concurrent.MVar (readMVar) import Data.Map.Strict ((!)) diff --git a/hs-abci-server/package.yaml b/hs-abci-server/package.yaml index d54289cf..e1a2afe5 100644 --- a/hs-abci-server/package.yaml +++ b/hs-abci-server/package.yaml @@ -25,26 +25,6 @@ default-extensions: - MultiParamTypeClasses - FunctionalDependencies - -dependencies: -- base >= 4.7 && < 5 -- aeson -- aeson-casing -- base16-bytestring -- bytestring -- conduit -- conduit-extra -- data-default-class -- lens -- memory -- proto-lens -- proto-lens-runtime -- string-conversions -- text -- template-haskell -- time -- hs-abci-types - library: source-dirs: src ghc-options: @@ -54,6 +34,20 @@ library: - -Widentities - -Wincomplete-uni-patterns - -Wredundant-constraints + dependencies: + - aeson + - base >= 4.7 && < 5 + - bytestring + - base16-bytestring + - conduit + - conduit-extra + - data-default-class + - hs-abci-types + - lens + - proto-lens + - string-conversions + - text + exposed-modules: - Network.ABCI.Server - Network.ABCI.Server.App @@ -74,13 +68,11 @@ tests: dependencies: - - generic-arbitrary + - base >= 4.7 && < 5 + - bytestring - hs-abci-server - hspec - hspec-core - hspec-discover - binary - - lens-labels - - proto-lens-arbitrary - QuickCheck - - quickcheck-instances diff --git a/hs-abci-server/test/Network/ABCI/Test/Server/AppSpec.hs b/hs-abci-server/test/Network/ABCI/Test/Server/AppSpec.hs index e0a29c88..7c4d764a 100644 --- a/hs-abci-server/test/Network/ABCI/Test/Server/AppSpec.hs +++ b/hs-abci-server/test/Network/ABCI/Test/Server/AppSpec.hs @@ -1,4 +1,4 @@ -module Network.ABCI.Test.Server.AppSpec where +module Network.ABCI.Test.Server.AppSpec (spec) where import Data.Bifunctor (first) import qualified Data.Binary.Put as Put diff --git a/hs-abci-types/package.yaml b/hs-abci-types/package.yaml index df6edff7..8cec6e5c 100644 --- a/hs-abci-types/package.yaml +++ b/hs-abci-types/package.yaml @@ -36,22 +36,6 @@ default-extensions: - FunctionalDependencies -dependencies: -- base >= 4.7 && < 5 -- aeson -- aeson-casing -- base16-bytestring -- bytestring -- data-default-class -- lens -- memory -- proto-lens -- proto-lens-runtime -- string-conversions -- text -- template-haskell -- time - library: source-dirs: src ghc-options: @@ -61,12 +45,29 @@ library: - -Widentities - -Wincomplete-uni-patterns - -Wredundant-constraints + dependencies: + - aeson + - aeson-casing + - base >= 4.7 && < 5 + - bytestring + - data-default-class + - memory + - lens + - proto-lens + - proto-lens-runtime + - text + - template-haskell + - time exposed-modules: - Data.ByteArray.HexString - Data.ByteArray.Base64String + - Network.ABCI.Types.Messages.Common - Network.ABCI.Types.Messages.Request - Network.ABCI.Types.Messages.Response - Network.ABCI.Types.Messages.FieldTypes + other-modules: + - Data.Time.Calendar.Private + - Data.Time.Orphans generated-exposed-modules: - Proto.Types - Proto.Types_Fields @@ -96,13 +97,15 @@ tests: dependencies: + - base + - bytestring - generic-arbitrary - hs-abci-types - hspec - hspec-core - hspec-discover - - binary - - lens-labels + - lens + - proto-lens - proto-lens-arbitrary - QuickCheck - quickcheck-instances diff --git a/hs-abci-types/src/Data/Time/Calendar/Private.hs b/hs-abci-types/src/Data/Time/Calendar/Private.hs index 5a3e9f68..1d0b5926 100644 --- a/hs-abci-types/src/Data/Time/Calendar/Private.hs +++ b/hs-abci-types/src/Data/Time/Calendar/Private.hs @@ -1,6 +1,11 @@ -module Data.Time.Calendar.Private where -- venored from: http://hackage.haskell.org/package/time-1.9.3/docs/src/Data.Time.Calendar.Private.html -import Data.Fixed + +module Data.Time.Calendar.Private + ( PadOption (..) + , ShowPadded (..) + , quotBy + , remBy + ) where data PadOption = Pad Int Char | NoPad @@ -22,43 +27,9 @@ instance ShowPadded Int where showPaddedNum pad i | i < 0 = '-':(showPaddedNum pad (negate i)) showPaddedNum pad i = showPadded pad $ show i -show2Fixed :: Pico -> String -show2Fixed x | x < 10 = '0':(showFixed True x) -show2Fixed x = showFixed True x - -show2 :: (ShowPadded t) => t -> String -show2 = showPaddedNum $ Pad 2 '0' - -show3 :: (ShowPadded t) => t -> String -show3 = showPaddedNum $ Pad 3 '0' - -show4 :: (ShowPadded t) => t -> String -show4 = showPaddedNum $ Pad 4 '0' - -mod100 :: (Integral i) => i -> i -mod100 x = mod x 100 - -div100 :: (Integral i) => i -> i -div100 x = div x 100 - -clip :: (Ord t) => t -> t -> t -> t -clip a _ x | x < a = a -clip _ b x | x > b = b -clip _ _ x = x - -clipValid :: (Ord t) => t -> t -> t -> Maybe t -clipValid a _ x | x < a = Nothing -clipValid _ b x | x > b = Nothing -clipValid _ _ x = Just x - quotBy :: (Real a,Integral b) => a -> a -> b quotBy d n = truncate ((toRational n) / (toRational d)) remBy :: Real a => a -> a -> a remBy d n = n - (fromInteger f) * d where f = quotBy d n - -quotRemBy :: (Real a,Integral b) => a -> a -> (b,a) -quotRemBy d n = let - f = quotBy d n - in (f,n - (fromIntegral f) * d) diff --git a/hs-abci-types/src/Data/Time/Orphans.hs b/hs-abci-types/src/Data/Time/Orphans.hs index 199f7983..68b4e89b 100644 --- a/hs-abci-types/src/Data/Time/Orphans.hs +++ b/hs-abci-types/src/Data/Time/Orphans.hs @@ -1,6 +1,6 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} -module Data.Time.Orphans where -- vendored from: http://hackage.haskell.org/package/time-1.9.3/docs/src/Data.Time.Format.Format.Class.html +{-# OPTIONS_GHC -fno-warn-orphans #-} +module Data.Time.Orphans () where import Data.Fixed (Pico) import Data.Time.Calendar.Private (PadOption (..), ShowPadded (..), diff --git a/hs-abci-types/test/Network/ABCI/Test/Types/MessagesSpec.hs b/hs-abci-types/test/Network/ABCI/Test/Types/MessagesSpec.hs index 04c6c6c5..2ce41e68 100644 --- a/hs-abci-types/test/Network/ABCI/Test/Types/MessagesSpec.hs +++ b/hs-abci-types/test/Network/ABCI/Test/Types/MessagesSpec.hs @@ -1,4 +1,4 @@ -module Network.ABCI.Test.Types.MessagesSpec where +module Network.ABCI.Test.Types.MessagesSpec (spec) where import Control.Lens (Iso', from, set, to, @@ -83,13 +83,6 @@ scrubTimestamp ts = ts & T.seconds %~ abs & T.nanos %~ (`mod` 1000000000) . abs -scrubTimestampField - :: HasField a "time" T.Timestamp - => a - -> a -scrubTimestampField a = - a & PT.time %~ scrubTimestamp - scrubTimestampFieldMaybe :: HasField a "maybe'time" (Maybe T.Timestamp) => a diff --git a/hs-iavl-client/package.yaml b/hs-iavl-client/package.yaml index 06e08a34..e2f32258 100644 --- a/hs-iavl-client/package.yaml +++ b/hs-iavl-client/package.yaml @@ -36,25 +36,6 @@ default-extensions: - MultiParamTypeClasses - FunctionalDependencies - -dependencies: -- base >= 4.7 && < 5 -- aeson -- aeson-casing -- base16-bytestring -- bytestring -- data-default-class -- lens -- memory -- proto-lens -- proto-lens-runtime -- string-conversions -- text -- template-haskell -- time -- http2-client -- http2-client-grpc - library: source-dirs: src ghc-options: @@ -64,8 +45,14 @@ library: - -Widentities - -Wincomplete-uni-patterns - -Wredundant-constraints + dependencies: + - base >= 4.7 && < 5 + - http2-client + - http2-client-grpc + - proto-lens + - proto-lens-runtime exposed-modules: - - Database.IAVL.RPCCall + - Database.IAVL.RPC generated-exposed-modules: - Proto.Iavl.Api - Proto.Iavl.Api_Fields @@ -85,13 +72,12 @@ tests: - -with-rtsopts=-N dependencies: - - generic-arbitrary + - base >= 4.7 && < 5 - hs-iavl-client - hspec - hspec-core - hspec-discover - - binary - - lens-labels - - proto-lens-arbitrary - - QuickCheck - - quickcheck-instances + - http2-client + - http2-client-grpc + - lens + - proto-lens diff --git a/hs-iavl-client/src/Database/IAVL/RPCCall.hs b/hs-iavl-client/src/Database/IAVL/RPC.hs similarity index 99% rename from hs-iavl-client/src/Database/IAVL/RPCCall.hs rename to hs-iavl-client/src/Database/IAVL/RPC.hs index 0c7c64ea..7438983c 100644 --- a/hs-iavl-client/src/Database/IAVL/RPCCall.hs +++ b/hs-iavl-client/src/Database/IAVL/RPC.hs @@ -1,4 +1,4 @@ -module Database.IAVL.RPCCall where +module Database.IAVL.RPC where import Data.ProtoLens.Message (defMessage) import Network.GRPC.Client (RPC (..), RawReply) diff --git a/hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs b/hs-iavl-client/test/Database/IAVL/RPCSpec.hs similarity index 99% rename from hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs rename to hs-iavl-client/test/Database/IAVL/RPCSpec.hs index e31a2645..a60ae32e 100644 --- a/hs-iavl-client/test/Database/IAVL/RPCCallSpec.hs +++ b/hs-iavl-client/test/Database/IAVL/RPCSpec.hs @@ -1,10 +1,9 @@ - -module Database.IAVL.RPCCallSpec where +module Database.IAVL.RPCSpec (spec) where import Control.Lens ((&), (.~), (^.)) import Control.Monad (void) import Data.ProtoLens.Message (defMessage) -import Database.IAVL.RPCCall +import Database.IAVL.RPC import Network.GRPC.Client (RawReply, uncompressed) import Network.GRPC.Client.Helpers (GrpcClient, GrpcClientConfig (..), grpcClientConfigSimple, diff --git a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs index 3fb24b22..c4ddbef3 100644 --- a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs +++ b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs @@ -1,4 +1,4 @@ -module KVStore.Test.KVSpec where +module KVStore.Test.KVSpec (spec) where import Control.Lens (to, (^.)) import Control.Monad.Catch (try) diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index 306427dd..08df8389 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -32,26 +32,23 @@ default-extensions: - OverloadedStrings - GeneralizedNewtypeDeriving - dependencies: - aeson -- aeson-casing - base >= 4.7 && < 5 -- base16-bytestring - bytestring -- data-default-class - exceptions -- hs-abci-types -- http-client -- http-conduit -- memory -- mtl -- random -- text -- time +- data-default-class library: source-dirs: src + dependencies: + - aeson-casing + - hs-abci-types + - http-client + - http-conduit + - mtl + - random + - text ghc-options: - -Werror - -Wall @@ -61,6 +58,7 @@ library: - -Wredundant-constraints exposed-modules: - Network.Tendermint.Client + - Network.Tendermint.Client.Internal.RPCClient tests: hs-tendermint-client-kv: @@ -75,12 +73,9 @@ tests: - -rtsopts - -with-rtsopts=-N dependencies: - - aeson - aeson-pretty - - binary - hs-abci-types - hs-tendermint-client - hspec - lens - - QuickCheck - string-conversions diff --git a/hs-tendermint-client/src/Network/Tendermint/Client.hs b/hs-tendermint-client/src/Network/Tendermint/Client.hs index 255d363d..ae0219bc 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client.hs @@ -4,6 +4,7 @@ module Network.Tendermint.Client -- * ReExports , RPC.Config(..) , RPC.JsonRpcException(..) + , RPC.RpcError(..) ) where diff --git a/stack.yaml b/stack.yaml index 1e248a75..921f4fe2 100644 --- a/stack.yaml +++ b/stack.yaml @@ -45,3 +45,5 @@ extra-deps: explicit-setup-deps: hs-abci-server: true + +ghc-options: {"$locals": -ddump-to-file -ddump-hi} From 998a8b541c82e763df3e509e57b7d2b3be95b57b Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Fri, 3 Jan 2020 11:26:06 -0500 Subject: [PATCH 29/70] Hs abci test utils (#151) * Init hs-abci-test-utils * import packages and use correct default extensions * use directory module hierarchy * update project files with new utils lib * nameservice tests passing * use utils in ss * use new user module in nameservice tests * delete special msg encoder * use user util module in simple storage tests * move response checkers to utils library * move request runners to new request module * Update readme * add markdown-unlit as a direct dep to tutorial --- Makefile | 4 +- hs-abci-examples/nameservice/package.yaml | 12 +- .../Nameservice/Modules/Nameservice/Types.hs | 7 +- .../src/Nameservice/Modules/Token/Types.hs | 5 +- .../test/Nameservice/Test/E2ESpec.hs | 212 ++++-------------- hs-abci-examples/simple-storage/package.yaml | 13 +- .../test/SimpleStorage/Test/E2ESpec.hs | 81 +------ hs-abci-sdk/package.yaml | 4 - hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 1 - .../src/Tendermint/SDK/BaseApp/Events.hs | 28 +-- hs-abci-test-utils/README.md | 11 + hs-abci-test-utils/package.yaml | 76 +++++++ .../src/Tendermint/Utils}/Client.hs | 2 +- .../src/Tendermint/Utils/Events.hs | 33 +++ .../src/Tendermint/Utils/Request.hs | 59 +++++ .../src/Tendermint/Utils/Response.hs | 36 +++ .../src/Tendermint/Utils/User.hs | 46 ++++ hs-abci-test-utils/test/Spec.hs | 2 + .../test/Tendermint/Utils}/Test/EventSpec.hs | 6 +- stack.yaml | 1 + 20 files changed, 340 insertions(+), 299 deletions(-) create mode 100644 hs-abci-test-utils/README.md create mode 100644 hs-abci-test-utils/package.yaml rename {hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query => hs-abci-test-utils/src/Tendermint/Utils}/Client.hs (98%) create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/Events.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/Request.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/Response.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/User.hs create mode 100644 hs-abci-test-utils/test/Spec.hs rename {hs-abci-sdk/test/Tendermint/SDK => hs-abci-test-utils/test/Tendermint/Utils}/Test/EventSpec.hs (78%) diff --git a/Makefile b/Makefile index fff5f91a..9ae43fa4 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,7 @@ hlint: ## Run hlint on all haskell projects hs-tendermint-client \ hs-abci-extra \ hs-abci-sdk \ + hs-abci-test-utils \ hs-abci-examples/simple-storage \ hs-abci-examples/nameservice \ hs-iavl-client @@ -30,6 +31,7 @@ stylish: ## Run stylish-haskell over all haskell projects ./hs-tendermint-client \ ./hs-abci-examples \ ./hs-abci-sdk \ + ./hs-abci-test-utils \ ./hs-abci-server \ ./hs-iavl-client \ -name "*.hs" | xargs stack exec stylish-haskell -- -c ./.stylish_haskell.yaml -i @@ -49,7 +51,7 @@ install: ## Runs stack install to compile library and counter example app stack install test-libraries: install ## Run the haskell test suite for all haskell libraries - stack test hs-abci-types hs-abci-server hs-abci-sdk + stack test hs-abci-types hs-abci-server hs-abci-sdk hs-abci-test-utils test-iavl-client: ## test the iavl client library basic operation (requires grpc service running on port 8090) stack test hs-iavl-client diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index c6ebedb7..65d617be 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -58,6 +58,7 @@ library: - hs-abci-extra - hs-abci-server - hs-abci-sdk + - hs-abci-test-utils - hs-abci-types - http-client - katip @@ -149,6 +150,7 @@ tests: - hs-abci-sdk - hs-abci-server - lens + - markdown-unlit - nameservice - polysemy - polysemy-plugin @@ -161,23 +163,19 @@ tests: other-modules: - Nameservice.Test.E2ESpec ghc-options: + - -Werror + - -Wall - -threaded - -rtsopts - -with-rtsopts=-N dependencies: - - aeson - - aeson-pretty - base >= 4.7 && < 5 - - bytestring - data-default-class - hs-abci-sdk - hs-abci-types + - hs-abci-test-utils - hs-tendermint-client - hspec - lens - nameservice - - proto3-suite - - secp256k1-haskell - servant - - string-conversions - - text diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index f64a8ad5..9bf19811 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -19,6 +19,7 @@ import qualified Proto3.Wire.Encode as Encode import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) +import qualified Tendermint.Utils.Events as Event -------------------------------------------------------------------------------- @@ -106,7 +107,7 @@ instance FromJSON NameClaimed where parseJSON = A.genericParseJSON nameClaimedAesonOptions instance BaseApp.ToEvent NameClaimed where makeEventType _ = "NameClaimed" -instance BaseApp.FromEvent NameClaimed +instance Event.FromEvent NameClaimed data NameRemapped = NameRemapped { nameRemappedName :: Name @@ -123,7 +124,7 @@ instance FromJSON NameRemapped where parseJSON = A.genericParseJSON nameRemappedAesonOptions instance BaseApp.ToEvent NameRemapped where makeEventType _ = "NameRemapped" -instance BaseApp.FromEvent NameRemapped +instance Event.FromEvent NameRemapped data NameDeleted = NameDeleted { nameDeletedName :: Name @@ -138,4 +139,4 @@ instance FromJSON NameDeleted where parseJSON = A.genericParseJSON nameDeletedAesonOptions instance BaseApp.ToEvent NameDeleted where makeEventType _ = "NameDeleted" -instance BaseApp.FromEvent NameDeleted +instance Event.FromEvent NameDeleted diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs index a575cb5a..786cbde7 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs @@ -20,6 +20,7 @@ import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address, addressFromBytes, addressToBytes) +import qualified Tendermint.Utils.Events as Event -------------------------------------------------------------------------------- @@ -96,7 +97,7 @@ instance FromJSON Faucetted where parseJSON = A.genericParseJSON faucettedAesonOptions instance BaseApp.ToEvent Faucetted where makeEventType _ = "Faucetted" -instance BaseApp.FromEvent Faucetted +instance Event.FromEvent Faucetted data TransferEvent = TransferEvent { transferEventAmount :: Amount @@ -116,4 +117,4 @@ instance A.FromJSON TransferEvent where instance BaseApp.ToEvent TransferEvent where makeEventType _ = "TransferEvent" -instance BaseApp.FromEvent TransferEvent +instance Event.FromEvent TransferEvent diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index e697e9eb..ad108060 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,61 +1,39 @@ module Nameservice.Test.E2ESpec (spec) where -import Control.Lens ((^.)) -import Crypto.Secp256k1 (CompactRecSig (..), - SecKey, derivePubKey, - exportCompactRecSig, - secKey) -import Data.Aeson (ToJSON) -import Data.Aeson.Encode.Pretty (encodePretty) -import qualified Data.ByteArray.Base64String as Base64 -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString, snoc) -import qualified Data.ByteString as BS -import qualified Data.ByteString.Lazy as BL -import Data.ByteString.Short (fromShort) -import Data.Default.Class (def) -import Data.Either (partitionEithers) -import Data.Maybe (fromJust) +import Control.Lens ((^.)) +import Data.Default.Class (def) import Data.Proxy -import Data.String (fromString) -import Data.String.Conversions (cs) -import Data.Text (Text) -import Data.Word (Word32) -import Nameservice.Modules.Nameservice (BuyName (..), - DeleteName (..), - Name (..), - NameClaimed (..), - NameDeleted (..), - NameRemapped (..), - SetName (..), - Whois (..)) -import qualified Nameservice.Modules.Nameservice as N (Api) -import Nameservice.Modules.Token (Amount (..), - FaucetAccount (..), - Faucetted (..), - Transfer (..), - TransferEvent (..)) -import qualified Nameservice.Modules.Token as T (Api) -import Nameservice.Modules.TypedMessage (TypedMessage (..)) -import Network.ABCI.Types.Messages.FieldTypes (Event (..)) -import qualified Network.ABCI.Types.Messages.Response as Response -import qualified Network.Tendermint.Client as RPC -import Proto3.Suite (Message, - toLazyByteString) -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp (FromEvent (..), - QueryApi) -import Tendermint.SDK.BaseApp.Query (QueryArgs (..), - defaultQueryWithData) -import Tendermint.SDK.BaseApp.Query.Client (ClientResponse (..), - HasClient (..), - RunClient (..)) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (Secp256k1, - addressFromPubKey) -import Tendermint.SDK.Types.Address (Address (..)) -import Tendermint.SDK.Types.Transaction (RawTransaction (..), - signRawTransaction) +import Nameservice.Modules.Nameservice (BuyName (..), + DeleteName (..), + Name (..), + NameClaimed (..), + NameDeleted (..), + NameRemapped (..), + SetName (..), Whois (..)) +import qualified Nameservice.Modules.Nameservice as N (Api) +import Nameservice.Modules.Token (Amount (..), + FaucetAccount (..), + Faucetted (..), + Transfer (..), + TransferEvent (..)) +import qualified Nameservice.Modules.Token as T (Api) +import Nameservice.Modules.TypedMessage (TypedMessage (..)) +import qualified Network.ABCI.Types.Messages.Response as Response +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + defaultQueryWithData) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.Utils.Client (ClientResponse (..), + HasClient (..)) +import Tendermint.Utils.Request (ensureCheckAndDeliverResponseCodes, + getDeliverTxResponse, + getQueryResponseSuccess, + runRPC) +import Tendermint.Utils.Response (ensureDeliverResponseCode, + ensureEventLogged) +import Tendermint.Utils.User (User (..), makeUser, mkSignedRawTransactionWithRoute) import Test.Hspec spec :: Spec @@ -121,8 +99,7 @@ spec = do ensureCheckAndDeliverResponseCodes (0,2) rawTx it "Can buy an existing name (success 0)" $ do - let oldVal = "goodbye to a world" - newVal = "hello (again) world" + let newVal = "hello (again) world" msg = TypedMessage "BuyName" (encode $ BuyName 300 satoshi newVal addr2) claimedLog = NameClaimed addr2 satoshi newVal 300 rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg @@ -204,15 +181,14 @@ spec = do senderAfterFoundAmount <- getQueryResponseSuccess $ getBalance senderAfterQueryReq senderAfterFoundAmount `shouldBe` Amount 1200 -runRPC :: forall a. RPC.TendermintM a -> IO a -runRPC = RPC.runTendermintM rpcConfig - where - rpcConfig :: RPC.Config - rpcConfig = - let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 - prettyPrint :: forall b. ToJSON b => String -> b -> IO () - prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) - in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") +-------------------------------------------------------------------------------- +user1 :: User +user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" + +user2 :: User +user2 = makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" + +-------------------------------------------------------------------------------- faucetAccount :: User -> IO () faucetAccount User{userAddress, userPrivKey} = do @@ -223,107 +199,6 @@ faucetAccount User{userAddress, userPrivKey} = do ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "Faucetted" faucetEvent --- executes a query and ensures a 0 response code -getQueryResponseSuccess :: RPC.TendermintM (ClientResponse a) -> IO a -getQueryResponseSuccess query = do - ClientResponse{clientResponseData,clientResponseRaw} <- runRPC query - let responseCode = clientResponseRaw ^. Response._queryCode - responseCode `shouldBe` 0 - return . fromJust $ clientResponseData - --- executes a request, then returns the checkTx response -getCheckTxResponse :: RawTransaction -> IO Response.CheckTx -getCheckTxResponse rawTx = do - let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - fmap RPC.resultBroadcastTxCommitCheckTx . runRPC $ - RPC.broadcastTxCommit txReq - --- executes a request, then returns the deliverTx response -getDeliverTxResponse :: RawTransaction -> IO Response.DeliverTx -getDeliverTxResponse rawTx = do - let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ - RPC.broadcastTxCommit txReq - -ensureCheckAndDeliverResponseCodes :: (Word32, Word32) -> RawTransaction -> IO () -ensureCheckAndDeliverResponseCodes codes rawTx = do - let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - resp <- runRPC $ RPC.broadcastTxCommit txReq - let checkResp = RPC.resultBroadcastTxCommitCheckTx resp - deliverResp = RPC.resultBroadcastTxCommitDeliverTx resp - codes `shouldBe` (checkResp ^. Response._checkTxCode, deliverResp ^. Response._deliverTxCode) - - --- get the logged events from a deliver response, -deliverTxEvents :: FromEvent e => Response.DeliverTx -> Text -> IO ([Text],[e]) -deliverTxEvents deliverResp eventName = do - let deliverEvents = deliverResp ^. Response._deliverTxEvents - filtered = filter ((== eventName) . eventType) deliverEvents - return . partitionEithers . map fromEvent $ filtered - --- ensures there are no errors when parsing event logs and contains the expectedEvent -ensureEventLogged :: (Eq e, Show e, FromEvent e) => Response.DeliverTx -> Text -> e -> IO () -ensureEventLogged deliverResp eventName expectedEvent = do - (errs, events) <- deliverTxEvents deliverResp eventName - errs `shouldBe` mempty - events `shouldSatisfy` elem expectedEvent - --- check for a specific check response code -ensureCheckResponseCode :: Response.CheckTx -> Word32 -> IO () -ensureCheckResponseCode checkResp code = do - let checkRespCode = checkResp ^. Response._checkTxCode - checkRespCode `shouldBe` code - --- check for a specific deliver response code -ensureDeliverResponseCode :: Response.DeliverTx -> Word32 -> IO () -ensureDeliverResponseCode deliverResp code = do - let deliverRespCode = deliverResp ^. Response._deliverTxCode - deliverRespCode `shouldBe` code - --------------------------------------------------------------------------------- - -decodeValue :: HasCodec a => Base64.Base64String -> a -decodeValue = (\(Right a) -> a) . decode . Base64.toBytes - -encodeRawTx :: RawTransaction -> Base64.Base64String -encodeRawTx = Base64.fromBytes . encode - -encodeMsgData :: Message a => a -> BS.ByteString -encodeMsgData = BL.toStrict . toLazyByteString - --- sign a trx with a user's private key -mkSignedRawTransactionWithRoute :: Message a => BS.ByteString -> SecKey -> a -> RawTransaction -mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned - where unsigned = RawTransaction { rawTransactionData = encodeMsgData msg - , rawTransactionRoute = cs route - , rawTransactionSignature = "" - } - sig = signRawTransaction algProxy privateKey unsigned - sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } - -data User = User - { userPrivKey :: SecKey - , userAddress :: Address - } - -user1 :: User -user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" - -user2 :: User -user2 = makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" - -makeUser :: String -> User -makeUser privKeyStr = - let privateKey = fromJust . secKey . Hex.toBytes . fromString $ privKeyStr - pubKey = derivePubKey privateKey - address = addressFromPubKey (Proxy @Secp256k1) pubKey - in User privateKey address - -algProxy :: Proxy Secp256k1 -algProxy = Proxy - --------------------------------------------------------------------------------- - getWhois :: QueryArgs Name -> RPC.TendermintM (ClientResponse Whois) getBalance :: QueryArgs Address -> RPC.TendermintM (ClientResponse Amount) @@ -332,6 +207,3 @@ apiP = Proxy (getBalance :<|> getWhois) = genClient (Proxy :: Proxy RPC.TendermintM) apiP def - -encodeCompactRecSig :: CompactRecSig -> ByteString -encodeCompactRecSig (CompactRecSig r s v) = snoc (fromShort r <> fromShort s) v diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index 722466ed..51eb52d5 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -50,6 +50,11 @@ library: - -fplugin=Polysemy.Plugin - -Werror - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-record-updates + - -Wincomplete-uni-patterns + - -Wredundant-constraints dependencies: - aeson - base >= 4.7 && < 5 @@ -126,19 +131,13 @@ tests: - -rtsopts - -with-rtsopts=-N dependencies: - - aeson - - aeson-pretty - base >= 4.7 && < 5 - - bytestring - - cereal - data-default-class - hs-abci-sdk - simple-storage - hs-abci-types + - hs-abci-test-utils - hs-tendermint-client - hspec - lens - - mtl - - secp256k1-haskell - servant - - string-conversions diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index a1a752f4..30d59dea 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -1,37 +1,21 @@ module SimpleStorage.Test.E2ESpec (spec) where import Control.Lens ((^.)) -import Control.Monad.Reader (ReaderT) -import Crypto.Secp256k1 (SecKey, - exportCompactRecSig, - secKey) -import Data.Aeson (ToJSON) -import Data.Aeson.Encode.Pretty (encodePretty) import qualified Data.ByteArray.Base64String as Base64 -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString) import Data.Default.Class (def) -import Data.Maybe (fromJust) import Data.Proxy -import qualified Data.Serialize as Serial -import Data.String (fromString) -import Data.String.Conversions (cs) -import qualified Network.ABCI.Types.Messages.Request as Req import qualified Network.ABCI.Types.Messages.Response as Resp import qualified Network.Tendermint.Client as RPC import Servant.API ((:>)) import qualified SimpleStorage.Modules.SimpleStorage as SS import Tendermint.SDK.BaseApp.Query (QueryArgs (..)) -import Tendermint.SDK.BaseApp.Query.Client (ClientResponse (..), - HasClient (..), - RunClient (..)) import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (Secp256k1) -import Tendermint.SDK.Types.Transaction (RawTransaction (..), - signRawTransaction) +import Tendermint.Utils.Client (ClientResponse (..), + HasClient (..)) +import Tendermint.Utils.Request (runRPC) +import Tendermint.Utils.User (User (..), makeUser, mkSignedRawTransactionWithRoute) import Test.Hspec - spec :: Spec spec = do describe "SimpleStorage E2E - via hs-tendermint-client" $ do @@ -51,7 +35,7 @@ spec = do it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do let txMsg = SS.UpdateCount $ SS.UpdateCountTx "irakli" 4 - tx = mkSignedRawTransaction (userPrivKey user1) txMsg + tx = mkSignedRawTransactionWithRoute "simple_storage" (userPrivKey user1) txMsg txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = Base64.fromBytes . encode $ tx } @@ -65,64 +49,15 @@ spec = do , queryArgsHeight = 0 , queryArgsProve = False } - ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq + ClientResponse{clientResponseData = Just foundCount} <- runRPC $ getCount queryReq foundCount `shouldBe` SS.Count 4 -runRPC :: forall a. RPC.TendermintM a -> IO a -runRPC = RPC.runTendermintM rpcConfig - where - rpcConfig :: RPC.Config - rpcConfig = - let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 - prettyPrint :: forall b. ToJSON b => String -> b -> IO () - prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) - in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") - -newtype QueryRunner a = QueryRunner - {_runQueryRunner :: ReaderT RPC.Config IO a} - deriving (Functor, Applicative, Monad) - -runQueryRunner :: QueryRunner a -> IO a -runQueryRunner = runRPC . _runQueryRunner - -instance RunClient QueryRunner where - runQuery Req.Query{..} = - let rpcQ = RPC.RequestABCIQuery - { RPC.requestABCIQueryPath = Just queryPath - , RPC.requestABCIQueryData = Hex.fromBytes @ByteString . Base64.toBytes $ queryData - , RPC.requestABCIQueryHeight = Just $ queryHeight - , RPC.requestABCIQueryProve = queryProve - } - in RPC.resultABCIQueryResponse <$> QueryRunner (RPC.abciQuery rpcQ) - -------------------------------------------------------------------------------- -getCount :: QueryArgs SS.CountKey -> QueryRunner (ClientResponse SS.Count) +getCount :: QueryArgs SS.CountKey -> RPC.TendermintM (ClientResponse SS.Count) getCount = let apiP = Proxy :: Proxy ("simple_storage" :> SS.Api) - in genClient (Proxy :: Proxy QueryRunner) apiP def - --- sign a tx with a user's private key -mkSignedRawTransaction :: SecKey -> SS.SimpleStorageMessage -> RawTransaction -mkSignedRawTransaction privateKey msg = sign unsigned - where unsigned = RawTransaction { rawTransactionData = encode msg - , rawTransactionRoute = "simple_storage" - , rawTransactionSignature = "" - } - sig = signRawTransaction algProxy privateKey unsigned - sign rt = rt { rawTransactionSignature = Serial.encode $ exportCompactRecSig sig } - -data User = User - { userPrivKey :: SecKey - } + in genClient (Proxy :: Proxy RPC.TendermintM) apiP def user1 :: User user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" - -makeUser :: String -> User -makeUser privKeyStr = - let privateKey = fromJust . secKey . Hex.toBytes . fromString $ privKeyStr - in User privateKey - -algProxy :: Proxy Secp256k1 -algProxy = Proxy diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 184c42b3..12528619 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -59,7 +59,6 @@ library: - errors - hs-abci-server - hs-abci-types - - hs-tendermint-client - http-types - katip - lens @@ -105,7 +104,6 @@ library: - Tendermint.SDK.BaseApp.Metrics.Prometheus - Tendermint.SDK.BaseApp.Query - Tendermint.SDK.BaseApp.Query.Class - - Tendermint.SDK.BaseApp.Query.Client - Tendermint.SDK.BaseApp.Query.Delayed - Tendermint.SDK.BaseApp.Query.Router - Tendermint.SDK.BaseApp.Query.Store @@ -135,7 +133,6 @@ tests: other-modules: - Tendermint.SDK.Test.AuthTreeStoreSpec - Tendermint.SDK.Test.CryptoSpec - - Tendermint.SDK.Test.EventSpec - Tendermint.SDK.Test.GasSpec - Tendermint.SDK.Test.MetricsSpec @@ -148,7 +145,6 @@ tests: - -with-rtsopts=-N dependencies: - - aeson - base >= 4.7 && < 5 - bytestring - containers diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 2a12ffda..685daea0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -34,7 +34,6 @@ module Tendermint.SDK.BaseApp -- * Events , Event(..) - , FromEvent(..) , ToEvent(..) , emit diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index 956308c9..1c54f3c1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -1,7 +1,6 @@ module Tendermint.SDK.BaseApp.Events ( Event(..) , ToEvent(..) - , FromEvent(..) , emit , makeEvent , EventBuffer @@ -11,7 +10,6 @@ module Tendermint.SDK.BaseApp.Events ) where import qualified Control.Concurrent.MVar as MVar -import Control.Error (fmapL) import Control.Monad (void) import Control.Monad.IO.Class import qualified Data.Aeson as A @@ -21,8 +19,7 @@ import qualified Data.ByteString as BS import qualified Data.List as L import Data.Proxy import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.Exts (fromList, toList) +import GHC.Exts (toList) import Network.ABCI.Types.Messages.FieldTypes (Event (..), KVPair (..)) import Polysemy (Embed, Member, Sem, @@ -47,29 +44,6 @@ class ToEvent e where A.Object obj -> bimap cs (cs . A.encode) <$> toList obj _ -> mempty --- data LoggableEvent a = LoggableEvent a --- logEvent :: - --- | A class that can parse event log items in the deliverTx response. Primarily --- | useful for client applications and testing. -class ToEvent e => FromEvent e where - fromEvent :: Event -> Either Text e - - default fromEvent :: A.FromJSON e => Event -> Either Text e - fromEvent Event{eventType, eventAttributes} = - let expectedType = makeEventType (Proxy @e) - in if cs eventType /= expectedType - then fail ("Couldn't match expected event type " <> expectedType <> - " with found type " <> cs eventType) - else - let fromKVPair :: KVPair -> Either String (Text, A.Value) - fromKVPair (KVPair k v) = do - value <- A.eitherDecode . cs @BS.ByteString . Base64.toBytes $ v - return (cs @BS.ByteString . Base64.toBytes $ k, value) - in fmapL cs $ do - kvPairs <- traverse fromKVPair eventAttributes - A.eitherDecode . A.encode . A.Object . fromList $ kvPairs - -- This is the internal implementation of the interpreter for event -- logging. We allocate a buffer that can queue events as they are thrown, -- then flush the buffer at the end of transaction execution. It will diff --git a/hs-abci-test-utils/README.md b/hs-abci-test-utils/README.md new file mode 100644 index 00000000..4264d308 --- /dev/null +++ b/hs-abci-test-utils/README.md @@ -0,0 +1,11 @@ +# hs-abci-test-utils + +Utils for apps and tests. + +Includes the following: + +* `Client.hs` - Client interface for parsing ABCI responses +* `Events.hs` - Interface for parsing loggable events +* `Request.hs` - Test utils for executing requests/queries +* `Response.hs` - Test utils for checking response codes and event logs +* `User.hs` - Test utils for creating users and signed transactions diff --git a/hs-abci-test-utils/package.yaml b/hs-abci-test-utils/package.yaml new file mode 100644 index 00000000..1cc15008 --- /dev/null +++ b/hs-abci-test-utils/package.yaml @@ -0,0 +1,76 @@ +name: hs-abci-test-utils +version: 0.1.0.0 +github: "f-o-a-m/hs-abci/hs-abci-test-utils" +license: BSD3 +author: "Author name here" +maintainer: "example@example.com" +copyright: "2019 Author name here" + +extra-source-files: +- README.md + +# Metadata used when publishing your package +# synopsis: Short description of your package +# category: Web + +# To avoid duplicated efforts in documentation and dealing with the +# complications of embedding Haddock markup inside cabal files, it is +# common to point users to the README.md file. +description: Please see the README on GitHub at + +default-extensions: + - DefaultSignatures + - DeriveGeneric + - FlexibleInstances + - MultiParamTypeClasses + - NamedFieldPuns + - OverloadedStrings + - TypeApplications + - TypeFamilies + - RecordWildCards + - ScopedTypeVariables + - TypeOperators + +library: + source-dirs: src + ghc-options: + - -Werror + - -Wall + - -Wcompat + - -Widentities + - -Wincomplete-uni-patterns + - -Wredundant-constraints + dependencies: + - aeson + - aeson-pretty + - base >= 4.7 && < 5 + - bytestring + - errors + - hspec + - lens + - mtl + - secp256k1-haskell + - servant + - string-conversions + - text + - hs-abci-types + - hs-abci-sdk + - hs-tendermint-client + +tests: + hs-abci-test-utils-test: + main: Spec.hs + source-dirs: test + ghc-options: + - -Werror + - -Wall + - -threaded + - -rtsopts + - -with-rtsopts=-N + dependencies: + - aeson + - base >= 4.7 && < 5 + - hs-abci-sdk + - hs-abci-test-utils + - hspec + - hspec-core diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Client.hs b/hs-abci-test-utils/src/Tendermint/Utils/Client.hs similarity index 98% rename from hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Client.hs rename to hs-abci-test-utils/src/Tendermint/Utils/Client.hs index 466e84b9..d076a9fd 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Client.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/Client.hs @@ -1,6 +1,6 @@ {-# LANGUAGE UndecidableInstances #-} -module Tendermint.SDK.BaseApp.Query.Client +module Tendermint.Utils.Client ( RunClient(..) , HasClient(..) , ClientResponse(..) diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Events.hs b/hs-abci-test-utils/src/Tendermint/Utils/Events.hs new file mode 100644 index 00000000..4f4e9cd3 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/Events.hs @@ -0,0 +1,33 @@ +module Tendermint.Utils.Events where + +import Control.Error (fmapL) +import qualified Data.Aeson as A +import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteString as BS +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Exts (fromList) +import Network.ABCI.Types.Messages.FieldTypes (Event (..), + KVPair (..)) +import Tendermint.SDK.BaseApp.Events (ToEvent, makeEventType) + +-- | A class that can parse event log items in the deliverTx response. Primarily +-- | useful for client applications and testing. +class ToEvent e => FromEvent e where + fromEvent :: Event -> Either Text e + + default fromEvent :: A.FromJSON e => Event -> Either Text e + fromEvent Event{eventType, eventAttributes} = + let expectedType = makeEventType (Proxy @e) + in if cs eventType /= expectedType + then fail ("Couldn't match expected event type " <> expectedType <> + " with found type " <> cs eventType) + else + let fromKVPair :: KVPair -> Either String (Text, A.Value) + fromKVPair (KVPair k v) = do + value <- A.eitherDecode . cs @BS.ByteString . Base64.toBytes $ v + return (cs @BS.ByteString . Base64.toBytes $ k, value) + in fmapL cs $ do + kvPairs <- traverse fromKVPair eventAttributes + A.eitherDecode . A.encode . A.Object . fromList $ kvPairs diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Request.hs b/hs-abci-test-utils/src/Tendermint/Utils/Request.hs new file mode 100644 index 00000000..b2e61461 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/Request.hs @@ -0,0 +1,59 @@ +module Tendermint.Utils.Request where + +import Control.Lens ((^.)) +import Data.Aeson (ToJSON) +import Data.Aeson.Encode.Pretty (encodePretty) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Maybe (fromJust) +import Data.String.Conversions (cs) +import Data.Word (Word32) +import qualified Network.ABCI.Types.Messages.Response as Response +import qualified Network.Tendermint.Client as RPC +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..)) +import Tendermint.Utils.Client (ClientResponse (..)) +import Test.Hspec + +runRPC :: forall a. RPC.TendermintM a -> IO a +runRPC = RPC.runTendermintM rpcConfig + where + rpcConfig :: RPC.Config + rpcConfig = + let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 + prettyPrint :: forall b. ToJSON b => String -> b -> IO () + prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) + in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") + +-- executes a query and ensures a 0 response code +getQueryResponseSuccess :: RPC.TendermintM (ClientResponse a) -> IO a +getQueryResponseSuccess query = do + ClientResponse{clientResponseData,clientResponseRaw} <- runRPC query + let responseCode = clientResponseRaw ^. Response._queryCode + responseCode `shouldBe` 0 + return . fromJust $ clientResponseData + +-- executes a request, then returns the checkTx response +getCheckTxResponse :: RawTransaction -> IO Response.CheckTx +getCheckTxResponse rawTx = do + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + fmap RPC.resultBroadcastTxCommitCheckTx . runRPC $ + RPC.broadcastTxCommit txReq + +-- executes a request, then returns the deliverTx response +getDeliverTxResponse :: RawTransaction -> IO Response.DeliverTx +getDeliverTxResponse rawTx = do + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ + RPC.broadcastTxCommit txReq + +-- executes a request, check deliver and response codes +ensureCheckAndDeliverResponseCodes :: (Word32, Word32) -> RawTransaction -> IO () +ensureCheckAndDeliverResponseCodes codes rawTx = do + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } + resp <- runRPC $ RPC.broadcastTxCommit txReq + let checkResp = RPC.resultBroadcastTxCommitCheckTx resp + deliverResp = RPC.resultBroadcastTxCommitDeliverTx resp + codes `shouldBe` (checkResp ^. Response._checkTxCode, deliverResp ^. Response._deliverTxCode) + +encodeRawTx :: RawTransaction -> Base64.Base64String +encodeRawTx = Base64.fromBytes . encode diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Response.hs b/hs-abci-test-utils/src/Tendermint/Utils/Response.hs new file mode 100644 index 00000000..21aad542 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/Response.hs @@ -0,0 +1,36 @@ +module Tendermint.Utils.Response where + +import Control.Lens ((^.)) +import Data.Either (partitionEithers) +import Data.Text (Text) +import Data.Word (Word32) +import Network.ABCI.Types.Messages.FieldTypes (Event (..)) +import qualified Network.ABCI.Types.Messages.Response as Response +import Tendermint.Utils.Events (FromEvent (..)) +import Test.Hspec + +-- get the logged events from a deliver response, +deliverTxEvents :: FromEvent e => Response.DeliverTx -> Text -> IO ([Text],[e]) +deliverTxEvents deliverResp eventName = do + let deliverEvents = deliverResp ^. Response._deliverTxEvents + filtered = filter ((== eventName) . eventType) deliverEvents + return . partitionEithers . map fromEvent $ filtered + +-- ensures there are no errors when parsing event logs and contains the expectedEvent +ensureEventLogged :: (Eq e, Show e, FromEvent e) => Response.DeliverTx -> Text -> e -> IO () +ensureEventLogged deliverResp eventName expectedEvent = do + (errs, events) <- deliverTxEvents deliverResp eventName + errs `shouldBe` mempty + events `shouldSatisfy` elem expectedEvent + +-- check for a specific check response code +ensureCheckResponseCode :: Response.CheckTx -> Word32 -> IO () +ensureCheckResponseCode checkResp code = do + let checkRespCode = checkResp ^. Response._checkTxCode + checkRespCode `shouldBe` code + +-- check for a specific deliver response code +ensureDeliverResponseCode :: Response.DeliverTx -> Word32 -> IO () +ensureDeliverResponseCode deliverResp code = do + let deliverRespCode = deliverResp ^. Response._deliverTxCode + deliverRespCode `shouldBe` code diff --git a/hs-abci-test-utils/src/Tendermint/Utils/User.hs b/hs-abci-test-utils/src/Tendermint/Utils/User.hs new file mode 100644 index 00000000..4564fdff --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/User.hs @@ -0,0 +1,46 @@ +module Tendermint.Utils.User where + +import Crypto.Secp256k1 (CompactRecSig (..), SecKey, + derivePubKey, + exportCompactRecSig, secKey) +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString, snoc) +import qualified Data.ByteString as BS +import Data.ByteString.Short (fromShort) +import Data.Maybe (fromJust) +import Data.Proxy +import Data.String (fromString) +import Data.String.Conversions (cs) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (Secp256k1, addressFromPubKey) +import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..), + signRawTransaction) + +data User = User + { userPrivKey :: SecKey + , userAddress :: Address + } + +makeUser :: String -> User +makeUser privKeyStr = + let privateKey = fromJust . secKey . Hex.toBytes . fromString $ privKeyStr + pubKey = derivePubKey privateKey + address = addressFromPubKey (Proxy @Secp256k1) pubKey + in User privateKey address + +algProxy :: Proxy Secp256k1 +algProxy = Proxy + +-- sign a trx with a user's private key +mkSignedRawTransactionWithRoute :: HasCodec a => BS.ByteString -> SecKey -> a -> RawTransaction +mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned + where unsigned = RawTransaction { rawTransactionData = encode msg + , rawTransactionRoute = cs route + , rawTransactionSignature = "" + } + sig = signRawTransaction algProxy privateKey unsigned + sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } + +encodeCompactRecSig :: CompactRecSig -> ByteString +encodeCompactRecSig (CompactRecSig r s v) = snoc (fromShort r <> fromShort s) v diff --git a/hs-abci-test-utils/test/Spec.hs b/hs-abci-test-utils/test/Spec.hs new file mode 100644 index 00000000..fcb16768 --- /dev/null +++ b/hs-abci-test-utils/test/Spec.hs @@ -0,0 +1,2 @@ +{-# OPTIONS_GHC -F -pgmF hspec-discover #-} +{-# OPTIONS_GHC -fno-warn-missing-import-lists #-} diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs b/hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs similarity index 78% rename from hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs rename to hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs index f5d7cb7d..bc2064a9 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/EventSpec.hs +++ b/hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs @@ -1,9 +1,9 @@ -module Tendermint.SDK.Test.EventSpec (spec) where +module Tendermint.Utils.Test.EventSpec (spec) where import qualified Data.Aeson as A import GHC.Generics (Generic) -import Tendermint.SDK.BaseApp.Events (FromEvent (..), ToEvent (..), - makeEvent) +import Tendermint.SDK.BaseApp.Events (ToEvent (..), makeEvent) +import Tendermint.Utils.Events (FromEvent (..)) import Test.Hspec spec :: Spec diff --git a/stack.yaml b/stack.yaml index 921f4fe2..f32754da 100644 --- a/stack.yaml +++ b/stack.yaml @@ -11,6 +11,7 @@ packages: - ./hs-abci-server - ./hs-abci-extra - ./hs-abci-sdk +- ./hs-abci-test-utils - ./hs-abci-examples/simple-storage - ./hs-abci-examples/nameservice - ./hs-iavl-client From 9bd714db780e68cc34c1cba263319460263278f1 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Fri, 3 Jan 2020 12:00:38 -0800 Subject: [PATCH 30/70] Event buffer fix (#167) * transaction router now uses events, gas, and errors separately * update tests * fix e2e tes' t * fix metrics interpreteR * fix tutorial --- .../src/Nameservice/Modules/Nameservice.hs | 2 +- .../Nameservice/Modules/Nameservice/Router.hs | 3 +- .../src/Nameservice/Modules/Token.hs | 2 +- .../test/Nameservice/Test/E2ESpec.hs | 34 ++++--- .../tutorial/Foundations/Modules.md | 7 +- .../tutorial/Tutorial/Nameservice/Module.md | 2 +- .../SimpleStorage/Modules/SimpleStorage.hs | 2 +- .../Modules/SimpleStorage/Keeper.hs | 17 ++-- .../Modules/SimpleStorage/Router.hs | 13 +-- hs-abci-sdk/package.yaml | 3 +- hs-abci-sdk/protos/types/transaction.proto | 5 +- .../Tendermint/SDK/Application/Handlers.hs | 47 ++++----- .../src/Tendermint/SDK/Application/Module.hs | 95 ++++++++++--------- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 6 +- .../src/Tendermint/SDK/BaseApp/BaseApp.hs | 15 +-- .../src/Tendermint/SDK/BaseApp/CoreEff.hs | 12 +-- .../src/Tendermint/SDK/BaseApp/Errors.hs | 46 +++------ .../src/Tendermint/SDK/BaseApp/Events.hs | 58 +---------- hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs | 25 ++--- .../src/Tendermint/SDK/BaseApp/Metrics.hs | 2 +- .../SDK/BaseApp/Metrics/Prometheus.hs | 7 +- .../src/Tendermint/SDK/BaseApp/Transaction.hs | 74 +++++++++++++++ hs-abci-sdk/src/Tendermint/SDK/Codec.hs | 4 + .../src/Tendermint/SDK/Modules/Auth.hs | 2 +- .../src/Tendermint/SDK/Types/Effects.hs | 10 ++ .../src/Tendermint/SDK/Types/Transaction.hs | 6 ++ .../src/Tendermint/SDK/Types/TxResult.hs | 33 +++++-- .../test/Tendermint/SDK/Test/CryptoSpec.hs | 1 + .../test/Tendermint/SDK/Test/GasSpec.hs | 12 ++- .../src/Tendermint/Utils/User.hs | 1 + 30 files changed, 298 insertions(+), 248 deletions(-) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Types/Effects.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index 6541eaf8..c9cccbae 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -48,7 +48,7 @@ import Tendermint.SDK.Application (Module (..), import Tendermint.SDK.BaseApp (BaseAppEffs) type NameserviceM r = - Module "nameservice" NameserviceMessage Api NameserviceEffs r + Module "nameservice" NameserviceMessage () Api NameserviceEffs r nameserviceModule :: Members BaseAppEffs r diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 8b575f1e..848ce5c6 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -6,7 +6,7 @@ import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members, Sem) -import Tendermint.SDK.BaseApp (BaseAppEffs, +import Tendermint.SDK.BaseApp (BaseAppEffs, TxEffs, incCount, withTimer) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), @@ -16,6 +16,7 @@ router :: Members TokenEffs r => Members NameserviceEffs r => Members BaseAppEffs r + => Members TxEffs r => RoutedTx NameserviceMessage -> Sem r () router (RoutedTx Tx{txMsg}) = diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index a51fb3fc..c42d4b64 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -45,7 +45,7 @@ import Tendermint.SDK.Application (Module (..), import Tendermint.SDK.BaseApp (BaseAppEffs) import Tendermint.SDK.Types.Address (Address) -type TokenM r = Module "token" TokenMessage Api TokenEffs r +type TokenM r = Module "token" TokenMessage () Api TokenEffs r tokenModule :: Members BaseAppEffs r diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index ad108060..31202f12 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -159,27 +159,37 @@ spec = do clientResponseData `shouldBe` Nothing it "Can fail a transfer (failure 1)" $ do - let msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 2000) + let senderBeforeQueryReq = defaultQueryWithData addr2 + addr2Balance <- getQueryResponseSuccess $ getBalance senderBeforeQueryReq + let tooMuchToTransfer = addr2Balance + 1 + msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 tooMuchToTransfer) rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg ensureCheckAndDeliverResponseCodes (0,1) rawTx it "Can transfer (success 0)" $ do - let senderBeforeQueryReq = defaultQueryWithData addr2 - senderBeforeFoundAmount <- getQueryResponseSuccess $ getBalance senderBeforeQueryReq - senderBeforeFoundAmount `shouldBe` Amount 1700 - let msg = TypedMessage "Transfer" (encode $ Transfer addr1 addr2 500) - transferEvent = TransferEvent 500 addr1 addr2 + balance1 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 + balance2 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr2 + let transferAmount = 1 + msg = TypedMessage "Transfer" $ encode + Transfer + { transferFrom = addr1 + , transferTo = addr2 + , transferAmount = transferAmount + } + transferEvent = TransferEvent + { transferEventAmount = transferAmount + , transferEventTo = addr2 + , transferEventFrom = addr1 + } rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "TransferEvent" transferEvent -- check balances - let receiverQueryReq = defaultQueryWithData addr1 - receiverFoundAmount <- getQueryResponseSuccess $ getBalance receiverQueryReq - receiverFoundAmount `shouldBe` Amount 1800 - let senderAfterQueryReq = defaultQueryWithData addr2 - senderAfterFoundAmount <- getQueryResponseSuccess $ getBalance senderAfterQueryReq - senderAfterFoundAmount `shouldBe` Amount 1200 + balance1' <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 + balance1' `shouldBe` balance1 - transferAmount + balance2' <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr2 + balance2' `shouldBe` balance2 + transferAmount -------------------------------------------------------------------------------- user1 :: User diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md b/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md index c52a5689..2473573b 100644 --- a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md +++ b/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md @@ -5,9 +5,9 @@ A `Module` has a very specific meaning in the context of this SDK. A `Module` is something between a library and a small state machine. It is built on top of the `BaseApp` abstraction in the sense that all `Module`s must be explicitly interpeted in terms of `BaseApp` in order to compile the application. The full type definition is ~~~ haskell ignore -data Module (name :: Symbol) msg (api :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: RoutedTx msg -> Sem r () - , moduleTxChecker :: RoutedTx msg -> Sem r () +data Module (name :: Symbol) msg (api :: *) (val :: *) (s :: EffectRow) (r :: EffectRow) = Module + { moduleTxDeliverer :: RoutedTx msg -> Sem r val + , moduleTxChecker :: RoutedTx msg -> Sem r val , moduleQueryServer :: RouteT api (Sem r) , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } @@ -17,6 +17,7 @@ where the type parameters - `name` is the name of the module, e.g. `"bank"`. - `msg` is the type of the incoming messages the module must handle. +- `val` is the type of the return value (must be common across all messages the module receives). - `api` is the query api for querying state in the url format (more on this later). - `s` is the set of effects introduced by this module. - `r` is the global set of effects that this module will run in when part of a larger application (more on this later). diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md index 85c180e5..00a9622a 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md @@ -20,7 +20,7 @@ import Tendermint.SDK.BaseApp (BaseAppEffs) -- a convenient type alias type NameserviceM r = - Module NameserviceModuleName NameserviceMessage Api NameserviceEffs r + Module NameserviceModuleName NameserviceMessage () Api NameserviceEffs r nameserviceModule :: Members BaseAppEffs r diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index b284a43b..bfd694b4 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -18,7 +18,7 @@ import Tendermint.SDK.Application (Module (..), import qualified Tendermint.SDK.BaseApp as BaseApp type SimpleStorageM r = - Module "simple_storage" SimpleStorageMessage Api SimpleStorageEffs r + Module "simple_storage" SimpleStorageMessage () Api SimpleStorageEffs r simpleStorageModule :: Member SimpleStorage r diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs index 30465740..0339283a 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -3,7 +3,7 @@ module SimpleStorage.Modules.SimpleStorage.Keeper ( SimpleStorage , SimpleStorageEffs , storeKey - , putCount + , updateCount , getCount , eval ) where @@ -29,14 +29,19 @@ makeSem ''SimpleStorage type SimpleStorageEffs = '[SimpleStorage] +updateCount + :: Members '[SimpleStorage, Output BaseApp.Event] r + => Count + -> Sem r () +updateCount count = do + putCount count + BaseApp.emit $ CountSet count + eval :: forall r. - Members '[BaseApp.RawStore, Output BaseApp.Event, Error BaseApp.AppError] r + Members '[BaseApp.RawStore, Error BaseApp.AppError] r => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) eval = interpret (\case - PutCount count -> do - BaseApp.put storeKey CountKey count - BaseApp.emit $ CountSet count - + PutCount count -> BaseApp.put storeKey CountKey count GetCount -> fromJust <$> BaseApp.get storeKey CountKey ) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs index cc066094..aaab2442 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -2,24 +2,25 @@ module SimpleStorage.Modules.SimpleStorage.Router ( router ) where -import Polysemy (Members, Sem) -import Polysemy.Output (Output) +import Polysemy (Member, Members, + Sem) import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, - putCount) + updateCount) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) -import Tendermint.SDK.BaseApp (Event) +import Tendermint.SDK.BaseApp (TxEffs) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) router - :: Members [SimpleStorage, Output Event] r + :: Member SimpleStorage r + => Members TxEffs r => RoutedTx SimpleStorageMessage -> Sem r () router (RoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of UpdateCount UpdateCountTx{updateCountTxCount} -> - putCount (Count updateCountTxCount) + updateCount (Count updateCountTxCount) diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 12528619..0e8db62d 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -115,9 +115,10 @@ library: - Tendermint.SDK.Codec - Tendermint.SDK.Crypto - Tendermint.SDK.Modules.Auth + - Tendermint.SDK.Types.Address + - Tendermint.SDK.Types.Effects - Tendermint.SDK.Types.Message - Tendermint.SDK.Types.Transaction - - Tendermint.SDK.Types.Address - Tendermint.SDK.Types.TxResult generated-exposed-modules: diff --git a/hs-abci-sdk/protos/types/transaction.proto b/hs-abci-sdk/protos/types/transaction.proto index e810079a..5c195d49 100644 --- a/hs-abci-sdk/protos/types/transaction.proto +++ b/hs-abci-sdk/protos/types/transaction.proto @@ -3,6 +3,7 @@ package Transaction; message RawTransaction { bytes data = 1; - bytes signature = 2; - string route = 3; + int64 gas = 2; + bytes signature = 3; + string route = 4; } \ No newline at end of file diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index 696ebf06..f6a6bd96 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -4,7 +4,8 @@ module Tendermint.SDK.Application.Handlers , makeApp ) where -import Control.Lens (to, (&), (.~), (^.)) +import Control.Lens (from, to, (&), (.~), + (^.)) import Crypto.Hash (Digest) import Crypto.Hash.Algorithms (SHA256) import qualified Data.ByteArray.Base64String as Base64 @@ -21,19 +22,15 @@ import Polysemy.Error (Error, catch) import qualified Tendermint.SDK.Application.Module as M import qualified Tendermint.SDK.BaseApp.BaseApp as BA import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) -import Tendermint.SDK.BaseApp.Errors (AppError, - checkTxAppError, - deliverTxAppError, - queryAppError) -import Tendermint.SDK.BaseApp.Events (withEventBuffer) +import Tendermint.SDK.BaseApp.Errors (AppError, queryAppError, + txResultAppError) import Tendermint.SDK.BaseApp.Query (HasRouter) import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) import qualified Tendermint.SDK.BaseApp.Store as Store import Tendermint.SDK.Crypto (RecoverableSignatureSchema, SignatureSchema (..)) import Tendermint.SDK.Types.TxResult (checkTxTxResult, - deliverTxTxResult, - txResultEvents) + deliverTxTxResult) type Handler mt r = Request mt -> Sem r (Response mt) @@ -93,13 +90,9 @@ makeHandlers HandlersContext{..} = let compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a compileToBaseApp = M.eval modules - txRouter context = M.txRouter signatureAlgP context modules + txRouter context = compileToBaseApp . M.txRouter signatureAlgP context modules queryRouter = compileToBaseApp . M.queryRouter modules - transactionHandler router bs = do - events <- withEventBuffer . compileToBaseApp $ router bs - pure $ def & txResultEvents .~ events - query (RequestQuery q) = Store.applyScope $ catch (do @@ -112,27 +105,25 @@ makeHandlers HandlersContext{..} = beginBlock _ = Store.applyScope (def <$ Store.beginBlock) - checkTx (RequestCheckTx _checkTx) = Store.applyScope $ - catch - (do - txResult <- transactionHandler (txRouter M.CheckTxContext) $ - _checkTx ^. Req._checkTxTx . to Base64.toBytes - return $ ResponseCheckTx $ def & checkTxTxResult .~ txResult + checkTx (RequestCheckTx _checkTx) = Store.applyScope $ do + res <- catch + ( let txBytes = _checkTx ^. Req._checkTxTx . to Base64.toBytes + in txRouter M.CheckTxContext txBytes ) (\(err :: AppError) -> - return . ResponseCheckTx $ def & checkTxAppError .~ err + return $ def & txResultAppError .~ err ) + return . ResponseCheckTx $ res ^. from checkTxTxResult - deliverTx (RequestDeliverTx _deliverTx) = Store.applyScope $ - catch @AppError - (do - txResult <- transactionHandler (txRouter M.DeliverTxContext) $ - _deliverTx ^. Req._deliverTxTx . to Base64.toBytes - return $ ResponseDeliverTx $ def & deliverTxTxResult .~ txResult + deliverTx (RequestDeliverTx _deliverTx) = Store.applyScope $ do + res <- catch @AppError + ( let txBytes = _deliverTx ^. Req._deliverTxTx . to Base64.toBytes + in txRouter M.DeliverTxContext txBytes ) (\(err :: AppError) -> - return . ResponseDeliverTx $ def & deliverTxAppError .~ err + return $ def & txResultAppError .~ err ) + return . ResponseDeliverTx $ res ^. from deliverTxTxResult commit :: Handler 'MTCommit (BA.ScopedBaseApp 'Consensus core) commit _ = Store.applyScope $ do @@ -152,7 +143,7 @@ makeHandlers HandlersContext{..} = makeApp :: forall alg ms r core. - Member (Error AppError) r + Members [Error AppError, Embed IO] r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => M.TxRouter ms r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 93a5a58b..934d76c0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -6,47 +6,50 @@ module Tendermint.SDK.Application.Module , Modules(..) , QueryRouter(Api) , queryRouter - , TxRouteContext(..) + , RoutingContext(..) , TxRouter , txRouter , voidRouter , Eval(..) ) where -import Crypto.Hash (Digest) -import Crypto.Hash.Algorithms (SHA256) -import Data.ByteString (ByteString) +import Control.Monad.IO.Class (liftIO) +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import Data.ByteString (ByteString) import Data.Proxy -import Data.String.Conversions (cs) -import qualified Data.Validation as V +import Data.String.Conversions (cs) +import qualified Data.Validation as V import Data.Void -import GHC.TypeLits (KnownSymbol, Symbol, - symbolVal) -import Polysemy (EffectRow, Member, Members, - Sem) -import Polysemy.Error (Error) -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp ((:&), AppError, BaseApp, - BaseAppEffs, SDKError (..), - throwSDKError) -import qualified Tendermint.SDK.BaseApp.Query as Q -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (RecoverableSignatureSchema, - SignatureSchema (..)) -import Tendermint.SDK.Types.Message (Msg (..), - ValidateMessage (..), - formatMessageSemanticError) -import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..), - parseTx) - -data Module (name :: Symbol) msg (api :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: RoutedTx msg -> Sem r () - , moduleTxChecker :: RoutedTx msg -> Sem r () +import GHC.TypeLits (KnownSymbol, Symbol, + symbolVal) +import Polysemy (EffectRow, Embed, Member, + Members, Sem) +import Polysemy.Error (Error) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp ((:&), AppError, BaseApp, + BaseAppEffs, SDKError (..), + throwSDKError) +import qualified Tendermint.SDK.BaseApp.Query as Q +import qualified Tendermint.SDK.BaseApp.Transaction as T +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (RecoverableSignatureSchema, + SignatureSchema (..)) +import Tendermint.SDK.Types.Message (Msg (..), + ValidateMessage (..), + formatMessageSemanticError) +import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..), + parseTx) +import Tendermint.SDK.Types.TxResult (TxResult) + +data Module (name :: Symbol) msg val (api :: *) (s :: EffectRow) (r :: EffectRow) = Module + { moduleTxDeliverer :: RoutedTx msg -> Sem (T.TxEffs :& r) val + , moduleTxChecker :: RoutedTx msg -> Sem (T.TxEffs :& r) val , moduleQueryServer :: Q.RouteT api (Sem r) , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } -voidModuleMessages :: Module name msg api s r -> Module name Void api s r +voidModuleMessages :: Module name msg val api s r -> Module name Void Void api s r voidModuleMessages m = m { moduleTxDeliverer = voidRouter , moduleTxChecker = voidRouter @@ -65,7 +68,7 @@ defaultTxChecker (RoutedTx Tx{txMsg}) = data Modules (ms :: [*]) r where NilModules :: Modules '[] r - (:+) :: Module name msg api s r -> Modules ms r -> Modules (Module name msg api s r ': ms) r + (:+) :: Module name msg val api s r -> Modules ms r -> Modules (Module name msg val api s r ': ms) r infixr 5 :+ @@ -82,18 +85,17 @@ class QueryRouter ms r where type Api ms :: * routeQuery :: Modules ms r -> Q.RouteT (Api ms) (Sem r) -instance QueryRouter '[Module name msg api s r] r where - type Api '[Module name msg api s r] = name :> api +instance QueryRouter '[Module name msg val api s r] r where + type Api '[Module name msg val api s r] = name :> api routeQuery (m :+ NilModules) = moduleQueryServer m -instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg api s r ': m' ': ms) r where - type Api (Module name msg api s r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) +instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg val api s r ': m' ': ms) r where + type Api (Module name msg val api s r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) routeQuery (m :+ rest) = moduleQueryServer m :<|> routeQuery rest -------------------------------------------------------------------------------- -data TxRouteContext = - CheckTxContext | DeliverTxContext +data RoutingContext = CheckTxContext | DeliverTxContext txRouter :: forall alg ms r . @@ -102,10 +104,10 @@ txRouter => Message alg ~ Digest SHA256 => TxRouter ms r => Proxy alg - -> TxRouteContext + -> RoutingContext -> Modules ms r -> ByteString - -> Sem r () + -> Sem r TxResult txRouter (p :: Proxy alg) routeContext ms bs = let etx = decode bs >>= parseTx p in case etx of @@ -113,18 +115,18 @@ txRouter (p :: Proxy alg) routeContext ms bs = Right tx -> routeTx routeContext ms tx class TxRouter ms r where - routeTx :: forall alg. TxRouteContext -> Modules ms r -> Tx alg ByteString -> Sem r () + routeTx :: forall alg. RoutingContext -> Modules ms r -> Tx alg ByteString -> Sem r TxResult instance (Member (Error AppError) r) => TxRouter '[] r where routeTx _ NilModules Tx{txRoute} = throwSDKError $ UnmatchedRoute txRoute -instance {-# OVERLAPPING #-} (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void api s r ': ms) r where +instance {-# OVERLAPPING #-} (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void val api s r ': ms) r where routeTx routeContext (_ :+ rest) tx@Tx{txRoute} | symbolVal (Proxy :: Proxy name) == cs txRoute = throwSDKError $ UnmatchedRoute txRoute | otherwise = routeTx routeContext rest tx -instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCodec msg, KnownSymbol name) => TxRouter (Module name msg api s r ': ms) r where +instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCodec msg, HasCodec val, Member (Embed IO) r, KnownSymbol name) => TxRouter (Module name msg val api s r ': ms) r where routeTx routeContext (m :+ rest) tx@Tx{..} | symbolVal (Proxy :: Proxy name) == cs txRoute = do msg <- case decode $ msgData txMsg of @@ -132,7 +134,8 @@ instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCode Right (msg :: msg) -> return msg let msg' = txMsg {msgData = msg} tx' = RoutedTx $ tx {txMsg = msg'} - case routeContext of + ctx <- liftIO $ T.newTransactionContext tx' + T.eval ctx $ case routeContext of CheckTxContext -> moduleTxChecker m tx' DeliverTxContext -> moduleTxDeliverer m tx' | otherwise = routeTx routeContext rest tx @@ -154,10 +157,10 @@ class Eval ms core where -> forall a. Sem (Effs ms core) a -> Sem (BaseApp core) a -instance Eval '[Module name msg api s r] core where - type Effs '[Module name msg api s r] core = s :& BaseApp core +instance Eval '[Module name msg val api s r] core where + type Effs '[Module name msg val api s r] core = s :& BaseApp core eval (m :+ NilModules) = moduleEval m -instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name msg api s r ': m' ': ms) core where - type Effs (Module name msg api s r ': m' ': ms) core = s :& (Effs (m': ms)) core +instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name msg val api s r ': m' ': ms) core where + type Effs (Module name msg val api s r ': m' ': ms) core = s :& (Effs (m': ms)) core eval (m :+ rest) = eval rest . moduleEval m diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 685daea0..9b856536 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -12,7 +12,6 @@ module Tendermint.SDK.BaseApp , Context(..) , contextLogConfig , contextPrometheusEnv - , contextEventBuffer , contextAuthTree , makeContext , runCoreEffs @@ -52,6 +51,9 @@ module Tendermint.SDK.BaseApp , CountName(..) , HistogramName(..) + -- * Transaction + , TxEffs + -- * Query , Queryable(..) , FromQueryData(..) @@ -70,3 +72,5 @@ import Tendermint.SDK.BaseApp.Logger import Tendermint.SDK.BaseApp.Metrics import Tendermint.SDK.BaseApp.Query import Tendermint.SDK.BaseApp.Store +import Tendermint.SDK.BaseApp.Transaction +import Tendermint.SDK.Types.Effects ((:&)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs index 4e7d8967..89f719bd 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs @@ -5,20 +5,16 @@ module Tendermint.SDK.BaseApp.BaseApp , ScopedEff(..) , compileScopedEff , compileToCoreEffs - , (:&) ) where import Control.Exception (throwIO) import Control.Monad.IO.Class (liftIO) import Polysemy (Sem) import Polysemy.Error (Error, runError) -import Polysemy.Output (Output) import Polysemy.Resource (Resource, resourceToIO) import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) import Tendermint.SDK.BaseApp.Errors (AppError) -import Tendermint.SDK.BaseApp.Events (Event, - evalWithBuffer) import Tendermint.SDK.BaseApp.Logger (Logger) import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL import Tendermint.SDK.BaseApp.Metrics (Metrics) @@ -27,26 +23,18 @@ import Tendermint.SDK.BaseApp.Store (ApplyScope, Connect RawStore, ResolveScope (..)) import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT +import Tendermint.SDK.Types.Effects ((:&)) -- | Concrete row of effects for the BaseApp. Note that because there does -- | not exist an interpreter for an untagged 'RawStore', you must scope -- | these effects before they can be interpreted. type BaseAppEffs = [ RawStore - , Output Event , Metrics , Logger , Resource , Error AppError ] - --- | This type family gives a nice syntax for combining multiple lists of effects. -type family (as :: [a]) :& (bs :: [a]) :: [a] where - '[] :& bs = bs - (a ': as) :& bs = a ': (as :& bs) - -infixr 5 :& - type BaseApp r = BaseAppEffs :& r type ScopedBaseApp (s :: ConnectionScope) r = ApplyScope s (BaseApp r) @@ -60,7 +48,6 @@ compileToCoreEffs action = do resourceToIO . KL.evalKatip . Prometheus.evalWithMetrics . - evalWithBuffer . resolveScope $ action either (liftIO . throwIO) return eRes diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs index 48cae792..f6f7b918 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs @@ -7,7 +7,6 @@ module Tendermint.SDK.BaseApp.CoreEff , Context(..) , contextLogConfig , contextPrometheusEnv - , contextEventBuffer , contextAuthTree , makeContext , runCoreEffs @@ -21,8 +20,6 @@ import Polysemy (Embed, Members, Sem, runM) import Polysemy.Reader (Reader, asks, local, runReader) -import Tendermint.SDK.BaseApp.Events (EventBuffer, - newEventBuffer) import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P import Tendermint.SDK.BaseApp.Store (MergeScopes, @@ -32,8 +29,7 @@ import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT -- | CoreEffs is one level below BaseAppEffs, and provides one possible -- | interpretation for its effects to IO. type CoreEffs = - '[ Reader EventBuffer - , MergeScopes + '[ MergeScopes , Reader KL.LogConfig , Reader (Maybe P.PrometheusEnv) , Reader AT.AuthTreeState @@ -54,7 +50,6 @@ instance (Members CoreEffs r) => K.KatipContext (Sem r) where data Context = Context { _contextLogConfig :: KL.LogConfig , _contextPrometheusEnv :: Maybe P.PrometheusEnv - , _contextEventBuffer :: EventBuffer , _contextAuthTree :: AT.AuthTreeState } @@ -70,12 +65,10 @@ makeContext KL.InitialLogNamespace{..} scrapingCfg = do Just scfg -> P.emptyState >>= \es -> pure . Just $ P.PrometheusEnv es scfg authTreeState <- AT.initAuthTreeState - eb <- newEventBuffer logCfg <- mkLogConfig _initialLogEnvironment _initialLogProcessName pure $ Context { _contextLogConfig = logCfg , _contextPrometheusEnv = metCfg - , _contextEventBuffer = eb , _contextAuthTree = authTreeState } where @@ -101,5 +94,4 @@ runCoreEffs Context{..} = runReader _contextAuthTree . runReader _contextPrometheusEnv . runReader _contextLogConfig . - AT.evalMergeScopes . - runReader _contextEventBuffer + AT.evalMergeScopes diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index 7a23a383..461ef293 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -2,8 +2,7 @@ module Tendermint.SDK.BaseApp.Errors ( AppError(..) , IsAppError(..) , queryAppError - , checkTxAppError - , deliverTxAppError + , txResultAppError , SDKError(..) , throwSDKError ) where @@ -15,6 +14,7 @@ import Data.Word (Word32) import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy import Polysemy.Error (Error, throw) +import Tendermint.SDK.Types.TxResult (TxResult (..)) -- | This type represents a common error response for the query, checkTx, -- | and deliver tx abci-messages. @@ -31,7 +31,7 @@ instance Exception AppError class IsAppError e where makeAppError :: e -> AppError --- | This class is used to set the 'AppError' data into the appropriate +-- | This lens is used to set the 'AppError' data into the appropriate -- | response fields for the query abci-message. queryAppError :: Lens' Response.Query AppError queryAppError = lens g s @@ -47,36 +47,20 @@ queryAppError = lens g s , Response.queryLog = appErrorMessage } --- | This class is used to set the 'AppError' data into the appropriate --- | response fields for the checkTx abci-message. -checkTxAppError :: Lens' Response.CheckTx AppError -checkTxAppError = lens g s +-- | This lens is used to set the 'AppError' data into the appropriate +-- | response fields for the checkTx/deliverTx abci-message. +txResultAppError :: Lens' TxResult AppError +txResultAppError = lens g s where - g Response.CheckTx{..} = AppError - { appErrorCode = checkTxCode - , appErrorCodespace = checkTxCodespace - , appErrorMessage = checkTxLog + g TxResult{..} = AppError + { appErrorCode = _txResultCode + , appErrorCodespace = _txResultCodespace + , appErrorMessage = _txResultLog } - s checkTx AppError{..} = checkTx - { Response.checkTxCode = appErrorCode - , Response.checkTxCodespace = appErrorCodespace - , Response.checkTxLog = appErrorMessage - } - --- | This class is used to set the 'AppError' data into the appropriate --- | response fields for the deliverTx abci-message. -deliverTxAppError :: Lens' Response.DeliverTx AppError -deliverTxAppError = lens g s - where - g Response.DeliverTx{..} = AppError - { appErrorCode = deliverTxCode - , appErrorCodespace = deliverTxCodespace - , appErrorMessage = deliverTxLog - } - s deliverTx AppError{..} = deliverTx - { Response.deliverTxCode = appErrorCode - , Response.deliverTxCodespace = appErrorCodespace - , Response.deliverTxLog = appErrorMessage + s txResult AppError{..} = txResult + { _txResultCode = appErrorCode + , _txResultCodespace = appErrorCodespace + , _txResultLog = appErrorMessage } -------------------------------------------------------------------------------- diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index 1c54f3c1..bb4e744c 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -3,30 +3,19 @@ module Tendermint.SDK.BaseApp.Events , ToEvent(..) , emit , makeEvent - , EventBuffer - , newEventBuffer - , withEventBuffer - , evalWithBuffer ) where -import qualified Control.Concurrent.MVar as MVar -import Control.Monad (void) -import Control.Monad.IO.Class import qualified Data.Aeson as A import Data.Bifunctor (bimap) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteString as BS -import qualified Data.List as L import Data.Proxy import Data.String.Conversions (cs) import GHC.Exts (toList) import Network.ABCI.Types.Messages.FieldTypes (Event (..), KVPair (..)) -import Polysemy (Embed, Member, Sem, - interpret) -import Polysemy.Output (Output (..), output) -import Polysemy.Reader (Reader (..), ask) -import Polysemy.Resource (Resource, onException) +import Polysemy (Member, Sem) +import Polysemy.Output (Output, output) {- TODO : These JSON instances are fragile but convenient. We @@ -44,41 +33,6 @@ class ToEvent e where A.Object obj -> bimap cs (cs . A.encode) <$> toList obj _ -> mempty --- This is the internal implementation of the interpreter for event --- logging. We allocate a buffer that can queue events as they are thrown, --- then flush the buffer at the end of transaction execution. It will --- also flush in the event that exceptions are thrown. - -data EventBuffer = EventBuffer (MVar.MVar [Event]) - -newEventBuffer :: IO EventBuffer -newEventBuffer = EventBuffer <$> MVar.newMVar [] - -appendEvent - :: MonadIO (Sem r) - => Event - -> EventBuffer - -> Sem r () -appendEvent e (EventBuffer b) = do - liftIO (MVar.modifyMVar_ b (pure . (e :))) - -flushEventBuffer - :: MonadIO (Sem r) - => EventBuffer - -> Sem r [Event] -flushEventBuffer (EventBuffer b) = do - liftIO (L.reverse <$> MVar.swapMVar b []) - -withEventBuffer - :: Member Resource r - => Member (Reader EventBuffer) r - => MonadIO (Sem r) - => Sem r () - -> Sem r [Event] -withEventBuffer action = do - buffer <- ask - onException (action *> flushEventBuffer buffer) (void $ flushEventBuffer buffer) - makeEvent :: ToEvent e => e @@ -94,11 +48,3 @@ emit => e -> Sem r () emit e = output $ makeEvent e - -evalWithBuffer - :: Member (Embed IO) r - => Member (Reader EventBuffer) r - => (forall a. Sem (Output Event ': r) a -> Sem r a) -evalWithBuffer action = interpret (\case - Output e -> ask >>= appendEvent e - ) action diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs index c2b91d4e..9244de20 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs @@ -1,39 +1,40 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.BaseApp.Gas ( GasMeter(..) + , GasAmount(..) , withGas , eval ) where -import Control.Monad.IO.Class (MonadIO (..)) import Data.Int (Int64) -import qualified Data.IORef as Ref -import Polysemy (Embed, Members, Sem, interpretH, +import Polysemy (Members, Sem, interpretH, makeSem, raise, runT) import Polysemy.Error (Error) +import Polysemy.State (State, get, put) import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (OutOfGasException), throwSDKError) +newtype GasAmount = GasAmount { unGasAmount :: Int64 } deriving (Eq, Show, Num, Ord) + data GasMeter m a where - WithGas :: forall m a. Int64 -> m a -> GasMeter m a + WithGas :: forall m a. GasAmount -> m a -> GasMeter m a makeSem ''GasMeter + eval - :: Members [Error AppError, Embed IO] r - => Ref.IORef Int64 - -> Sem (GasMeter ': r) a + :: Members [Error AppError, State GasAmount] r + => Sem (GasMeter ': r) a -> Sem r a -eval meter = interpretH (\case +eval = interpretH (\case WithGas gasCost action -> do - remainingGas <- liftIO $ Ref.readIORef meter + remainingGas <- get let balanceAfterAction = remainingGas - gasCost if balanceAfterAction < 0 then throwSDKError OutOfGasException else do - liftIO $ Ref.writeIORef meter balanceAfterAction + put balanceAfterAction a <- runT action - raise $ eval meter a + raise $ eval a ) - diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs index 91ef3f98..38c0f484 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics.hs @@ -28,6 +28,6 @@ data Metrics m a where -- | Increments the count of a specific message IncCount :: CountName -> Metrics m () -- | Times an action and records it in a histogram - WithTimer :: HistogramName -> m a -> Metrics m () + WithTimer :: HistogramName -> m a -> Metrics m a makeSem ''Metrics diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs index ee155085..840cad72 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -166,7 +166,9 @@ evalNothing evalNothing = do interpretH (\case IncCount _ -> pureT () - WithTimer _ _ -> pureT () + WithTimer _ action -> do + a <- runT action + raise $ evalNothing a ) evalMetrics @@ -203,8 +205,7 @@ evalMetrics state@MetricsState{..} = do end <- liftIO $ getCurrentTime let time = fromRational . (* 1000.0) . toRational $ (end `diffUTCTime` start) observeHistogram state histName time - _ <- raise $ evalMetrics state a - pureT () + raise $ evalMetrics state a ) -- | Updates a histogram with an observed value diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs new file mode 100644 index 00000000..94d0568f --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs @@ -0,0 +1,74 @@ +module Tendermint.SDK.BaseApp.Transaction + ( TxEffs + , TransactionContext(..) + , newTransactionContext + , eval + ) where + +import Control.Lens ((&), (.~)) +import Control.Monad.IO.Class (liftIO) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Default.Class (def) +import Data.IORef (IORef, newIORef, readIORef) +import Polysemy (Embed, Member, Sem, + raiseUnder) +import Polysemy.Error (Error, runError) +import Polysemy.Output (Output, + runOutputMonoidAssocR) +import Polysemy.State (State, runStateIORef) +import Tendermint.SDK.BaseApp.Errors (AppError, txResultAppError) +import qualified Tendermint.SDK.BaseApp.Events as E +import qualified Tendermint.SDK.BaseApp.Gas as G +import Tendermint.SDK.Codec (HasCodec (encode)) +import Tendermint.SDK.Types.Effects ((:&)) +import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) +import Tendermint.SDK.Types.TxResult (TxResult, txResultData, + txResultEvents, + txResultGasUsed, + txResultGasWanted) + +type TxEffs = + [ Output E.Event + , G.GasMeter + , Error AppError + ] + +data TransactionContext = TransactionContext + { gas :: IORef G.GasAmount + } + +newTransactionContext + :: RoutedTx msg + -> IO TransactionContext +newTransactionContext (RoutedTx Tx{txGas}) = do + initialGas <- newIORef $ G.GasAmount txGas + pure TransactionContext + { gas = initialGas + } + +eval + :: forall r a. + HasCodec a + => Member (Embed IO) r + => TransactionContext + -> Sem (TxEffs :& r) a + -> Sem r TxResult +eval TransactionContext{..} action = do + initialGas <- liftIO $ readIORef gas + eRes <- + runError . + runStateIORef gas . + G.eval . + raiseUnder @(State G.GasAmount) $ + runOutputMonoidAssocR (pure @[]) action + gasRemaining <- liftIO $ readIORef gas + let gasUsed = initialGas - gasRemaining + baseResponse = + def & txResultGasWanted .~ G.unGasAmount initialGas + & txResultGasUsed .~ G.unGasAmount gasUsed + return $ case eRes of + Left e -> + baseResponse & txResultAppError .~ e + Right (events, a) -> + baseResponse & txResultEvents .~ events + & txResultData .~ Base64.fromBytes (encode a) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs index acdb6c47..9e779474 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs @@ -14,5 +14,9 @@ class HasCodec a where encode :: a -> BS.ByteString decode :: BS.ByteString -> Either Text a +instance HasCodec () where + encode = const "" + decode = const $ pure () + defaultSDKAesonOptions :: String -> Options defaultSDKAesonOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 19fbe10e..6924a104 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -22,7 +22,7 @@ import Tendermint.SDK.Modules.Auth.Keeper import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types -type AuthM r = Module AuthModule Void Api AuthEffs r +type AuthM r = Module AuthModule Void Void Api AuthEffs r authModule :: Members BaseAppEffs r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Effects.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Effects.hs new file mode 100644 index 00000000..9aef2ae7 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Effects.hs @@ -0,0 +1,10 @@ +module Tendermint.SDK.Types.Effects + ( (:&) + ) where + +-- | This type family gives a nice syntax for combining multiple lists of effects. +type family (as :: [a]) :& (bs :: [a]) :: [a] where + '[] :& bs = bs + (a ': as) :& bs = a ': (as :& bs) + +infixr 5 :& diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index b1baacd9..5fcd40fa 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -7,6 +7,7 @@ import Crypto.Hash (Digest, hashWith) import Crypto.Hash.Algorithms (SHA256 (..)) import Data.Bifunctor (bimap) import Data.ByteString (ByteString) +import Data.Int (Int64) import qualified Data.ProtoLens as P import Data.Proxy import Data.String.Conversions (cs) @@ -25,6 +26,7 @@ import Tendermint.SDK.Types.Message (Msg (..)) data Tx alg msg = Tx { txMsg :: Msg msg , txRoute :: Text + , txGas :: Int64 , txSignature :: RecoverableSignature alg , txSignBytes :: Message alg , txSigner :: PubKey alg @@ -42,6 +44,7 @@ instance Functor (Tx alg) where data RawTransaction = RawTransaction { rawTransactionData :: ByteString -- ^ the encoded message via protobuf encoding + , rawTransactionGas :: Int64 , rawTransactionRoute :: Text -- ^ module name , rawTransactionSignature :: ByteString @@ -55,10 +58,12 @@ instance Wrapped RawTransaction where t RawTransaction {..} = P.defMessage & T.data' .~ rawTransactionData + & T.gas .~ rawTransactionGas & T.route .~ cs rawTransactionRoute & T.signature .~ rawTransactionSignature f message = RawTransaction { rawTransactionData = message ^. T.data' + , rawTransactionGas = message ^. T.gas , rawTransactionRoute = message ^. T.route , rawTransactionSignature = message ^. T.signature } @@ -103,6 +108,7 @@ parseTx p rawTx@RawTransaction{..} = do , msgAuthor = addressFromPubKey p signerPubKey } , txRoute = cs rawTransactionRoute + , txGas = rawTransactionGas , txSignature = recSig , txSignBytes = signBytes , txSigner = signerPubKey diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs index 6d0f6ab1..42a4de4f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/TxResult.hs @@ -1,12 +1,13 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.Types.TxResult where -import Control.Lens (Lens', lens) +import Control.Lens (Iso', iso) import Control.Lens.TH (makeLenses) import Data.ByteArray.Base64String (Base64String) import Data.Default.Class (Default (..)) import Data.Int (Int64) import Data.Text (Text) +import Data.Word (Word32) import Network.ABCI.Types.Messages.FieldTypes (Event, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Response as Response @@ -18,6 +19,9 @@ data TxResult = TxResult , _txResultGasWanted :: Int64 , _txResultGasUsed :: Int64 , _txResultEvents :: [Event] + , _txResultCode :: Word32 + , _txResultLog :: Text + , _txResultCodespace :: Text } deriving Show makeLenses ''TxResult @@ -29,12 +33,15 @@ instance Default TxResult where , _txResultGasWanted = 0 , _txResultGasUsed = 0 , _txResultEvents = [] + , _txResultCode = 0 + , _txResultLog = "" + , _txResultCodespace = "" } -- | This class is used to set the 'TxResult' data into the appropriate -- | response fields for the CheckTx abci-message. -checkTxTxResult :: Lens' Response.CheckTx TxResult -checkTxTxResult = lens g s +checkTxTxResult :: Iso' Response.CheckTx TxResult +checkTxTxResult = iso g s where g Response.CheckTx{..} = TxResult { _txResultData = checkTxData @@ -42,19 +49,25 @@ checkTxTxResult = lens g s , _txResultGasWanted = unWrappedVal checkTxGasWanted , _txResultGasUsed = unWrappedVal checkTxGasUsed , _txResultEvents = checkTxEvents + , _txResultCode = checkTxCode + , _txResultLog = checkTxLog + , _txResultCodespace = checkTxCodespace } - s checkTx TxResult{..} = checkTx + s TxResult{..} = Response.CheckTx { Response.checkTxData = _txResultData , Response.checkTxInfo = _txResultInfo , Response.checkTxGasWanted = WrappedVal _txResultGasWanted , Response.checkTxGasUsed = WrappedVal _txResultGasUsed , Response.checkTxEvents = _txResultEvents + , Response.checkTxCode = _txResultCode + , Response.checkTxCodespace = _txResultCodespace + , Response.checkTxLog = _txResultLog } -- | This class is used to set the 'TxResult' data into the appropriate -- | response fields for the DeliverTx abci-message. -deliverTxTxResult :: Lens' Response.DeliverTx TxResult -deliverTxTxResult = lens g s +deliverTxTxResult :: Iso' Response.DeliverTx TxResult +deliverTxTxResult = iso g s where g Response.DeliverTx{..} = TxResult { _txResultData = deliverTxData @@ -62,11 +75,17 @@ deliverTxTxResult = lens g s , _txResultGasWanted = unWrappedVal deliverTxGasWanted , _txResultGasUsed = unWrappedVal deliverTxGasUsed , _txResultEvents = deliverTxEvents + , _txResultCode = deliverTxCode + , _txResultLog = deliverTxLog + , _txResultCodespace = deliverTxCodespace } - s deliverTx TxResult{..} = deliverTx + s TxResult{..} = Response.DeliverTx { Response.deliverTxData = _txResultData , Response.deliverTxInfo = _txResultInfo , Response.deliverTxGasWanted = WrappedVal _txResultGasWanted , Response.deliverTxGasUsed = WrappedVal _txResultGasUsed , Response.deliverTxEvents = _txResultEvents + , Response.deliverTxCode = _txResultCode + , Response.deliverTxCodespace = _txResultCodespace + , Response.deliverTxLog = _txResultLog } diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs index c0245140..d85d6f58 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs @@ -20,6 +20,7 @@ spec = describe "Crypto Tests" $ do { rawTransactionData = "abcd" , rawTransactionSignature = "" , rawTransactionRoute= "dog" + , rawTransactionGas = 10 } signature = signRawTransaction algProxy privateKey rawTxWithoutSig rawTxWithSig = rawTxWithoutSig {rawTransactionSignature = diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs index 3d892412..91e7730e 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/GasSpec.hs @@ -4,10 +4,10 @@ module Tendermint.SDK.Test.GasSpec (spec) where import Control.Monad.IO.Class (MonadIO (..)) import Data.Either (isRight) -import Data.Int (Int64) import qualified Data.IORef as Ref import Polysemy import Polysemy.Error (Error, runError) +import Polysemy.State (State, runStateIORef) import Tendermint.SDK.BaseApp.Errors (AppError (..)) import qualified Tendermint.SDK.BaseApp.Gas as G import Test.Hspec @@ -22,10 +22,16 @@ evalDog = interpret $ \case Bark -> pure () eval - :: Ref.IORef Int64 + :: Ref.IORef G.GasAmount -> Sem [Dog, G.GasMeter, Error AppError, Embed IO] a -> IO (Either AppError a) -eval meter = runM . runError . G.eval meter . evalDog +eval meter = + runM . + runError . + runStateIORef meter . + G.eval . + raiseUnder @(State G.GasAmount) . + evalDog spec :: Spec spec = describe "Gas Tests" $ do diff --git a/hs-abci-test-utils/src/Tendermint/Utils/User.hs b/hs-abci-test-utils/src/Tendermint/Utils/User.hs index 4564fdff..c8302940 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/User.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/User.hs @@ -38,6 +38,7 @@ mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned where unsigned = RawTransaction { rawTransactionData = encode msg , rawTransactionRoute = cs route , rawTransactionSignature = "" + , rawTransactionGas = 0 } sig = signRawTransaction algProxy privateKey unsigned sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } From 1101bd10e49a4de48020dd7c80ab23c388eac93a Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Mon, 6 Jan 2020 12:20:41 -0500 Subject: [PATCH 31/70] Impl Logger AddContext summand (#162) * add katip-es deps * wip es scribe/logger * add metrics instructions * toggle response/request logs to ES or console * add some comments and rename app logger middleware for clarity * some code cleanup * wip * wip almost builds * builds * config verbosity levels * add addContext summand * stylish * use addContext in nameservice * add datadog logs and elk installation instructions * es * add contextevent type to add event type field for logs * use local namespace in res/req middleware * test image * update readme * adjust vertical screenshots * use different images * change image links to master branch these images will now only show after this branch is merged * add image for creating a filter in kibana discovery * use third person * add default select method * rm unused imports * use ContextEvent in token and nameservice modules * move context event to events * Add logEvent and emitAndLogEvent * Switch logEvent to info * Remove event_type logItem from resp/req loggers * remove emitAndLogEvent * stylish * stylish * Add TxEff constraint to token router Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- hs-abci-examples/nameservice/README.md | 130 +++++++++++++++++- .../nameservice/images/create-filter.png | Bin 0 -> 73861 bytes .../nameservice/images/create-index.png | Bin 0 -> 152455 bytes .../nameservice/images/kibana-discover.png | Bin 0 -> 6361 bytes .../nameservice/images/kibana-management.png | Bin 0 -> 7203 bytes .../nameservice/images/logs-tab.png | Bin 0 -> 4793 bytes .../nameservice/images/prometheus-metrics.png | Bin 0 -> 74945 bytes .../src/Nameservice/Application.hs | 1 - .../nameservice/src/Nameservice/Config.hs | 2 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 63 +++++---- .../Nameservice/Modules/Nameservice/Types.hs | 4 + .../src/Nameservice/Modules/Token/Keeper.hs | 30 ++-- .../src/Nameservice/Modules/Token/Router.hs | 8 +- .../src/Nameservice/Modules/Token/Types.hs | 5 +- .../ABCI/Server/Middleware/RequestLogger.hs | 3 +- .../ABCI/Server/Middleware/ResponseLogger.hs | 39 +----- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 6 + .../src/Tendermint/SDK/BaseApp/Events.hs | 22 +++ .../src/Tendermint/SDK/BaseApp/Logger.hs | 19 ++- .../Tendermint/SDK/BaseApp/Logger/Katip.hs | 39 +++++- 20 files changed, 276 insertions(+), 95 deletions(-) create mode 100644 hs-abci-examples/nameservice/images/create-filter.png create mode 100644 hs-abci-examples/nameservice/images/create-index.png create mode 100644 hs-abci-examples/nameservice/images/kibana-discover.png create mode 100644 hs-abci-examples/nameservice/images/kibana-management.png create mode 100644 hs-abci-examples/nameservice/images/logs-tab.png create mode 100644 hs-abci-examples/nameservice/images/prometheus-metrics.png diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index 363571ab..31a14f99 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -1,6 +1,6 @@ # nameservice -## Metrics +## Metrics via Prometheus Run `nameservice` via the `Makefile`: @@ -8,16 +8,140 @@ Run `nameservice` via the `Makefile`: make deploy-nameservice-local ``` -Along with running `nameservice`, this also starts a Prometheus] metrics server. +Along with running `nameservice`, this command also starts a Prometheus metrics server. By default, the metrics server runs on `localhost:9200`. To use a different port, set the `STATS_PORT` environment variable to the desired port value. To see these metrics in Datadog, follow the [Prometheus host config instructions](https://docs.datadoghq.com/getting_started/integrations/prometheus/?tab=host#pagetitle) -to configure a local Datadog agent to scrape the endpoint. +to configure a local Datadog agent to scrape the endpoint. At minimum, to scrape all +`nameservice` prometheus metrics, the appropriate `conf.yaml` (described above) should +contain the following settings: + +```yaml +init_config: + +instances: + + - prometheus_url: http://localhost:9200/metrics + ## namespace option prefixes all metric names in datadog + namespace: prometheus + ## metrics names used in the nameservice app + metrics: + - count_buy + - count_set + - count_delete + - histogram_buy* + - histogram_set* + - histogram_delete* +``` Alternatively, use the `docker-compose` command: ``` make deploy-nameservice-docker ``` + +Once the `nameservice` server is running, start a Tendermint node: + +```bash +> tendermint init +> tendermint node +``` + +Then run the `nameservice` tests: + +```bash +make test-nameservice +``` + +Once the test run is completed, you should now be able to view metrics +on [Datadog's metrics explorer](https://app.datadoghq.com/metric/explorer). +Firstly, ensure that the prometheus server is populated with metrics by +visiting `localhost:9200/metrics` in a browser. It should look something like this: + +``` +# TYPE count_buy counter +count_buy 4 +# TYPE count_delete counter +count_delete 1 +# TYPE count_set counter +count_set 2 +# TYPE histogram_buy histogram +histogram_buy_bucket{le="1.0e-4"} 0.0 +... +# TYPE histogram_delete histogram +histogram_delete_bucket{le="1.0e-4"} 0.0 +... +# TYPE histogram_set histogram +histogram_set_bucket{le="1.0e-4"} 0.0 +... +``` + +Now, in Datadog's metrics explorer, we can search for metrics +prefixed with the `namespace` value (i.e., `prometheus`) set above: + + + +## Alternative Logging + +In addition to Prometheus metrics, the `nameservice` app includes options for +logging to Elasticsearch (ES) and Datadog metrics logging. By default, the app +logs everything to the console: + +``` +[2019-12-20 16:19:27][nameservice.server][Info][local][PID 89617][ThreadId 21][type:info] Request Received +[2019-12-20 16:19:27][nameservice.server][Info][local][PID 89617][ThreadId 21][message_type:info][response_time:2.6e-5][message_count:1] +``` + +These logs include request/response info and some event and metric logging. + +Alternatively, the app is set up to log the same information to ES +and Datadog by setting the following environment variables: + +```bash +DD_API_KEY ## Datadog API key +ES_HOST ## Elasticsearch server host +ES_PORT ## Elasticsearch server port +``` + +We recommend using the [ELK (v683) docker image](https://hub.docker.com/r/sebp/elk/tags) +to run an ES server alongside a Kibana instance to search and filter incoming logs. + +```bash +> docker pull sebp/elk:683 +> docker run -p 5601:5601 -p 9201:9200 -p 5044:5044 -it --name elk sebp/elk:683 +``` + +**NOTE:** This command remaps the `ES_PORT` value from `9200` to `9201` to avoid collision with +the Prometheus server that `nameservice` runs by default. The Kibana instance runs on port `5601`. + +After both Kibana and ES are running, start `nameservice` and a Tendermint node +via the commands: + +```bash +> DD_API_KEY= ES_HOST=localhost ES_PORT=9201 make deploy-nameservice +> tendermint init +> tendermint node +``` + +At this point, there should be no logs printed on the console. If a valid +Datadog API Key is provided, metrics logs are available on +[Datadog's log explorer](https://app.datadoghq.com/logs). + + + +To view ES logs on Kibana, go to `Management` on the sidebar and create a Kibana index pattern for `nameservice`: + + + + + +Finally, under `Discover`, we can add filters to view specific logs: + + + +For example, after running `nameservice` tests, we can add a filter to see +event logs for `NameClaimed`, `NameRemapped` and `NameDeleted`: + + diff --git a/hs-abci-examples/nameservice/images/create-filter.png b/hs-abci-examples/nameservice/images/create-filter.png new file mode 100644 index 0000000000000000000000000000000000000000..3e541b56c1d25eca0bf4eeb963da40ce32df63a4 GIT binary patch literal 73861 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 aC`VF4HO{8wQqm8AA1w|2tHqb^g#9m`E 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 uE>3gvg*<`a1a5&mDoS>^Kp literal 0 HcmV?d00001 diff --git a/hs-abci-examples/nameservice/images/kibana-management.png b/hs-abci-examples/nameservice/images/kibana-management.png new file mode 100644 index 0000000000000000000000000000000000000000..c2d6292001268f90d799b64707c64d3745e12bcb GIT binary patch literal 7203 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 I$(e`#53be#+W-In literal 0 HcmV?d00001 diff --git a/hs-abci-examples/nameservice/images/logs-tab.png b/hs-abci-examples/nameservice/images/logs-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..5b6e7c040e3eb916b25464ecbbbc9f21ea8621b1 GIT binary patch literal 4793 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 F{{YcBpF98n literal 0 HcmV?d00001 diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 934309da..0eb21fcb 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -14,7 +14,6 @@ import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A - type EffR = N.NameserviceEffs :& T.TokenEffs :& diff --git a/hs-abci-examples/nameservice/src/Nameservice/Config.hs b/hs-abci-examples/nameservice/src/Nameservice/Config.hs index 217a585c..ce85c684 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Config.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Config.hs @@ -51,7 +51,7 @@ makeAppConfig = do ddApiKey <- cs <$> MaybeT (lookupEnv "DD_API_KEY") pure $ P.MetricsScrapingConfig prometheusPort ddApiKey metricsMap <- newMVar empty - c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") prometheusEnv + c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "nameservice") prometheusEnv prometheusServer <- newIORef Nothing addScribesToLogEnv $ AppConfig { _baseAppContext = c diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index a7e8e10e..0292da47 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -9,7 +9,7 @@ import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Types import Nameservice.Modules.Token (Token, TokenEffs, burn, mint, transfer) -import Polysemy (Member, Members, Sem, +import Polysemy (Members, Sem, interpret, makeSem) import Polysemy.Error (Error, mapError, throw) @@ -26,7 +26,7 @@ makeSem ''NameserviceKeeper type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] storeKey :: BaseApp.StoreKey NameserviceModuleName -storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @ NameserviceModuleName +storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @NameserviceModuleName eval :: Members [BaseApp.RawStore, Error BaseApp.AppError] r @@ -50,7 +50,7 @@ eval = mapError BaseApp.makeAppError . evalNameservice -------------------------------------------------------------------------------- setName - :: Member (Output BaseApp.Event) r + :: Members [BaseApp.Logger, Output BaseApp.Event] r => Members NameserviceEffs r => SetName -> Sem r () @@ -63,14 +63,16 @@ setName SetName{..} = do then throw $ UnauthorizedSet "Setter must be the owner of the Name." else do putWhois setNameName currentWhois {whoisValue = setNameValue} - BaseApp.emit NameRemapped - { nameRemappedName = setNameName - , nameRemappedNewValue = setNameValue - , nameRemappedOldValue = whoisValue - } + let event = NameRemapped + { nameRemappedName = setNameName + , nameRemappedNewValue = setNameValue + , nameRemappedOldValue = whoisValue + } + BaseApp.emit event + BaseApp.logEvent event deleteName - :: Members [Token, Output BaseApp.Event] r + :: Members [BaseApp.Logger, Token, Output BaseApp.Event] r => Members NameserviceEffs r => DeleteName -> Sem r () @@ -84,13 +86,14 @@ deleteName DeleteName{..} = do else do mint deleteNameOwner whoisPrice deleteWhois deleteNameName - BaseApp.emit NameDeleted - { nameDeletedName = deleteNameName - } - + let event = NameDeleted + { nameDeletedName = deleteNameName + } + BaseApp.emit event + BaseApp.logEvent event buyName - :: Member (Output BaseApp.Event) r + :: Members [BaseApp.Logger, Output BaseApp.Event] r => Members TokenEffs r => Members NameserviceEffs r => BuyName @@ -108,7 +111,7 @@ buyName msg = do Just whois -> buyClaimedName msg whois where buyUnclaimedName - :: Member (Output BaseApp.Event) r + :: Members [BaseApp.Logger, Output BaseApp.Event] r => Members TokenEffs r => Members NameserviceEffs r => BuyName @@ -121,17 +124,19 @@ buyName msg = do , whoisPrice = buyNameBid } putWhois buyNameName whois - BaseApp.emit NameClaimed - { nameClaimedOwner = buyNameBuyer - , nameClaimedName = buyNameName - , nameClaimedValue = buyNameValue - , nameClaimedBid = buyNameBid - } + let event = NameClaimed + { nameClaimedOwner = buyNameBuyer + , nameClaimedName = buyNameName + , nameClaimedValue = buyNameValue + , nameClaimedBid = buyNameBid + } + BaseApp.emit event + BaseApp.logEvent event buyClaimedName :: Members NameserviceEffs r => Members TokenEffs r - => Member (Output BaseApp.Event) r + => Members [BaseApp.Logger, Output BaseApp.Event] r => BuyName -> Whois -> Sem r () @@ -145,11 +150,13 @@ buyName msg = do , whoisPrice = buyNameBid , whoisValue = buyNameValue } - BaseApp.emit NameClaimed - { nameClaimedOwner = buyNameBuyer - , nameClaimedName = buyNameName - , nameClaimedValue = buyNameValue - , nameClaimedBid = buyNameBid - } + let event = NameClaimed + { nameClaimedOwner = buyNameBuyer + , nameClaimedName = buyNameName + , nameClaimedValue = buyNameValue + , nameClaimedBid = buyNameBid + } + BaseApp.emit event + BaseApp.logEvent event else throw (InsufficientBid "Bid must exceed the price.") diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 9bf19811..fc395cfe 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -107,6 +107,7 @@ instance FromJSON NameClaimed where parseJSON = A.genericParseJSON nameClaimedAesonOptions instance BaseApp.ToEvent NameClaimed where makeEventType _ = "NameClaimed" +instance BaseApp.Select NameClaimed instance Event.FromEvent NameClaimed data NameRemapped = NameRemapped @@ -124,6 +125,7 @@ instance FromJSON NameRemapped where parseJSON = A.genericParseJSON nameRemappedAesonOptions instance BaseApp.ToEvent NameRemapped where makeEventType _ = "NameRemapped" +instance BaseApp.Select NameRemapped instance Event.FromEvent NameRemapped data NameDeleted = NameDeleted @@ -139,4 +141,6 @@ instance FromJSON NameDeleted where parseJSON = A.genericParseJSON nameDeletedAesonOptions instance BaseApp.ToEvent NameDeleted where makeEventType _ = "NameDeleted" +instance BaseApp.Select NameDeleted instance Event.FromEvent NameDeleted + diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs index 65d498e7..258c2641 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs @@ -45,15 +45,18 @@ eval = mapError BaseApp.makeAppError . evalToken -------------------------------------------------------------------------------- faucetAccount - :: Members [Error TokenError, Output BaseApp.Event, Token] r + :: Members [BaseApp.Logger, Output BaseApp.Event] r + => Members TokenEffs r => FaucetAccount -> Sem r () faucetAccount FaucetAccount{..} = do mint faucetAccountTo faucetAccountAmount - BaseApp.emit Faucetted - { faucettedAccount = faucetAccountTo - , faucettedAmount = faucetAccountAmount - } + let event = Faucetted + { faucettedAccount = faucetAccountTo + , faucettedAmount = faucetAccountAmount + } + BaseApp.emit event + BaseApp.logEvent event getBalance :: Member Token r @@ -63,7 +66,8 @@ getBalance address = fromMaybe (Amount 0) <$> getBalance' address transfer - :: Members [Error TokenError, Output BaseApp.Event, Token] r + :: Members [BaseApp.Logger, Output BaseApp.Event] r + => Members TokenEffs r => Address -> Amount -> Address @@ -79,15 +83,17 @@ transfer addr1 amount addr2 = do -- update both balances putBalance addr1 newBalance1 putBalance addr2 newBalance2 - BaseApp.emit $ TransferEvent - { transferEventAmount = amount - , transferEventTo = addr2 - , transferEventFrom = addr1 - } + let event = TransferEvent + { transferEventAmount = amount + , transferEventTo = addr2 + , transferEventFrom = addr1 + } + BaseApp.emit event + BaseApp.logEvent event else throw (InsufficientFunds "Insufficient funds for transfer.") burn - :: Members [Error TokenError, Token] r + :: Members TokenEffs r => Address -> Amount -> Sem r () diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs index 38e8db31..f78ed406 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs @@ -5,15 +5,15 @@ import Nameservice.Modules.Token.Keeper (TokenEffs, burn, import Nameservice.Modules.Token.Messages (Burn (..), TokenMessage (..), Transfer (..)) -import Polysemy (Member, Members, Sem) -import Polysemy.Output (Output) -import Tendermint.SDK.BaseApp (Event) +import Polysemy (Members, Sem) +import Tendermint.SDK.BaseApp (BaseAppEffs, TxEffs) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) router :: Members TokenEffs r - => Member (Output Event) r + => Members BaseAppEffs r + => Members TxEffs r => RoutedTx TokenMessage -> Sem r () router (RoutedTx Tx{txMsg}) = diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs index 786cbde7..0997fdd1 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs @@ -97,6 +97,7 @@ instance FromJSON Faucetted where parseJSON = A.genericParseJSON faucettedAesonOptions instance BaseApp.ToEvent Faucetted where makeEventType _ = "Faucetted" +instance BaseApp.Select Faucetted instance Event.FromEvent Faucetted data TransferEvent = TransferEvent @@ -110,11 +111,9 @@ transferEventAesonOptions = defaultNameserviceOptions "transferEvent" instance A.ToJSON TransferEvent where toJSON = A.genericToJSON transferEventAesonOptions - instance A.FromJSON TransferEvent where parseJSON = A.genericParseJSON transferEventAesonOptions - instance BaseApp.ToEvent TransferEvent where makeEventType _ = "TransferEvent" - +instance BaseApp.Select TransferEvent instance Event.FromEvent TransferEvent diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs index cada162f..3d6905df 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs @@ -49,4 +49,5 @@ mkRequestLoggerM (App app) = App $ \ req -> logRequest req >> app req --------------------------------------------------------------------------- -- | Request logger function. logRequest :: (KatipContext m) => Request t -> m () -logRequest req = katipAddContext (Loggable req) $ logFM InfoS "Request Received" +logRequest req = localKatipNamespace (<> "server") $ + katipAddContext (Loggable req) $ logFM InfoS "Request Received" diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs index 8176ab9a..5a1074e5 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs @@ -1,17 +1,13 @@ module Network.ABCI.Server.Middleware.ResponseLogger - ( -- * Basic stdout logging - mkLogStdout - , mkLogStdoutDev - -- * Custom Loggers - , mkResponseLogger + ( -- * Custom Loggers + mkResponseLogger , mkResponseLoggerM ) where -import Control.Monad.IO.Class (MonadIO, liftIO) +import Control.Monad.IO.Class (MonadIO) import qualified Data.Aeson as A import Katip import Network.ABCI.Server.App (App (..), MessageType, Middleware, Response (..)) -import System.IO (stdout) --------------------------------------------------------------------------- -- Types @@ -28,32 +24,6 @@ instance LogItem (Loggable (Response (t :: MessageType))) where payloadKeys V3 _ = AllKeys payloadKeys _ _ = SomeKeys ["type"] ---------------------------------------------------------------------------- --- mkLogStdout --------------------------------------------------------------------------- --- | Creates a production request logger as middleware for ABCI requests. --- Uses Lowest possible verbosity. -mkLogStdout :: (MonadIO m) => m (Middleware m) -mkLogStdout = do - handleScribe <- liftIO $ mkHandleScribe ColorIfTerminal stdout (permitItem InfoS) V0 - le <- liftIO (registerScribe "stdout" handleScribe defaultScribeSettings - =<< initLogEnv "ABCI" "production") - let ns = "Server" - pure $ mkResponseLogger le ns - ---------------------------------------------------------------------------- --- mkLogStdoutDev --------------------------------------------------------------------------- --- | Creates a request logger as middleware for ABCI requests. --- Uses highest possible verbosity. -mkLogStdoutDev :: (MonadIO m) => m (Middleware m) -mkLogStdoutDev = do - handleScribe <- liftIO $ mkHandleScribe ColorIfTerminal stdout (permitItem DebugS) V3 - le <- liftIO (registerScribe "stdout" handleScribe defaultScribeSettings - =<< initLogEnv "ABCI" "development") - let ns = "Server" - pure $ mkResponseLogger le ns - --------------------------------------------------------------------------- -- mkResponseLogger --------------------------------------------------------------------------- @@ -82,4 +52,5 @@ mkResponseLoggerM (App app) = App $ \ req -> do --------------------------------------------------------------------------- -- | Response logger function. logResponse :: (KatipContext m) => Response t -> m () -logResponse req = katipAddContext (Loggable req) $ logFM InfoS "Response Received" +logResponse req = localKatipNamespace (<> "server") $ + katipAddContext (Loggable req) $ logFM InfoS "Response Received" diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 9b856536..af73f92e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -34,7 +34,9 @@ module Tendermint.SDK.BaseApp -- * Events , Event(..) , ToEvent(..) + , ContextEvent(..) , emit + , logEvent -- * Gas , GasMeter @@ -42,7 +44,11 @@ module Tendermint.SDK.BaseApp -- * Logger , Logger , Tendermint.SDK.BaseApp.Logger.log + , addContext + , LogSelect(..) , Severity(..) + , Select(..) + , Verbosity(..) -- * Metrics , Metrics diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index bb4e744c..53ea9a08 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -1,7 +1,9 @@ module Tendermint.SDK.BaseApp.Events ( Event(..) , ToEvent(..) + , ContextEvent(..) , emit + , logEvent , makeEvent ) where @@ -16,6 +18,7 @@ import Network.ABCI.Types.Messages.FieldTypes (Event (..), KVPair (..)) import Polysemy (Member, Sem) import Polysemy.Output (Output, output) +import qualified Tendermint.SDK.BaseApp.Logger as Log {- TODO : These JSON instances are fragile but convenient. We @@ -48,3 +51,22 @@ emit => e -> Sem r () emit e = output $ makeEvent e + +-- | Special event wrapper to add contextual event_type info +newtype ContextEvent t = ContextEvent t +instance (A.ToJSON a, ToEvent a) => A.ToJSON (ContextEvent a) where + toJSON (ContextEvent a) = + A.object [ "event_type" A..= makeEventType (Proxy :: Proxy a) + , "event" A..= A.toJSON a + ] +instance Log.Select a => Log.Select (ContextEvent a) where + select v (ContextEvent a) = Log.select v a + +logEvent + :: forall e r. + (A.ToJSON e, ToEvent e, Log.Select e) + => Member Log.Logger r + => e + -> Sem r () +logEvent event = Log.addContext (ContextEvent event) $ + Log.log Log.Info (cs $ makeEventType (Proxy :: Proxy e)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs index 5c14d96b..4155d415 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs @@ -3,17 +3,30 @@ module Tendermint.SDK.BaseApp.Logger ( Logger(..) , Tendermint.SDK.BaseApp.Logger.log + , addContext + , LogSelect(..) + , Select(..) , Severity(..) + , Verbosity(..) ) where -import Data.Text (Text) -import Polysemy (makeSem) +import Data.Aeson (ToJSON (..)) +import Data.Text (Text) +import Polysemy (makeSem) data Severity = Debug | Info | Warning | Error | Exception deriving (Eq, Ord) +data LogSelect = All | Some [Text] +data Verbosity = V0 | V1 | V2 | V3 + +-- | Class for selecting object keys for contextual logging +class Select a where + select :: Verbosity -> a -> LogSelect + default select :: Verbosity -> a -> LogSelect + select _ _ = All -- | Effect allowing for console logging. data Logger m a where Log :: Severity -> Text -> Logger m () - -- AddContext :: Object -> m a -> Logger m a + AddContext :: (Select x, ToJSON x) => x -> m a -> Logger m a makeSem ''Logger diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs index 9af1321d..99575beb 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs @@ -14,12 +14,37 @@ module Tendermint.SDK.BaseApp.Logger.Katip ) where import Control.Lens.TH (makeLenses) +import qualified Data.Aeson as A import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text) import qualified Katip as K -import Polysemy (Sem, interpret) -import Tendermint.SDK.BaseApp.Logger (Logger (..), Severity (..)) +import Polysemy (Sem, interpretH, pureT, raise, + runT) +import Tendermint.SDK.BaseApp.Logger + +newtype Object a = Object a + +instance Select a => Select (Object a) where + select v (Object x) = select v x + +instance A.ToJSON a => K.ToObject (Object a) where + toObject (Object a) = case A.toJSON a of + A.Object o -> o + _ -> mempty + +instance (A.ToJSON a, Select a) => K.LogItem (Object a) where + payloadKeys = interpretFromSelect + where + interpretFromSelect kVerbosity obj = + let selectRes = select (kVerbToVerb kVerbosity) obj + in case selectRes of + All -> K.AllKeys + Some ts -> K.SomeKeys ts + kVerbToVerb K.V0 = V0 + kVerbToVerb K.V1 = V1 + kVerbToVerb K.V2 = V2 + kVerbToVerb K.V3 = V3 data LogConfig = LogConfig { _logNamespace :: K.Namespace @@ -41,10 +66,14 @@ evalKatip => Sem (Logger ': r) a -> Sem r a evalKatip = do - interpret (\case - Log severity msg -> - K.localKatipNamespace (<> "application") $ + interpretH (\case + Log severity msg -> do + raise $ K.localKatipNamespace (<> "application") $ K.logFM (coerceSeverity severity) (fromString . cs $ msg) + pureT () + AddContext obj action -> do + a <- runT action + raise $ K.katipAddContext (Object obj) (evalKatip a) ) where coerceSeverity :: Severity -> K.Severity From 225228b5a22b970a756a7e0b01bb899e0c8eaefc Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Mon, 6 Jan 2020 13:12:22 -0500 Subject: [PATCH 32/70] Remove util dep from nameservice app (#172) +create orphan module for events --- hs-abci-examples/nameservice/package.yaml | 2 +- .../src/Nameservice/Modules/Nameservice/Types.hs | 5 ----- .../src/Nameservice/Modules/Token/Types.hs | 3 --- .../nameservice/test/Nameservice/Test/E2ESpec.hs | 1 + .../test/Nameservice/Test/EventOrphans.hs | 16 ++++++++++++++++ hs-abci-test-utils/README.md | 2 +- 6 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 hs-abci-examples/nameservice/test/Nameservice/Test/EventOrphans.hs diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 65d617be..670a3e92 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -58,7 +58,6 @@ library: - hs-abci-extra - hs-abci-server - hs-abci-sdk - - hs-abci-test-utils - hs-abci-types - http-client - katip @@ -162,6 +161,7 @@ tests: source-dirs: test other-modules: - Nameservice.Test.E2ESpec + - Nameservice.Test.EventOrphans ghc-options: - -Werror - -Wall diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index fc395cfe..90ac46bc 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -19,7 +19,6 @@ import qualified Proto3.Wire.Encode as Encode import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) -import qualified Tendermint.Utils.Events as Event -------------------------------------------------------------------------------- @@ -108,7 +107,6 @@ instance FromJSON NameClaimed where instance BaseApp.ToEvent NameClaimed where makeEventType _ = "NameClaimed" instance BaseApp.Select NameClaimed -instance Event.FromEvent NameClaimed data NameRemapped = NameRemapped { nameRemappedName :: Name @@ -126,7 +124,6 @@ instance FromJSON NameRemapped where instance BaseApp.ToEvent NameRemapped where makeEventType _ = "NameRemapped" instance BaseApp.Select NameRemapped -instance Event.FromEvent NameRemapped data NameDeleted = NameDeleted { nameDeletedName :: Name @@ -142,5 +139,3 @@ instance FromJSON NameDeleted where instance BaseApp.ToEvent NameDeleted where makeEventType _ = "NameDeleted" instance BaseApp.Select NameDeleted -instance Event.FromEvent NameDeleted - diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs index 0997fdd1..13a6d796 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs @@ -20,7 +20,6 @@ import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address, addressFromBytes, addressToBytes) -import qualified Tendermint.Utils.Events as Event -------------------------------------------------------------------------------- @@ -98,7 +97,6 @@ instance FromJSON Faucetted where instance BaseApp.ToEvent Faucetted where makeEventType _ = "Faucetted" instance BaseApp.Select Faucetted -instance Event.FromEvent Faucetted data TransferEvent = TransferEvent { transferEventAmount :: Amount @@ -116,4 +114,3 @@ instance A.FromJSON TransferEvent where instance BaseApp.ToEvent TransferEvent where makeEventType _ = "TransferEvent" instance BaseApp.Select TransferEvent -instance Event.FromEvent TransferEvent diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 31202f12..3183b13c 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -18,6 +18,7 @@ import Nameservice.Modules.Token (Amount (..), TransferEvent (..)) import qualified Nameservice.Modules.Token as T (Api) import Nameservice.Modules.TypedMessage (TypedMessage (..)) +import Nameservice.Test.EventOrphans () import qualified Network.ABCI.Types.Messages.Response as Response import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..), (:>)) diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/EventOrphans.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/EventOrphans.hs new file mode 100644 index 00000000..85602217 --- /dev/null +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/EventOrphans.hs @@ -0,0 +1,16 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Nameservice.Test.EventOrphans where + +import Nameservice.Modules.Nameservice (NameClaimed, NameDeleted, + NameRemapped) +import Nameservice.Modules.Token (Faucetted, TransferEvent) +import qualified Tendermint.Utils.Events as Event + +-- Orphan instances for retrieving event logs for unit testing + +instance Event.FromEvent NameClaimed +instance Event.FromEvent NameRemapped +instance Event.FromEvent NameDeleted +instance Event.FromEvent Faucetted +instance Event.FromEvent TransferEvent diff --git a/hs-abci-test-utils/README.md b/hs-abci-test-utils/README.md index 4264d308..c5e2d124 100644 --- a/hs-abci-test-utils/README.md +++ b/hs-abci-test-utils/README.md @@ -1,6 +1,6 @@ # hs-abci-test-utils -Utils for apps and tests. +Utils for tests. Includes the following: From e61c78f9e1d33a0020cb65dbb94421ccbca58086 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 6 Jan 2020 15:47:54 -0800 Subject: [PATCH 33/70] Martin/nameservice elk compose (#177) * Add ELK compose file for nameservice - Uses `wait-for-it.sh` - On mac, set `ES_HOST=docker.for.mac.localhost` * move inside namespace dir, edit compose file, make command * remove unused script Co-authored-by: Carl Factora --- Makefile | 6 +- .../nameservice/docker-compose-elk.yaml | 59 +++++++++++++++++++ 2 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 hs-abci-examples/nameservice/docker-compose-elk.yaml diff --git a/Makefile b/Makefile index 9ae43fa4..068f3133 100644 --- a/Makefile +++ b/Makefile @@ -62,9 +62,13 @@ test-iavl-client: ## test the iavl client library basic operation (requires grpc ##################### deploy-simple-storage-docker: install ## run the simple storage docker network - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml up --build + docker-compose -f hs-abci-examples/simple-storage/docker-compose-elk.yaml up --build + +deploy-nameservice-elk-docker: install ## run the nameservice docker network with elk stack for logging + docker-compose -f hs-abci-examples/nameservice/docker-compose-elk.yaml up --build deploy-nameservice-docker: install ## run the nameservice docker network + DD_API_KEY=$(DD_API_KEY) \ docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml up --build deploy-simple-storage-local: install ## run the simple storage locally diff --git a/hs-abci-examples/nameservice/docker-compose-elk.yaml b/hs-abci-examples/nameservice/docker-compose-elk.yaml new file mode 100644 index 00000000..702bd717 --- /dev/null +++ b/hs-abci-examples/nameservice/docker-compose-elk.yaml @@ -0,0 +1,59 @@ +version: '3.7' +services: + tendermint-init: + image: tendermint/tendermint:v0.32.8 + command: init + volumes: + - tendermint-storage:/tendermint + tendermint: + depends_on: + - tendermint-init + - nameservice + image: tendermint/tendermint:v0.32.8 + command: node --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://nameservice:26658 + volumes: + - tendermint-storage:/tendermint + restart: always + ports: + - "26656-26657:26656-26657" + nameservice: + entrypoint: /usr/local/bin/nameservice + build: + context: ../../. + dockerfile: Dockerfile + image: hs-abci:test + environment: + - DD_API_KEY=${DD_API_KEY} + - ES_HOST=elk + - ES_PORT=9200 + - STATS_PORT=5555 + restart: always + depends_on: + - elk + - datadog + expose: + - "26658" + - "5555" + datadog: + image: datadog/agent:latest + environment: + - DD_API_KEY=${DD_API_KEY} + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /proc/:/host/proc/:ro + - /sys/fs/cgroup:/host/sys/fs/cgroup:ro + labels: + com.datadoghq.ad.check_names: '["openmetrics"]' + com.datadoghq.ad.init_configs: '["{}"]' + com.datadoghq.ad.instances: '["{\"prometheus_url\":\"http://%%host%%:5555/metrics \",\"namespace\":\"nameservice\",\"metrics\":[\"count_buy\" \"count_set\" \"count_delete\" \"histogram_buy*\" \"histogram_set*\" \"histogram_delete*\"]}"]' + elk: + image: sebp/elk:683 + ports: + - "5601:5601" + - "9200:9200" + - "5044:5044" + expose: + - "9200" + - "5601" +volumes: + tendermint-storage: From f50b67352183ea5d85511abe8a2fe0291bafa4e4 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 7 Jan 2020 09:41:40 -0800 Subject: [PATCH 34/70] Simplify logger with UUID (#176) * make echo and flush debug messages * simplify logging middleware * tested, seems to work * log level from environment * added note about environment variables * remove parens --- hs-abci-examples/nameservice/README.md | 10 ++ .../nameservice/src/Nameservice/Config.hs | 45 +++++-- .../nameservice/src/Nameservice/Server.hs | 36 +++--- hs-abci-examples/simple-storage/README.md | 10 ++ .../src/SimpleStorage/Config.hs | 48 +++++-- .../src/SimpleStorage/Server.hs | 36 +++--- hs-abci-extra/package.yaml | 2 + .../Network/ABCI/Server/Middleware/Logger.hs | 121 ++++++++++++++++++ .../ABCI/Server/Middleware/MetricsLogger.hs | 15 ++- .../ABCI/Server/Middleware/RequestLogger.hs | 53 -------- .../ABCI/Server/Middleware/ResponseLogger.hs | 56 -------- .../Tendermint/SDK/BaseApp/Logger/Katip.hs | 2 +- hs-abci-server/package.yaml | 3 + hs-abci-server/src/Network/ABCI/Server/App.hs | 56 +++++++- 14 files changed, 314 insertions(+), 179 deletions(-) create mode 100644 hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs delete mode 100644 hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs delete mode 100644 hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index 31a14f99..c7c3ae14 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -1,5 +1,15 @@ # nameservice +## Environment Variables +- LOG_SEVERITY (defaults to **info**) : minimum log severtiy level {debug, info, notice, warning, error, critical, alert, emergency} +- LOG_VERBOSITY (defaults to **0**) : for each loggable data point, the level of information actually logged {0, 1, 2, 3} +- ES_HOST (optional) : hostname of the elasticsearch instance for logging +- ES_PORT (optional) : port number of elasticsearch instance for logging +- STATS_PORT (optional) : port to run the prometheus metrics server. +- DD_API_KEY (optional) : key to post metrics data to datadog + +**NOTE** If you do not provide both of the elasticsearch variables, the logger will default to logging to the console. + ## Metrics via Prometheus Run `nameservice` via the `Makefile`: diff --git a/hs-abci-examples/nameservice/src/Nameservice/Config.hs b/hs-abci-examples/nameservice/src/Nameservice/Config.hs index ce85c684..9e18be25 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Config.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Config.hs @@ -61,19 +61,43 @@ makeAppConfig = do addScribesToLogEnv :: AppConfig -> IO AppConfig addScribesToLogEnv cfg = do + logLevel <- makeLogLevel consoleCfg <- makeConsoleLoggingConfig let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv mdatadogApiKey = cfg ^? baseAppContext . BaseApp.contextPrometheusEnv . _Just . P.envMetricsScrapingConfig . P.dataDogApiKey addScribes = runKleisli $ - makeKatipScribe consoleCfg - >>> maybe returnA makeMetricsScribe mdatadogApiKey + makeKatipScribe consoleCfg logLevel + >>> maybe returnA (makeMetricsScribe logLevel) mdatadogApiKey scribesLogEnv <- addScribes initialLogEnv pure $ cfg & baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv -------------------------------------------------------------------------------- +data LogLevel = LogLevel + { severity :: K.Severity + , verbosity :: K.Verbosity + } + +makeLogLevel :: IO LogLevel +makeLogLevel = do + -- LOG_SEVERITY should be in {debug, info, notice, warning, error, critical, alert, emergency} + msev <- lookupEnv "LOG_SEVERITY" + let s = fromMaybe K.InfoS (parseSeverity =<< msev) + -- LOG_VERBOSITY should be in {0,1,2,3} + mverb <- lookupEnv "LOG_VERBOSITY" + let v = fromMaybe K.V0 (parseVerbosity =<< mverb) + return LogLevel {severity = s, verbosity = v} + where + parseSeverity = K.textToSeverity . cs + parseVerbosity v + | v == "0" = Just K.V0 + | v == "1" = Just K.V1 + | v == "2" = Just K.V2 + | v == "3" = Just K.V3 + | otherwise = Nothing + data KatipConfig = ES {host :: String, port :: String} | Console makeConsoleLoggingConfig :: IO KatipConfig @@ -83,27 +107,26 @@ makeConsoleLoggingConfig = do pure $ fromMaybe Console mEsConfig -- makes a log environment for console logs / ES logs -makeKatipScribe :: KatipConfig -> Kleisli IO K.LogEnv K.LogEnv -makeKatipScribe kcfg = Kleisli $ \le -> - let verbosity = K.V0 - in case kcfg of +makeKatipScribe :: KatipConfig -> LogLevel -> Kleisli IO K.LogEnv K.LogEnv +makeKatipScribe kcfg LogLevel{..} = Kleisli $ \le -> + case kcfg of Console -> do - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) verbosity + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity K.registerScribe "stdout" handleScribe K.defaultScribeSettings le ES {host, port} -> do mgr <- Client.newManager Client.defaultManagerSettings let serverAddress = "http://" <> host <> ":" <> port bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "nameservice") - (BH.MappingName "application-logs") (K.permitItem K.DebugS) verbosity + (BH.MappingName "application-logs") (K.permitItem severity) verbosity K.registerScribe "es" esScribe K.defaultScribeSettings le -------------------------------------------------------------------------------- -- makes a log environment for metrics logs -makeMetricsScribe :: Text -> Kleisli IO K.LogEnv K.LogEnv -makeMetricsScribe key = Kleisli $ \le -> do +makeMetricsScribe :: LogLevel -> Text -> Kleisli IO K.LogEnv K.LogEnv +makeMetricsScribe LogLevel{..} key = Kleisli $ \le -> do let apiKey = DD.APIKey key datadogScribeSettings <- DD.mkDatadogScribeSettings DD.directAPIConnectionParams (DD.DirectAuth apiKey) - scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem K.InfoS) K.V0 + scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem severity) verbosity K.registerScribe "datadog" scribe K.defaultScribeSettings le diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index ff71d897..1ea5e0c3 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -1,22 +1,21 @@ module Nameservice.Server (makeAndServeApplication) where -import Data.Foldable (fold) -import Data.IORef (writeIORef) -import Data.Monoid (Endo (..)) -import Nameservice.Application (handlersContext) -import Nameservice.Config (AppConfig (..)) -import Network.ABCI.Server (serveApp) -import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met -import qualified Network.ABCI.Server.Middleware.RequestLogger as Req -import qualified Network.ABCI.Server.Middleware.ResponseLogger as Res -import Polysemy (Sem) -import Tendermint.SDK.Application (createIOApp, - makeApp) -import Tendermint.SDK.BaseApp (Context (..), - CoreEffs, - runCoreEffs) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) +import Data.Foldable (fold) +import Data.IORef (writeIORef) +import Data.Monoid (Endo (..)) +import Nameservice.Application (handlersContext) +import Nameservice.Config (AppConfig (..)) +import Network.ABCI.Server (serveApp) +import Network.ABCI.Server.App (Middleware) +import qualified Network.ABCI.Server.Middleware.Logger as Logger +import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met +import Polysemy (Sem) +import Tendermint.SDK.Application (createIOApp, + makeApp) +import Tendermint.SDK.BaseApp (Context (..), + CoreEffs, + runCoreEffs) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication AppConfig{..} = do @@ -31,8 +30,7 @@ makeAndServeApplication AppConfig{..} = do application = makeApp handlersContext middleware :: Middleware (Sem CoreEffs) middleware = appEndo . fold $ - [ Endo Req.mkRequestLoggerM - , Endo Res.mkResponseLoggerM + [ Endo Logger.mkLoggerM , Endo $ Met.mkMetricsLoggerM _serverMetricsMap ] serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-examples/simple-storage/README.md b/hs-abci-examples/simple-storage/README.md index 1e8fd4ef..a04972ed 100644 --- a/hs-abci-examples/simple-storage/README.md +++ b/hs-abci-examples/simple-storage/README.md @@ -4,6 +4,16 @@ The example application is meant to test all of the other hs-abci libraries and It's a simple application called **Simple Storage** that maintains a shared 32-byte integer and allows users to update and query the count. +## Environment Variables +- LOG_SEVERITY (defaults to **info**) : minimum log severtiy level {debug, info, notice, warning, error, critical, alert, emergency} +- LOG_VERBOSITY (defaults to **0**) : for each loggable data point, the level of information actually logged {0, 1, 2, 3} +- ES_HOST (optional) : hostname of the elasticsearch instance for logging +- ES_PORT (optional) : port number of elasticsearch instance for logging +- STATS_PORT (optional) : port to run the prometheus metrics server. +- DD_API_KEY (optional) : key to post metrics data to datadog + +**NOTE** If you do not provide both of the elasticsearch variables, the logger will default to logging to the console. + ## Running with Docker There is a `docker-compose.yaml` file in this directory. If you use the `make` command from the project root diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs index f83bf4cc..9d586279 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs @@ -60,19 +60,44 @@ makeAppConfig = do addScribesToLogEnv :: AppConfig -> IO AppConfig addScribesToLogEnv cfg = do + logLevel <- makeLogLevel consoleCfg <- makeConsoleLoggingConfig let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv mdatadogApiKey = cfg ^? baseAppContext . BaseApp.contextPrometheusEnv . _Just . P.envMetricsScrapingConfig . P.dataDogApiKey addScribes = runKleisli $ - makeKatipScribe consoleCfg - >>> maybe returnA makeMetricsScribe mdatadogApiKey + makeKatipScribe consoleCfg logLevel + >>> maybe returnA (makeMetricsScribe logLevel) mdatadogApiKey scribesLogEnv <- addScribes initialLogEnv pure $ cfg & baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv -------------------------------------------------------------------------------- +data LogLevel = LogLevel + { severity :: K.Severity + , verbosity :: K.Verbosity + } + +makeLogLevel :: IO LogLevel +makeLogLevel = do + -- LOG_SEVERITY should be in {debug, info, notice, warning, error, critical, alert, emergency} + msev <- lookupEnv "LOG_SEVERITY" + let s = fromMaybe K.InfoS (parseSeverity =<< msev) + -- LOG_VERBOSITY should be in {0,1,2,3} + mverb <- lookupEnv "LOG_VERBOSITY" + let v = fromMaybe K.V0 (parseVerbosity =<< mverb) + return LogLevel {severity = s, verbosity = v} + where + parseSeverity = K.textToSeverity . cs + parseVerbosity v + | v == "0" = Just K.V0 + | v == "1" = Just K.V1 + | v == "2" = Just K.V2 + | v == "3" = Just K.V3 + | otherwise = Nothing + + data KatipConfig = ES {host :: String, port :: String} | Console makeConsoleLoggingConfig :: IO KatipConfig @@ -82,27 +107,26 @@ makeConsoleLoggingConfig = do pure $ fromMaybe Console mEsConfig -- makes a log environment for console logs / ES logs -makeKatipScribe :: KatipConfig -> Kleisli IO K.LogEnv K.LogEnv -makeKatipScribe kcfg = Kleisli $ \le -> - let verbosity = K.V0 - in case kcfg of +makeKatipScribe :: KatipConfig -> LogLevel -> Kleisli IO K.LogEnv K.LogEnv +makeKatipScribe kcfg LogLevel{..} = Kleisli $ \le -> + case kcfg of Console -> do - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem K.DebugS) verbosity + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity K.registerScribe "stdout" handleScribe K.defaultScribeSettings le ES {host, port} -> do mgr <- Client.newManager Client.defaultManagerSettings let serverAddress = "http://" <> host <> ":" <> port bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr - esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "nameservice") - (BH.MappingName "application-logs") (K.permitItem K.DebugS) verbosity + esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "simple-storage") + (BH.MappingName "application-logs") (K.permitItem severity) verbosity K.registerScribe "es" esScribe K.defaultScribeSettings le -------------------------------------------------------------------------------- -- makes a log environment for metrics logs -makeMetricsScribe :: Text -> Kleisli IO K.LogEnv K.LogEnv -makeMetricsScribe key = Kleisli $ \le -> do +makeMetricsScribe :: LogLevel -> Text -> Kleisli IO K.LogEnv K.LogEnv +makeMetricsScribe LogLevel{..} key = Kleisli $ \le -> do let apiKey = DD.APIKey key datadogScribeSettings <- DD.mkDatadogScribeSettings DD.directAPIConnectionParams (DD.DirectAuth apiKey) - scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem K.InfoS) K.V0 + scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem severity) verbosity K.registerScribe "datadog" scribe K.defaultScribeSettings le diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs index 15ba555b..cb7ff58a 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs @@ -1,22 +1,21 @@ module SimpleStorage.Server (makeAndServeApplication) where -import Data.Foldable (fold) -import Data.IORef (writeIORef) -import Data.Monoid (Endo (..)) -import Network.ABCI.Server (serveApp) -import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met -import qualified Network.ABCI.Server.Middleware.RequestLogger as Req -import qualified Network.ABCI.Server.Middleware.ResponseLogger as Res -import Polysemy (Sem) -import SimpleStorage.Application (handlersContext) -import SimpleStorage.Config (AppConfig (..)) -import Tendermint.SDK.Application (createIOApp, - makeApp) -import Tendermint.SDK.BaseApp (Context (..), - CoreEffs, - runCoreEffs) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) +import Data.Foldable (fold) +import Data.IORef (writeIORef) +import Data.Monoid (Endo (..)) +import Network.ABCI.Server (serveApp) +import Network.ABCI.Server.App (Middleware) +import qualified Network.ABCI.Server.Middleware.Logger as Logger +import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met +import Polysemy (Sem) +import SimpleStorage.Application (handlersContext) +import SimpleStorage.Config (AppConfig (..)) +import Tendermint.SDK.Application (createIOApp, + makeApp) +import Tendermint.SDK.BaseApp (Context (..), + CoreEffs, + runCoreEffs) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication AppConfig{..} = do @@ -31,8 +30,7 @@ makeAndServeApplication AppConfig{..} = do application = makeApp handlersContext middleware :: Middleware (Sem CoreEffs) middleware = appEndo . fold $ - [ Endo Req.mkRequestLoggerM - , Endo Res.mkResponseLoggerM + [ Endo Logger.mkLoggerM , Endo $ Met.mkMetricsLoggerM _serverMetricsMap ] serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-extra/package.yaml b/hs-abci-extra/package.yaml index 2e054a7e..98b78ce0 100644 --- a/hs-abci-extra/package.yaml +++ b/hs-abci-extra/package.yaml @@ -44,7 +44,9 @@ library: - containers - katip - hs-abci-server + - hs-abci-types - time + - transformers ghc-options: - -Werror - -Wall diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs new file mode 100644 index 00000000..31817203 --- /dev/null +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs @@ -0,0 +1,121 @@ +module Network.ABCI.Server.Middleware.Logger + ( -- * Custom Loggers + mkLogger + , mkLoggerM + ) where + +import Control.Monad.IO.Class (MonadIO) +import Control.Monad.Trans.Class (lift) +import qualified Data.Aeson as A +import Data.ByteArray.HexString (HexString) +import Data.String (fromString) +import Katip +import Network.ABCI.Server.App (App (..), MessageType, Middleware, + Request (..), Response (..), + demoteRequestType, hashRequest, + msgTypeKey, transformApp) + +--------------------------------------------------------------------------- +-- Types +--------------------------------------------------------------------------- +-- | Loggable newtype wrapper +newtype Loggable a = Loggable a + +instance ToObject (Loggable (Request (t :: MessageType))) where + toObject (Loggable v) = case A.toJSON v of + A.Object o -> o + _ -> error "Contract violation: `toJSON` of any `Request t` must result with json object" + +instance LogItem (Loggable (Request (t :: MessageType))) where + payloadKeys V3 _ = AllKeys + payloadKeys _ _ = SomeKeys ["type"] + +instance ToObject (Loggable (Response (t :: MessageType))) where + toObject (Loggable v) = case A.toJSON v of + A.Object o -> o + _ -> error "Contract violation: `toJSON` of any `Response t` must result with json object" + +instance LogItem (Loggable (Response (t :: MessageType))) where + payloadKeys V3 _ = AllKeys + payloadKeys _ _ = SomeKeys ["type"] + +--------------------------------------------------------------------------- +-- mkLogger +--------------------------------------------------------------------------- +-- | Logger middleware for ABCI messages with custom 'Katip.LogEnv' +-- and 'Katip.Namespace'. This method makes it easy use various scribes such as +-- . +mkLogger + :: MonadIO m + => LogEnv + -> Namespace + -> Middleware m +mkLogger le ns = + transformApp (runKatipContextT le () ns) . mkLoggerM . transformApp lift + +--------------------------------------------------------------------------- +-- mkLoggerM +--------------------------------------------------------------------------- +-- | Logger middleware for ABCI messages in app with KatipContext. +-- Great for `App m` with a `KatipContext` instance. +mkLoggerM + :: KatipContext m + => Middleware m +mkLoggerM (App app) = App $ \ req -> do + let globalContext = GlobalMessageContext + { messageHash = hashRequest req + , messageType = demoteRequestType req + } + katipAddContext globalContext $ do + katipAddNamespace (fromString "server") $ + logRequest req + resp <- katipAddNamespace (fromString "application") $ + app req + katipAddNamespace (fromString "server") $ + logResponse resp + return resp + +--------------------------------------------------------------------------- +-- Common +--------------------------------------------------------------------------- + +data GlobalMessageContext = GlobalMessageContext + { messageHash :: HexString + , messageType :: MessageType + } + +instance A.ToJSON GlobalMessageContext where + toJSON GlobalMessageContext {..} = + A.object [ "message_type" A..= msgTypeKey messageType + , "message_hash" A..= messageHash + ] + +instance ToObject GlobalMessageContext + +instance LogItem GlobalMessageContext where + payloadKeys _ _ = AllKeys + +-- | Request logger function. +logRequest + :: KatipContext m + => Request t + -> m () +logRequest req = katipAddContext (Loggable req) $ + logFM logLevel "Request Received" + where + logLevel = case req of + RequestFlush _ -> DebugS + RequestEcho _ -> DebugS + _ -> InfoS + +logResponse + :: KatipContext m + => Response t + -> m () +logResponse resp = katipAddContext (Loggable resp) $ + logFM logLevel "Response Sent" + where + logLevel = case resp of + ResponseFlush _ -> DebugS + ResponseEcho _ -> DebugS + _ -> InfoS diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs index 573616c2..e070e602 100644 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs @@ -31,7 +31,7 @@ mkMetricsLogger mvarMap le ns (App app) = App $ \ req -> do metrics = Metrics mt count (diffUTCTime endTime startTime) newMetMap = Map.insert mt count metMap in pure (newMetMap, metrics) - runKatipContextT le () ns $ logMetrics metrics + runKatipContextT le () ns $ logMetrics req metrics pure res --------------------------------------------------------------------------- @@ -50,16 +50,21 @@ mkMetricsLoggerM mvarMap (App app) = App $ \ req -> do metrics = Metrics mt count (diffUTCTime endTime startTime) newMetMap = Map.insert mt count metMap in pure (newMetMap, metrics) - localKatipNamespace (<> "server") $ - logMetrics metrics + logMetrics req metrics pure res --------------------------------------------------------------------------- -- Common --------------------------------------------------------------------------- -- | Metrics logger function. -logMetrics :: (KatipContext m) => Metrics -> m () -logMetrics metrics = katipAddContext metrics $ logFM InfoS "" +logMetrics :: (KatipContext m) => Request (t :: MessageType) -> Metrics -> m () +logMetrics req metrics = + let logLevel = case req of + RequestFlush _ -> DebugS + RequestEcho _ -> DebugS + _ -> InfoS + in localKatipNamespace (<> "server") $ + katipAddContext metrics $ logFM logLevel "" data Metrics = Metrics diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs deleted file mode 100644 index 3d6905df..00000000 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/RequestLogger.hs +++ /dev/null @@ -1,53 +0,0 @@ -module Network.ABCI.Server.Middleware.RequestLogger - ( -- * Custom Loggers - mkRequestLogger - , mkRequestLoggerM - ) where - -import Control.Monad.IO.Class (MonadIO) -import qualified Data.Aeson as A -import Katip -import Network.ABCI.Server.App (App (..), MessageType, Middleware, - Request (..)) - ---------------------------------------------------------------------------- --- Types ---------------------------------------------------------------------------- --- | Loggable newtype wrapper -newtype Loggable a = Loggable a - -instance ToObject (Loggable (Request (t :: MessageType))) where - toObject (Loggable v) = case A.toJSON v of - A.Object o -> o - _ -> error "Contract violation: `toJSON` of any `Request t` must result with json object" - -instance LogItem (Loggable (Request (t :: MessageType))) where - payloadKeys V3 _ = AllKeys - payloadKeys _ _ = SomeKeys ["type"] - ---------------------------------------------------------------------------- --- mkRequestLogger ---------------------------------------------------------------------------- --- | Request logger middleware for ABCI requests with custom 'Katip.LogEnv' --- and 'Katip.Namespace'. This method makes it easy use various scribes such as --- . -mkRequestLogger :: (MonadIO m) => LogEnv -> Namespace -> Middleware m -mkRequestLogger le ns (App app) = App $ \ req -> do - runKatipContextT le () ns $ logRequest req - app req - ---------------------------------------------------------------------------- --- mkRequestLoggerM ---------------------------------------------------------------------------- --- | Request logger middleware for ABCI requests in app with KatipContext. --- Great for `App m` with a `KatipContext` instance. -mkRequestLoggerM :: (KatipContext m) => Middleware m -mkRequestLoggerM (App app) = App $ \ req -> logRequest req >> app req - ---------------------------------------------------------------------------- --- Common ---------------------------------------------------------------------------- --- | Request logger function. -logRequest :: (KatipContext m) => Request t -> m () -logRequest req = localKatipNamespace (<> "server") $ - katipAddContext (Loggable req) $ logFM InfoS "Request Received" diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs deleted file mode 100644 index 5a1074e5..00000000 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/ResponseLogger.hs +++ /dev/null @@ -1,56 +0,0 @@ -module Network.ABCI.Server.Middleware.ResponseLogger - ( -- * Custom Loggers - mkResponseLogger - , mkResponseLoggerM - ) where -import Control.Monad.IO.Class (MonadIO) -import qualified Data.Aeson as A -import Katip -import Network.ABCI.Server.App (App (..), MessageType, Middleware, - Response (..)) - ---------------------------------------------------------------------------- --- Types ---------------------------------------------------------------------------- --- | Loggable newtype wrapper -newtype Loggable a = Loggable a - -instance ToObject (Loggable (Response (t :: MessageType))) where - toObject (Loggable v) = case A.toJSON v of - A.Object o -> o - _ -> error "Contract violation: `toJSON` of any `Response t` must result with json object" - -instance LogItem (Loggable (Response (t :: MessageType))) where - payloadKeys V3 _ = AllKeys - payloadKeys _ _ = SomeKeys ["type"] - ---------------------------------------------------------------------------- --- mkResponseLogger ---------------------------------------------------------------------------- --- | Response logger middleware for ABCI requests with custom 'Katip.LogEnv' --- and 'Katip.Namespace'. This method makes it easy use various scribes such as --- . -mkResponseLogger :: (MonadIO m) => LogEnv -> Namespace -> Middleware m -mkResponseLogger le ns (App app) = App $ \ req -> do - res <- app req - runKatipContextT le () ns $ logResponse res - pure res - ---------------------------------------------------------------------------- --- mkResponseLoggerM ---------------------------------------------------------------------------- --- | Response logger middleware for ABCI requests in app with KatipContext. --- Great for `App m` with a `KatipContext` instance. -mkResponseLoggerM :: (KatipContext m) => Middleware m -mkResponseLoggerM (App app) = App $ \ req -> do - res <- app req - logResponse res - pure res - ---------------------------------------------------------------------------- --- Common ---------------------------------------------------------------------------- --- | Response logger function. -logResponse :: (KatipContext m) => Response t -> m () -logResponse req = localKatipNamespace (<> "server") $ - katipAddContext (Loggable req) $ logFM InfoS "Response Received" diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs index 99575beb..64ac0c4c 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs @@ -68,7 +68,7 @@ evalKatip evalKatip = do interpretH (\case Log severity msg -> do - raise $ K.localKatipNamespace (<> "application") $ + raise $ K.logFM (coerceSeverity severity) (fromString . cs $ msg) pureT () AddContext obj action -> do diff --git a/hs-abci-server/package.yaml b/hs-abci-server/package.yaml index e1a2afe5..802b70c6 100644 --- a/hs-abci-server/package.yaml +++ b/hs-abci-server/package.yaml @@ -24,6 +24,7 @@ default-extensions: - OverloadedStrings - MultiParamTypeClasses - FunctionalDependencies +- TypeApplications library: source-dirs: src @@ -41,9 +42,11 @@ library: - base16-bytestring - conduit - conduit-extra + - cryptonite - data-default-class - hs-abci-types - lens + - memory - proto-lens - string-conversions - text diff --git a/hs-abci-server/src/Network/ABCI/Server/App.hs b/hs-abci-server/src/Network/ABCI/Server/App.hs index 490e7bb4..c6b39ed4 100644 --- a/hs-abci-server/src/Network/ABCI/Server/App.hs +++ b/hs-abci-server/src/Network/ABCI/Server/App.hs @@ -1,5 +1,19 @@ - -module Network.ABCI.Server.App where +module Network.ABCI.Server.App + ( App(..) + , runApp + , transformApp + , withProto + , Middleware + , MessageType(..) + , demoteRequestType + , msgTypeKey + , Request(..) + , hashRequest + , Response(..) + , LPByteStrings(..) + , decodeLengthPrefix + , encodeLengthPrefix + ) where import Control.Lens ((?~), (^.)) import Control.Lens.Wrapped (Wrapped (..), @@ -26,6 +40,10 @@ import qualified Network.ABCI.Server.App.DecodeError as DecodeError import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response +import Crypto.Hash (hashWith) +import Crypto.Hash.Algorithms (SHA256 (..)) +import Data.ByteArray (convert) +import qualified Data.ByteArray.HexString as Hex import Data.Default.Class (Default (..)) import Data.ProtoLens.Message (Message (defMessage)) import Data.ProtoLens.Prism (( # )) @@ -60,7 +78,21 @@ msgTypeKey m = case m of MTEndBlock -> "endBlock" MTCommit -> "commit" -reqParseJSON :: FromJSON inner => MessageType -> (inner -> Request t) -> Value -> Parser (Request t) +demoteRequestType :: forall (t :: MessageType). Request t -> MessageType +demoteRequestType req = case req of + RequestEcho _ -> MTEcho + RequestInfo _ -> MTInfo + RequestSetOption _ -> MTSetOption + RequestQuery _ -> MTQuery + RequestCheckTx _ -> MTCheckTx + RequestFlush _ -> MTFlush + RequestInitChain _ -> MTInitChain + RequestBeginBlock _ -> MTBeginBlock + RequestDeliverTx _ -> MTDeliverTx + RequestEndBlock _ -> MTEndBlock + RequestCommit _ -> MTCommit + +reqParseJSON :: forall t inner. FromJSON inner => MessageType -> (inner -> Request t) -> Value -> Parser (Request t) reqParseJSON msgType ctr = withObject ("req:" <> expectedType) $ \v -> do actualType <- v .: "type" if actualType == expectedType @@ -138,6 +170,24 @@ instance FromJSON (Request 'MTDeliverTx) where parseJSON = reqParseJSON MTDelive instance FromJSON (Request 'MTEndBlock) where parseJSON = reqParseJSON MTEndBlock RequestEndBlock instance FromJSON (Request 'MTCommit) where parseJSON = reqParseJSON MTCommit RequestCommit +hashRequest + :: forall (t :: MessageType). + Request t + -> Hex.HexString +hashRequest req = + let requestBytes :: BS.ByteString = case req of + RequestEcho v -> PL.encodeMessage $ v ^. _Wrapped' + RequestFlush v -> PL.encodeMessage $ v ^. _Wrapped' + RequestInfo v -> PL.encodeMessage $ v ^. _Wrapped' + RequestSetOption v -> PL.encodeMessage $ v ^. _Wrapped' + RequestInitChain v -> PL.encodeMessage $ v ^. _Wrapped' + RequestQuery v -> PL.encodeMessage $ v ^. _Wrapped' + RequestBeginBlock v -> PL.encodeMessage $ v ^. _Wrapped' + RequestCheckTx v -> PL.encodeMessage $ v ^. _Wrapped' + RequestDeliverTx v -> PL.encodeMessage $ v ^. _Wrapped' + RequestEndBlock v -> PL.encodeMessage $ v ^. _Wrapped' + RequestCommit v -> PL.encodeMessage $ v ^. _Wrapped' + in Hex.fromBytes @BS.ByteString . convert $ hashWith SHA256 requestBytes withProto :: (forall (t :: MessageType). Request t -> a) From 732ca541cb38ba3cb45e5309897439c1e244a84b Mon Sep 17 00:00:00 2001 From: Ilya Ostrovskiy Date: Tue, 7 Jan 2020 15:09:38 -0500 Subject: [PATCH 35/70] Run stack setup before copying the project into Docker, to allow GHC setup to be cached (#182) --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index f20b7d7d..4e2dac2c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,11 +2,13 @@ FROM haskell:8 RUN apt-get update && apt-get install --assume-yes protobuf-compiler libsecp256k1-dev +COPY stack.yaml /tmp/stack.resolver-dummy.yaml +RUN stack --resolver `cat /tmp/stack.resolver-dummy.yaml | grep resolver | sed 's/resolver://'` setup && stack exec -- ghc --version + # Install GHC. WORKDIR /project COPY . /project # Install project to /usr/local/bin -RUN stack setup && stack exec -- ghc --version RUN stack build --copy-bins --local-bin-path /usr/local/bin From 7747f0c4c8275bda32ff4cd1bb2a5780d5c1e1d8 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Thu, 9 Jan 2020 11:00:53 -0800 Subject: [PATCH 36/70] Better metrics (#185) * simple-storage compiles * nameservice and tests compile * Remove DD_API_KEY env var everywhere occurred * weeder * fix tests --- Makefile | 7 - hs-abci-examples/nameservice/README.md | 4 +- .../nameservice/docker-compose-elk.yaml | 3 - .../nameservice/docker-compose.yaml | 3 - hs-abci-examples/nameservice/package.yaml | 2 - .../nameservice/src/Nameservice/Config.hs | 103 ++++++-------- .../nameservice/src/Nameservice/Server.hs | 43 +++--- .../test/Nameservice/Test/E2ESpec.hs | 31 +++-- hs-abci-examples/simple-storage/README.md | 1 - hs-abci-examples/simple-storage/package.yaml | 2 - .../src/SimpleStorage/Config.hs | 101 ++++++-------- .../src/SimpleStorage/Server.hs | 43 +++--- hs-abci-extra/package.yaml | 2 + .../Network/ABCI/Server/Middleware/Metrics.hs | 126 ++++++++++++++++++ .../ABCI/Server/Middleware/MetricsLogger.hs | 105 --------------- .../SDK/BaseApp/Metrics/Prometheus.hs | 22 +-- .../test/Tendermint/SDK/Test/MetricsSpec.hs | 8 +- hs-abci-server/src/Network/ABCI/Server/App.hs | 1 + 18 files changed, 293 insertions(+), 314 deletions(-) create mode 100644 hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs delete mode 100644 hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs diff --git a/Makefile b/Makefile index 068f3133..13e63311 100644 --- a/Makefile +++ b/Makefile @@ -68,20 +68,13 @@ deploy-nameservice-elk-docker: install ## run the nameservice docker network wit docker-compose -f hs-abci-examples/nameservice/docker-compose-elk.yaml up --build deploy-nameservice-docker: install ## run the nameservice docker network - DD_API_KEY=$(DD_API_KEY) \ docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml up --build deploy-simple-storage-local: install ## run the simple storage locally - ES_HOST=$(ES_HOST) \ - ES_PORT=$(ES_PORT) \ - DD_API_KEY=$(DD_API_KEY) \ STATS_PORT=$(STATS_PORT) \ stack exec simple-storage deploy-nameservice-local: install ## run the nameservice locally - ES_HOST=$(ES_HOST) \ - ES_PORT=$(ES_PORT) \ - DD_API_KEY=$(DD_API_KEY) \ STATS_PORT=$(STATS_PORT) \ stack exec nameservice diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index c7c3ae14..03be8e89 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -6,7 +6,6 @@ - ES_HOST (optional) : hostname of the elasticsearch instance for logging - ES_PORT (optional) : port number of elasticsearch instance for logging - STATS_PORT (optional) : port to run the prometheus metrics server. -- DD_API_KEY (optional) : key to post metrics data to datadog **NOTE** If you do not provide both of the elasticsearch variables, the logger will default to logging to the console. @@ -110,7 +109,6 @@ Alternatively, the app is set up to log the same information to ES and Datadog by setting the following environment variables: ```bash -DD_API_KEY ## Datadog API key ES_HOST ## Elasticsearch server host ES_PORT ## Elasticsearch server port ``` @@ -130,7 +128,7 @@ After both Kibana and ES are running, start `nameservice` and a Tendermint node via the commands: ```bash -> DD_API_KEY= ES_HOST=localhost ES_PORT=9201 make deploy-nameservice +> ES_HOST=localhost ES_PORT=9201 make deploy-nameservice > tendermint init > tendermint node ``` diff --git a/hs-abci-examples/nameservice/docker-compose-elk.yaml b/hs-abci-examples/nameservice/docker-compose-elk.yaml index 702bd717..21cc74bc 100644 --- a/hs-abci-examples/nameservice/docker-compose-elk.yaml +++ b/hs-abci-examples/nameservice/docker-compose-elk.yaml @@ -23,7 +23,6 @@ services: dockerfile: Dockerfile image: hs-abci:test environment: - - DD_API_KEY=${DD_API_KEY} - ES_HOST=elk - ES_PORT=9200 - STATS_PORT=5555 @@ -36,8 +35,6 @@ services: - "5555" datadog: image: datadog/agent:latest - environment: - - DD_API_KEY=${DD_API_KEY} volumes: - /var/run/docker.sock:/var/run/docker.sock - /proc/:/host/proc/:ro diff --git a/hs-abci-examples/nameservice/docker-compose.yaml b/hs-abci-examples/nameservice/docker-compose.yaml index b7f572be..222a48fc 100644 --- a/hs-abci-examples/nameservice/docker-compose.yaml +++ b/hs-abci-examples/nameservice/docker-compose.yaml @@ -22,7 +22,6 @@ services: dockerfile: Dockerfile image: hs-abci:test environment: - - DD_API_KEY=${DD_API_KEY} - STATS_PORT=9200 restart: always entrypoint: /usr/local/bin/nameservice @@ -31,8 +30,6 @@ services: - "9200" datadog: image: datadog/agent:latest - environment: - - DD_API_KEY=${DD_API_KEY} volumes: - /var/run/docker.sock:/var/run/docker.sock - /proc/:/host/proc/:ro diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index 670a3e92..f6494519 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -53,7 +53,6 @@ library: - base >= 4.7 && < 5 - bloodhound - bytestring - - containers - errors - hs-abci-extra - hs-abci-server @@ -61,7 +60,6 @@ library: - hs-abci-types - http-client - katip - - katip-datadog - katip-elasticsearch - lens - polysemy diff --git a/hs-abci-examples/nameservice/src/Nameservice/Config.hs b/hs-abci-examples/nameservice/src/Nameservice/Config.hs index 9e18be25..10ecb41d 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Config.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Config.hs @@ -4,43 +4,32 @@ module Nameservice.Config ( AppConfig(..) , baseAppContext , prometheusServerThreadId - , serverMetricsMap , makeAppConfig ) where -import Control.Arrow (Kleisli (..), - returnA, (>>>)) -import Control.Concurrent (ThreadId) -import Control.Concurrent.MVar (MVar, newMVar) -import Control.Error (MaybeT (..), - runMaybeT) -import Control.Lens (makeLenses, (&), - (.~), (^.), (^?), - _Just) -import Data.IORef (IORef, newIORef) -import Data.Map.Strict (Map, empty) -import Data.Maybe (fromMaybe) -import Data.String.Conversions (cs) -import Data.Text (Text) -import qualified Database.V5.Bloodhound as BH -import qualified Katip as K -import qualified Katip.Scribes.Datadog.TCP as DD -import qualified Katip.Scribes.ElasticSearch as ES -import Network.ABCI.Server.Middleware.MetricsLogger (OrderedMessageType) -import qualified Network.HTTP.Client as Client +import Control.Concurrent (ThreadId) +import Control.Error (MaybeT (..), + runMaybeT) +import Control.Lens (makeLenses, (&), + (.~), (^.)) +import Data.IORef (IORef, newIORef) +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import qualified Database.V5.Bloodhound as BH +import qualified Katip as K +import qualified Katip.Scribes.ElasticSearch as ES +import qualified Network.HTTP.Client as Client import System.Environment -import System.IO (stdout) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P -import Text.Read (read) - +import System.IO (stdout) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P +import Text.Read (read) data AppConfig = AppConfig { _baseAppContext :: BaseApp.Context , _prometheusServerThreadId :: IORef (Maybe ThreadId) - , _serverMetricsMap :: MVar (Map OrderedMessageType Integer) } makeLenses ''AppConfig @@ -48,28 +37,20 @@ makeAppConfig :: IO AppConfig makeAppConfig = do prometheusEnv <- runMaybeT $ do prometheusPort <- read <$> MaybeT (lookupEnv "STATS_PORT") - ddApiKey <- cs <$> MaybeT (lookupEnv "DD_API_KEY") - pure $ P.MetricsScrapingConfig prometheusPort ddApiKey - metricsMap <- newMVar empty + pure $ P.MetricsScrapingConfig prometheusPort c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "nameservice") prometheusEnv prometheusServer <- newIORef Nothing addScribesToLogEnv $ AppConfig { _baseAppContext = c , _prometheusServerThreadId = prometheusServer - , _serverMetricsMap = metricsMap } addScribesToLogEnv :: AppConfig -> IO AppConfig addScribesToLogEnv cfg = do logLevel <- makeLogLevel - consoleCfg <- makeConsoleLoggingConfig + loggingCfg <- makeLoggingConfig let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv - mdatadogApiKey = cfg ^? baseAppContext . BaseApp.contextPrometheusEnv . - _Just . P.envMetricsScrapingConfig . P.dataDogApiKey - addScribes = runKleisli $ - makeKatipScribe consoleCfg logLevel - >>> maybe returnA (makeMetricsScribe logLevel) mdatadogApiKey - scribesLogEnv <- addScribes initialLogEnv + scribesLogEnv <- makeKatipScribe loggingCfg logLevel initialLogEnv pure $ cfg & baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv @@ -98,35 +79,29 @@ makeLogLevel = do | v == "3" = Just K.V3 | otherwise = Nothing + data KatipConfig = ES {host :: String, port :: String} | Console -makeConsoleLoggingConfig :: IO KatipConfig -makeConsoleLoggingConfig = do +makeLoggingConfig :: IO KatipConfig +makeLoggingConfig = do mEsConfig <- runMaybeT $ ES <$> (MaybeT $ lookupEnv "ES_HOST") <*> (MaybeT $ lookupEnv "ES_PORT") pure $ fromMaybe Console mEsConfig -- makes a log environment for console logs / ES logs -makeKatipScribe :: KatipConfig -> LogLevel -> Kleisli IO K.LogEnv K.LogEnv -makeKatipScribe kcfg LogLevel{..} = Kleisli $ \le -> - case kcfg of - Console -> do - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity - K.registerScribe "stdout" handleScribe K.defaultScribeSettings le - ES {host, port} -> do - mgr <- Client.newManager Client.defaultManagerSettings - let serverAddress = "http://" <> host <> ":" <> port - bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr - esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "nameservice") - (BH.MappingName "application-logs") (K.permitItem severity) verbosity - K.registerScribe "es" esScribe K.defaultScribeSettings le - --------------------------------------------------------------------------------- - --- makes a log environment for metrics logs -makeMetricsScribe :: LogLevel -> Text -> Kleisli IO K.LogEnv K.LogEnv -makeMetricsScribe LogLevel{..} key = Kleisli $ \le -> do - let apiKey = DD.APIKey key - datadogScribeSettings <- DD.mkDatadogScribeSettings DD.directAPIConnectionParams (DD.DirectAuth apiKey) - scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem severity) verbosity - K.registerScribe "datadog" scribe K.defaultScribeSettings le +makeKatipScribe + :: KatipConfig + -> LogLevel + -> K.LogEnv + -> IO K.LogEnv +makeKatipScribe kcfg LogLevel{..} le = case kcfg of + Console -> do + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity + K.registerScribe "stdout" handleScribe K.defaultScribeSettings le + ES {host, port} -> do + mgr <- Client.newManager Client.defaultManagerSettings + let serverAddress = "http://" <> host <> ":" <> port + bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr + esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "nameservice") + (BH.MappingName "application-logs") (K.permitItem severity) verbosity + K.registerScribe "es" esScribe K.defaultScribeSettings le diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-examples/nameservice/src/Nameservice/Server.hs index 1ea5e0c3..148e3b2e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Server.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Server.hs @@ -1,21 +1,25 @@ module Nameservice.Server (makeAndServeApplication) where +import Control.Lens ((^?), _Just) +import Data.Foldable (fold) +import Data.IORef (writeIORef) +import Data.Monoid (Endo (..)) +import Nameservice.Application (handlersContext) +import Nameservice.Config (AppConfig (..)) +import Network.ABCI.Server (serveApp) +import Network.ABCI.Server.App (Middleware) +import qualified Network.ABCI.Server.Middleware.Logger as Logger +import qualified Network.ABCI.Server.Middleware.Metrics as Met +import Polysemy (Sem) -import Data.Foldable (fold) -import Data.IORef (writeIORef) -import Data.Monoid (Endo (..)) -import Nameservice.Application (handlersContext) -import Nameservice.Config (AppConfig (..)) -import Network.ABCI.Server (serveApp) -import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.Logger as Logger -import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met -import Polysemy (Sem) -import Tendermint.SDK.Application (createIOApp, - makeApp) -import Tendermint.SDK.BaseApp (Context (..), - CoreEffs, - runCoreEffs) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) +import Tendermint.SDK.Application (createIOApp, + makeApp) +import Tendermint.SDK.BaseApp (Context (..), + CoreEffs, + contextPrometheusEnv, + runCoreEffs) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (envMetricsState, + forkMetricsServer, + metricsRegistry) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication AppConfig{..} = do @@ -25,12 +29,17 @@ makeAndServeApplication AppConfig{..} = do Just prometheusEnv -> do prometheusThreadId <- forkMetricsServer prometheusEnv writeIORef _prometheusServerThreadId (Just prometheusThreadId) + metricsMiddleware <- + case _baseAppContext ^? contextPrometheusEnv . _Just . envMetricsState . metricsRegistry of + Nothing -> pure id + Just registry -> Met.mkMetricsMiddleware Met.defaultBuckets registry + let nat :: forall a. Sem CoreEffs a -> IO a nat = runCoreEffs _baseAppContext application = makeApp handlersContext middleware :: Middleware (Sem CoreEffs) middleware = appEndo . fold $ [ Endo Logger.mkLoggerM - , Endo $ Met.mkMetricsLoggerM _serverMetricsMap + , Endo metricsMiddleware ] serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 3183b13c..3d798dfa 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,6 +1,7 @@ module Nameservice.Test.E2ESpec (spec) where import Control.Lens ((^.)) +import Control.Monad (void) import Data.Default.Class (def) import Data.Proxy import Nameservice.Modules.Nameservice (BuyName (..), @@ -44,8 +45,9 @@ spec = do privateKey1 = userPrivKey user1 addr2 = userAddress user2 privateKey2 = userPrivKey user2 + faucetAmount = 1000 - beforeAll (do faucetAccount user1; faucetAccount user2) $ + beforeAll (do faucetAccount user1 faucetAmount; faucetAccount user2 faucetAmount) $ describe "Nameservice Spec" $ do it "Can query /health to make sure the node is alive" $ do resp <- runRPC RPC.health @@ -53,8 +55,7 @@ spec = do it "Can query account balances" $ do let queryReq = defaultQueryWithData addr1 - foundAmount <- getQueryResponseSuccess $ getBalance queryReq - foundAmount `shouldBe` Amount 1000 + void $ getQueryResponseSuccess $ getBalance queryReq it "Can create a name (success 0)" $ do let val = "hello world" @@ -100,9 +101,13 @@ spec = do ensureCheckAndDeliverResponseCodes (0,2) rawTx it "Can buy an existing name (success 0)" $ do - let newVal = "hello (again) world" - msg = TypedMessage "BuyName" (encode $ BuyName 300 satoshi newVal addr2) - claimedLog = NameClaimed addr2 satoshi newVal 300 + balance1 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 + balance2 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr2 + Whois{whoisPrice} <- getQueryResponseSuccess $ getWhois $ defaultQueryWithData satoshi + let purchaseAmount = whoisPrice + 1 + newVal = "hello (again) world" + msg = TypedMessage "BuyName" (encode $ BuyName purchaseAmount satoshi newVal addr2) + claimedLog = NameClaimed addr2 satoshi newVal purchaseAmount rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 @@ -110,14 +115,14 @@ spec = do -- check for updated balances - seller: addr1, buyer: addr2 let sellerQueryReq = defaultQueryWithData addr1 sellerFoundAmount <- getQueryResponseSuccess $ getBalance sellerQueryReq - sellerFoundAmount `shouldBe` Amount 1300 + sellerFoundAmount `shouldBe` (balance1 + purchaseAmount) let buyerQueryReq = defaultQueryWithData addr2 buyerFoundAmount <- getQueryResponseSuccess $ getBalance buyerQueryReq - buyerFoundAmount `shouldBe` Amount 700 + buyerFoundAmount `shouldBe` (balance2 - purchaseAmount) -- check for ownership changes let queryReq = defaultQueryWithData satoshi foundWhois <- getQueryResponseSuccess $ getWhois queryReq - foundWhois `shouldBe` Whois "hello (again) world" addr2 300 + foundWhois `shouldBe` Whois "hello (again) world" addr2 purchaseAmount -- @NOTE: this is possibly a problem with the go application too -- https://cosmos.network/docs/tutorial/buy-name.html#msg @@ -201,10 +206,10 @@ user2 = makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e03 -------------------------------------------------------------------------------- -faucetAccount :: User -> IO () -faucetAccount User{userAddress, userPrivKey} = do - let msg = TypedMessage "FaucetAccount" (encode $ FaucetAccount userAddress 1000) - faucetEvent = Faucetted userAddress 1000 +faucetAccount :: User -> Amount -> IO () +faucetAccount User{userAddress, userPrivKey} amount = do + let msg = TypedMessage "FaucetAccount" (encode $ FaucetAccount userAddress amount) + faucetEvent = Faucetted userAddress amount rawTx = mkSignedRawTransactionWithRoute "token" userPrivKey msg deliverResp <- getDeliverTxResponse rawTx ensureDeliverResponseCode deliverResp 0 diff --git a/hs-abci-examples/simple-storage/README.md b/hs-abci-examples/simple-storage/README.md index a04972ed..175d8090 100644 --- a/hs-abci-examples/simple-storage/README.md +++ b/hs-abci-examples/simple-storage/README.md @@ -10,7 +10,6 @@ allows users to update and query the count. - ES_HOST (optional) : hostname of the elasticsearch instance for logging - ES_PORT (optional) : port number of elasticsearch instance for logging - STATS_PORT (optional) : port to run the prometheus metrics server. -- DD_API_KEY (optional) : key to post metrics data to datadog **NOTE** If you do not provide both of the elasticsearch variables, the logger will default to logging to the console. diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index 51eb52d5..ad62f94c 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -62,7 +62,6 @@ library: - bytestring - cereal - cereal-text - - containers - cryptonite - errors - hs-abci-extra @@ -72,7 +71,6 @@ library: - hs-abci-sdk - http-client - katip - - katip-datadog - katip-elasticsearch - lens - memory diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs index 9d586279..022d4590 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs @@ -4,42 +4,32 @@ module SimpleStorage.Config ( AppConfig(..) , baseAppContext , prometheusServerThreadId - , serverMetricsMap , makeAppConfig ) where -import Control.Arrow (Kleisli (..), - returnA, (>>>)) -import Control.Concurrent (ThreadId) -import Control.Concurrent.MVar (MVar, newMVar) -import Control.Error (MaybeT (..), - runMaybeT) -import Control.Lens (makeLenses, (&), - (.~), (^.), (^?), - _Just) -import Data.IORef (IORef, newIORef) -import Data.Map.Strict (Map, empty) -import Data.Maybe (fromMaybe) -import Data.String.Conversions (cs) -import Data.Text (Text) -import qualified Database.V5.Bloodhound as BH -import qualified Katip as K -import qualified Katip.Scribes.Datadog.TCP as DD -import qualified Katip.Scribes.ElasticSearch as ES -import Network.ABCI.Server.Middleware.MetricsLogger (OrderedMessageType) -import qualified Network.HTTP.Client as Client +import Control.Concurrent (ThreadId) +import Control.Error (MaybeT (..), + runMaybeT) +import Control.Lens (makeLenses, (&), + (.~), (^.)) +import Data.IORef (IORef, newIORef) +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import qualified Database.V5.Bloodhound as BH +import qualified Katip as K +import qualified Katip.Scribes.ElasticSearch as ES +import qualified Network.HTTP.Client as Client import System.Environment -import System.IO (stdout) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P -import Text.Read (read) +import System.IO (stdout) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P +import Text.Read (read) data AppConfig = AppConfig { _baseAppContext :: BaseApp.Context , _prometheusServerThreadId :: IORef (Maybe ThreadId) - , _serverMetricsMap :: MVar (Map OrderedMessageType Integer) } makeLenses ''AppConfig @@ -47,28 +37,20 @@ makeAppConfig :: IO AppConfig makeAppConfig = do prometheusEnv <- runMaybeT $ do prometheusPort <- read <$> MaybeT (lookupEnv "STATS_PORT") - ddApiKey <- cs <$> MaybeT (lookupEnv "DD_API_KEY") - pure $ P.MetricsScrapingConfig prometheusPort ddApiKey - metricsMap <- newMVar empty + pure $ P.MetricsScrapingConfig prometheusPort c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") prometheusEnv prometheusServer <- newIORef Nothing addScribesToLogEnv $ AppConfig { _baseAppContext = c , _prometheusServerThreadId = prometheusServer - , _serverMetricsMap = metricsMap } addScribesToLogEnv :: AppConfig -> IO AppConfig addScribesToLogEnv cfg = do logLevel <- makeLogLevel - consoleCfg <- makeConsoleLoggingConfig + loggingCfg <- makeLoggingConfig let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv - mdatadogApiKey = cfg ^? baseAppContext . BaseApp.contextPrometheusEnv . - _Just . P.envMetricsScrapingConfig . P.dataDogApiKey - addScribes = runKleisli $ - makeKatipScribe consoleCfg logLevel - >>> maybe returnA (makeMetricsScribe logLevel) mdatadogApiKey - scribesLogEnv <- addScribes initialLogEnv + scribesLogEnv <- makeKatipScribe loggingCfg logLevel initialLogEnv pure $ cfg & baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv @@ -100,33 +82,26 @@ makeLogLevel = do data KatipConfig = ES {host :: String, port :: String} | Console -makeConsoleLoggingConfig :: IO KatipConfig -makeConsoleLoggingConfig = do +makeLoggingConfig :: IO KatipConfig +makeLoggingConfig = do mEsConfig <- runMaybeT $ ES <$> (MaybeT $ lookupEnv "ES_HOST") <*> (MaybeT $ lookupEnv "ES_PORT") pure $ fromMaybe Console mEsConfig -- makes a log environment for console logs / ES logs -makeKatipScribe :: KatipConfig -> LogLevel -> Kleisli IO K.LogEnv K.LogEnv -makeKatipScribe kcfg LogLevel{..} = Kleisli $ \le -> - case kcfg of - Console -> do - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity - K.registerScribe "stdout" handleScribe K.defaultScribeSettings le - ES {host, port} -> do - mgr <- Client.newManager Client.defaultManagerSettings - let serverAddress = "http://" <> host <> ":" <> port - bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr - esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "simple-storage") - (BH.MappingName "application-logs") (K.permitItem severity) verbosity - K.registerScribe "es" esScribe K.defaultScribeSettings le - --------------------------------------------------------------------------------- - --- makes a log environment for metrics logs -makeMetricsScribe :: LogLevel -> Text -> Kleisli IO K.LogEnv K.LogEnv -makeMetricsScribe LogLevel{..} key = Kleisli $ \le -> do - let apiKey = DD.APIKey key - datadogScribeSettings <- DD.mkDatadogScribeSettings DD.directAPIConnectionParams (DD.DirectAuth apiKey) - scribe <- DD.mkDatadogScribe datadogScribeSettings (K.permitItem severity) verbosity - K.registerScribe "datadog" scribe K.defaultScribeSettings le +makeKatipScribe + :: KatipConfig + -> LogLevel + -> K.LogEnv + -> IO K.LogEnv +makeKatipScribe kcfg LogLevel{..} le = case kcfg of + Console -> do + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity + K.registerScribe "stdout" handleScribe K.defaultScribeSettings le + ES {host, port} -> do + mgr <- Client.newManager Client.defaultManagerSettings + let serverAddress = "http://" <> host <> ":" <> port + bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr + esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "simple-storage") + (BH.MappingName "application-logs") (K.permitItem severity) verbosity + K.registerScribe "es" esScribe K.defaultScribeSettings le diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs index cb7ff58a..606ff074 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs @@ -1,21 +1,25 @@ module SimpleStorage.Server (makeAndServeApplication) where -import Data.Foldable (fold) -import Data.IORef (writeIORef) -import Data.Monoid (Endo (..)) -import Network.ABCI.Server (serveApp) -import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.Logger as Logger -import qualified Network.ABCI.Server.Middleware.MetricsLogger as Met -import Polysemy (Sem) -import SimpleStorage.Application (handlersContext) -import SimpleStorage.Config (AppConfig (..)) -import Tendermint.SDK.Application (createIOApp, - makeApp) -import Tendermint.SDK.BaseApp (Context (..), - CoreEffs, - runCoreEffs) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (forkMetricsServer) +import Control.Lens ((^?), _Just) +import Data.Foldable (fold) +import Data.IORef (writeIORef) +import Data.Monoid (Endo (..)) +import Network.ABCI.Server (serveApp) +import Network.ABCI.Server.App (Middleware) +import qualified Network.ABCI.Server.Middleware.Logger as Logger +import qualified Network.ABCI.Server.Middleware.Metrics as Met +import Polysemy (Sem) +import SimpleStorage.Application (handlersContext) +import SimpleStorage.Config (AppConfig (..)) +import Tendermint.SDK.Application (createIOApp, + makeApp) +import Tendermint.SDK.BaseApp (Context (..), + CoreEffs, + contextPrometheusEnv, + runCoreEffs) +import Tendermint.SDK.BaseApp.Metrics.Prometheus (envMetricsState, + forkMetricsServer, + metricsRegistry) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication AppConfig{..} = do @@ -25,12 +29,17 @@ makeAndServeApplication AppConfig{..} = do Just prometheusEnv -> do prometheusThreadId <- forkMetricsServer prometheusEnv writeIORef _prometheusServerThreadId (Just prometheusThreadId) + metricsMiddleware <- + case _baseAppContext ^? contextPrometheusEnv . _Just . envMetricsState . metricsRegistry of + Nothing -> pure id + Just registry -> Met.mkMetricsMiddleware Met.defaultBuckets registry + let nat :: forall a. Sem CoreEffs a -> IO a nat = runCoreEffs _baseAppContext application = makeApp handlersContext middleware :: Middleware (Sem CoreEffs) middleware = appEndo . fold $ [ Endo Logger.mkLoggerM - , Endo $ Met.mkMetricsLoggerM _serverMetricsMap + , Endo metricsMiddleware ] serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-extra/package.yaml b/hs-abci-extra/package.yaml index 98b78ce0..224ff151 100644 --- a/hs-abci-extra/package.yaml +++ b/hs-abci-extra/package.yaml @@ -45,6 +45,8 @@ library: - katip - hs-abci-server - hs-abci-types + - prometheus + - string-conversions - time - transformers ghc-options: diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs new file mode 100644 index 00000000..465b146f --- /dev/null +++ b/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs @@ -0,0 +1,126 @@ +module Network.ABCI.Server.Middleware.Metrics + ( defaultBuckets + , mkMetricsMiddleware + ) where + +import Control.Monad (forM_) +import Control.Monad.IO.Class (MonadIO, liftIO) +import qualified Data.IORef as Ref +import qualified Data.Map.Strict as Map +import Data.String.Conversions (cs) +import Data.Time (diffUTCTime, + getCurrentTime) +import Network.ABCI.Server.App (App (..), + MessageType (..), + Middleware, + demoteRequestType, + msgTypeKey) +import qualified System.Metrics.Prometheus.Concurrent.Registry as Registry +import qualified System.Metrics.Prometheus.Metric.Counter as Counter +import qualified System.Metrics.Prometheus.Metric.Histogram as Histogram +import qualified System.Metrics.Prometheus.MetricId as MetricId + +--------------------------------------------------------------------------- +-- mkMetrics +--------------------------------------------------------------------------- +-- | Metrics logger middleware for ABCI server already within the KatipContext. +-- Great for `App m` with a `KatipContext` instance. + +mkMetricsMiddleware + :: MonadIO m + => [Histogram.UpperBound] + -> Registry.Registry + -> IO (Middleware m) +mkMetricsMiddleware buckets registry = do + Config{..} <- makeConfig buckets registry + return $ \(App app) -> App $ \ req -> do + startTime <- liftIO getCurrentTime + res <- app req + endTime <- liftIO getCurrentTime + let msgType = demoteRequestType req + duration = realToFrac $ diffUTCTime endTime startTime + liftIO $ do + incRequestCounter cfgCounterMap msgType + addToHistogram cfgHistogramMap msgType duration + pure res + where + + incRequestCounter counterMapRef msgType = do + counter <- do + counterMap <- Ref.readIORef counterMapRef + case Map.lookup msgType counterMap of + Nothing -> error $ "Impossible missing counter for " <> msgTypeKey msgType + Just c -> return c + Counter.inc counter + + addToHistogram histogramMapRef msgType duration = do + histogram <- do + histMap <- Ref.readIORef histogramMapRef + case Map.lookup msgType histMap of + Nothing -> error $ "Impossible missing histogram for " <> msgTypeKey msgType + Just c -> return c + Histogram.observe duration histogram + +data Config = Config + { cfgRegistry :: Registry.Registry + , cfgHistogramBuckets :: [Histogram.UpperBound] + , cfgCounterMap :: Ref.IORef (Map.Map MessageType Counter.Counter) + , cfgHistogramMap :: Ref.IORef (Map.Map MessageType Histogram.Histogram) + } + +makeConfig + :: [Histogram.UpperBound] + -> Registry.Registry + -> IO Config +makeConfig bounds registry = do + counterMap <- Ref.newIORef Map.empty + histMap <- Ref.newIORef Map.empty + let cfg = Config + { cfgRegistry = registry + , cfgHistogramBuckets = bounds + , cfgCounterMap = counterMap + , cfgHistogramMap = histMap + } + registerMetrics cfg + return cfg + +registerMetrics + :: Config + -> IO () +registerMetrics Config{..} = do + registerHistograms cfgHistogramBuckets cfgRegistry cfgHistogramMap + registerCounters cfgRegistry cfgCounterMap + where + + registerHistograms + :: [Histogram.UpperBound] + -> Registry.Registry + -> Ref.IORef (Map.Map MessageType Histogram.Histogram) + -> IO () + registerHistograms buckets registry histRef = + let histName = "abci_request_duration_seconds" + in forM_ [MTEcho .. MTCommit] $ \messageType -> do + let labels = MetricId.Labels . Map.fromList $ + [ ("message_type", cs $ msgTypeKey messageType) + ] + hist <- Registry.registerHistogram histName labels buckets registry + Ref.modifyIORef' histRef (Map.insert messageType hist) + + + registerCounters + :: Registry.Registry + -> Ref.IORef (Map.Map MessageType Counter.Counter) + -> IO () + registerCounters registry counterRef = + let counterName = "abci_request_total" + in forM_ [MTEcho .. MTCommit] $ \messageType -> do + let labels = MetricId.Labels . Map.fromList $ + [ ("message_type", cs $ msgTypeKey messageType) + ] + counter <- Registry.registerCounter counterName labels registry + Ref.modifyIORef' counterRef (Map.insert messageType counter) + +-- buckets with upper bounds [0.005, 0.01, 0.015 ... 5.0] +-- measured in seconds +defaultBuckets :: [Histogram.UpperBound] +defaultBuckets = [0.0001, 0.0005, 0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1.0, 5.0, 10.0] diff --git a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs b/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs deleted file mode 100644 index e070e602..00000000 --- a/hs-abci-extra/src/Network/ABCI/Server/Middleware/MetricsLogger.hs +++ /dev/null @@ -1,105 +0,0 @@ -module Network.ABCI.Server.Middleware.MetricsLogger - ( -- * Custom Loggers - mkMetricsLogger - , mkMetricsLoggerM - , OrderedMessageType(..) - ) where -import Control.Concurrent.MVar (MVar, modifyMVar) -import Control.Monad.IO.Class (MonadIO, liftIO) -import qualified Data.Aeson as A -import Data.Map.Strict (Map) -import qualified Data.Map.Strict as Map -import Data.Time (NominalDiffTime, diffUTCTime, - getCurrentTime) -import Katip -import Network.ABCI.Server.App (App (..), MessageType (..), - Middleware, Request (..), msgTypeKey) - ---------------------------------------------------------------------------- --- mkMetricsLogger ---------------------------------------------------------------------------- --- | Request logger middleware for ABCI metrics with custom 'Katip.LogEnv' --- and 'Katip.Namespace'. -mkMetricsLogger :: (MonadIO m) => MVar (Map OrderedMessageType Integer) -> LogEnv -> Namespace -> Middleware m -mkMetricsLogger mvarMap le ns (App app) = App $ \ req -> do - startTime <- liftIO getCurrentTime - res <- app req - endTime <- liftIO getCurrentTime - metrics <- liftIO $ modifyMVar mvarMap $ \metMap -> - let mt = requestToMessageType req - count = maybe 1 (+1) (metMap Map.!? mt) - metrics = Metrics mt count (diffUTCTime endTime startTime) - newMetMap = Map.insert mt count metMap - in pure (newMetMap, metrics) - runKatipContextT le () ns $ logMetrics req metrics - pure res - ---------------------------------------------------------------------------- --- mkMetricsLoggerM ---------------------------------------------------------------------------- --- | Metrics logger middleware for ABCI server already within the KatipContext. --- Great for `App m` with a `KatipContext` instance. -mkMetricsLoggerM :: (KatipContext m) => MVar (Map OrderedMessageType Integer) -> Middleware m -mkMetricsLoggerM mvarMap (App app) = App $ \ req -> do - startTime <- liftIO getCurrentTime - res <- app req - endTime <- liftIO getCurrentTime - metrics <- liftIO $ modifyMVar mvarMap $ \metMap -> - let mt = requestToMessageType req - count = maybe 1 (+1) (metMap Map.!? mt) - metrics = Metrics mt count (diffUTCTime endTime startTime) - newMetMap = Map.insert mt count metMap - in pure (newMetMap, metrics) - logMetrics req metrics - pure res - ---------------------------------------------------------------------------- --- Common ---------------------------------------------------------------------------- --- | Metrics logger function. -logMetrics :: (KatipContext m) => Request (t :: MessageType) -> Metrics -> m () -logMetrics req metrics = - let logLevel = case req of - RequestFlush _ -> DebugS - RequestEcho _ -> DebugS - _ -> InfoS - in localKatipNamespace (<> "server") $ - katipAddContext metrics $ logFM logLevel "" - - -data Metrics = Metrics - { metricsMessageType :: OrderedMessageType - , metricsMessageCount :: Integer - , metricsResponseTime :: NominalDiffTime - } -instance A.ToJSON Metrics where - toJSON Metrics{..} = A.object - [ "message_type" A..= A.toJSON ((msgTypeKey $ unOrderedMessageType metricsMessageType) :: String) - , "message_count" A..= A.toJSON metricsMessageCount - , "response_time" A..= A.toJSON metricsResponseTime - ] - -instance ToObject Metrics -instance LogItem Metrics where - payloadKeys _ _ = AllKeys - -newtype OrderedMessageType = OrderedMessageType {unOrderedMessageType :: MessageType} - -instance Eq OrderedMessageType where - (==) (OrderedMessageType a) (OrderedMessageType b) = msgTypeKey a == msgTypeKey b - -instance Ord OrderedMessageType where - (<=) (OrderedMessageType a) (OrderedMessageType b) = msgTypeKey a <= msgTypeKey b - -requestToMessageType :: Request (t :: MessageType) -> OrderedMessageType -requestToMessageType (RequestEcho _) = OrderedMessageType MTEcho -requestToMessageType (RequestInfo _) = OrderedMessageType MTInfo -requestToMessageType (RequestSetOption _) = OrderedMessageType MTSetOption -requestToMessageType (RequestQuery _) = OrderedMessageType MTQuery -requestToMessageType (RequestCheckTx _) = OrderedMessageType MTCheckTx -requestToMessageType (RequestFlush _) = OrderedMessageType MTFlush -requestToMessageType (RequestInitChain _) = OrderedMessageType MTInitChain -requestToMessageType (RequestBeginBlock _) = OrderedMessageType MTBeginBlock -requestToMessageType (RequestDeliverTx _) = OrderedMessageType MTDeliverTx -requestToMessageType (RequestEndBlock _) = OrderedMessageType MTEndBlock -requestToMessageType (RequestCommit _) = OrderedMessageType MTCommit diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs index 840cad72..db06fc61 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -4,8 +4,10 @@ module Tendermint.SDK.BaseApp.Metrics.Prometheus ( -- config and setup MetricsScrapingConfig(..) , prometheusPort - , dataDogApiKey , MetricsState(..) + , metricsRegistry + , metricsCounters + , metricsHistograms , PrometheusEnv(..) , envMetricsState , envMetricsScrapingConfig @@ -59,10 +61,11 @@ import Tendermint.SDK.BaseApp.Metrics (CountName (..), type MetricsMap a = Map (Text, MetricId.Labels) a data MetricsState = MetricsState - { metricsRegistry :: Registry.Registry - , metricsCounters :: MVar (MetricsMap Counter.Counter) - , metricsHistograms :: MVar (MetricsMap Histogram.Histogram) + { _metricsRegistry :: Registry.Registry + , _metricsCounters :: MVar (MetricsMap Counter.Counter) + , _metricsHistograms :: MVar (MetricsMap Histogram.Histogram) } +makeLenses ''MetricsState -- | Intermediary prometheus registry index key data MetricIdentifier = MetricIdentifier @@ -116,7 +119,6 @@ metricIdStorable c = (fixMetricName $ metricIdName c, fixMetricLabels $ metricId -- | Core metrics config data MetricsScrapingConfig = MetricsScrapingConfig { _prometheusPort :: Int - , _dataDogApiKey :: Text } makeLenses ''MetricsScrapingConfig @@ -143,7 +145,7 @@ forkMetricsServer metCfg = liftIO $ let PrometheusEnv{..} = metCfg port = _prometheusPort $ _envMetricsScrapingConfig MetricsState{..} = _envMetricsState - in forkIO $ Http.serveHttpTextMetrics port ["metrics"] (Registry.sample metricsRegistry) + in forkIO $ Http.serveHttpTextMetrics port ["metrics"] (Registry.sample _metricsRegistry) -------------------------------------------------------------------------------- -- eval @@ -184,11 +186,11 @@ evalMetrics state@MetricsState{..} = do let c@MetricIdentifier{..} = countToIdentifier ctrName cid = metricIdStorable c cMetricIdName = MetricId.Name metricIdName - liftIO $ modifyMVar_ metricsCounters $ \counterMap -> + liftIO $ modifyMVar_ _metricsCounters $ \counterMap -> case Map.lookup cid counterMap of Nothing -> do newCtr <- liftIO $ - Registry.registerCounter cMetricIdName metricIdLabels metricsRegistry + Registry.registerCounter cMetricIdName metricIdLabels _metricsRegistry let newCounterMap = insert cid newCtr counterMap liftIO $ Counter.inc newCtr pure newCounterMap @@ -214,11 +216,11 @@ observeHistogram MetricsState{..} histName val = liftIO $ do let h@MetricIdentifier{..} = histogramToIdentifier histName hid = metricIdStorable h hMetricIdName = MetricId.Name metricIdName - modifyMVar_ metricsHistograms $ \histMap -> + modifyMVar_ _metricsHistograms $ \histMap -> case Map.lookup hid histMap of Nothing -> do newHist <- - Registry.registerHistogram hMetricIdName metricIdLabels metricIdHistoBuckets metricsRegistry + Registry.registerHistogram hMetricIdName metricIdLabels metricIdHistoBuckets _metricsRegistry let newHistMap = insert hid newHist histMap Histogram.observe val newHist pure $ newHistMap diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs index d6036b30..26ab0e3c 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/MetricsSpec.hs @@ -40,17 +40,17 @@ spec = describe "Metrics tests" $ do state@MetricsState{..} <- emptyState -- Creates new count and sets it to 1 _ <- eval state $ incCount countName - newCtrIndex <- readMVar metricsCounters + newCtrIndex <- readMVar _metricsCounters newCtrValue <- Counter.sample $ newCtrIndex ! cid Counter.unCounterSample newCtrValue `shouldBe` 1 -- register should contain new counter metric - newRegistrySample <- Registry.sample metricsRegistry + newRegistrySample <- Registry.sample _metricsRegistry let registryMap = RSample.unRegistrySample newRegistrySample (Metric.CounterMetricSample registryCtrSample) = registryMap ! cMetricId Counter.unCounterSample registryCtrSample `shouldBe` 1 -- increment it again _ <- eval state $ incCount countName - incCtrIndex <- readMVar metricsCounters + incCtrIndex <- readMVar _metricsCounters incCtrValue <- Counter.sample $ incCtrIndex ! cid Counter.unCounterSample incCtrValue `shouldBe` 2 @@ -62,7 +62,7 @@ spec = describe "Metrics tests" $ do -- time an action _ <- eval state $ withTimer buckettedHistName shine -- check registry hist buckets - newRegistrySample <- Registry.sample metricsRegistry + newRegistrySample <- Registry.sample _metricsRegistry let registryMap = RSample.unRegistrySample newRegistrySample (Metric.HistogramMetricSample registryHistSample) = registryMap ! buckettedHMetricId histBuckets = Histogram.histBuckets registryHistSample diff --git a/hs-abci-server/src/Network/ABCI/Server/App.hs b/hs-abci-server/src/Network/ABCI/Server/App.hs index c6b39ed4..c77b8b68 100644 --- a/hs-abci-server/src/Network/ABCI/Server/App.hs +++ b/hs-abci-server/src/Network/ABCI/Server/App.hs @@ -63,6 +63,7 @@ data MessageType | MTDeliverTx | MTEndBlock | MTCommit + deriving (Eq, Ord, Enum) msgTypeKey :: MessageType -> String msgTypeKey m = case m of From 6345a3182795497e8d0d4d37a8c1d97249a0b44a Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 13 Jan 2020 15:29:51 -0800 Subject: [PATCH 37/70] Make datadog work (#186) * autodiscovery template * datadog seems to work * rename docker files, remove local option * fix makefile * remove datadog image from test --- .dockerignore | 1 + .travis.yml | 4 +- Makefile | 21 +++---- .../conf.d/openmetrics.d/conf.yaml | 10 ++++ .../nameservice/docker-compose-elk.yaml | 56 ------------------- .../nameservice/docker-compose-test.yaml | 32 +++++++++++ .../nameservice/docker-compose.yaml | 33 +++++++---- .../Nameservice/Modules/Nameservice/Router.hs | 12 ++-- .../simple-storage/docker-compose.yaml | 2 + .../SDK/BaseApp/Metrics/Prometheus.hs | 2 +- 10 files changed, 85 insertions(+), 88 deletions(-) create mode 100644 hs-abci-examples/nameservice/conf.d/openmetrics.d/conf.yaml delete mode 100644 hs-abci-examples/nameservice/docker-compose-elk.yaml create mode 100644 hs-abci-examples/nameservice/docker-compose-test.yaml diff --git a/.dockerignore b/.dockerignore index 4766dffb..341d88f6 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,3 +3,4 @@ **/.stack **/stack.yaml.lock **/.git +hs-abci-examples/nameservice/conf.d diff --git a/.travis.yml b/.travis.yml index 378124df..284f734e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -59,9 +59,9 @@ jobs: - name: "Test Nameservice Example E2E" script: - make docker-test-prebake - - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml -p test-hs-abci-examples-nameservice-e2e up -d + - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e up -d - make test-nameservice - - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local + - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local - name: "Test Nameservice Tutorial" script: - travis_wait 120 stack --skip-ghc-check install markdown-unlit-0.5.0 diff --git a/Makefile b/Makefile index 13e63311..9e757b0f 100644 --- a/Makefile +++ b/Makefile @@ -62,21 +62,18 @@ test-iavl-client: ## test the iavl client library basic operation (requires grpc ##################### deploy-simple-storage-docker: install ## run the simple storage docker network - docker-compose -f hs-abci-examples/simple-storage/docker-compose-elk.yaml up --build + docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml up --build -deploy-nameservice-elk-docker: install ## run the nameservice docker network with elk stack for logging - docker-compose -f hs-abci-examples/nameservice/docker-compose-elk.yaml up --build - -deploy-nameservice-docker: install ## run the nameservice docker network +deploy-nameservice: install ## run the nameservice docker network with elk stack for logging docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml up --build -deploy-simple-storage-local: install ## run the simple storage locally - STATS_PORT=$(STATS_PORT) \ - stack exec simple-storage +deploy-nameservice-test: install ## run the nameservice docker network for testing + docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml up --build + -deploy-nameservice-local: install ## run the nameservice locally - STATS_PORT=$(STATS_PORT) \ - stack exec nameservice +##################### +# Tests +##################### test-kv-store: install ## Run the test suite for the client interface stack test hs-tendermint-client @@ -90,8 +87,6 @@ test-nameservice: install ## Run the test suite for the nameservice example appl test-tutorial: install ## Make sure the tutorial builds stack test nameservice:tutorial - - ##################### # CI Support ##################### diff --git a/hs-abci-examples/nameservice/conf.d/openmetrics.d/conf.yaml b/hs-abci-examples/nameservice/conf.d/openmetrics.d/conf.yaml new file mode 100644 index 00000000..ea7fc699 --- /dev/null +++ b/hs-abci-examples/nameservice/conf.d/openmetrics.d/conf.yaml @@ -0,0 +1,10 @@ +## This is configured to work with the docker network, if you want to run in a +## different setting you will need to change 'prometheus_url'. +init_config: + +instances: + - prometheus_url: http://nameservice:5555/metrics + ## namespace option prefixes all metric names in datadog + namespace: nameservice + ## metrics names used in the nameservice app + metrics: ["*"] diff --git a/hs-abci-examples/nameservice/docker-compose-elk.yaml b/hs-abci-examples/nameservice/docker-compose-elk.yaml deleted file mode 100644 index 21cc74bc..00000000 --- a/hs-abci-examples/nameservice/docker-compose-elk.yaml +++ /dev/null @@ -1,56 +0,0 @@ -version: '3.7' -services: - tendermint-init: - image: tendermint/tendermint:v0.32.8 - command: init - volumes: - - tendermint-storage:/tendermint - tendermint: - depends_on: - - tendermint-init - - nameservice - image: tendermint/tendermint:v0.32.8 - command: node --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://nameservice:26658 - volumes: - - tendermint-storage:/tendermint - restart: always - ports: - - "26656-26657:26656-26657" - nameservice: - entrypoint: /usr/local/bin/nameservice - build: - context: ../../. - dockerfile: Dockerfile - image: hs-abci:test - environment: - - ES_HOST=elk - - ES_PORT=9200 - - STATS_PORT=5555 - restart: always - depends_on: - - elk - - datadog - expose: - - "26658" - - "5555" - datadog: - image: datadog/agent:latest - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - /proc/:/host/proc/:ro - - /sys/fs/cgroup:/host/sys/fs/cgroup:ro - labels: - com.datadoghq.ad.check_names: '["openmetrics"]' - com.datadoghq.ad.init_configs: '["{}"]' - com.datadoghq.ad.instances: '["{\"prometheus_url\":\"http://%%host%%:5555/metrics \",\"namespace\":\"nameservice\",\"metrics\":[\"count_buy\" \"count_set\" \"count_delete\" \"histogram_buy*\" \"histogram_set*\" \"histogram_delete*\"]}"]' - elk: - image: sebp/elk:683 - ports: - - "5601:5601" - - "9200:9200" - - "5044:5044" - expose: - - "9200" - - "5601" -volumes: - tendermint-storage: diff --git a/hs-abci-examples/nameservice/docker-compose-test.yaml b/hs-abci-examples/nameservice/docker-compose-test.yaml new file mode 100644 index 00000000..b7f15d9d --- /dev/null +++ b/hs-abci-examples/nameservice/docker-compose-test.yaml @@ -0,0 +1,32 @@ +version: '3.7' +services: + tendermint-init: + image: tendermint/tendermint:v0.32.8 + command: init + volumes: + - tendermint-storage:/tendermint + tendermint: + depends_on: + - tendermint-init + - nameservice + image: tendermint/tendermint:v0.32.8 + command: node --rpc.laddr=tcp://0.0.0.0:26657 --proxy_app=tcp://nameservice:26658 + volumes: + - tendermint-storage:/tendermint + restart: always + ports: + - "26656-26657:26656-26657" + nameservice: + build: + context: ../../. + dockerfile: Dockerfile + image: hs-abci:test + environment: + - STATS_PORT=9200 + restart: always + entrypoint: /usr/local/bin/nameservice + expose: + - "26658" + - "9200" +volumes: + tendermint-storage: diff --git a/hs-abci-examples/nameservice/docker-compose.yaml b/hs-abci-examples/nameservice/docker-compose.yaml index 222a48fc..51efa18c 100644 --- a/hs-abci-examples/nameservice/docker-compose.yaml +++ b/hs-abci-examples/nameservice/docker-compose.yaml @@ -17,26 +17,39 @@ services: ports: - "26656-26657:26656-26657" nameservice: + entrypoint: /usr/local/bin/nameservice build: context: ../../. dockerfile: Dockerfile image: hs-abci:test environment: - - STATS_PORT=9200 + - ES_HOST=elk + - ES_PORT=9200 + - STATS_PORT=5555 restart: always - entrypoint: /usr/local/bin/nameservice + depends_on: + - elk expose: - "26658" - - "9200" + - "5555:5555" datadog: image: datadog/agent:latest + depends_on: + - nameservice + restart: always + environment: + - DD_API_KEY=${DD_API_KEY} volumes: - - /var/run/docker.sock:/var/run/docker.sock - /proc/:/host/proc/:ro - - /sys/fs/cgroup:/host/sys/fs/cgroup:ro - labels: - com.datadoghq.ad.check_names: '["openmetrics"]' - com.datadoghq.ad.init_configs: '["{}"]' - com.datadoghq.ad.instances: '["{\"prometheus_url\":\"http://%%host%%:9200/metrics \",\"namespace\":\"nameservice\",\"metrics\":[\"count_buy\" \"count_set\" \"count_delete\" \"histogram_buy*\" \"histogram_set*\" \"histogram_delete*\"]}"]' + - ./conf.d/openmetrics.d:/etc/datadog-agent/conf.d/openmetrics.d + elk: + image: sebp/elk:683 + ports: + - "5601:5601" + - "9200:9200" + - "5044:5044" + expose: + - "9200" + - "5601" volumes: - tendermint-storage: + tendermint-storage: \ No newline at end of file diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 848ce5c6..b77e799f 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -23,11 +23,11 @@ router (RoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of NSetName msg -> do - incCount "count_set" - withTimer "histogram_set" $ setName msg + incCount "set_total" + withTimer "set_duration_seconds" $ setName msg NBuyName msg -> do - incCount "count_buy" - withTimer "histogram_buy" $ buyName msg + incCount "buy_total" + withTimer "buy_duration_seconds" $ buyName msg NDeleteName msg -> do - incCount "count_delete" - withTimer "histogram_delete" $ deleteName msg + incCount "delete_total" + withTimer "delete_duration_seconds" $ deleteName msg diff --git a/hs-abci-examples/simple-storage/docker-compose.yaml b/hs-abci-examples/simple-storage/docker-compose.yaml index 4ad1ed9d..6e3649d1 100644 --- a/hs-abci-examples/simple-storage/docker-compose.yaml +++ b/hs-abci-examples/simple-storage/docker-compose.yaml @@ -21,6 +21,8 @@ services: context: ../../. dockerfile: Dockerfile image: hs-abci:test + environment: + - DD_API_KEY=${DD_API_KEY} restart: always entrypoint: /usr/local/bin/simple-storage expose: diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs index db06fc61..56d34285 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -205,7 +205,7 @@ evalMetrics state@MetricsState{..} = do start <- liftIO $ getCurrentTime a <- runT action end <- liftIO $ getCurrentTime - let time = fromRational . (* 1000.0) . toRational $ (end `diffUTCTime` start) + let time = realToFrac (end `diffUTCTime` start) observeHistogram state histName time raise $ evalMetrics state a ) From 3f17cf583b8ee8a48f4d775039ebf9582b61cfa0 Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Mon, 13 Jan 2020 23:43:38 -0500 Subject: [PATCH 38/70] Add Nonce (#187) * Add modifyAccount * Add nonce to rawTransaction * Change txRouter to use RawTransaction * cleanup * wip add antehandler * rename RoutedTx to PreRoutedTx; reverse changes to handlers * impl basic nonceChecker + propagate * add antehandler to tutorial + cleanup * wip trying to fix crytpo spec * revert some types and fix test * Add runAnteHandler * compiles * wip: add postAnteHandler * add msg to NonceException * wip: compiles, but doesn't solve nonce problem * Add account queries in Apps * WIP: only tx nonce isn't being populated properly * add logs * add routecontext to antehandlers * Fix nameservice tests +fix modifyAccount * remove printf debugging * fix merge issues * remove comment * move nonce query to utils library * add some binds * PR concerns * remove docs page * remove modifyAccount * stylish Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- .gitignore | 1 + .../src/Nameservice/Application.hs | 4 +- .../Nameservice/Modules/Nameservice/Router.hs | 6 +- .../src/Nameservice/Modules/Token/Router.hs | 6 +- .../test/Nameservice/Test/E2ESpec.hs | 35 ++++----- .../Tutorial/Nameservice/Application.md | 3 +- .../src/SimpleStorage/Application.hs | 4 +- .../Modules/SimpleStorage/Router.hs | 6 +- .../test/SimpleStorage/Test/E2ESpec.hs | 4 +- hs-abci-sdk/package.yaml | 1 + hs-abci-sdk/protos/types/transaction.proto | 1 + hs-abci-sdk/src/Tendermint/SDK/Application.hs | 3 + .../Tendermint/SDK/Application/AnteHandler.hs | 58 +++++++++++++++ .../Tendermint/SDK/Application/Handlers.hs | 71 +++++++++++-------- .../src/Tendermint/SDK/Application/Module.hs | 43 +++++------ .../src/Tendermint/SDK/BaseApp/Errors.hs | 11 ++- .../src/Tendermint/SDK/BaseApp/Transaction.hs | 6 +- .../src/Tendermint/SDK/Modules/Auth.hs | 2 + .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 44 +++++++++--- .../src/Tendermint/SDK/Modules/Auth/Types.hs | 16 ++++- .../src/Tendermint/SDK/Types/Transaction.hs | 34 +++++---- .../test/Tendermint/SDK/Test/CryptoSpec.hs | 7 +- hs-abci-test-utils/package.yaml | 2 + .../src/Tendermint/Utils/User.hs | 48 ++++++++++--- 24 files changed, 282 insertions(+), 134 deletions(-) create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs diff --git a/.gitignore b/.gitignore index b40ba9c4..1405be03 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ *.cabal *~ .ci-bins/ +*.dump-hi diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-examples/nameservice/src/Nameservice/Application.hs index 0eb21fcb..6fa25729 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Application.hs @@ -8,7 +8,8 @@ import Data.Proxy import qualified Nameservice.Modules.Nameservice as N import qualified Nameservice.Modules.Token as T import Tendermint.SDK.Application (HandlersContext (..), - Modules (..)) + Modules (..), + baseAppAnteHandler) import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Crypto (Secp256k1) @@ -31,6 +32,7 @@ handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules , compileToCore = BaseApp.compileScopedEff + , anteHandler = baseAppAnteHandler } where nameserviceModules :: Modules NameserviceModules EffR diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index b77e799f..4a274226 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -9,7 +9,7 @@ import Polysemy (Members, Sem) import Tendermint.SDK.BaseApp (BaseAppEffs, TxEffs, incCount, withTimer) import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (RoutedTx (..), +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) router @@ -17,9 +17,9 @@ router => Members NameserviceEffs r => Members BaseAppEffs r => Members TxEffs r - => RoutedTx NameserviceMessage + => PreRoutedTx NameserviceMessage -> Sem r () -router (RoutedTx Tx{txMsg}) = +router (PreRoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of NSetName msg -> do diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs index f78ed406..b427a342 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs @@ -8,15 +8,15 @@ import Nameservice.Modules.Token.Messages (Burn (..), import Polysemy (Members, Sem) import Tendermint.SDK.BaseApp (BaseAppEffs, TxEffs) import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) router :: Members TokenEffs r => Members BaseAppEffs r => Members TxEffs r - => RoutedTx TokenMessage + => PreRoutedTx TokenMessage -> Sem r () -router (RoutedTx Tx{txMsg}) = +router (PreRoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of TFaucetAccount faucet -> diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index 3d798dfa..f2caa15d 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -42,9 +42,7 @@ spec :: Spec spec = do let satoshi = Name "satoshi" addr1 = userAddress user1 - privateKey1 = userPrivKey user1 addr2 = userAddress user2 - privateKey2 = userPrivKey user2 faucetAmount = 1000 beforeAll (do faucetAccount user1 faucetAmount; faucetAccount user2 faucetAmount) $ @@ -61,8 +59,7 @@ spec = do let val = "hello world" msg = TypedMessage "BuyName" (encode $ BuyName 0 satoshi val addr1) claimedLog = NameClaimed addr1 satoshi val 0 - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user1 msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "NameClaimed" claimedLog @@ -85,8 +82,7 @@ spec = do newVal = "goodbye to a world" msg = TypedMessage "SetName" (encode $ SetName satoshi addr1 newVal) remappedLog = NameRemapped satoshi oldVal newVal - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user1 msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "NameRemapped" remappedLog -- check for changes @@ -97,8 +93,7 @@ spec = do it "Can fail to set a name (failure 2)" $ do -- try to set a name without being the owner let msg = TypedMessage "SetName" (encode $ SetName satoshi addr2 "goodbye to a world") - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg - ensureCheckAndDeliverResponseCodes (0,2) rawTx + ensureCheckAndDeliverResponseCodes (0,2) =<< mkSignedRawTransactionWithRoute "nameservice" user2 msg it "Can buy an existing name (success 0)" $ do balance1 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 @@ -108,8 +103,7 @@ spec = do newVal = "hello (again) world" msg = TypedMessage "BuyName" (encode $ BuyName purchaseAmount satoshi newVal addr2) claimedLog = NameClaimed addr2 satoshi newVal purchaseAmount - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user2 msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "NameClaimed" claimedLog -- check for updated balances - seller: addr1, buyer: addr2 @@ -134,8 +128,7 @@ spec = do let val = "hello (again) world" msg = TypedMessage "BuyName" (encode $ BuyName 500 satoshi val addr2) claimedLog = NameClaimed addr2 satoshi val 500 - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user2 msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "NameClaimed" claimedLog -- check balance after @@ -146,14 +139,12 @@ spec = do it "Can fail to buy a name (failure 1)" $ do -- try to buy at a lower price let msg = TypedMessage "BuyName" (encode $ BuyName 100 satoshi "hello (again) world" addr1) - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey1 msg - ensureCheckAndDeliverResponseCodes (0,1) rawTx + mkSignedRawTransactionWithRoute "nameservice" user1 msg >>= ensureCheckAndDeliverResponseCodes (0,1) it "Can delete names (success 0)" $ do let msg = TypedMessage "DeleteName" (encode $ DeleteName addr2 satoshi) deletedLog = NameDeleted satoshi - rawTx = mkSignedRawTransactionWithRoute "nameservice" privateKey2 msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user2 msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "NameDeleted" deletedLog -- name shouldn't exist @@ -169,8 +160,7 @@ spec = do addr2Balance <- getQueryResponseSuccess $ getBalance senderBeforeQueryReq let tooMuchToTransfer = addr2Balance + 1 msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 tooMuchToTransfer) - rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg - ensureCheckAndDeliverResponseCodes (0,1) rawTx + ensureCheckAndDeliverResponseCodes (0,1) =<< mkSignedRawTransactionWithRoute "token" user2 msg it "Can transfer (success 0)" $ do balance1 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 @@ -187,8 +177,7 @@ spec = do , transferEventTo = addr2 , transferEventFrom = addr1 } - rawTx = mkSignedRawTransactionWithRoute "token" privateKey1 msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "token" user1 msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "TransferEvent" transferEvent -- check balances @@ -198,6 +187,7 @@ spec = do balance2' `shouldBe` balance2 + transferAmount -------------------------------------------------------------------------------- + user1 :: User user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" @@ -207,11 +197,10 @@ user2 = makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e03 -------------------------------------------------------------------------------- faucetAccount :: User -> Amount -> IO () -faucetAccount User{userAddress, userPrivKey} amount = do +faucetAccount user@User{userAddress} amount = do let msg = TypedMessage "FaucetAccount" (encode $ FaucetAccount userAddress amount) faucetEvent = Faucetted userAddress amount - rawTx = mkSignedRawTransactionWithRoute "token" userPrivKey msg - deliverResp <- getDeliverTxResponse rawTx + deliverResp <- mkSignedRawTransactionWithRoute "token" user msg >>= getDeliverTxResponse ensureDeliverResponseCode deliverResp 0 ensureEventLogged deliverResp "Faucetted" faucetEvent diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md index 325572e6..1eb05d17 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md @@ -42,7 +42,7 @@ import Nameservice.Modules.Token (tokenModule, TokenM, TokenEffs) import Network.ABCI.Server.App (App) import Polysemy (Sem) import Tendermint.SDK.Modules.Auth (authModule, AuthEffs, AuthM) -import Tendermint.SDK.Application (Modules(..), HandlersContext(..), makeApp) +import Tendermint.SDK.Application (Modules(..), HandlersContext(..), baseAppAnteHandler, makeApp) import Tendermint.SDK.BaseApp (BaseApp, CoreEffs, (:&), compileScopedEff) import Tendermint.SDK.Crypto (Secp256k1) ~~~ @@ -76,6 +76,7 @@ handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules , compileToCore = compileScopedEff + , anteHandler = baseAppAnteHandler } where nameserviceModules :: Modules NameserviceModules EffR diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs index c378f6dc..bca6eeb5 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs @@ -7,7 +7,8 @@ module SimpleStorage.Application import Data.Proxy import SimpleStorage.Modules.SimpleStorage as SimpleStorage import Tendermint.SDK.Application (HandlersContext (..), - Modules (..)) + Modules (..), + baseAppAnteHandler) import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Crypto (Secp256k1) @@ -30,6 +31,7 @@ handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = simpleStorageModules , compileToCore = BaseApp.compileScopedEff + , anteHandler = baseAppAnteHandler } where simpleStorageModules :: Modules SimpleStorageModules EffR diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs index aaab2442..817363df 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -10,16 +10,16 @@ import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) import Tendermint.SDK.BaseApp (TxEffs) import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (RoutedTx (..), +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) router :: Member SimpleStorage r => Members TxEffs r - => RoutedTx SimpleStorageMessage + => PreRoutedTx SimpleStorageMessage -> Sem r () -router (RoutedTx Tx{txMsg}) = +router (PreRoutedTx Tx{txMsg}) = let Msg{msgData} = txMsg in case msgData of UpdateCount UpdateCountTx{updateCountTxCount} -> diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 30d59dea..cc31c2d4 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -35,8 +35,8 @@ spec = do it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do let txMsg = SS.UpdateCount $ SS.UpdateCountTx "irakli" 4 - tx = mkSignedRawTransactionWithRoute "simple_storage" (userPrivKey user1) txMsg - txReq = RPC.RequestBroadcastTxCommit + tx <- mkSignedRawTransactionWithRoute "simple_storage" user1 txMsg + let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = Base64.fromBytes . encode $ tx } deliverResp <- fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ RPC.broadcastTxCommit txReq diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 0e8db62d..69ff765a 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -89,6 +89,7 @@ library: - -Wredundant-constraints exposed-modules: - Tendermint.SDK.Application + - Tendermint.SDK.Application.AnteHandler - Tendermint.SDK.Application.App - Tendermint.SDK.Application.Module - Tendermint.SDK.Application.Handlers diff --git a/hs-abci-sdk/protos/types/transaction.proto b/hs-abci-sdk/protos/types/transaction.proto index 5c195d49..d7c19d4e 100644 --- a/hs-abci-sdk/protos/types/transaction.proto +++ b/hs-abci-sdk/protos/types/transaction.proto @@ -6,4 +6,5 @@ message RawTransaction { int64 gas = 2; bytes signature = 3; string route = 4; + uint64 nonce = 5; } \ No newline at end of file diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application.hs b/hs-abci-sdk/src/Tendermint/SDK/Application.hs index c939e35d..2205f5e3 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application.hs @@ -3,10 +3,13 @@ module Tendermint.SDK.Application , Module(..) , defaultTxChecker , HandlersContext(..) + , AnteHandler(..) + , baseAppAnteHandler , createIOApp , makeApp ) where +import Tendermint.SDK.Application.AnteHandler import Tendermint.SDK.Application.App import Tendermint.SDK.Application.Handlers import Tendermint.SDK.Application.Module diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs new file mode 100644 index 00000000..98f63600 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs @@ -0,0 +1,58 @@ +module Tendermint.SDK.Application.AnteHandler + ( AnteHandler(..) + , applyAnteHandler + , baseAppAnteHandler + ) where + +import Control.Monad (unless) +import Polysemy +import Polysemy.Error (Error) +import qualified Tendermint.SDK.Application.Module as M +import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), + throwSDKError) +import qualified Tendermint.SDK.Modules.Auth as A +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) + +data AnteHandler r where + AnteHandler :: (forall msg. M.Router r msg -> M.Router r msg) -> AnteHandler r + +instance Semigroup (AnteHandler r) where + (<>) (AnteHandler h1) (AnteHandler h2) = + AnteHandler $ h1 . h2 + +instance Monoid (AnteHandler r) where + mempty = AnteHandler id + +applyAnteHandler :: AnteHandler r -> M.Router r msg -> M.Router r msg +applyAnteHandler (AnteHandler ah) = ($) ah + +nonceAnteHandler + :: Members A.AuthEffs r + => Member (Error AppError) r + => AnteHandler r +nonceAnteHandler = AnteHandler $ \(M.Router router) -> + M.Router $ \tx@(PreRoutedTx Tx{..}) -> do + let Msg{msgAuthor} = txMsg + mAcnt <- A.getAccount msgAuthor + account <- case mAcnt of + Just a@A.Account{accountNonce} -> do + unless (accountNonce <= txNonce) $ + throwSDKError (NonceException accountNonce txNonce) + pure a + Nothing -> do + unless (txNonce == 0) $ + throwSDKError (NonceException 0 txNonce) + A.createAccount msgAuthor + result <- router tx + A.putAccount msgAuthor $ + account { A.accountNonce = A.accountNonce account + 1} + pure result + +baseAppAnteHandler + :: Members A.AuthEffs r + => Member (Error AppError) r + => AnteHandler r +baseAppAnteHandler = mconcat $ + [ nonceAnteHandler + ] diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index f6a6bd96..2c01588b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -4,33 +4,42 @@ module Tendermint.SDK.Application.Handlers , makeApp ) where -import Control.Lens (from, to, (&), (.~), - (^.)) -import Crypto.Hash (Digest) -import Crypto.Hash.Algorithms (SHA256) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Default.Class (Default (..)) +import Control.Lens (from, to, (&), (.~), + (^.)) +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Default.Class (Default (..)) import Data.Proxy -import Network.ABCI.Server.App (App (..), - MessageType (..), - Request (..), - Response (..)) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp +import Network.ABCI.Server.App (App (..), + MessageType (..), + Request (..), + Response (..)) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp import Polysemy -import Polysemy.Error (Error, catch) -import qualified Tendermint.SDK.Application.Module as M -import qualified Tendermint.SDK.BaseApp.BaseApp as BA -import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) -import Tendermint.SDK.BaseApp.Errors (AppError, queryAppError, - txResultAppError) -import Tendermint.SDK.BaseApp.Query (HasRouter) -import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) -import qualified Tendermint.SDK.BaseApp.Store as Store -import Tendermint.SDK.Crypto (RecoverableSignatureSchema, - SignatureSchema (..)) -import Tendermint.SDK.Types.TxResult (checkTxTxResult, - deliverTxTxResult) +import Polysemy.Error (Error, catch) +import Tendermint.SDK.Application.AnteHandler (AnteHandler, + applyAnteHandler) +import qualified Tendermint.SDK.Application.Module as M +import qualified Tendermint.SDK.BaseApp.BaseApp as BA +import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) +import Tendermint.SDK.BaseApp.Errors (AppError, + SDKError (..), + queryAppError, + throwSDKError, + txResultAppError) +import Tendermint.SDK.BaseApp.Query (HasRouter) +import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) +import qualified Tendermint.SDK.BaseApp.Store as Store +import Tendermint.SDK.Crypto (RecoverableSignatureSchema, + SignatureSchema (..)) +import qualified Tendermint.SDK.Modules.Auth as A +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), + parseTx) +import Tendermint.SDK.Types.TxResult (checkTxTxResult, + deliverTxTxResult) + type Handler mt r = Request mt -> Sem r (Response mt) @@ -70,12 +79,14 @@ data HandlersContext alg ms r core = HandlersContext { signatureAlgP :: Proxy alg , modules :: M.Modules ms r , compileToCore :: forall a. BA.ScopedEff core a -> Sem core a + , anteHandler :: AnteHandler r } -- Common function between checkTx and deliverTx makeHandlers :: forall alg ms r core. - Member (Error AppError) r + Members A.AuthEffs r + => Member (Error AppError) r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => M.TxRouter ms r @@ -90,7 +101,10 @@ makeHandlers HandlersContext{..} = let compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a compileToBaseApp = M.eval modules - txRouter context = compileToBaseApp . M.txRouter signatureAlgP context modules + routerWithAH context = applyAnteHandler anteHandler $ M.txRouter context modules + txRouter context bs = case parseTx signatureAlgP bs of + Left err -> throwSDKError $ ParseError err + Right tx -> compileToBaseApp $ M.runRouter (routerWithAH context) (PreRoutedTx tx) queryRouter = compileToBaseApp . M.queryRouter modules query (RequestQuery q) = Store.applyScope $ @@ -143,7 +157,8 @@ makeHandlers HandlersContext{..} = makeApp :: forall alg ms r core. - Members [Error AppError, Embed IO] r + Members A.AuthEffs r + => Members [Error AppError, Embed IO] r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => M.TxRouter ms r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 934d76c0..6c7cd20b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -7,6 +7,7 @@ module Tendermint.SDK.Application.Module , QueryRouter(Api) , queryRouter , RoutingContext(..) + , Router(..) , TxRouter , txRouter , voidRouter @@ -14,8 +15,6 @@ module Tendermint.SDK.Application.Module ) where import Control.Monad.IO.Class (liftIO) -import Crypto.Hash (Digest) -import Crypto.Hash.Algorithms (SHA256) import Data.ByteString (ByteString) import Data.Proxy import Data.String.Conversions (cs) @@ -33,18 +32,15 @@ import Tendermint.SDK.BaseApp ((:&), AppError, BaseApp, import qualified Tendermint.SDK.BaseApp.Query as Q import qualified Tendermint.SDK.BaseApp.Transaction as T import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (RecoverableSignatureSchema, - SignatureSchema (..)) import Tendermint.SDK.Types.Message (Msg (..), ValidateMessage (..), formatMessageSemanticError) -import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..), - parseTx) +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) import Tendermint.SDK.Types.TxResult (TxResult) data Module (name :: Symbol) msg val (api :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: RoutedTx msg -> Sem (T.TxEffs :& r) val - , moduleTxChecker :: RoutedTx msg -> Sem (T.TxEffs :& r) val + { moduleTxDeliverer :: PreRoutedTx msg -> Sem (T.TxEffs :& r) val + , moduleTxChecker :: PreRoutedTx msg -> Sem (T.TxEffs :& r) val , moduleQueryServer :: Q.RouteT api (Sem r) , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } @@ -58,9 +54,9 @@ voidModuleMessages m = defaultTxChecker :: Member (Error AppError) r => ValidateMessage msg - => RoutedTx msg + => PreRoutedTx msg -> Sem r () -defaultTxChecker (RoutedTx Tx{txMsg}) = +defaultTxChecker (PreRoutedTx Tx{txMsg}) = case validateMessage txMsg of V.Failure err -> throwSDKError . MessageValidation . map formatMessageSemanticError $ err @@ -97,22 +93,15 @@ instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg val api s r ': data RoutingContext = CheckTxContext | DeliverTxContext +data Router r msg = Router { runRouter :: PreRoutedTx msg -> Sem r TxResult } + txRouter - :: forall alg ms r . - RecoverableSignatureSchema alg - => Member (Error AppError) r - => Message alg ~ Digest SHA256 - => TxRouter ms r - => Proxy alg - -> RoutingContext + :: TxRouter ms r + => RoutingContext -> Modules ms r - -> ByteString - -> Sem r TxResult -txRouter (p :: Proxy alg) routeContext ms bs = - let etx = decode bs >>= parseTx p - in case etx of - Left errMsg -> throwSDKError $ ParseError ("Transaction ParseError: " <> errMsg) - Right tx -> routeTx routeContext ms tx + -> Router r ByteString +txRouter routeContext ms = Router $ \(PreRoutedTx tx) -> + routeTx routeContext ms tx class TxRouter ms r where routeTx :: forall alg. RoutingContext -> Modules ms r -> Tx alg ByteString -> Sem r TxResult @@ -133,7 +122,7 @@ instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCode Left err -> throwSDKError $ ParseError err Right (msg :: msg) -> return msg let msg' = txMsg {msgData = msg} - tx' = RoutedTx $ tx {txMsg = msg'} + tx' = PreRoutedTx $ tx {txMsg = msg'} ctx <- liftIO $ T.newTransactionContext tx' T.eval ctx $ case routeContext of CheckTxContext -> moduleTxChecker m tx' @@ -142,9 +131,9 @@ instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCode voidRouter :: forall a r. - RoutedTx Void + PreRoutedTx Void -> Sem r a -voidRouter (RoutedTx tx) = +voidRouter (PreRoutedTx tx) = let Tx{txMsg} = tx Msg{msgData} = txMsg in pure $ absurd msgData diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index 461ef293..c643f77a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -9,8 +9,9 @@ module Tendermint.SDK.BaseApp.Errors import Control.Exception (Exception) import Control.Lens (Lens', lens) +import Data.String.Conversions (cs) import Data.Text (Text, intercalate) -import Data.Word (Word32) +import Data.Word (Word32, Word64) import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy import Polysemy.Error (Error, throw) @@ -79,6 +80,7 @@ data SDKError = | OutOfGasException | MessageValidation [Text] | SignatureRecoveryError Text + | NonceException Word64 Word64 -- | As of right now it's not expected that one can recover from an 'SDKError', -- | so we are throwing them as 'AppError's directly. @@ -123,3 +125,10 @@ instance IsAppError SDKError where , appErrorCodespace = "sdk" , appErrorMessage = "Signature Recovery Error: " <> msg } + + makeAppError (NonceException expected found) = AppError + { appErrorCode = 7 + , appErrorCodespace = "sdk" + , appErrorMessage = "Incorrect Transaction Nonce: Expected " <> (cs . show $ toInteger expected) <> + " but got " <> (cs . show $ toInteger found) <> "." + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs index 94d0568f..6e5008bf 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs @@ -21,7 +21,7 @@ import qualified Tendermint.SDK.BaseApp.Events as E import qualified Tendermint.SDK.BaseApp.Gas as G import Tendermint.SDK.Codec (HasCodec (encode)) import Tendermint.SDK.Types.Effects ((:&)) -import Tendermint.SDK.Types.Transaction (RoutedTx (..), Tx (..)) +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) import Tendermint.SDK.Types.TxResult (TxResult, txResultData, txResultEvents, txResultGasUsed, @@ -38,9 +38,9 @@ data TransactionContext = TransactionContext } newTransactionContext - :: RoutedTx msg + :: PreRoutedTx msg -> IO TransactionContext -newTransactionContext (RoutedTx Tx{txGas}) = do +newTransactionContext (PreRoutedTx Tx{txGas}) = do initialGas <- newIORef $ G.GasAmount txGas pure TransactionContext { gas = initialGas diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 6924a104..0cb7d980 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -3,9 +3,11 @@ module Tendermint.SDK.Modules.Auth , authModule , AuthEffs + , Account(..) , Accounts(..) , getAccount , putAccount + , createAccount , eval , Api diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs index 1af9a87a..2b6f5c3c 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -3,9 +3,10 @@ module Tendermint.SDK.Modules.Auth.Keeper where import Polysemy -import Polysemy.Error (Error) +import Polysemy.Error (Error, mapError, throw) import Tendermint.SDK.BaseApp (AppError, RawStore, - StoreKey (..), get, put) + StoreKey (..), get, + makeAppError, put) import Tendermint.SDK.Modules.Auth.Types import Tendermint.SDK.Types.Address (Address) @@ -15,19 +16,40 @@ data Accounts m a where makeSem ''Accounts -type AuthEffs = '[Accounts] +type AuthEffs = '[Accounts, Error AuthError] storeKey :: StoreKey AuthModule storeKey = StoreKey "auth" +createAccount + :: Members [Accounts, Error AuthError] r + => Address + -> Sem r Account +createAccount addr = do + mAcct <- getAccount addr + case mAcct of + Just _ -> throw $ AccountAlreadExists addr + Nothing -> do + let emptyAccount = Account + { accountCoins = [] + , accountNonce = 0 + } + putAccount addr emptyAccount + pure emptyAccount + eval :: Members [RawStore, Error AppError] r - => Sem (Accounts : r) a + => Sem (Accounts : Error AuthError : r) a -> Sem r a -eval = - interpret (\case - GetAccount addr -> - get storeKey addr - PutAccount addr acnt -> - put storeKey addr acnt - ) +eval = mapError makeAppError . evalAuth + where + evalAuth :: Members [RawStore, Error AppError] r + => Sem (Accounts : r) a + -> Sem r a + evalAuth = + interpret (\case + GetAccount addr -> + get storeKey addr + PutAccount addr acnt -> + put storeKey addr acnt + ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs index 0e310347..df8b1d38 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -7,13 +7,16 @@ import Control.Lens (Wrapped (..), from, iso, view, _Unwrapped') import Data.Bifunctor (bimap) import qualified Data.ProtoLens as P +import Data.Proxy (Proxy (..)) import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word import GHC.Generics (Generic) +import GHC.TypeLits (symbolVal) import qualified Proto.Modules.Auth as A import qualified Proto.Modules.Auth_Fields as A -import Tendermint.SDK.BaseApp (IsKey (..), Queryable (..)) +import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..), + IsKey (..), Queryable (..)) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) @@ -66,3 +69,14 @@ instance IsKey Address AuthModule where instance Queryable Account where type Name Account = "account" + +data AuthError = + AccountAlreadExists Address + +instance IsAppError AuthError where + makeAppError (AccountAlreadExists addr) = + AppError + { appErrorCode = 1 + , appErrorCodespace = cs (symbolVal $ Proxy @AuthModule) + , appErrorMessage = "Account Already Exists " <> (cs . show $ addr) <> "." + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index 5fcd40fa..babe57f4 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -12,6 +12,7 @@ import qualified Data.ProtoLens as P import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text) +import Data.Word (Word64) import GHC.Generics (Generic) import qualified Proto.Types.Transaction as T import qualified Proto.Types.Transaction_Fields as T @@ -20,7 +21,6 @@ import Tendermint.SDK.Crypto (MakeDigest (..), RecoverableSignatureSchema (..), SignatureSchema (..)) import Tendermint.SDK.Types.Message (Msg (..)) - -- Our standard transaction type parameterized by the signature schema 'alg' -- and an underlying message type 'msg'. data Tx alg msg = Tx @@ -30,6 +30,7 @@ data Tx alg msg = Tx , txSignature :: RecoverableSignature alg , txSignBytes :: Message alg , txSigner :: PubKey alg + , txNonce :: Word64 } instance Functor (Tx alg) where @@ -48,6 +49,7 @@ data RawTransaction = RawTransaction , rawTransactionRoute :: Text -- ^ module name , rawTransactionSignature :: ByteString + , rawTransactionNonce :: Word64 } deriving Generic instance Wrapped RawTransaction where @@ -61,11 +63,13 @@ instance Wrapped RawTransaction where & T.gas .~ rawTransactionGas & T.route .~ cs rawTransactionRoute & T.signature .~ rawTransactionSignature + & T.nonce .~ rawTransactionNonce f message = RawTransaction { rawTransactionData = message ^. T.data' , rawTransactionGas = message ^. T.gas , rawTransactionRoute = message ^. T.route , rawTransactionSignature = message ^. T.signature + , rawTransactionNonce = message ^. T.nonce } instance HasCodec RawTransaction where @@ -85,8 +89,8 @@ signRawTransaction -> RecoverableSignature alg signRawTransaction p priv tx = signRecoverableMessage p priv (makeDigest tx) --- | Attempt to parse a 'RawTransaction' as a 'Tx' without attempting --- | to parse the underlying message. This is done as a preprocessing +-- | Attempt to parse a Bytestring into a 'RawTransaction' then as a 'Tx' without +-- | attempting to parse the underlying message. This is done as a preprocessing -- | step to the router, allowing for failure before the router is ever -- | reached. parseTx @@ -94,28 +98,30 @@ parseTx RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 => Proxy alg - -> RawTransaction + -> ByteString -> Either Text (Tx alg ByteString) -parseTx p rawTx@RawTransaction{..} = do +parseTx p bs = do + rawTx@RawTransaction{..} <- decode bs recSig <- note "Unable to parse transaction signature as a recovery signature." $ - makeRecoverableSignature p rawTransactionSignature + makeRecoverableSignature p rawTransactionSignature let txForSigning = rawTx {rawTransactionSignature = ""} signBytes = makeDigest txForSigning signerPubKey <- note "Signature recovery failed." $ recover p recSig signBytes - return Tx + return $ Tx { txMsg = Msg - { msgData = rawTransactionData - , msgAuthor = addressFromPubKey p signerPubKey - } + { msgData = rawTransactionData + , msgAuthor = addressFromPubKey p signerPubKey + } , txRoute = cs rawTransactionRoute , txGas = rawTransactionGas , txSignature = recSig , txSignBytes = signBytes , txSigner = signerPubKey + , txNonce = rawTransactionNonce } -data RoutedTx msg where - RoutedTx :: Tx alg msg -> RoutedTx msg +data PreRoutedTx msg where + PreRoutedTx :: Tx alg msg -> PreRoutedTx msg -instance Functor RoutedTx where - fmap f (RoutedTx tx) = RoutedTx $ fmap f tx +instance Functor PreRoutedTx where + fmap f (PreRoutedTx tx) = PreRoutedTx $ fmap f tx diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs index d85d6f58..139a46d2 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs @@ -9,6 +9,7 @@ import Data.ByteString.Short (fromShort) import Data.Maybe (fromJust) import Data.Proxy import Data.String (fromString) +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Crypto (Secp256k1) import Tendermint.SDK.Types.Transaction import Test.Hspec @@ -21,12 +22,14 @@ spec = describe "Crypto Tests" $ do , rawTransactionSignature = "" , rawTransactionRoute= "dog" , rawTransactionGas = 10 + , rawTransactionNonce = 0 } signature = signRawTransaction algProxy privateKey rawTxWithoutSig rawTxWithSig = rawTxWithoutSig {rawTransactionSignature = encodeCompactSig $ exportCompactRecSig signature} - eTx = parseTx algProxy rawTxWithSig - Tx{..} = case eTx of + -- @NOTE: this is kinda dumb bc parseTx decodes a bs into a rawTx + eTx = parseTx algProxy . encode $ rawTxWithSig + Tx {..} = case eTx of Left errMsg -> error $ show errMsg Right a -> a txSigner `shouldBe` derivePubKey privateKey diff --git a/hs-abci-test-utils/package.yaml b/hs-abci-test-utils/package.yaml index 1cc15008..05ebbd4d 100644 --- a/hs-abci-test-utils/package.yaml +++ b/hs-abci-test-utils/package.yaml @@ -19,6 +19,7 @@ extra-source-files: description: Please see the README on GitHub at default-extensions: + - DataKinds - DefaultSignatures - DeriveGeneric - FlexibleInstances @@ -45,6 +46,7 @@ library: - aeson-pretty - base >= 4.7 && < 5 - bytestring + - data-default-class - errors - hspec - lens diff --git a/hs-abci-test-utils/src/Tendermint/Utils/User.hs b/hs-abci-test-utils/src/Tendermint/Utils/User.hs index c8302940..43578759 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/User.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/User.hs @@ -7,15 +7,26 @@ import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString, snoc) import qualified Data.ByteString as BS import Data.ByteString.Short (fromShort) +import Data.Default.Class (def) import Data.Maybe (fromJust) import Data.Proxy import Data.String (fromString) import Data.String.Conversions (cs) +import Data.Word (Word64) +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:>)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + defaultQueryWithData) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Crypto (Secp256k1, addressFromPubKey) +import Tendermint.SDK.Modules.Auth (Account (..)) +import qualified Tendermint.SDK.Modules.Auth as Auth import Tendermint.SDK.Types.Address (Address (..)) import Tendermint.SDK.Types.Transaction (RawTransaction (..), signRawTransaction) +import Tendermint.Utils.Client (ClientResponse (..), + HasClient (..)) +import Tendermint.Utils.Request (runRPC) data User = User { userPrivKey :: SecKey @@ -32,16 +43,33 @@ makeUser privKeyStr = algProxy :: Proxy Secp256k1 algProxy = Proxy --- sign a trx with a user's private key -mkSignedRawTransactionWithRoute :: HasCodec a => BS.ByteString -> SecKey -> a -> RawTransaction -mkSignedRawTransactionWithRoute route privateKey msg = sign unsigned - where unsigned = RawTransaction { rawTransactionData = encode msg - , rawTransactionRoute = cs route - , rawTransactionSignature = "" - , rawTransactionGas = 0 - } - sig = signRawTransaction algProxy privateKey unsigned - sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } +getAccount :: QueryArgs Address -> RPC.TendermintM (ClientResponse Account) +getAccount = + let apiP = Proxy :: Proxy ("auth" :> Auth.Api) + in genClient (Proxy :: Proxy RPC.TendermintM) apiP def + +getAccountNonce :: Address -> IO Word64 +getAccountNonce userAddress = do + let query = getAccount $ defaultQueryWithData userAddress + ClientResponse{clientResponseData} <- runRPC query + case clientResponseData of + -- unitialized account = 0 nonce + Nothing -> return 0 + Just Account {accountNonce} -> return accountNonce + +-- sign a trx with a user's private key and add the user's account nonce +mkSignedRawTransactionWithRoute :: HasCodec a => BS.ByteString -> User -> a -> IO RawTransaction +mkSignedRawTransactionWithRoute route User{userAddress, userPrivKey} msg = do + nonce <- getAccountNonce userAddress + let unsigned = RawTransaction { rawTransactionData = encode msg + , rawTransactionRoute = cs route + , rawTransactionSignature = "" + , rawTransactionGas = 0 + , rawTransactionNonce = nonce + } + sig = signRawTransaction algProxy userPrivKey unsigned + sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } + return . sign $ unsigned encodeCompactRecSig :: CompactRecSig -> ByteString encodeCompactRecSig (CompactRecSig r s v) = snoc (fromShort r <> fromShort s) v From 7cb05d4f17cb25fd155f54814bc1bcea4f989cbc Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Wed, 15 Jan 2020 14:21:44 -0800 Subject: [PATCH 39/70] update logging and metrics docs (#192) * Testing docs * remove bells and whistles from simple-storage * wip * logging readme and images * metrics docs * reworked readme * remove kriss stuff * PR comments * weeder Co-authored-by: Kristoffer Josefsson --- hs-abci-examples/nameservice/README.md | 156 ++---------------- hs-abci-examples/nameservice/docs/Logging.md | 83 ++++++++++ hs-abci-examples/nameservice/docs/Metrics.md | 11 ++ .../nameservice/images/create-filter.png | Bin 73861 -> 0 bytes .../nameservice/images/create-index.png | Bin 152455 -> 0 bytes .../nameservice/images/kibana-discover.png | Bin 6361 -> 0 bytes .../nameservice/images/kibana-management.png | Bin 7203 -> 0 bytes .../nameservice/images/kibana_discover.png | Bin 0 -> 535811 bytes .../images/kibana_discover_filter.png | Bin 0 -> 514114 bytes .../kibana_discover_filter_advanced.png | Bin 0 -> 572381 bytes .../nameservice/images/kibana_management.png | Bin 0 -> 335697 bytes .../images/kibana_management_2.png | Bin 0 -> 500606 bytes .../images/kibana_welcome_screen.png | Bin 0 -> 275884 bytes .../nameservice/images/logs-tab.png | Bin 4793 -> 0 bytes .../nameservice/images/prometheus-metrics.png | Bin 74945 -> 0 bytes hs-abci-examples/simple-storage/README.md | 26 +-- hs-abci-examples/simple-storage/app/Main.hs | 10 +- hs-abci-examples/simple-storage/package.yaml | 4 - .../src/SimpleStorage/Config.hs | 63 ++----- .../src/SimpleStorage/Server.hs | 41 ++--- hs-abci-extra/README.md | 2 +- hs-abci-sdk/README.md | 2 +- 22 files changed, 137 insertions(+), 261 deletions(-) create mode 100644 hs-abci-examples/nameservice/docs/Logging.md create mode 100644 hs-abci-examples/nameservice/docs/Metrics.md delete mode 100644 hs-abci-examples/nameservice/images/create-filter.png delete mode 100644 hs-abci-examples/nameservice/images/create-index.png delete mode 100644 hs-abci-examples/nameservice/images/kibana-discover.png delete mode 100644 hs-abci-examples/nameservice/images/kibana-management.png create mode 100644 hs-abci-examples/nameservice/images/kibana_discover.png create mode 100644 hs-abci-examples/nameservice/images/kibana_discover_filter.png create mode 100644 hs-abci-examples/nameservice/images/kibana_discover_filter_advanced.png create mode 100644 hs-abci-examples/nameservice/images/kibana_management.png create mode 100644 hs-abci-examples/nameservice/images/kibana_management_2.png create mode 100644 hs-abci-examples/nameservice/images/kibana_welcome_screen.png delete mode 100644 hs-abci-examples/nameservice/images/logs-tab.png delete mode 100644 hs-abci-examples/nameservice/images/prometheus-metrics.png diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index 03be8e89..f538add1 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -1,155 +1,23 @@ -# nameservice +# Nameservice -## Environment Variables -- LOG_SEVERITY (defaults to **info**) : minimum log severtiy level {debug, info, notice, warning, error, critical, alert, emergency} -- LOG_VERBOSITY (defaults to **0**) : for each loggable data point, the level of information actually logged {0, 1, 2, 3} -- ES_HOST (optional) : hostname of the elasticsearch instance for logging -- ES_PORT (optional) : port number of elasticsearch instance for logging -- STATS_PORT (optional) : port to run the prometheus metrics server. - -**NOTE** If you do not provide both of the elasticsearch variables, the logger will default to logging to the console. - -## Metrics via Prometheus - -Run `nameservice` via the `Makefile`: - -``` -make deploy-nameservice-local -``` - -Along with running `nameservice`, this command also starts a Prometheus metrics server. -By default, the metrics server runs on `localhost:9200`. To use a different port, -set the `STATS_PORT` environment variable to the desired port value. - -To see these metrics in Datadog, follow the -[Prometheus host config instructions](https://docs.datadoghq.com/getting_started/integrations/prometheus/?tab=host#pagetitle) -to configure a local Datadog agent to scrape the endpoint. At minimum, to scrape all -`nameservice` prometheus metrics, the appropriate `conf.yaml` (described above) should -contain the following settings: - -```yaml -init_config: - -instances: - - - prometheus_url: http://localhost:9200/metrics - ## namespace option prefixes all metric names in datadog - namespace: prometheus - ## metrics names used in the nameservice app - metrics: - - count_buy - - count_set - - count_delete - - histogram_buy* - - histogram_set* - - histogram_delete* -``` - -Alternatively, use the `docker-compose` command: - -``` -make deploy-nameservice-docker -``` - -Once the `nameservice` server is running, start a Tendermint node: - -```bash -> tendermint init -> tendermint node -``` - -Then run the `nameservice` tests: - -```bash -make test-nameservice -``` +The Nameservice application is a sample application meant to showcase the SDK. It roughly follows the example application from the golang cosmos-sdk, which you can find [here](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). -Once the test run is completed, you should now be able to view metrics -on [Datadog's metrics explorer](https://app.datadoghq.com/metric/explorer). -Firstly, ensure that the prometheus server is populated with metrics by -visiting `localhost:9200/metrics` in a browser. It should look something like this: +There is also a [tutorial](./tutorial/README.md) that explains how the Nameservice app was built. -``` -# TYPE count_buy counter -count_buy 4 -# TYPE count_delete counter -count_delete 1 -# TYPE count_set counter -count_set 2 -# TYPE histogram_buy histogram -histogram_buy_bucket{le="1.0e-4"} 0.0 -... -# TYPE histogram_delete histogram -histogram_delete_bucket{le="1.0e-4"} 0.0 -... -# TYPE histogram_set histogram -histogram_set_bucket{le="1.0e-4"} 0.0 -... -``` +## Running the Application -Now, in Datadog's metrics explorer, we can search for metrics -prefixed with the `namespace` value (i.e., `prometheus`) set above: +The Nameservice application depends on a few services, so we provide a `docker-compose.yaml` file and highly suggest running the application in Docker. There is a `make deploy-nameservice` command which can be run from the project root to deploy the application. - +**NOTE** This will also attempt build the nameservice binaries in Docker, which can take a long time. If you are on (ubuntu) linux, you can use the `make docker-test-prebake` command first to build the application locally and copy the binaries to the correct image. If you then run `make deploy-nameservice`, it will automatically use these binaries instead of rebuilding in Docker. -## Alternative Logging -In addition to Prometheus metrics, the `nameservice` app includes options for -logging to Elasticsearch (ES) and Datadog metrics logging. By default, the app -logs everything to the console: +### Environment Variables -``` -[2019-12-20 16:19:27][nameservice.server][Info][local][PID 89617][ThreadId 21][type:info] Request Received -[2019-12-20 16:19:27][nameservice.server][Info][local][PID 89617][ThreadId 21][message_type:info][response_time:2.6e-5][message_count:1] -``` +You can provide the following environment variables when running `make deploy-nameservice` to customize the logger output: -These logs include request/response info and some event and metric logging. - -Alternatively, the app is set up to log the same information to ES -and Datadog by setting the following environment variables: - -```bash -ES_HOST ## Elasticsearch server host -ES_PORT ## Elasticsearch server port -``` - -We recommend using the [ELK (v683) docker image](https://hub.docker.com/r/sebp/elk/tags) -to run an ES server alongside a Kibana instance to search and filter incoming logs. - -```bash -> docker pull sebp/elk:683 -> docker run -p 5601:5601 -p 9201:9200 -p 5044:5044 -it --name elk sebp/elk:683 -``` - -**NOTE:** This command remaps the `ES_PORT` value from `9200` to `9201` to avoid collision with -the Prometheus server that `nameservice` runs by default. The Kibana instance runs on port `5601`. - -After both Kibana and ES are running, start `nameservice` and a Tendermint node -via the commands: - -```bash -> ES_HOST=localhost ES_PORT=9201 make deploy-nameservice -> tendermint init -> tendermint node -``` - -At this point, there should be no logs printed on the console. If a valid -Datadog API Key is provided, metrics logs are available on -[Datadog's log explorer](https://app.datadoghq.com/logs). - - - -To view ES logs on Kibana, go to `Management` on the sidebar and create a Kibana index pattern for `nameservice`: - - - - - -Finally, under `Discover`, we can add filters to view specific logs: - - +- LOG_SEVERITY (defaults to **info**) : minimum log severtiy level {debug, info, notice, warning, error, critical, alert, emergency} +- LOG_VERBOSITY (defaults to **0**) : for each loggable data point, the level of information actually logged {0, 1, 2, 3} -For example, after running `nameservice` tests, we can add a filter to see -event logs for `NameClaimed`, `NameRemapped` and `NameDeleted`: +## Logging and Metrics - +There is a lot to say about how [logging](./docs/Logging.md) and [metrics](./docs/Metrics.md) are managed if you decide to use them. \ No newline at end of file diff --git a/hs-abci-examples/nameservice/docs/Logging.md b/hs-abci-examples/nameservice/docs/Logging.md new file mode 100644 index 00000000..c53a98db --- /dev/null +++ b/hs-abci-examples/nameservice/docs/Logging.md @@ -0,0 +1,83 @@ +# Logging + +The SDK has built in support for structured logging via the [katip](https://hackage.haskell.org/package/katip) logging library. Even still, the SDK is agnostic to where you want your logs to go. Katip logs are managed by *scribes* whos job is precisely this, so it depends on which scribes you use. The two most common scribes are the [console scribe](https://hackage.haskell.org/package/katip-0.8.3.0/docs/Katip-Scribes-Handle.html#v:mkHandleScribe) and the [Elasticsearch scribe](https://hackage.haskell.org/package/katip-elasticsearch). + +The Nameservice application has support for either scribe -- it will use Elasticsearch if you provide the `ES_HOST` and `ES_PORT` environment variables or otherwise will default to console logging. The docker deployment is configured to use Elasticsearch. + +## Logging to Elasticsearch + +The docker network includes an `elk` image (Elasticsearch, Logstash, Kibana) for persisting and querying logs. You can read more about this stack [here](https://www.elastic.co/what-is/elk-stack), but the summary is that it is a powerful solution for hosting searchable structured logs. + +When logging to Elasticsearch, you can use the Kibana dashboard for creating queries and visualizations. We will cover the basics here. If you have already launched the docker network, you can view the Kibana dashboard by going to http://localhost:5601/app/kibana. You should see something like + + + +To create an index (a searchable pattern), click on the *Management* tab, click *Create Index*, and enter `nameservice` as the pattern. You should see something like this: + + + +You can ignore the advanced options, e.g. time filter, for now: + + + +To view and search the logs, you can click the `Discover` tab. You should see all of the logs in the resulting search, from both server and application: + + + +## Searching a Log Index + +### Log Structure + +The log structure is effectively a JSON object (with nesting). There are a few fields that are worth pointing out: + +- `message_type`: the abci message type for the message that caused the logs, e.g. `beginBlock`, `deliverTx`, etc. +- `message_hash`: the SHA256 of the protobuf encoded bytes for the abci message that caused the logs. +- `ns` (namespace): a list of increasingly specific scopes for where the log originated. In this case, `nameservice` is the root namespace, `server` or `application` is the next scope. + +Remember that the basic lifescycle of an `ABCI` message is that it first comes to the ABCI-server from tendermint, is then handed off to your application for processing, and finally the response is sent from the ABCI-server back to tendermint. In order to better track this lifecycle, we highly recommend you use the [logging middleware](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs). This middleware will attach the `message_type` and `message_hash` to the context for every single log that is produced, meaning that you can get a trace for a given message by simply searching its hash. + +### Querying the Logs + +You can create custom search filters in the *Discover* tab, just click the *Add a filter* button near the search bar. For example, we can filter all of the logs for those that correspond to a *deliverTx* message: + + + +(**NOTE**: If you run the e2e tests against the docker network, you should see search results corresponding to the transactions created by the test suite. ) + +Similarly, you can compose multiple filters to obtain only those logs emitted by the application itself during a *deliverTx* context, i.e. by filtering for `application` on the `ns` namespace field: + + + +### Indexing Transaction Events + +If you view the results from the filter `message_type=deliverTx, ns=application`, you might see results from the e2e test suite like + +```json +... + "data": { + "message_type": "deliverTx", + "event": { + "old_value": "hello world", + "name": "satoshi", + "new_value": "goodbye to a world" + }, + "event_type": "NameRemapped", + "message_hash": "e9190e5b24e066eb3b967fb39ba9e8ec250393d5c61400b3ed2a9528d967d5e1" + }, + "msg": "NameRemapped", +... +``` + +This log corresponds to an event emitted by the `Nameservice` module during transaction execution, namely the `NameRemapped` event that happens when the owner of a name changes the corresponding value. This is because of the following `BaseApp.logEvent` statement in the `setName` handler: + +```haskell + let event = NameRemapped + { nameRemappedName = setNameName + , nameRemappedNewValue = setNameValue + , nameRemappedOldValue = whoisValue + } + BaseApp.emit event + BaseApp.logEvent event +``` + +In this way the log index serves as a rudimentary event indexer for transaction events as well. \ No newline at end of file diff --git a/hs-abci-examples/nameservice/docs/Metrics.md b/hs-abci-examples/nameservice/docs/Metrics.md new file mode 100644 index 00000000..eef73cbb --- /dev/null +++ b/hs-abci-examples/nameservice/docs/Metrics.md @@ -0,0 +1,11 @@ +# Metrics + +The SDK has some built in support for metrics via [prometheus](https://prometheus.io/), but ultimately you may choose a different runtime interpretation for the metrics, or even choose to ignore it entirely. + +The Nameservice application uses application specific metrics, for instance increasing the message counters for module level messages, or for timing module responses. It also uses the server metrics via the [metrics middleware](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs) to count abci messages and time server responses. This middleware is highly recommended in any production system. + +## Setting up metrics + +The nameservice application docker network is configured to run a prometheus metrics server and a datadog agent to scrape metrics and push to [datadog](https://www.datadoghq.com/). You must supply a datadog api key as an environment variable *DD_API_KEY* when you launch the network if you want to do do this. If you don't already have an account, you can create one and receive a two week free trial to play around with this application. + +To simply test if prometheus is indeed collecting your metrics, you can visit `localhost:5555/metrics` and you should see something. (`5555` is the default value for the `STATS_PORT` environment variable in the docker compose file.) \ No newline at end of file diff --git a/hs-abci-examples/nameservice/images/create-filter.png b/hs-abci-examples/nameservice/images/create-filter.png deleted file mode 100644 index 3e541b56c1d25eca0bf4eeb963da40ce32df63a4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 73861 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 aC`VF4HO{8wQqm8AA1w|2tHqb^g#9m`E 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 uE>3gvg*<`a1a5&mDoS>^Kp diff --git a/hs-abci-examples/nameservice/images/kibana-management.png b/hs-abci-examples/nameservice/images/kibana-management.png deleted file mode 100644 index c2d6292001268f90d799b64707c64d3745e12bcb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7203 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 I$(e`#53be#+W-In diff --git a/hs-abci-examples/nameservice/images/kibana_discover.png b/hs-abci-examples/nameservice/images/kibana_discover.png new file mode 100644 index 0000000000000000000000000000000000000000..3695504e4ee12ac1a260ba9fc97765a2d9b2d05f GIT binary patch literal 535811 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%0AQ%Fi}TYkA{^7t_cG{`atz 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 z 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

(2ZqE=gh@i9dz|qwGpV!0TMTwU|B^-u+&bs!^ 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ℜ!HIR>3&dpj>oNFoJ^ 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

p!vaj<1)W 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%0Am1yTSuqv`{+ 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%0A)&FvBFzis36c$O%(psXBR%;tz7tLlUE|cu(+S zZ=zSdBW~WqM1@Dtw+#o6fUzzSbOvogl0P@eXWX-(OIGk&Se4y+MTxK?nbqF!vSsu5 z{YGD|cqHZ-7PYp$@hi2~0?&fk=PYaZCrS%A?(6byhE-jbI1!MLrZy}!~{-=tU z)MDM*z#4@G8ALzZx1aif10Yv(+NX6rCk3tAIi9QU z6T35u5g5!kR?ncBcsZR#WPBuDv#C>JJU?GlvN6Q|*&}n~kmzKC;CgUQb?iDfR6|j2T&FB+z{^gF~SJfY#|uP0H#b&MJC95Kf+f|zZ@gmK-+gO^&^Gh7Vef-G45_bj=&e^OHoRYMcC6 zK8B$fFVJysu6J&o!`ZNZXOuG>V1Kp?IU&z~(W>pHp-h@DeN(#K*0>sN>)G0u47vA+ z1T}4OZzwvvyqn=Jw?9>!o7x0=8T~#`H5nnEez%7sif|m&RLp_L)}UsEQbo1;YgtE2 z`pz+IpiXqi_>y5#xn1P?|2ngnMt9cB1v97s_6txv6Qg87>UaZAgx2omw*1 z^Ypy$xME&d4{l}3aJiJcCVR}HN_+FC6D`YgR1gX}o92Cf6=3WBCR}9pS$s?}*rgRx zJ=nzlkI;)ShhieZFcZDQZ*8`FyI}NLZ})o`)ORZv$mIsQb?t_OxxED^hEGuAzJ4t- z$;@1S)de=yUoLhBzC&M(+%Mnoy#<_OCTW}Hs4F90sm$}kM?5ER*mgG%J}Pi$5*LXZWvC!aLEdAgjG7psdnt%D8Y z^LljI_1eY7L7N153080?DBKtmdKEX&%r;n%a@BlsZeq7tB@-A?rz9JtA|!dWLMI-H zm-rc}otT35X1uuJrNO69I;qn6cD}7j`7$9dAT&#*Ee2~nTZZ()KJ@e1l2Sf)o27{U zd-%bqFeKoq&pcf15ZL0YRLl)r0y!N~VpM~E{r)b6MNSeLIda4D#rf~ma;>GB`hzK_ z$&{uhT~Aq1Tl3whcf`y7y6_VTM%qN2RI&5}o^N7p9DBfr2L~Bp&Vi*!LP|D-j|Hw5 zJINSpu(`!V>}-#C!h-i$t-&K1d|C}Q%loi|4RA9CfVJ5^jHK0IrVQ@H1{o4bn09f^ z=@k(dklHc-iiolR3ObNbe8}OQ7bvaiUC19%A?db6PN!R(Z8!4~ztaVK$JT%;Jz4(y zUnI(T08~wWX{xmCg!A5kTGQW|6PC0U2ErG+mCm(X@+&zJ+)reODNER5I0$S60|`~1 zK*NHZMo{-4_epI;bk~VW+{w1)V+YJaPCK4-B)8q410(`wc`8M@om-F62CgK=?%oY0 zCL53#?VOGYI0-+J2Qa;@on+>&^uO;G<#epi<6)93<8z!q17hOi*cK|Gg$IU*c^*cMsja+dR@rWQXL4Nbq zq6%K-TWKzjOLa{gVgN?^H`O1~Xc)LhK(}xl_Mi}0NYpA!g%ifyaKmEFARg(Wh^ZT} z7?`-h*orx}MIr{wF|`vA^Zq^vF0`UiZb&;=HwN@r=PpmLhIgJZumOTCC59LSsSKGq zWG3d}rFJpIOqLAIfrRayVKzKQ>3Vp<{jGsjX+@=ej3=LUf;v0Vvx++<8x7%z)Bwj) zgegmaUqCQCWGTqIlRhF#72hK08X%`A@ipSv{}5nkzs-R|3-xe$0px=N(M1?uqk+*L{ejR>89_#dfGmPFw#ZpU@EYJ{y z7XWjGj}yGTR{kc3$F7zxrP;9YEBZuC+Qq4E!7LuE5C4;qdTdM0GK$&4Or5JVhs^0= zGRts1^kSiW>OQ*VLhF~*YjV5Myq)^*i{7LM4X^mhZQbvl%RVOJtpS)H%7MZOD2FU; z`q-hbAWVL{3mIeF0kI}En?NAr@j*YJUuKluW^N7r?s(B<%*ZBC9ukt}UDo=o=jHmt z`E-#>Oqsm>SEmi5?>7U-%V&NctvCzq9^Kt++lYHmc^TXq_uMSJoh>@n>6upBHdyL> zPhP`%tP7#dUh8@PW?L%I$N=VphXT+P>A1AP;x`9Y?! zG<0T6_Y;NZ{WAR#&nGz>OmnXXz~@Y8*c;BY834N(N>jmTz=UG*7A8s^)I}Zm=8|M~ zk-qyEH!{PPbAS3pXgrh06{r(oxN!SJyM1BS5PItD#sGycaFlKa9E-Hd*^8h? zeHc2X@P^?MMH7|bP!Z!2fD$6KkLFQ?_Q+w{Oe3=nYT=r)Iy{g40Sqlv_ZVS{A0l`A5Bfb{k54WF?n5sy_!9})2d%e(nI>mdo(q{>^H zt{a5*Ax4hw<_uF(^d$_`{$Eg|!9f1>O3FdLkv(LN*k~&+8Qyrc%jG5^RDP5V--@Xi0W=4~dL3T%NkiZv z7J;~_w2d^VcdWVgK?#TU`2A;TAURV z@jZWiRk-KzIt;dgakYXF7pz1r(&kTT%;|u6Sp$>Qh_455;Vm$5{UJ z=0)7_+dG7-`%40&Z2oVKElADkdTNz!*O`RcSjlw9q7nOFdRg1xHPZ@Z1mPpZWBP_+ zZ=(c*k$=86l9QebN7$;>Wl0zx-NXp5sjp+ctC?u}d<9F&%Kn7XL_gIH`Ek96AWQED z0n^NIhllvA@)2IMiwN}Z@pJ_ir zYR>V`@bZZHpxC{EDv(JRG9A;&iagR%JYM2TH&oz#4c^g*!i4zir!-98A0cQ%<6W{V zEseHx+9v$o*D^Sse((8>*yy@wkp;XUsWl49mS!sMQ%&Sjsin0sEX-zL%@iX2x?~Lm z5$FYOj^@i%2V&W9wntOt!+4nOHUm#u0sx7pCUV0X_XVh5qU#8gA<<(Q)LYIr5M~mA z!`s0e^jwx(0Ya$7;F_Ouatx2cmO<;_TSciJp+Sq4)@GG>-;X%3pYE?!xjL>()hZL_ zi}XM-sZ?~jISBHXn!JiIcCg(atbSbuwn?;eh28d1KK!>!aeF$jx=nVzgK<>3HIGXN z2O1=8#h>C-ZG?{aoF8$+(To=yMntCJwgnZgnoitTV2rSP0>WgGf9m%|;-YU!=t0WG zWQGlWvV8KqT_c;qOthG{>$|fwKKLO~u8)cItFjSjMPZJ9sN5-}Fu+FtY?t$mBTK{5 zGTFZcLBY{Yv{oF6Kp`~FhJCujb7(ii=R2FlY&Bt~foCt@HNOnZh^c}F5tfo#PPEFie3&$zU&pF#U~|8Uh0SQ1ulikcfH%2VFSjI)RF^iCmQ-w={&Mf2 z@v^T3ug+uEV6y8T1!t}fdzv~XGo#&2nv5r+juP(s{O$EsksZ5pC>l#(T6f{t%H?g3 zWlEb@<8=Ea=G*?X$Zyldf;J(W1Jz!?uOn94Z*q)zkS1@9Q|a!vp#Exmm=F&rF87CA zbXx6F?5DFe@iKz71cQuPj(|q))7`o8-9({mD*K!CT_6gwg>ej;*tr`$VLWx#HsSxx zj$x&sl=9_r**a{;=%Mm7?%{vWmjP@{>=PrsZ~x64G3r=&MD?S z6slUN?#9sb#Fv1bQE~Y>`~3@i{f3Q=jV7lo_UfkVU6)FKhY0B%TivGD)>OSdrhuDekgcro%}nJm#GcTl{TQvw8Qdxs3aMYlR%R zXqXaqZVr%3ZYDzs5u=#`@*u9~0Id+Lk``B2SINZuP^gzm8l!1kd*KSVT8%b%)8$%f zB+1zX`p&<85lbQ_UMXwGPluNArC}Nivg}fb916+gK|@@Q>p? zmZYi0HpiAvuGX=)e6dv0AGlo~lbRT83)%6w74sxw>ORg^>crnD(sfA@a-O7r2-H`u z!DqR64aVp4K{OaND%+hoj^DZB90hwM8_Cy>h-s_1v_W}aQ-`*J?ICh#oivOfs7iyS zX-Q?!)Z1oRo-5_D-VENe>W&b(j|`?K7+drsy7J-G4mMwkOy+nSR)o2hICABN^alE1 zmYke3#{z4`D`Uy@wChK}F+Cb16gw6j6?AK+NIYpv2LJQh_;R$7LAT~qUUQq467@KJ zzbRw%B5IF)eoVRLq@Pzg-M+f?3#K=Q%-kHzc{0KjJ5hv9WDx{IqNyTz!6=lX-4%&+m{y@IH+;a@R8o^tP z>KyU5RV-ysH{TU-KeP?7WQP1q*twg5mt`NXFGOe(j;SdCherPMJFY&|TSynIQ&43b z4}C|Y5-W1O`Xh+^xh-Oy$?H0iBinjgq)|&wr`NtjJeG`hn$OQzYs%u*yff^$UCjg; z{(__vEG_tSN0_kBMUj{Hmsz<% zNHI62tE-rN(7xt-8v^w;dn~u85p_RKAlh(h?;K5D6OX~)b$xSfjt(!BikCX@0<62A zc1Q8(+JXEsHV~#mpM53xM!_f##09By)VF>5E-NtW-6>bc%c zgre!3_I^OUy&&cXGR%OK27n%^M3c*Bl*Qyk!*Up7mJ$!Wqf&ZL1HaAcQIs1uz0hDS zEx`i-M?E`<7O5-m%mHP2-E{;c;TxYhNE`t#-gJIX%|PGrSTbp|r$4`$k5|2ucIRtN z>%S51g1+_pUf(D#xHogufNZP!Q9-gt$Zf98odXb@WL(%8a5#s{W5JvW&fs&mu!OG2U1LXHnQGfR+4dgK55w3>dAqb(HA5f~L&)E~ zri6AQRW?4zALhPMjMitazpBe>{)ib*J0+-WvF>E~VZt+gVl(^YgV|$JjApxQ)`xUA zX1L>Wig2v#a-k+KF(iVIx=ZF8gf^vDQsvW_O91y(_i3y20?Cz3Iw5a7S6sTL$^jbG zj|?pr>Zn2`UYe&w0N?zb9ug(Z+=4J`n(~;WF^yIT_uS?FyPZp@N8qq`K&f2Qkr6yD zqynNpGgh!WPh>g;a^pQ)i7<3+-dEDv2*Z}4H#U5idG^vA<2_pVaYM_pu``0gL$Tag z+apOSa@x)Lh%uJV3vXE)baX;@vA>4y4pT-o6BAg( zzr<8$%|h%6MUkEHOC5Dhy+-X#5m46LU(Sjp9WkI6Ntw;^{fH~}fmn_FPW-&a`^{va zKgC|xWMZrxhXUofXasJEgQ*1B8Ei0)LarBQ1d1ILWMZXuAt{v-PY#t02__1s%W2iE z!8Y$ZDs4hc_w5Qy2x9)5TUxshP&vW7{bXUg!31Yd`5y%^KF=7OH+cy{0ptpu-o{$c zGae|}L-BCfZY>}5k5Uh@iFJn&u$X^TeA5p3rx-`*g5u*Kk$Rm@8iT-4@BZmnu=-

=f(nWp#)T9;X>6F+8`BQLNS8$`t-C4lO$OC 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�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 z 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

tXyR(Qv>AuWe)HPLT(TsN!7#HcOa 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

w+wx5! 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%0A%#*?5Z+@<7F} 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≧; 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

a;qb;V8Xi7tO7N2!i(34B2sL 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%0A0++sq-#!MYjh|El3M#+_0SD6EQ{cPdl$_^_FK(t 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%0AqtVpkk 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�p^lgY(Xs@vo{IYNT5)KUI||F_~69D@x=SG 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 C0D&>cr}Qw@V*20<=*%WcEMy(DLM!(B0=t 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%0AZ|znt-%vldM7z%bn_H|!MK)Ifmc9L! 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 <2K?r$nh#_&xAF)b*whmS!nwL)h~G^D7-9yOZ@k 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�c= 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

qI_8YI$cNU3`0 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� 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 literal 0 HcmV?d00001 diff --git a/hs-abci-examples/nameservice/images/kibana_management_2.png b/hs-abci-examples/nameservice/images/kibana_management_2.png new file mode 100644 index 0000000000000000000000000000000000000000..6e5686d1a40f6e8411c79733d01bbf37a0078ebd GIT binary patch literal 500606 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≫EPH8epEijg6rs^;wCnfGq)n@ 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�I3z@n$AQN^!!4{Xu}&pT4W+G$7eF;Kw>V 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

FD$yC1XxDRxX+YQB=_+`KzZ#ng- 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⩔aYOk3?UDBbtNJZ-_o2tx z-Cg(YRZDy@8trjH_7xz!9aJPf

pD_kkq5;(Y%xCRCBV31`zf?Sd?`qF8c)tA20w z9*8oDXLusA5WMo0cH-xB^V^^NERQ?MDh3*E5=pP9ABQ!-C@s7DfJdI!=@_%M=;#d0 z4q|d!9|N+7c?f3m#J}TAgWQC#trf^AAc*FHuYo4KQdU%fQ`j%xM~*(3Wcf?x*oirN zrS9qUY@5w;mc6xZV_1+qe+O1~3Uj5#M|?Z6+=KoL#yT|%W#%>en)0LPeV%K>>?4$c z5jWhy4OoLWz4c3>OzzD}1g$3?_nv|vjAkdQAgi#dn9rKt*blJr~!&kW``A-{W&)g2u5Cf&9ZDX3Cw zCh7h7BBtu6lj`Xy@s;b2mw-`g^`8<3uZNrO=s*2zZ`gdEt7!v@T2^Pi6Vi<3%phmU zcMBtVzz@$p>&QrI4I!o#U+qA!mR(kkCilf&+Zvup`vs{aN;x6Wio+|R+C`$yZQN8F_ z3!`#kaU?VOhvIgn9$(b08d<>Fh2EvTrytYXE^doX?(A}M?#1A-L)+aM7v?csBYR(D z@SjM9^jqSW*!Sn_f#M9{`Tfri%4(fFm~nL0FD0jX`~^u?jx?8AiD`0KgVWF5wQH`I z>^kUt64gBB;rs2$d=~}hUn3(=e1eCUUY1z6s2#F=WW2-P2UE&y{}SHSRUqS|I!zR! zhsw%*B}RyQBHkO1niO)K{pWK zx1EN(ZLfLH*5~z3X-wO7AmKl|?`Pwd^IizIUaGsl>%{RU5z(txja~!pr0W0SarCIb zneky}j8%!!aQ0e=!ctNF7R|53GMknYJVDbgg2m)cVl+ESmUA$YH7j8rTOU?8tJ9or z2fvr?BRkL@_*$4^z~#MNxuYeu>!bt;dX27Z3ub8j<5yAQbeV08| z&^GK-Uojn_zWJk4N4}n_VCMpl;tCuOLFR7V!8OfZRmnYugF4D`@n(pM3FS?PgjGq3 z0h9t<>71C~z^XXNuK%Uc+8lW&X)A+r)iJCIi!RFEgNy@F2 z9yAKMC5|q+2mrf1x}5 z=pD2$%U2AI^5I#iGodQQ$`J*L|p*6YPeUbazb<{c1048mWGCDMi*$l~eS)Cti?M9hO?X@7Q) zi4~I_?WRM1_MpdAcW7Rp*OU2y*Je3uFu1QHQ3C(vIlCt*2sKAs&ykW*)T*hRdAHkD zBv*f~g6NAY7#%coD2_rW&MBS=a~H<;SPTzDrUY`gnL%R)G!JWG^bWa_-NCjg!hu{M zTSW1K2#(4c?DR4+*P?8Hj6QXhnv)Kqum`*FcF&Qr-F5?8QZk5JFq?Xrz~^W`DA;{ovmYgjZPkrwUeYv5#6?%opldZD zdIcE-5r{zTW@UG$)8U@;>HOSZg ziIUrIv$!(S6n-%*W|^9K8W#9Cb!if# z=xk`>`xNWYLJ`Bho1e;Y&Y^Z!S8&ZBixtoG?M9*Exraojj#VkQ820V;WHmT^@X&aZ zIIt^tt9&dClTA@gYbU@W9mqjev2)6843oZ#Nkx4vj-*LKyZEsV!XXQP=a#Njw)KTx zzawakAT>=d^S6!-t&d&Ta0@kRNk$G3>+$vt#bX}qx6Yzby$#g#^^Z0%<<{gFUjLUb zlSNa&fR48vx~@&$x3kf#%dOn8#;I=}dvFI=OkKnKD^SILm9#O`Q*utgwSSD`{txT! zj~2)-`_rcA1A1g=YpL)~?0mc-eVo2l*EP1J_%IA4Vh|Nmo`rW%ub4FWaNd$jR8PH- zFjtdLm#^L1gTQYIqvmj%#Nl%PO&#q6gw>g{jTf7NEA>WG;jn=w7caNpnFf_wmLuV= zK`hGTgFm+6NifMIo34I6g9oi~>x|FLi#2;`xw_msG{6Tf%8Z}9c)rzpQx;dWV3_rN z_(pYY6gt3-Y0@$!W7?tbyOWf=0t`HKcdjP0h1cqwNT;gX*M@)5w63G9&K!PL164$G zzc^1f+HR_Fc^wXWPrpfbE^a22hy?|Vq8TrW_Smk-UH&QQBom(v}ncV<;_@uWh2*#9; z4{{>>&ouu3+nN9V=D4T898_CJQn3!72@BYK``J$WV_N(@DM=RuVP3+9x~)gu+b+9%zW8{aDUF7|U6#d9tqAu_kw{1KVy{Rhzid8)GK2 z;b&%8?#CUKe%FcU{T-~AKGCyNW=#xmwo+M(S{hXl6B~futECdZ$Ms!=8F&S1M>ILl zYgED>L3M*I~}@R0xc&}Npa0IHw+4Uc&FC__Dr5JqRf~Guon?!cP^QVFU+lF z4KH--%DQZWDq&ijZ}~VYPKU+%uv$tYUbIyAGD?MOr~4F-0prDA{SK%L8^x?8wZJ}~ zYLk!s0l}p7afa_K9pvp3wB9JzfO6j1vhhYzzt)b^)oJOk zk>v+NW%8 z%Mkk(B}d_Gg{~uzFd3Ei0T?p+a00-YZVd|Vh{t+H$R~7BONkP0%bahqJIOf#bXs8S z)VBxZbj%BO4#-;1${>wk>*5gG9=-a_5L2366WOCPxFsBd-M8Zp+FaTDJ=nmus*|yq z4W|@Az=r8iev*prycqLt-EaAGc~-F{lrsZDMsvT7-Yk3KEgP;!H5Yj;*?BK|rh7)d zHnv-SZ-_xU(O7b4e|MISDq|wnUSzeCQikbw;yOsOUVzjgeGu>V*S1eo_DZIg{B(4i zF~Nl{Asc|{oEdmvGScSj`xwXrbfo1W_a1I{+4Un}JK<89bsWXva45T*#vuK;ry61G zJRnK0wDpCUFxSGUM`FK`UBgL{HyVc~a$5Z^$YYQOjhb(2b6!63!$Dop^9)#*UlNs9lijQPY^0}8uYIfGD=zTLCZ7)u!6<|}v9p!+|tc~xi zk+Zaf=!i{O?Zz4&Z2HA%|G;uaB`^<8%}>%CxySYfwbN8k&nNXZHXfo`MC^}_j`a7> z0A2*J>iHT)B+Hn+8A7jvHrOdSPEH|_VxNduM3f_tI5EE6fKz$qDUl#!fl9j+v@tyH)3QlT5UsNnO#H<*&mNeu-Ih~P{5W||}5* zJ{+|hwMN*Zqo;HsGICl0+&9b*fCl}3-m(^ZVB z?I>U-K33_J9y#@4zs5?m0v}sCNsGL!%epdU%S6^XL^Wn>qrI#yEgyP>93$c4hHOkT z@zZ{vhAigk6Uz~!(31cMYvkc#yH6`{lsN&FGe}T>l3ot&S;yTU`2K)blJf;+GCFW} z8Jw+{^VY;)yE<*RKhnOogM3&eF+65Rp*68*y;FY=A8ur{dw)oXZK8O7#B&_4bc-Db zSwGkB6f=ii=4?A1d^7hOvB*2uR=U&fJC(DQibT!*=`8CVP0lEYbmrtRnbN~x#uz1v zCiL3c2f#%`wLbjy2>d&*zaURia6Z%}^Iw_N@nnG_>$Sf|PC!s|Z&W>Rc=AT@i1EJB ze%wv&QWwAw>A7N?idrb0bjT(-e?)OI^f5npx0|gXQtedsq15{>(9ir+GWkDmUie3z z_0qH6`bRd}?f934-VY!Z0$%hV*iZFdCIUcFK4$9I)g-T<)Vyg9Fh&+)>j$3Y4*w+j zCXi((w?`>kpy^#SuZ}4AL@jB(5rg4T-IkcU1CiwG?2N#_?=|~3uIN4y8fj!70b6Grct;nG=j7-rrUVNs$9)9fKZ>ytpTPYo^={I7Zj}`ls5GT;F{u&T}LUp&OSzn?}BRmb-x;8-Y#>_`BhKNf?s*kap5>okQv#S z_Vrh9#QaNPJ*UJMrKeXK^x{&*zH%4%5iexi%mXQ1f5)9qB|Q~JhdQxpPwjaeE=k2Y z^5t4_$)byY4CAd`Q5u2-KT!=y6C+2I>O{O9Uz5f|zbq$Yps_-o#)BGk^*4c5zfALm zYkWO_p;6x1T49tK8by}W5w;I_ZlQFt8vjlO@5$E|xOAVbj*cHGTk%(|(wPd?Z2@EO z=fJg;e{`+?e9@k|<=HA<_b##?t1BwHejmbei_;C;CxqHO%im+`3IIeDE7B4Y!JZ2! z&5>8aDD{#3mC}4Yw}}!jOR&;;u&hqw-H`G`!^3_cMVY~u$iT-nj=^B?3g{uHfw7&| zG=p*lu->kGYMBlpEUw~D0g29-hWmog z1^0~c6#f0)jURt?Y+<@-e9#A>7ZyCHOF5rT*h9cWk}VEfxd^=|5Oj3)qxX^c%7PoY z+X_{CzWwXNZ@(S2ym{@aZCcmuGFv||#O1?O3Q~?D zzguzt&GN)sKtG>)nS?^%_Yj~v>hT}H@(?RIt4Wt1D;_z1F3uVk#j6H~z4ItMQzuT^ z9?2Od*YFjIeP|Rc%v;b0zJE|g^Xl0KH!rkDDIta>DcO!9{|T%AeR{V8WG20zVQ0^ zihyEW8u7_|s)_$2u;&i5qOwn=qNdVgo)@%bSdYNj$Tua$k)I{g#hS{&*R| zAD1dOGA*x3qx!Y+JguCy>X!G-7@R#Y%2ms}>i$>|s%_3kh@;fQ*67sOaD6izaVYb{_5Aa6tOkvH*ov>-NjOc$5vu5CM%zO zhhGf*y7f3&h_-ch2*+tzaK#DL#j_7Zp+|jAVLSO<((}!yCYV}gO8JUJp(M`pWp@Y z?PDtayZN!}mA6I?UMRIEgj-ABfRa4w6v0SS4PF>k4ehNyZg?-+r9R;CgpqJyQ#G~7 zvC_xsqASZ>kK0WZ0e-zSS##AGY;x4ztR*V`qtB5=|Cd~ul;1Ca|2-diBjIC)I=I00 zRz9|ONV)pEY{4Jg zo^|kZ+U7V)fLEbz4?Zz0KU+rLEeNu3jQJd3jlg=m?i#SREUO2q42pPJ+QD^nty?jL zg+iEQv#T{v2;=&#Ns_H3_@9W9tNY&XD?9k(@aH0apj61e*+^u6mly%qnJV=(W#sFB z9V`Xj?y9Tt@dapF`T&r#p+)L**g0||zHz=-TK)*ID-PSkiw9Q#=@><~#00*?x&3DZ z6c+}q&k(4zkQC5;2z3XhOrTcjyfDaDWZOd3CXX`tkg=SvXb1N>c$&9FeZCdTTbVJ|Y z73^(lYD;Lot)MwQdq)@r3DNC8wZHV>=o6`RGH~#`_}6`&{GkQnb;9|}BDjZ+MV_;d zz%wN;C{Pp~AhV|%OaMBw&pSeM&7_OnyC0f><8ua2Z0u*;T;dDOb_wi0a{jUz4^KI| z9=xUCdQ^Y&MN^v*^n%)zd+rv0pccT4Y<{j}B)=~t%)a3KBZPrXX0ONrwEAD2j!tIs zPw*PL;F?j1;}-(QgHw!mp^Qy`m{r~VS5Dy`&x<1{4(nV9?J2A7AfgWD%($xw9We0$ zD4b?P4rb82GPAUiIgQ^We4c79o!|>$Ug-SdTGul5Ne$R`H?5qKQ`k3cv8JtU&BbfO>o(X_W zLPihbv~K)8cl%#t!zers!kS8TTGv(+x2Hpt0H!QHGnrr? zuJjmAY68JtzPgtggv+jZE8`F|R~WQ|p`-*WH@-{968WOq4?ngG&no95Zkdij7vxT# zEAjy0>=qvfLDsjEREl?Jrm{vy5C^8W*(xoUbD}mX104LbFR2y>)LNL2hh@I08EkxW zsfp6*Id19yR3$VYyRFry>@1SdOsT>u@PK$cZ0uZ$jys(7@mq)Z)f$NPy&7=yb|ovR zigPLr{h(Q+7Ly#u%U|HsYO{c#=^WD>X<|R|&tZwh}7^*h1tsoqKJ1JzwEkOp=kNIF}7D$Ww8GxhQ> z{Ki=Qun-BV3b2OD@8;+TrT;O|6Hu$`!R5uMOS@qD@X*Y~{c*f_&(^_1>lUD#e}}G1 z2Ly(tubGvd4QQe}hzy4R5l(&~3IZpUy*k&P@!Dr)vGl-a>!_VTin+TU0FEvfg9sjPb zJJ$qd4=|h8OSL#tF$GwVGD9wSDkrF;&Fx}MdQoD4DXaplOVL`Y*gf4%kP{=d_}?(k zxP*T53bWJweQv!)8K)D#>KSiqUN+Q)=<6!8bG;?p47admGsU_TjO;Q)n+wiOQ9Rbc ztB1H_2f9BR3Wm@93EUbbwEDU*V z-~Hwa`<|+2-?(RjCp^(1C(2Sf;#0cEb~V6rsWstrtr&z@nVOG{O!)QQG3Y~{ZgwPe zKo&E{W_m>qX0@snZZE>q4+KA{(H^XQ(bvKR1P$Q4hlO8fhb(e}=5Ly|g;jamTEWxC zV%2td84CZM5w1vTMKAlpP3^RdGJUoLNZ z0fm6BBVR+d@Zrr87ZiF^Mxd5K$#^!V;dGepI$){mwGSAaEY{a@1WD4h{_$bJIR@qG zjW>vD+akMM_Wq^gECK$c5hY?3qA>Q#s~ZB3w4kc82y`Uq**SA(Ff z$^>zC8VV$~5VO)*H+T4OyMZo5&MlHl^6(OVwlmk<(EiO-TH3I2y{2yCY32+*N?e@A zrKB6$3yuYFb{?^&Tex*2=@c6oZYdQ`qh%>VCPzpe5MuaAlTfG{f1jG@H8m zJqKk8PVhf+{Ak=VH|X%UHWz-5U4G#)hO)1@bJLE9*AhDP)!-Q>T;=&l7}r)62{f$D$D|SjQck)iTQeL{mk{skD)xsv# z)5e%a7dbonJrW?&3qP>khV-qjQ`Jt+klW*S(K*aDo5a2``;CegZ58{H$!oHPEwl-( z>D9iD>`6|?bU5vKcyqoG+U`40++f{YRY86JA`xnkj^3Gim6RAm=jw4v&Q=mgTxF^@ zeI}PnYw?)CS531V)@*Do%*c?ztgRDtQjBbAZgqg}IMN&0uOOiUNLirIS5n*3r}GsW za`H!h$nnP;G|sD%!Z?HCBUXTaXzdydR#NGW; XJX&EFTbceF?{o9IVf;$Ns{1(YN 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

k1>Yb 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

!T2myVx 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

  • #70KUOcdROinFBo>?! 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�x}0*yB&pJpxtuY&_(BwgfUBl1IiHBvc%9Y; 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 z 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 Y>r_ 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 F{{YcBpF98n diff --git a/hs-abci-examples/simple-storage/README.md b/hs-abci-examples/simple-storage/README.md index 175d8090..da7e6542 100644 --- a/hs-abci-examples/simple-storage/README.md +++ b/hs-abci-examples/simple-storage/README.md @@ -7,11 +7,6 @@ allows users to update and query the count. ## Environment Variables - LOG_SEVERITY (defaults to **info**) : minimum log severtiy level {debug, info, notice, warning, error, critical, alert, emergency} - LOG_VERBOSITY (defaults to **0**) : for each loggable data point, the level of information actually logged {0, 1, 2, 3} -- ES_HOST (optional) : hostname of the elasticsearch instance for logging -- ES_PORT (optional) : port number of elasticsearch instance for logging -- STATS_PORT (optional) : port to run the prometheus metrics server. - -**NOTE** If you do not provide both of the elasticsearch variables, the logger will default to logging to the console. ## Running with Docker There is a `docker-compose.yaml` file in this directory. If you use the `make` command from the project root @@ -21,23 +16,8 @@ There is a `docker-compose.yaml` file in this directory. If you use the `make` c ``` it will build an image for simple-storage and launch it in a docker network -with a tendermint-core node. The port for simple-storage is not exposed outside of the docker network -- -if you would like to submit transactions or query state you must do it using the tendermint RPC. - -## Running Locally -Assuming you have a [Tendermint v0.32.2 binary](https://github.com/tendermint/tendermint/releases/tag/v0.32.2) in your path, you can start a tendermint core node with - -```bash -> tendermint init -> tendermint node --consensus.create_empty_blocks=false -``` - -The `--consensus.create_empty_blocks=false` flag is helpful for keeping the logs from being polluted with empty blocks. You can then then start the example application using - -```bash -> make deploy-simple-storage-local -``` +with a tendermint-core node. ## Application Messages -The application uses a protobuf file to define its [transaction messages](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-example/protos/simple-storage/messages.proto). Thus if you would like to post transactions to this application via RPC, you will need to first consume -this profobuf file. You can follow the pattern in the test suite using hs-tendermint-client. +The application uses a protobuf file to define its [transaction messages](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-examples/simple-storage/protos/simple-storage/messages.proto). Thus if you would like to post transactions to this application via RPC, you will need to first consume +this profobuf file. You can follow the pattern in the test suite using hs-tendermint-client. \ No newline at end of file diff --git a/hs-abci-examples/simple-storage/app/Main.hs b/hs-abci-examples/simple-storage/app/Main.hs index e44b170d..fd87ad5e 100644 --- a/hs-abci-examples/simple-storage/app/Main.hs +++ b/hs-abci-examples/simple-storage/app/Main.hs @@ -1,22 +1,16 @@ module Main where -import Control.Concurrent (killThread) import Control.Exception (bracket) import Control.Lens ((^.)) -import Data.IORef (readIORef) import qualified Katip as K import SimpleStorage.Config (baseAppContext, - makeAppConfig, - prometheusServerThreadId) + makeAppConfig) import SimpleStorage.Server (makeAndServeApplication) import qualified Tendermint.SDK.BaseApp as BaseApp import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL main :: IO () main = - let close cfg = do - _ <- K.closeScribes (cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv) - prometheusThreadId <- readIORef $ cfg ^. prometheusServerThreadId - maybe (pure ()) killThread prometheusThreadId + let close cfg = K.closeScribes (cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv) in bracket makeAppConfig close makeAndServeApplication diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index ad62f94c..a68c990b 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -58,20 +58,16 @@ library: dependencies: - aeson - base >= 4.7 && < 5 - - bloodhound - bytestring - cereal - cereal-text - cryptonite - - errors - hs-abci-extra - hs-abci-server - hs-abci-types - hs-abci-extra - hs-abci-sdk - - http-client - katip - - katip-elasticsearch - lens - memory - polysemy diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs index 022d4590..836886a9 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs @@ -3,54 +3,37 @@ module SimpleStorage.Config ( AppConfig(..) , baseAppContext - , prometheusServerThreadId , makeAppConfig ) where -import Control.Concurrent (ThreadId) -import Control.Error (MaybeT (..), - runMaybeT) -import Control.Lens (makeLenses, (&), - (.~), (^.)) -import Data.IORef (IORef, newIORef) -import Data.Maybe (fromMaybe) -import Data.String.Conversions (cs) -import qualified Database.V5.Bloodhound as BH -import qualified Katip as K -import qualified Katip.Scribes.ElasticSearch as ES -import qualified Network.HTTP.Client as Client +import Control.Lens (makeLenses, (&), (.~), + (^.)) +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import qualified Katip as K import System.Environment -import System.IO (stdout) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P -import Text.Read (read) +import System.IO (stdout) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Logger.Katip as KL data AppConfig = AppConfig { _baseAppContext :: BaseApp.Context - , _prometheusServerThreadId :: IORef (Maybe ThreadId) } makeLenses ''AppConfig makeAppConfig :: IO AppConfig makeAppConfig = do - prometheusEnv <- runMaybeT $ do - prometheusPort <- read <$> MaybeT (lookupEnv "STATS_PORT") - pure $ P.MetricsScrapingConfig prometheusPort - c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") prometheusEnv - prometheusServer <- newIORef Nothing + c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") Nothing addScribesToLogEnv $ AppConfig { _baseAppContext = c - , _prometheusServerThreadId = prometheusServer } addScribesToLogEnv :: AppConfig -> IO AppConfig addScribesToLogEnv cfg = do logLevel <- makeLogLevel - loggingCfg <- makeLoggingConfig let initialLogEnv = cfg ^. baseAppContext . BaseApp.contextLogConfig . KL.logEnv - scribesLogEnv <- makeKatipScribe loggingCfg logLevel initialLogEnv + scribesLogEnv <- makeKatipScribe logLevel initialLogEnv pure $ cfg & baseAppContext . BaseApp.contextLogConfig . KL.logEnv .~ scribesLogEnv @@ -79,29 +62,11 @@ makeLogLevel = do | v == "3" = Just K.V3 | otherwise = Nothing - -data KatipConfig = ES {host :: String, port :: String} | Console - -makeLoggingConfig :: IO KatipConfig -makeLoggingConfig = do - mEsConfig <- runMaybeT $ - ES <$> (MaybeT $ lookupEnv "ES_HOST") <*> (MaybeT $ lookupEnv "ES_PORT") - pure $ fromMaybe Console mEsConfig - -- makes a log environment for console logs / ES logs makeKatipScribe - :: KatipConfig - -> LogLevel + :: LogLevel -> K.LogEnv -> IO K.LogEnv -makeKatipScribe kcfg LogLevel{..} le = case kcfg of - Console -> do - handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity - K.registerScribe "stdout" handleScribe K.defaultScribeSettings le - ES {host, port} -> do - mgr <- Client.newManager Client.defaultManagerSettings - let serverAddress = "http://" <> host <> ":" <> port - bloodhoundEnv = BH.mkBHEnv (BH.Server $ cs serverAddress) mgr - esScribe <- ES.mkEsScribe ES.defaultEsScribeCfgV5 bloodhoundEnv (BH.IndexName "simple-storage") - (BH.MappingName "application-logs") (K.permitItem severity) verbosity - K.registerScribe "es" esScribe K.defaultScribeSettings le +makeKatipScribe LogLevel{..} le = do + handleScribe <- K.mkHandleScribe K.ColorIfTerminal stdout (K.permitItem severity) verbosity + K.registerScribe "stdout" handleScribe K.defaultScribeSettings le diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs index 606ff074..9e077a1b 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs @@ -1,45 +1,24 @@ module SimpleStorage.Server (makeAndServeApplication) where -import Control.Lens ((^?), _Just) -import Data.Foldable (fold) -import Data.IORef (writeIORef) -import Data.Monoid (Endo (..)) -import Network.ABCI.Server (serveApp) -import Network.ABCI.Server.App (Middleware) -import qualified Network.ABCI.Server.Middleware.Logger as Logger -import qualified Network.ABCI.Server.Middleware.Metrics as Met -import Polysemy (Sem) -import SimpleStorage.Application (handlersContext) -import SimpleStorage.Config (AppConfig (..)) -import Tendermint.SDK.Application (createIOApp, - makeApp) -import Tendermint.SDK.BaseApp (Context (..), - CoreEffs, - contextPrometheusEnv, - runCoreEffs) -import Tendermint.SDK.BaseApp.Metrics.Prometheus (envMetricsState, - forkMetricsServer, - metricsRegistry) +import Data.Foldable (fold) +import Data.Monoid (Endo (..)) +import Network.ABCI.Server (serveApp) +import Network.ABCI.Server.App (Middleware) +import qualified Network.ABCI.Server.Middleware.Logger as Logger +import Polysemy (Sem) +import SimpleStorage.Application (handlersContext) +import SimpleStorage.Config (AppConfig (..)) +import Tendermint.SDK.Application (createIOApp, makeApp) +import Tendermint.SDK.BaseApp (CoreEffs, runCoreEffs) makeAndServeApplication :: AppConfig -> IO () makeAndServeApplication AppConfig{..} = do putStrLn "Starting ABCI application..." - case _contextPrometheusEnv _baseAppContext of - Nothing -> pure () - Just prometheusEnv -> do - prometheusThreadId <- forkMetricsServer prometheusEnv - writeIORef _prometheusServerThreadId (Just prometheusThreadId) - metricsMiddleware <- - case _baseAppContext ^? contextPrometheusEnv . _Just . envMetricsState . metricsRegistry of - Nothing -> pure id - Just registry -> Met.mkMetricsMiddleware Met.defaultBuckets registry - let nat :: forall a. Sem CoreEffs a -> IO a nat = runCoreEffs _baseAppContext application = makeApp handlersContext middleware :: Middleware (Sem CoreEffs) middleware = appEndo . fold $ [ Endo Logger.mkLoggerM - , Endo metricsMiddleware ] serveApp $ createIOApp nat (middleware application) diff --git a/hs-abci-extra/README.md b/hs-abci-extra/README.md index 6005d94e..fc5ae47e 100644 --- a/hs-abci-extra/README.md +++ b/hs-abci-extra/README.md @@ -4,4 +4,4 @@ The goal here is to provide common features and example middleware implementatio ## Example using middleware -There is a small example using the RequestLogger middleware [here](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-example). +There is a small example using the Logger middleware [here](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/simple-storage). \ No newline at end of file diff --git a/hs-abci-sdk/README.md b/hs-abci-sdk/README.md index 169892f9..7f86b20a 100644 --- a/hs-abci-sdk/README.md +++ b/hs-abci-sdk/README.md @@ -15,7 +15,7 @@ The SDK makes heavy use of an effects system to separate different components of ### BaseApp Effects -`BaseApp` is the set of effects that the SDK is written in. Every other module developed during the course of application development must eventually be compiled to this set of effects. As of now, `BaseApp` effects allows for things like access to storage, error handling, event logging, console logging, etc. +`BaseApp` is the set of effects that the SDK is written in. Every other module developed during the course of application development must eventually be compiled to this set of effects. As of now, `BaseApp` effects allows for things like access to storage, error handling, metrics logging, console logging, etc. ### Application Specific Effects From 57b061fc33df7aeaabdc7a139e7f0f5e1eb0d5f4 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Thu, 16 Jan 2020 10:12:53 -0500 Subject: [PATCH 40/70] Update README.md --- hs-abci-examples/nameservice/README.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index f538add1..e8dd1604 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -1,23 +1,20 @@ # Nameservice -The Nameservice application is a sample application meant to showcase the SDK. It roughly follows the example application from the golang cosmos-sdk, which you can find [here](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). +The `Nameservice` application is a sample application that showcases the SDK. It roughly follows the example application from the golang cosmos-sdk, which you can find [here](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). -There is also a [tutorial](./tutorial/README.md) that explains how the Nameservice app was built. +Not that the app itself is also a [tutorial]!(./tutorial/README.md). This tutorial explains in depth how the Nameservice app was built. You're encouraged to read this tutorial after reading this. ## Running the Application -The Nameservice application depends on a few services, so we provide a `docker-compose.yaml` file and highly suggest running the application in Docker. There is a `make deploy-nameservice` command which can be run from the project root to deploy the application. - -**NOTE** This will also attempt build the nameservice binaries in Docker, which can take a long time. If you are on (ubuntu) linux, you can use the `make docker-test-prebake` command first to build the application locally and copy the binaries to the correct image. If you then run `make deploy-nameservice`, it will automatically use these binaries instead of rebuilding in Docker. +The `Nameservice` application depends on a few external services. We provide a `docker-compose.yaml` file and highly suggest running the application inside Docker. There is a `make deploy-nameservice` command which can be run from the project root to deploy the application. +**NOTE** This will also attempt build the nameservice binaries in Docker, which can take a long time. If you are on (Ubuntu) Linux, you can use the `make docker-test-prebake` command first to build the application locally and copy the binaries to the correct image. If you then run `make deploy-nameservice`, it will automatically use these binaries instead of rebuilding inside Docker. ### Environment Variables You can provide the following environment variables when running `make deploy-nameservice` to customize the logger output: -- LOG_SEVERITY (defaults to **info**) : minimum log severtiy level {debug, info, notice, warning, error, critical, alert, emergency} -- LOG_VERBOSITY (defaults to **0**) : for each loggable data point, the level of information actually logged {0, 1, 2, 3} - -## Logging and Metrics +- `LOG_SEVERITY` (defaults to **info**): minimum log severtiy level `{debug, info, notice, warning, error, critical, alert, emergency}` +- `LOG_VERBOSITY` (defaults to **0**) : for each loggable data point, the level of information actually logged `{0, 1, 2, 3}` -There is a lot to say about how [logging](./docs/Logging.md) and [metrics](./docs/Metrics.md) are managed if you decide to use them. \ No newline at end of file +## [Next: Logging](./docs/Logging.md) From 30863290a67f348e04b32caa97b63d4dfd26670c Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Thu, 16 Jan 2020 10:16:26 -0500 Subject: [PATCH 41/70] Update Logging.md --- hs-abci-examples/nameservice/docs/Logging.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hs-abci-examples/nameservice/docs/Logging.md b/hs-abci-examples/nameservice/docs/Logging.md index c53a98db..a9e89aaa 100644 --- a/hs-abci-examples/nameservice/docs/Logging.md +++ b/hs-abci-examples/nameservice/docs/Logging.md @@ -1,12 +1,12 @@ # Logging -The SDK has built in support for structured logging via the [katip](https://hackage.haskell.org/package/katip) logging library. Even still, the SDK is agnostic to where you want your logs to go. Katip logs are managed by *scribes* whos job is precisely this, so it depends on which scribes you use. The two most common scribes are the [console scribe](https://hackage.haskell.org/package/katip-0.8.3.0/docs/Katip-Scribes-Handle.html#v:mkHandleScribe) and the [Elasticsearch scribe](https://hackage.haskell.org/package/katip-elasticsearch). +The SDK has built in support for structured logging via the [katip](https://hackage.haskell.org/package/katip) logging library. Even still, the SDK is agnostic to where you want your logs to go. Katip logs are managed by *scribes* whos job is precisely this, so the output depends on which scribes you use. The two most common scribes are the [console scribe](https://hackage.haskell.org/package/katip-0.8.3.0/docs/Katip-Scribes-Handle.html#v:mkHandleScribe) and the [Elasticsearch scribe](https://hackage.haskell.org/package/katip-elasticsearch). -The Nameservice application has support for either scribe -- it will use Elasticsearch if you provide the `ES_HOST` and `ES_PORT` environment variables or otherwise will default to console logging. The docker deployment is configured to use Elasticsearch. +The `Nameservice` application has support for either scribe -- it will use Elasticsearch if you provide the `ES_HOST` and `ES_PORT` environment variables or otherwise will default to console logging. The docker deployment is configured to use Elasticsearch. ## Logging to Elasticsearch -The docker network includes an `elk` image (Elasticsearch, Logstash, Kibana) for persisting and querying logs. You can read more about this stack [here](https://www.elastic.co/what-is/elk-stack), but the summary is that it is a powerful solution for hosting searchable structured logs. +The docker network includes an `elk` image (Elasticsearch, Logstash, Kibana) for persisting and querying logs. You can read more about this stack [here](https://www.elastic.co/what-is/elk-stack). In summary `elk` is a powerful solution for hosting searchable structured logs. When logging to Elasticsearch, you can use the Kibana dashboard for creating queries and visualizations. We will cover the basics here. If you have already launched the docker network, you can view the Kibana dashboard by going to http://localhost:5601/app/kibana. You should see something like @@ -80,4 +80,6 @@ This log corresponds to an event emitted by the `Nameservice` module during tran BaseApp.logEvent event ``` -In this way the log index serves as a rudimentary event indexer for transaction events as well. \ No newline at end of file +In this way the log index serves as a rudimentary event indexer for transaction events as well. + +## [Next: Metrics](./docs/Metrics.md) From 332c653b90a8f7cd0c66818e095a295230f4e8eb Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Thu, 16 Jan 2020 10:16:55 -0500 Subject: [PATCH 42/70] Update Logging.md --- hs-abci-examples/nameservice/docs/Logging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hs-abci-examples/nameservice/docs/Logging.md b/hs-abci-examples/nameservice/docs/Logging.md index a9e89aaa..23287796 100644 --- a/hs-abci-examples/nameservice/docs/Logging.md +++ b/hs-abci-examples/nameservice/docs/Logging.md @@ -82,4 +82,4 @@ This log corresponds to an event emitted by the `Nameservice` module during tran In this way the log index serves as a rudimentary event indexer for transaction events as well. -## [Next: Metrics](./docs/Metrics.md) +## [Next: Metrics](./Metrics.md) From c4d9846e183d262b50d3c58adc4d15fc38da918d Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Thu, 16 Jan 2020 10:19:37 -0500 Subject: [PATCH 43/70] Update Metrics.md --- hs-abci-examples/nameservice/docs/Metrics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hs-abci-examples/nameservice/docs/Metrics.md b/hs-abci-examples/nameservice/docs/Metrics.md index eef73cbb..128fa7bf 100644 --- a/hs-abci-examples/nameservice/docs/Metrics.md +++ b/hs-abci-examples/nameservice/docs/Metrics.md @@ -2,10 +2,10 @@ The SDK has some built in support for metrics via [prometheus](https://prometheus.io/), but ultimately you may choose a different runtime interpretation for the metrics, or even choose to ignore it entirely. -The Nameservice application uses application specific metrics, for instance increasing the message counters for module level messages, or for timing module responses. It also uses the server metrics via the [metrics middleware](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs) to count abci messages and time server responses. This middleware is highly recommended in any production system. +The `Nameservice` application uses application specific metrics, for instance increasing the message counters for module level messages, or for timing module responses. It also uses the server metrics via the [metrics middleware](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs) to count ABCI messages and to time server responses. This middleware is highly recommended for any production system. ## Setting up metrics -The nameservice application docker network is configured to run a prometheus metrics server and a datadog agent to scrape metrics and push to [datadog](https://www.datadoghq.com/). You must supply a datadog api key as an environment variable *DD_API_KEY* when you launch the network if you want to do do this. If you don't already have an account, you can create one and receive a two week free trial to play around with this application. +The `Nameservice` Docker network is configured to run a prometheus metrics server in addition to a Datadog agent that scrapes and pushes metrics to [datadog](https://www.datadoghq.com/). You must supply a Datadog API key as an environment variable *DD_API_KEY* when you launch the network if you want to do do this. If you don't already have an account, you can [create one](https://www.datadoghq.com/free-datadog-trial/) and receive a two week free trial to play around with this application. -To simply test if prometheus is indeed collecting your metrics, you can visit `localhost:5555/metrics` and you should see something. (`5555` is the default value for the `STATS_PORT` environment variable in the docker compose file.) \ No newline at end of file +To simply test if prometheus is indeed collecting your metrics, you can visit `http://localhost:5555/metrics` and you should see something. (`5555` is the default value for the `STATS_PORT` environment variable in the docker compose file.) From 739d8cf26660591ee5def83e7e3a82d5ef1d2a58 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Thu, 16 Jan 2020 10:21:44 -0500 Subject: [PATCH 44/70] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a9df83e7..b26809fc 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ This is the official repository for the Haskell implementation of the ABCI serve SDK for developing applications backed by the Tendermint replication engine. You can read more about Tendermint and the ABCI specs in their [documentation](https://tendermint.com/docs/spec/abci/). -To understand how to build a simple application using this library, see the literate haskell [walkthrough](https://github.com/f-o-a-m/hs-abci/tree/walkthrough/hs-abci-examples/nameservice/tutorial). +To understand how to build a simple application using this library, see the literate haskell [walkthrough](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-examples/nameservice/README.md). ## Build From c0a2aa2c8c52d41ce810166bf9a889f94067c584 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Thu, 16 Jan 2020 10:22:31 -0500 Subject: [PATCH 45/70] Update README.md --- hs-abci-examples/nameservice/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-examples/nameservice/README.md index e8dd1604..d51d4ccc 100644 --- a/hs-abci-examples/nameservice/README.md +++ b/hs-abci-examples/nameservice/README.md @@ -2,7 +2,7 @@ The `Nameservice` application is a sample application that showcases the SDK. It roughly follows the example application from the golang cosmos-sdk, which you can find [here](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). -Not that the app itself is also a [tutorial]!(./tutorial/README.md). This tutorial explains in depth how the Nameservice app was built. You're encouraged to read this tutorial after reading this. +Not that the app itself is also a [tutorial](./tutorial/README.md)! This tutorial explains in depth how the Nameservice app was built. You're encouraged to read this tutorial after reading this. ## Running the Application From e7954362e5c0113ddc5ac7624478f49aaf96a893 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 20 Jan 2020 07:57:12 -0800 Subject: [PATCH 46/70] Query params (#194) * added query params from servant * Added evalToIO * simple storage test compiles with flag * Query spec compiles * fix tests * fix tests * add query request type * updated client * rework query args * weeder * Added capture * added captures to query client --- .../Nameservice/Modules/Nameservice/Query.hs | 6 +- .../src/Nameservice/Modules/Token/Query.hs | 4 +- .../test/Nameservice/Test/E2ESpec.hs | 4 +- .../tutorial/Tutorial/Nameservice/Query.md | 6 +- .../Modules/SimpleStorage/Query.hs | 6 +- hs-abci-sdk/package.yaml | 13 +- .../Tendermint/SDK/Application/Handlers.hs | 4 +- .../src/Tendermint/SDK/Application/Module.hs | 8 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 9 + .../src/Tendermint/SDK/BaseApp/Query.hs | 34 ++-- .../src/Tendermint/SDK/BaseApp/Query/Class.hs | 133 ++++++++----- .../Tendermint/SDK/BaseApp/Query/Delayed.hs | 132 +++++++------ .../Tendermint/SDK/BaseApp/Query/Router.hs | 79 ++++---- .../src/Tendermint/SDK/BaseApp/Query/Store.hs | 61 +++--- .../src/Tendermint/SDK/BaseApp/Query/Types.hs | 76 ++++++-- .../src/Tendermint/SDK/Modules/Auth/Query.hs | 6 +- .../test/Tendermint/SDK/Test/QuerySpec.hs | 54 +++++ .../test/Tendermint/SDK/Test/SimpleStorage.hs | 184 ++++++++++++++++++ hs-abci-test-utils/package.yaml | 1 + .../src/Tendermint/Utils/Client.hs | 133 +++++++++---- 20 files changed, 696 insertions(+), 257 deletions(-) create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index d76936d0..1786db0e 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -3,7 +3,7 @@ module Nameservice.Modules.Nameservice.Query where import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Name, Whois) -import Polysemy (Members, Sem) +import Polysemy (Members) import Polysemy.Error (Error) import qualified Tendermint.SDK.BaseApp as BaseApp @@ -17,6 +17,6 @@ type Api = BaseApp.QueryApi NameserviceContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api (Sem r) + => BaseApp.RouteT Api r server = - BaseApp.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) + BaseApp.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs index 6e1cea20..fd22193c 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs @@ -18,6 +18,6 @@ type Api = BaseApp.QueryApi TokenContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api (Sem r) + => BaseApp.RouteT Api r server = - BaseApp.storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy (Sem r)) + BaseApp.storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index f2caa15d..c6a388a6 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -74,7 +74,7 @@ spec = do ClientResponse{ clientResponseData, clientResponseRaw } <- runRPC $ getWhois queryReq let queryRespCode = clientResponseRaw ^. Response._queryCode -- storage failure - queryRespCode `shouldBe` 1 + queryRespCode `shouldBe` 2 clientResponseData `shouldBe` Nothing it "Can set a name value (success 0)" $ do @@ -152,7 +152,7 @@ spec = do ClientResponse{ clientResponseData, clientResponseRaw } <- runRPC $ getWhois queryReq let queryRespCode = clientResponseRaw ^. Response._queryCode -- storage failure - queryRespCode `shouldBe` 1 + queryRespCode `shouldBe` 2 clientResponseData `shouldBe` Nothing it "Can fail a transfer (failure 1)" $ do diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md index 63f16fef..ecf78502 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md @@ -8,7 +8,7 @@ module Tutorial.Nameservice.Query where import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Whois, Name) -import Polysemy (Sem, Members) +import Polysemy (Members) import Polysemy.Error (Error) import Tendermint.SDK.BaseApp (RawStore, AppError, RouteT, QueryApi, storeQueryHandlers) ~~~ @@ -31,9 +31,9 @@ To serve all the data registered with the `IsKey` class, we can use the `storeQu ~~~ haskell server :: Members [RawStore, Error AppError] r - => RouteT Api (Sem r) + => RouteT Api r server = - storeQueryHandlers (Proxy @NameserviceContents) storeKey (Proxy :: Proxy (Sem r)) + storeQueryHandlers (Proxy @NameserviceContents) storeKey (Proxy :: Proxy r) ~~~ Here `RouteT` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely copy-pasted from that codebase. diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs index e5a5f9a9..57d09fcb 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs @@ -5,7 +5,7 @@ module SimpleStorage.Modules.SimpleStorage.Query ) where import Data.Proxy -import Polysemy (Members, Sem) +import Polysemy (Members) import Polysemy.Error (Error) import SimpleStorage.Modules.SimpleStorage.Keeper (storeKey) import SimpleStorage.Modules.SimpleStorage.Types (Count, CountKey) @@ -18,7 +18,7 @@ type Api = BaseApp.QueryApi CountStoreContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api (Sem r) + => BaseApp.RouteT Api r server = BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) - storeKey (Proxy :: Proxy (Sem r)) + storeKey (Proxy :: Proxy r) diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 69ff765a..5b722021 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -59,11 +59,11 @@ library: - errors - hs-abci-server - hs-abci-types + - http-api-data - http-types - katip - lens - memory - - mmorph - mtl - polysemy - polysemy-plugin @@ -113,6 +113,7 @@ library: - Tendermint.SDK.BaseApp.Store.AuthTreeStore - Tendermint.SDK.BaseApp.Store.RawStore - Tendermint.SDK.BaseApp.Store.Scope + - Tendermint.SDK.BaseApp.Transaction - Tendermint.SDK.Codec - Tendermint.SDK.Crypto - Tendermint.SDK.Modules.Auth @@ -137,6 +138,8 @@ tests: - Tendermint.SDK.Test.CryptoSpec - Tendermint.SDK.Test.GasSpec - Tendermint.SDK.Test.MetricsSpec + - Tendermint.SDK.Test.SimpleStorage + - Tendermint.SDK.Test.QuerySpec ghc-options: - -fplugin=Polysemy.Plugin @@ -149,17 +152,23 @@ tests: dependencies: - base >= 4.7 && < 5 - bytestring + - cereal + - cereal-text - containers - - lens + - cryptonite - cereal - hs-abci-sdk - hs-abci-types - hspec - hspec-core - hspec-discover + - lens + - memory - polysemy - polysemy-plugin - polysemy-zoo - prometheus - secp256k1-haskell + - servant - string-conversions + - text diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index 2c01588b..ec6d707a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -91,7 +91,7 @@ makeHandlers => Message alg ~ Digest SHA256 => M.TxRouter ms r => M.QueryRouter ms r - => HasRouter (M.Api ms) + => HasRouter (M.Api ms) r => Members CoreEffs core => M.Eval ms core => M.Effs ms core ~ r @@ -163,7 +163,7 @@ makeApp => Message alg ~ Digest SHA256 => M.TxRouter ms r => M.QueryRouter ms r - => HasRouter (M.Api ms) + => HasRouter (M.Api ms) r => Members CoreEffs core => M.Eval ms core => M.Effs ms core ~ r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 6c7cd20b..6cb0f3b0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -41,7 +41,7 @@ import Tendermint.SDK.Types.TxResult (TxResult) data Module (name :: Symbol) msg val (api :: *) (s :: EffectRow) (r :: EffectRow) = Module { moduleTxDeliverer :: PreRoutedTx msg -> Sem (T.TxEffs :& r) val , moduleTxChecker :: PreRoutedTx msg -> Sem (T.TxEffs :& r) val - , moduleQueryServer :: Q.RouteT api (Sem r) + , moduleQueryServer :: Q.RouteT api r , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } @@ -72,14 +72,14 @@ infixr 5 :+ queryRouter :: QueryRouter ms r - => Q.HasRouter (Api ms) + => Q.HasRouter (Api ms) r => Modules ms r -> Q.QueryApplication (Sem r) -queryRouter (ms :: Modules ms r) = Q.serve (Proxy :: Proxy (Api ms)) (routeQuery ms) +queryRouter (ms :: Modules ms r) = Q.serve (Proxy :: Proxy (Api ms)) (Proxy :: Proxy r) (routeQuery ms) class QueryRouter ms r where type Api ms :: * - routeQuery :: Modules ms r -> Q.RouteT (Api ms) (Sem r) + routeQuery :: Modules ms r -> Q.RouteT (Api ms) r instance QueryRouter '[Module name msg val api s r] r where type Api '[Module name msg val api s r] = name :> api diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index af73f92e..c25795ca 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -25,6 +25,14 @@ module Tendermint.SDK.BaseApp , get , delete + -- * Query Routes + , Leaf + , QA + + -- * Scope + , ConnectionScope(..) + , applyScope + -- * Errors , AppError(..) , IsAppError(..) @@ -65,6 +73,7 @@ module Tendermint.SDK.BaseApp , FromQueryData(..) , QueryApi , RouteT + , QueryResult(..) , storeQueryHandlers ) where diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs index fd861a61..8800fe41 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs @@ -8,7 +8,13 @@ module Tendermint.SDK.BaseApp.Query , module Tendermint.SDK.BaseApp.Query.Store ) where +import Control.Lens ((&), (.~)) +import Data.Default.Class (def) import Data.Proxy +import Network.ABCI.Types.Messages.Request () +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Errors (makeAppError, + queryAppError) import Tendermint.SDK.BaseApp.Query.Class import Tendermint.SDK.BaseApp.Query.Delayed import Tendermint.SDK.BaseApp.Query.Router @@ -16,26 +22,24 @@ import Tendermint.SDK.BaseApp.Query.Store import Tendermint.SDK.BaseApp.Query.Types serveRouter - :: Monad m - => Router () m - -> QueryApplication m -serveRouter r = toApplication $ runRouter r () + :: Router () r + -> QueryApplication (Sem r) +serveRouter rtr = toApplication $ runRouter rtr () serve - :: HasRouter layout - => Monad m + :: HasRouter layout r => Proxy layout - -> RouteT layout m - -> QueryApplication m -serve p server = - toApplication (runRouter (route p (emptyDelayed (Route server))) ()) + -> Proxy r + -> RouteT layout r + -> QueryApplication (Sem r) +serve pl pr server = + toApplication (runRouter (route pl pr (emptyDelayed (Route server))) ()) toApplication - :: Monad m - => RoutingApplication m -> QueryApplication m + :: RoutingApplication r -> QueryApplication (Sem r) toApplication ra query = do - res <- ra query + res <- ra $ parseQueryRequest query case res of - Fail e -> pure $ responseQueryError query e - FailFatal e -> pure $ responseQueryError query e + Fail e -> pure $ def & queryAppError .~ makeAppError e + FailFatal e -> pure $ def & queryAppError .~ makeAppError e Route a -> pure a diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs index 8670b2b8..c4c7ed6d 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs @@ -1,78 +1,113 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.BaseApp.Query.Class where -import Control.Error -import Control.Monad.Morph (hoist) +import Control.Monad (join) import Data.Proxy import Data.String.Conversions (cs) +import Data.Text (Text) import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.HTTP.Types.URI (QueryText, + parseQueryText) +import Polysemy (Sem) import Servant.API -import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, addQueryArgs, - delayedFail) -import Tendermint.SDK.BaseApp.Query.Router (Router, Router' (..), +import Servant.API.Modifiers (FoldLenient, + FoldRequired, + RequestArgument, + unfoldRequestArgument) +import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, DelayedM, + addCapture, addParameter, + addQueryArgs, + delayedFail, withQuery) +import Tendermint.SDK.BaseApp.Query.Router (Router, + Router' (CaptureRouter), choice, methodRouter, pathRouter) import Tendermint.SDK.BaseApp.Query.Types (FromQueryData (..), Leaf, QA, QueryArgs (..), QueryError (..), - QueryResult, - Queryable (..)) + QueryRequest (..), + QueryResult) +import Tendermint.SDK.Codec (HasCodec) +import Web.HttpApiData (FromHttpApiData (..), + parseUrlPieceMaybe) -------------------------------------------------------------------------------- -- | This class is used to construct a router given a 'layout' type. The layout -- | is constructed using the combinators that appear in the instances here, no other -- | Servant combinators are recognized. -class HasRouter layout where +class HasRouter layout r where -- | A route handler. - type RouteT layout (m :: * -> *) :: * + type RouteT layout r :: * -- | Transform a route handler into a 'Router'. - route :: Monad m => Proxy layout -> Delayed m env (RouteT layout m) -> Router env m - hoistRoute :: (Monad m, Monad n) => Proxy layout -> (forall a. m a -> n a) -> RouteT layout m -> RouteT layout n + route :: Proxy layout -> Proxy r -> Delayed (Sem r) env (RouteT layout r) -> Router env r +instance (HasRouter a r, HasRouter b r) => HasRouter (a :<|> b) r where + type RouteT (a :<|> b) r = RouteT a r :<|> RouteT b r -instance (HasRouter a, HasRouter b) => HasRouter (a :<|> b) where - type RouteT (a :<|> b) m = RouteT a m :<|> RouteT b m - - route _ server = choice (route pa ((\ (a :<|> _) -> a) <$> server)) - (route pb ((\ (_ :<|> b) -> b) <$> server)) + route _ pr server = choice (route pa pr ((\ (a :<|> _) -> a) <$> server)) + (route pb pr ((\ (_ :<|> b) -> b) <$> server)) where pa = Proxy :: Proxy a pb = Proxy :: Proxy b - hoistRoute _ phi (a :<|> b) = - hoistRoute (Proxy :: Proxy a) phi a :<|> - hoistRoute (Proxy :: Proxy b) phi b - -instance (HasRouter sublayout, KnownSymbol path) => HasRouter (path :> sublayout) where +instance (HasRouter sublayout r, KnownSymbol path) => HasRouter (path :> sublayout) r where - type RouteT (path :> sublayout) m = RouteT sublayout m + type RouteT (path :> sublayout) r = RouteT sublayout r - route _ subserver = - pathRouter (cs (symbolVal proxyPath)) (route (Proxy :: Proxy sublayout) subserver) + route _ pr subserver = + pathRouter (cs (symbolVal proxyPath)) (route (Proxy :: Proxy sublayout) pr subserver) where proxyPath = Proxy :: Proxy path - hoistRoute _ = hoistRoute (Proxy :: Proxy sublayout) - - -instance (Queryable a, KnownSymbol (Name a)) => HasRouter (Leaf a) where - - type RouteT (Leaf a) m = ExceptT QueryError m (QueryResult a) - route _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter - where proxyPath = Proxy :: Proxy (Name a) - hoistRoute _ = hoist - - - -instance (FromQueryData a, HasRouter layout) - => HasRouter (QA a :> layout) where - - type RouteT (QA a :> layout) m = QueryArgs a -> RouteT layout m - - route _ d = - RQueryArgs $ - route (Proxy :: Proxy layout) - (addQueryArgs d $ \ qa -> case fromQueryData $ queryArgsData qa of - Left e -> delayedFail $ InvalidQuery e - Right v -> return qa {queryArgsData = v} - ) - hoistRoute _ phi f = hoistRoute (Proxy :: Proxy layout) phi . f +instance ( HasRouter sublayout r, KnownSymbol sym, FromHttpApiData a + , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods) + ) => HasRouter (QueryParam' mods sym a :> sublayout) r where + + type RouteT (QueryParam' mods sym a :> sublayout) r = RequestArgument mods a -> RouteT sublayout r + + route _ pr subserver = + let querytext :: QueryRequest -> Network.HTTP.Types.URI.QueryText + querytext q = parseQueryText . cs $ queryRequestParamString q + paramname = cs $ symbolVal (Proxy :: Proxy sym) + parseParam :: Monad m => QueryRequest -> DelayedM m (RequestArgument mods a) + parseParam q = unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev + where + mev :: Maybe (Either Text a) + mev = fmap parseQueryParam $ join $ lookup paramname $ querytext q + errReq = delayedFail $ InvalidQuery ("Query parameter " <> cs paramname <> " is required.") + errSt e = delayedFail $ InvalidQuery ("Error parsing query param " <> cs paramname <> " " <> cs e <> ".") + delayed = addParameter subserver $ withQuery parseParam + in route (Proxy :: Proxy sublayout) pr delayed + +instance (FromHttpApiData a, HasRouter sublayout r) => HasRouter (Capture' mods capture a :> sublayout) r where + + type RouteT (Capture' mods capture a :> sublayout) r = a -> RouteT sublayout r + + route _ pr subserver = + CaptureRouter $ + route (Proxy :: Proxy sublayout) + pr + (addCapture subserver $ \ txt -> case parseUrlPieceMaybe txt of + Nothing -> delayedFail PathNotFound + Just v -> return v + ) + +instance HasCodec a => HasRouter (Leaf a) r where + + type RouteT (Leaf a) r = Sem r (QueryResult a) + route _ _ = methodRouter + +instance (FromQueryData a, HasRouter sublayout r) + => HasRouter (QA a :> sublayout) r where + + type RouteT (QA a :> sublayout) r = QueryArgs a -> RouteT sublayout r + + route _ pr subserver = + let parseQueryArgs QueryRequest{..} = case fromQueryData queryRequestData of + Left e -> delayedFail $ InvalidQuery ("Error parsing query data, " <> cs e <> ".") + Right a -> pure QueryArgs + { queryArgsData = a + , queryArgsHeight = queryRequestHeight + , queryArgsProve = queryRequestProve + } + delayed = addQueryArgs subserver $ withQuery parseQueryArgs + in route (Proxy :: Proxy sublayout) pr delayed diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs index 1364d34c..f8f7d3a5 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs @@ -1,102 +1,124 @@ module Tendermint.SDK.BaseApp.Query.Delayed where -import Control.Error (ExceptT, runExceptT) import Control.Monad.Reader (MonadReader, ReaderT, ask, runReaderT) import Control.Monad.Trans (MonadTrans (..)) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Default.Class (def) -import Data.String.Conversions (cs) -import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response -import Tendermint.SDK.BaseApp.Query.Types (QueryError (..), +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Query.Types (QueryArgs (..), + QueryError (..), + QueryRequest (..), RouteResult (..), - RouteResultT (..)) + RouteResultT (..), + defaultQueryWithData) -------------------------------------------------------------------------------- -- NOTE: most of this was vendored and repurposed from servant newtype DelayedM m a = - DelayedM { runDelayedM' :: ReaderT Request.Query (RouteResultT m) a } - deriving (Functor, Applicative, Monad, MonadReader Request.Query) + DelayedM { runDelayedM' :: ReaderT QueryRequest (RouteResultT m) a } + deriving (Functor, Applicative, Monad, MonadReader QueryRequest) liftRouteResult :: Monad m => RouteResult a -> DelayedM m a liftRouteResult x = DelayedM $ lift $ RouteResultT . return $ x -runDelayedM :: DelayedM m a -> Request.Query -> m (RouteResult a) +runDelayedM :: DelayedM m a -> QueryRequest -> m (RouteResult a) runDelayedM m req = runRouteResultT $ runReaderT (runDelayedM' m) req -------------------------------------------------------------------------------- data Delayed m env a where - Delayed :: { delayedQueryArgs :: env -> DelayedM m qa - , delayedHandler :: qa -> Request.Query -> RouteResult a + Delayed :: { delayedCaptures :: env -> DelayedM m captures + , delayedQueryArgs :: DelayedM m qa + , delayedParams :: DelayedM m params + , delayedHandler :: captures -> qa -> params -> QueryRequest -> RouteResult a } -> Delayed m env a -instance Functor (Delayed m env) where +instance Functor m => Functor (Delayed m env) where fmap f Delayed{..} = - Delayed { delayedHandler = fmap (fmap f) . delayedHandler + Delayed { delayedHandler = \captures qa params q -> f <$> delayedHandler captures qa params q , .. } -runDelayed :: Monad m - => Delayed m env a - -> env - -> Request.Query - -> m (RouteResult a) +runDelayed + :: Monad m + => Delayed m env a + -> env + -> QueryRequest + -> m (RouteResult a) runDelayed Delayed{..} env = runDelayedM (do q <- ask - qa <- delayedQueryArgs env - liftRouteResult $ delayedHandler qa q + captures <- delayedCaptures env + qa <- delayedQueryArgs + params <- delayedParams + liftRouteResult $ delayedHandler captures qa params q ) -runAction :: Monad m - => Delayed m env (ExceptT QueryError m a) - -> env - -> Request.Query - -> (a -> RouteResult Response.Query) - -> m (RouteResult Response.Query) -runAction action env query k = - runDelayed action env query >>= go - where - go (Fail e) = pure $ Fail e - go (FailFatal e) = pure $ FailFatal e - go (Route a) = do - e <- runExceptT a - case e of - Left err -> pure $ Route (responseQueryError query err) - Right a' -> pure $ k a' +runAction + :: Delayed (Sem r) env (Sem r a) + -> env + -> QueryRequest + -> (a -> RouteResult Response.Query) + -> Sem r (RouteResult Response.Query) +runAction action env query k = do + res <- runDelayed action env query + case res of + Route a -> k <$> a + Fail e -> pure $ Fail e + FailFatal e -> pure $ FailFatal e -- | Fail with the option to recover. delayedFail :: Monad m => QueryError -> DelayedM m a delayedFail err = liftRouteResult $ Fail err -responseQueryError :: Request.Query -> QueryError -> Response.Query -responseQueryError Request.Query{..} e = - let msg = case e of - PathNotFound -> "Path Not Found" - ResourceNotFound -> "Resource Not Found: queryData=" <> cs (Base64.format queryData) - InvalidQuery m -> "Invalid Query: " <> m - InternalError _ -> "Internal Error" - in def { Response.queryCode = 1 - , Response.queryLog = cs msg - , Response.queryCodespace = queryPath - } - addQueryArgs :: Monad m - => Delayed m env (a -> b) - -> (qa -> DelayedM m a) - -> Delayed m (qa, env) b + => Delayed m env (QueryArgs a -> b) + -> DelayedM m (QueryArgs a) + -> Delayed m env b addQueryArgs Delayed{..} new = Delayed - { delayedQueryArgs = \ (qa, env) -> (,) <$> delayedQueryArgs env <*> new qa - , delayedHandler = \ (x, v) query -> ($ v) <$> delayedHandler x query + { delayedQueryArgs = (,) <$> delayedQueryArgs <*> new + , delayedHandler = \caps (qa, qaNew) p query -> ($ qaNew) <$> delayedHandler caps qa p query + , .. + } + +addCapture + :: Monad m + => Delayed m env (a -> b) + -> (captured -> DelayedM m a) + -> Delayed m (captured, env) b +addCapture Delayed{..} new = + Delayed + { delayedCaptures = \ (txt, env) -> (,) <$> delayedCaptures env <*> new txt + , delayedHandler = \ (x, v) qa p query -> ($ v) <$> delayedHandler x qa p query + , .. + } -- Note [Existential Record Update] + +addParameter + :: Monad m + => Delayed m env (a -> b) + -> DelayedM m a + -> Delayed m env b +addParameter Delayed {..} new = + Delayed + { delayedParams = (,) <$> delayedParams <*> new + , delayedHandler = \caps qa (p, pNew) query -> ($ pNew) <$> delayedHandler caps qa p query , .. } emptyDelayed :: Monad m => RouteResult a -> Delayed m b a emptyDelayed response = let r = pure () - in Delayed (const r) $ \_ _ -> response + qa = pure $ defaultQueryWithData () + in Delayed (const r) qa r $ \_ _ _ _ -> response + +-- | Gain access to the incoming request. +withQuery + :: Monad m + => (QueryRequest -> DelayedM m a) + -> DelayedM m a +withQuery f = do + req <- ask + f req diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs index d85b385f..83108299 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs @@ -1,91 +1,93 @@ module Tendermint.SDK.BaseApp.Query.Router where -import Control.Error -import Control.Lens (to, (&), (.~), (^.)) -import Data.ByteArray.Base64String (Base64String) +import Control.Lens ((&), (.~)) +import Data.ByteArray.Base64String (fromBytes) import Data.Default.Class (def) import Data.Map (Map) import qualified Data.Map as M import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.Encoding as T -import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response import Network.HTTP.Types (decodePathSegments) +import Polysemy (Sem) import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, runAction) -import Tendermint.SDK.BaseApp.Query.Types (QueryArgs (..), - QueryError (..), +import Tendermint.SDK.BaseApp.Query.Types (QueryError (..), + QueryRequest (..), QueryResult (..), - Queryable (..), RouteResult (..)) +import Tendermint.SDK.Codec (HasCodec (..)) -- NOTE: most of this was vendored and repurposed from servant data Router' env a = - RChoice (Router' env a) (Router' env a) - | RStatic (Map Text (Router' env a)) [env -> a] - | RQueryArgs (Router' (QueryArgs Base64String, env) a) + StaticRouter (Map Text (Router' env a)) [env -> a] + | CaptureRouter (Router' (Text, env) a) + | Choice (Router' env a) (Router' env a) -type RoutingApplication m = Request.Query -> m (RouteResult Response.Query) -type Router env m = Router' env (RoutingApplication m) +type RoutingApplication r = QueryRequest -> Sem r (RouteResult Response.Query) + +type Router env r = Router' env (RoutingApplication r) pathRouter :: Text -> Router' env a -> Router' env a -pathRouter t r = RStatic (M.singleton t r) [] +pathRouter t r = StaticRouter (M.singleton t r) [] leafRouter :: (env -> a) -> Router' env a -leafRouter l = RStatic M.empty [l] +leafRouter l = StaticRouter M.empty [l] choice :: Router' env a -> Router' env a -> Router' env a -choice (RStatic table1 ls1) (RStatic table2 ls2) = - RStatic (M.unionWith choice table1 table2) (ls1 ++ ls2) -choice router1 (RChoice router2 router3) = RChoice (choice router1 router2) router3 -choice router1 router2 = RChoice router1 router2 +choice (StaticRouter table1 ls1) (StaticRouter table2 ls2) = + StaticRouter (M.unionWith choice table1 table2) (ls1 ++ ls2) +choice (CaptureRouter router1) (CaptureRouter router2) = + CaptureRouter (choice router1 router2) +choice router1 (Choice router2 router3) = Choice (choice router1 router2) router3 +choice router1 router2 = Choice router1 router2 methodRouter - :: Monad m - => Queryable b - => Delayed m env (ExceptT QueryError m (QueryResult b)) - -> Router env m + :: HasCodec b + => Delayed (Sem r) env (Sem r (QueryResult b)) + -> Router env r methodRouter action = leafRouter route' where route' env query = runAction action env query $ \QueryResult{..} -> Route $ def & Response._queryIndex .~ queryResultIndex & Response._queryKey .~ queryResultKey - & Response._queryValue .~ encodeQueryResult queryResultData + & Response._queryValue .~ fromBytes (encode queryResultData) & Response._queryProof .~ queryResultProof & Response._queryHeight .~ queryResultHeight runRouter - :: Monad m - => Router env m + :: Router env r -> env - -> RoutingApplication m + -> RoutingApplication r runRouter router env query = case router of - RStatic table ls -> - let path = query ^. Request._queryPath . to (decodePathSegments . T.encodeUtf8) + StaticRouter table ls -> + let path = decodePathSegments . T.encodeUtf8 $ queryRequestPath query in case path of [] -> runChoice ls env query -- This case is to handle trailing slashes. [""] -> runChoice ls env query first : rest | Just router' <- M.lookup first table - -> let query' = query { Request.queryPath = T.intercalate "/" rest } + -> let query' = query { queryRequestPath = T.intercalate "/" rest } in runRouter router' env query' _ -> pure $ Fail PathNotFound - RQueryArgs r' -> - let qa = QueryArgs - { queryArgsData = query ^. Request._queryData - , queryArgsHeight = query ^. Request._queryHeight - , queryArgsProve = query ^. Request._queryProve - } - in runRouter r' (qa, env) query - RChoice r1 r2 -> + CaptureRouter router' -> + let path = decodePathSegments . T.encodeUtf8 $ queryRequestPath query + in case path of + [] -> pure $ Fail PathNotFound + -- This case is to handle trailing slashes. + [""] -> pure $ Fail PathNotFound + first : rest + -> let query' = query { queryRequestPath = T.intercalate "/" rest } + in runRouter router' (first, env) query' + Choice r1 r2 -> runChoice [runRouter r1, runRouter r2] env query -runChoice :: Monad m => [env -> RoutingApplication m] -> env -> RoutingApplication m +runChoice :: [env -> RoutingApplication r] -> env -> RoutingApplication r runChoice ls = case ls of [] -> \ _ _ -> pure $ Fail PathNotFound @@ -96,4 +98,3 @@ runChoice ls = case response1 of Fail _ -> runChoice rs env query _ -> pure response1 - diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index 48b73893..682fc276 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -2,21 +2,30 @@ module Tendermint.SDK.BaseApp.Query.Store where -import Control.Error (ExceptT, throwE) -import Control.Lens (to, (^.)) -import Control.Monad.Trans (lift) -import Data.ByteArray.Base64String (fromBytes) +import Control.Lens (to, (^.)) +import Data.ByteArray.Base64String (fromBytes) import Data.Proxy -import GHC.TypeLits (Symbol) -import Polysemy (Members, Sem) -import Polysemy.Error (Error) -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Errors (AppError) +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, Symbol, + symbolVal) +import Polysemy (Members, Sem) +import Polysemy.Error (Error, throw) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) import Tendermint.SDK.BaseApp.Query.Class +import Tendermint.SDK.BaseApp.Query.Router (methodRouter, pathRouter) import Tendermint.SDK.BaseApp.Query.Types -import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), - RawStore, StoreKey, get) -import Tendermint.SDK.Codec (HasCodec) +import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), + RawStore, StoreKey, get) +import Tendermint.SDK.Codec (HasCodec) + +data StoreLeaf a + +instance (Queryable a, KnownSymbol (Name a)) => HasRouter (StoreLeaf a) r where + + type RouteT (StoreLeaf a) r = Sem r (QueryResult a) + route _ _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter + where proxyPath = Proxy :: Proxy (Name a) class StoreQueryHandler a (ns :: Symbol) h where storeQueryHandler :: Proxy a -> StoreKey ns -> h @@ -27,12 +36,12 @@ instance , HasCodec a , Members [RawStore, Error AppError] r ) - => StoreQueryHandler a ns (QueryArgs k -> ExceptT QueryError (Sem r) (QueryResult a)) where + => StoreQueryHandler a ns (QueryArgs k -> (Sem r) (QueryResult a)) where storeQueryHandler _ storeKey QueryArgs{..} = do let key = queryArgsData - mRes <- lift $ get storeKey key + mRes <- get storeKey key case mRes of - Nothing -> throwE ResourceNotFound + Nothing -> throw . makeAppError $ ResourceNotFound Just (res :: a) -> pure $ QueryResult -- TODO: actually handle proofs { queryResultData = res @@ -42,36 +51,36 @@ instance , queryResultHeight = 0 } -class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) m where +class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) r where type QueryApi kvs :: * - storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy m -> RouteT (QueryApi kvs) m + storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy r -> RouteT (QueryApi kvs) r instance ( IsKey k ns , a ~ Value k ns , HasCodec a , Members [RawStore, Error AppError] r - ) => StoreQueryHandlers '[(k,a)] ns (Sem r) where - type QueryApi '[(k,a)] = QA k :> Leaf a + ) => StoreQueryHandlers '[(k,a)] ns r where + type QueryApi '[(k,a)] = QA k :> StoreLeaf a storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey instance ( IsKey k ns , a ~ Value k ns , HasCodec a - , StoreQueryHandlers ((k', a') ': as) ns (Sem r) + , StoreQueryHandlers ((k', a') ': as) ns r , Members [RawStore, Error AppError] r - ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns (Sem r) where + ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns r where type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) - storeQueryHandlers _ storeKey pm = + storeQueryHandlers _ storeKey pr = storeQueryHandler (Proxy :: Proxy a) storeKey :<|> - storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pm + storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pr allStoreHandlers :: forall (contents :: [*]) ns r. - StoreQueryHandlers contents ns (Sem r) + StoreQueryHandlers contents ns r => Proxy contents -> StoreKey ns -> Proxy r - -> RouteT (QueryApi contents) (Sem r) -allStoreHandlers pcs storeKey _ = storeQueryHandlers pcs storeKey (Proxy :: Proxy (Sem r)) + -> RouteT (QueryApi contents) r +allStoreHandlers pcs storeKey pr = storeQueryHandlers pcs storeKey pr diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs index bbe3dc23..d211a49b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs @@ -3,14 +3,15 @@ module Tendermint.SDK.BaseApp.Query.Types where import Control.Lens (from, (^.)) import Control.Monad (ap) import Control.Monad.Trans (MonadTrans (..)) -import Data.ByteArray.Base64String (Base64String, - fromBytes, toBytes) +import Data.ByteArray.Base64String (Base64String, toBytes) import Data.Int (Int64) -import Data.Text (Text) +import Data.Text (Text, breakOn, uncons) import GHC.TypeLits (Symbol) import Network.ABCI.Types.Messages.FieldTypes (Proof, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response +import Tendermint.SDK.BaseApp.Errors (AppError (..), + IsAppError (..)) import Tendermint.SDK.BaseApp.Store (RawKey (..)) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) @@ -25,17 +26,70 @@ type QueryApplication m = Request.Query -> m Response.Query -------------------------------------------------------------------------------- +data QueryRequest = QueryRequest + { queryRequestPath :: Text + , queryRequestParamString :: Text + , queryRequestData :: Base64String + , queryRequestProve :: Bool + , queryRequestHeight :: Int64 + } deriving (Eq, Show) + +parseQueryRequest + :: Request.Query + -> QueryRequest +parseQueryRequest Request.Query{..} = + let (path, queryStrQ) = breakOn "?" queryPath + queryStr = case Data.Text.uncons queryStrQ of + Nothing -> "" + Just ('?', rest) -> rest + _ -> error "Impossible result parsing query string from path." + in QueryRequest + { queryRequestPath = path + , queryRequestParamString = queryStr + , queryRequestData = queryData + , queryRequestProve = queryProve + , queryRequestHeight = unWrappedVal queryHeight + } + +-------------------------------------------------------------------------------- + data QueryError = PathNotFound | ResourceNotFound - | InvalidQuery String - | InternalError String + | InvalidQuery Text + | InternalError Text deriving (Show) +instance IsAppError QueryError where + makeAppError PathNotFound = + AppError + { appErrorCode = 1 + , appErrorCodespace = "query" + , appErrorMessage = "Path not found." + } + makeAppError ResourceNotFound = + AppError + { appErrorCode = 2 + , appErrorCodespace = "query" + , appErrorMessage = "Resource not found." + } + makeAppError (InvalidQuery msg) = + AppError + { appErrorCode = 3 + , appErrorCodespace = "query" + , appErrorMessage = "Invalid query: " <> msg + } + makeAppError (InternalError _) = + AppError + { appErrorCode = 4 + , appErrorCodespace = "query" + , appErrorMessage = "Internal error." + } + data QueryArgs a = QueryArgs { queryArgsProve :: Bool , queryArgsData :: a - , queryArgsHeight :: WrappedVal Int64 + , queryArgsHeight :: Int64 } deriving Functor -- wrap data with default query fields @@ -59,16 +113,8 @@ data QueryResult a = QueryResult -- | class representing objects which can be queried via the hs-abci query message. -- | Here the 'Name' is the leaf of the query url, e.g. if you can access a token -- | balance of type `Balance` at "token/balance", then 'Name Balance ~ "balance"'. -class Queryable a where +class HasCodec a => Queryable a where type Name a :: Symbol - encodeQueryResult :: a -> Base64String - decodeQueryResult :: Base64String -> Either Text a - - default encodeQueryResult :: HasCodec a => a -> Base64String - encodeQueryResult = fromBytes . encode - - default decodeQueryResult :: HasCodec a => Base64String -> Either Text a - decodeQueryResult = decode . toBytes -- | This class is used to parse the 'data' field of the query request message. -- | The default method assumes that the 'data' is simply the key for the diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs index 7d62df95..197bcf35 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs @@ -4,7 +4,7 @@ module Tendermint.SDK.Modules.Auth.Query ) where import Data.Proxy -import Polysemy (Members, Sem) +import Polysemy (Members) import Polysemy.Error (Error) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Modules.Auth.Keeper (storeKey) @@ -21,6 +21,6 @@ type Api = BaseApp.QueryApi AuthContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api (Sem r) + => BaseApp.RouteT Api r server = - BaseApp.storeQueryHandlers (Proxy :: Proxy AuthContents) storeKey (Proxy :: Proxy (Sem r)) + BaseApp.storeQueryHandlers (Proxy :: Proxy AuthContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs new file mode 100644 index 00000000..ce9191b0 --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs @@ -0,0 +1,54 @@ +module Tendermint.SDK.Test.QuerySpec (spec) where + +import qualified Data.ByteArray.Base64String as Base64 +import Data.Text (Text) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import qualified Tendermint.SDK.Application as App +import qualified Tendermint.SDK.Application.Module as M +import qualified Tendermint.SDK.BaseApp as BA +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Transaction as T +import Tendermint.SDK.Codec (HasCodec (..)) +import qualified Tendermint.SDK.Test.SimpleStorage as SS +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), + Tx (..)) +import Test.Hspec + +type Effs = SS.SimpleStorage ': BA.BaseApp BA.CoreEffs + +spec :: Spec +spec = beforeAll (BA.makeContext (KL.InitialLogNamespace "test" "spec") Nothing) $ + describe "Query tests" $ do + let modules :: App.Modules '[SS.SimpleStorageM Effs] Effs + modules = SS.simpleStorageModule App.:+ App.NilModules + ssServer = M.queryRouter modules + handler = App.moduleTxDeliverer SS.simpleStorageModule + it "Can make a new count and query it with a multiplier" $ \ctx -> do + let increaseCountMsg = Msg + { msgAuthor = undefined + , msgData = SS.UpdateCount $ SS.UpdateCountTx 1 + } + tx = PreRoutedTx $ Tx + { txMsg = increaseCountMsg + , txRoute = undefined + , txGas = 0 + , txSignature = undefined + , txSignBytes = undefined + , txSigner = undefined + , txNonce = undefined + } + txContext <- T.newTransactionContext tx + _ <- SS.evalToIO ctx . T.eval txContext $ handler tx + let q = Req.Query + -- TODO -- this shouldn't require / count + { queryPath = "/simple_storage/manipulated/1?factor=4" + , queryData = undefined + , queryProve = False + , queryHeight = 0 + } + Resp.Query{..} <- SS.evalToIO ctx $ ssServer q + queryCode `shouldBe` 0 + let resultCount = decode (Base64.toBytes queryValue) :: Either Text SS.Count + resultCount `shouldBe` Right 3 diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs new file mode 100644 index 00000000..454c892c --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -0,0 +1,184 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.Test.SimpleStorage + ( SimpleStorageMessage(..) + , SimpleStorageM + , SimpleStorage + , UpdateCountTx(..) + , simpleStorageModule + , evalToIO + , Count(..) + ) where + +import Control.Lens (iso, (^.)) +import Crypto.Hash (SHA256 (..), hashWith) +import Data.Bifunctor (first) +import Data.ByteArray (convert) +import qualified Data.ByteArray.Base64String as Base64 +import Data.ByteString (ByteString) +import Data.Int (Int32) +import Data.Maybe (fromJust) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.Serialize.Text () +import qualified Data.Serialize.Text () +import Data.String.Conversions (cs) +import GHC.Generics (Generic) +import Polysemy +import Polysemy.Error (Error) +import Servant.API +import Tendermint.SDK.Application (Module (..)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) + +-------------------------------------------------------------------------------- +-- Types +-------------------------------------------------------------------------------- + +newtype Count = Count Int32 deriving (Eq, Show, Ord, Num, Serialize.Serialize) + +data CountKey = CountKey + +instance HasCodec Count where + encode = Serialize.encode + decode = first cs . Serialize.decode + +instance BaseApp.RawKey CountKey where + rawKey = iso (\_ -> cs countKey) (const CountKey) + where + countKey :: ByteString + countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) + +instance BaseApp.IsKey CountKey "simple_storage" where + type Value CountKey "simple_storage" = Count + +instance BaseApp.FromQueryData CountKey + +instance BaseApp.Queryable Count where + type Name Count = "count" + +-------------------------------------------------------------------------------- +-- Message Types +-------------------------------------------------------------------------------- + +data SimpleStorageMessage = + UpdateCount UpdateCountTx + +data UpdateCountTx = UpdateCountTx + { updateCountTxCount :: Int32 + } deriving (Show, Eq, Generic) + +-------------------------------------------------------------------------------- +-- Keeper +-------------------------------------------------------------------------------- + +storeKey :: BaseApp.StoreKey "simple_storage" +storeKey = BaseApp.StoreKey "simple_storage" + +data SimpleStorage m a where + PutCount :: Count -> SimpleStorage m () + GetCount :: SimpleStorage m Count + +makeSem ''SimpleStorage + +type SimpleStorageEffs = '[SimpleStorage] + +updateCount + :: Member SimpleStorage r + => Count + -> Sem r () +updateCount count = putCount count + +eval + :: forall r. + Members '[BaseApp.RawStore, Error BaseApp.AppError] r + => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) +eval = interpret (\case + PutCount count -> BaseApp.put storeKey CountKey count + GetCount -> fromJust <$> BaseApp.get storeKey CountKey + ) + +-------------------------------------------------------------------------------- +-- Router +-------------------------------------------------------------------------------- + +router + :: Member SimpleStorage r + => Members BaseApp.TxEffs r + => PreRoutedTx SimpleStorageMessage + -> Sem r () +router (PreRoutedTx Tx{txMsg}) = + let Msg{msgData} = txMsg + in case msgData of + UpdateCount UpdateCountTx{updateCountTxCount} -> + updateCount (Count updateCountTxCount) + +-------------------------------------------------------------------------------- +-- Server +-------------------------------------------------------------------------------- + +type CountStoreContents = '[(CountKey, Count)] + +type GetMultipliedCount = + "manipulated" + :> Capture "subtract" Integer + :> QueryParam' '[Required, Strict] "factor" Integer + :> BaseApp.Leaf Count + +getMultipliedCount + :: Member SimpleStorage r + => Integer + -> Integer + -> Sem r (BaseApp.QueryResult Count) +getMultipliedCount subtractor multiplier = do + let m = fromInteger multiplier + s = fromInteger subtractor + c <- getCount + pure $ BaseApp.QueryResult + { queryResultData = m * c - s + , queryResultIndex = 0 + , queryResultKey = Base64.fromBytes $ CountKey ^. BaseApp.rawKey + , queryResultProof = Nothing + , queryResultHeight = 0 + } + +type Api = GetMultipliedCount :<|> BaseApp.QueryApi CountStoreContents + +server + :: forall r. + Members [SimpleStorage, BaseApp.RawStore, Error BaseApp.AppError] r + => BaseApp.RouteT Api r +server = + let storeHandlers = BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) + storeKey (Proxy :: Proxy r) + in getMultipliedCount :<|> storeHandlers + +-------------------------------------------------------------------------------- +-- Module Definition +-------------------------------------------------------------------------------- + +type SimpleStorageM r = + Module "simple_storage" SimpleStorageMessage () Api SimpleStorageEffs r + +simpleStorageModule + :: Member SimpleStorage r + => Members BaseApp.BaseAppEffs r + => SimpleStorageM r +simpleStorageModule = Module + { moduleTxDeliverer = router + , moduleTxChecker = router + , moduleQueryServer = server + , moduleEval = eval + } + +evalToIO + :: BaseApp.Context + -> Sem (SimpleStorage ': BaseApp.BaseApp BaseApp.CoreEffs) a + -> IO a +evalToIO context action = + BaseApp.runCoreEffs context . + BaseApp.compileToCoreEffs . + BaseApp.applyScope @'BaseApp.Consensus $ + eval action diff --git a/hs-abci-test-utils/package.yaml b/hs-abci-test-utils/package.yaml index 05ebbd4d..0e90e704 100644 --- a/hs-abci-test-utils/package.yaml +++ b/hs-abci-test-utils/package.yaml @@ -49,6 +49,7 @@ library: - data-default-class - errors - hspec + - http-api-data - lens - mtl - secp256k1-haskell diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Client.hs b/hs-abci-test-utils/src/Tendermint/Utils/Client.hs index d076a9fd..0a2b77a7 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/Client.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/Client.hs @@ -6,21 +6,28 @@ module Tendermint.Utils.Client , ClientResponse(..) ) where -import Control.Lens (to, (^.)) -import Control.Monad.Reader (ReaderT) -import qualified Data.ByteArray.Base64String as Base64 -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString) +import Control.Lens (to, (^.)) +import Control.Monad.Reader (ReaderT) +import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, symbolVal) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp -import qualified Network.Tendermint.Client as RPC -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), - Queryable (..)) -import Tendermint.SDK.BaseApp.Store (RawKey (..)) +import Data.String.Conversions (cs) +import Data.Text (Text, intercalate) +import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import qualified Network.Tendermint.Client as RPC +import Servant.API +import Servant.API.Modifiers +import Tendermint.SDK.BaseApp.Query.Store (StoreLeaf) +import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, + QueryArgs (..), + Queryable (..)) +import Tendermint.SDK.BaseApp.Store (RawKey (..)) +import Tendermint.SDK.Codec (HasCodec (decode)) +import Web.Internal.HttpApiData (ToHttpApiData (..)) class Monad m => RunClient m where -- | How to make a request. @@ -36,27 +43,62 @@ instance RunClient (ReaderT RPC.Config IO) where } in RPC.resultABCIQueryResponse <$> RPC.abciQuery rpcQ +type QueryStringList = [(Text, Text)] + class HasClient m layout where type ClientT (m :: * -> *) layout :: * - genClient :: Proxy m -> Proxy layout -> Req.Query -> ClientT m layout + genClient :: Proxy m -> Proxy layout -> (Req.Query, QueryStringList) -> ClientT m layout instance (HasClient m a, HasClient m b) => HasClient m (a :<|> b) where type ClientT m (a :<|> b) = ClientT m a :<|> ClientT m b - genClient pm _ q = genClient pm (Proxy @a) q :<|> genClient pm (Proxy @b) q + genClient pm _ (q,qs) = genClient pm (Proxy @a) (q,qs) :<|> genClient pm (Proxy @b) (q,qs) instance (KnownSymbol path, HasClient m a) => HasClient m (path :> a) where type ClientT m (path :> a) = ClientT m a - genClient pm _ q = genClient pm (Proxy @a) - q {Req.queryPath = Req.queryPath q <> "/" <> cs (symbolVal (Proxy @path))} + genClient pm _ (q,qs) = genClient pm (Proxy @a) + (q {Req.queryPath = Req.queryPath q <> "/" <> cs (symbolVal (Proxy @path))}, qs) + +appendToQueryString + :: Text -- ^ param name + -> Maybe Text -- ^ param value + -> QueryStringList + -> QueryStringList +appendToQueryString pname pvalue qs = + maybe qs (\v -> (pname, v) : qs) pvalue + +instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequired mods)) + => HasClient m (QueryParam' mods sym a :> api) where + + type ClientT m (QueryParam' mods sym a :> api) = RequiredArgument mods a -> ClientT m api + + -- if mparam = Nothing, we don't add it to the query string + genClient pm Proxy (q,qs) mparam = + genClient pm (Proxy :: Proxy api) $ foldRequiredArgument + (Proxy :: Proxy mods) add (maybe (q,qs) add) mparam + where + add :: a -> (Req.Query, QueryStringList) + add param = (q, appendToQueryString pname (Just $ toQueryParam param) qs) + + pname :: Text + pname = cs $ symbolVal (Proxy :: Proxy sym) instance (RawKey k, HasClient m a) => HasClient m (QA k :> a) where type ClientT m (QA k :> a) = QueryArgs k -> ClientT m a - genClient pm _ q QueryArgs{..} = genClient pm (Proxy @a) - q { Req.queryData = queryArgsData ^. rawKey . to Base64.fromBytes - , Req.queryHeight = queryArgsHeight - , Req.queryProve = queryArgsProve - } + genClient pm _ (q,qs) QueryArgs{..} = genClient pm (Proxy @a) + (q { Req.queryData = queryArgsData ^. rawKey . to Base64.fromBytes + , Req.queryHeight = WrappedVal queryArgsHeight + , Req.queryProve = queryArgsProve + }, qs) + +instance (ToHttpApiData a, HasClient m api) => HasClient m (Capture' mods capture a :> api) where + + type ClientT m (Capture' mods capture a :> api) = a -> ClientT m api + + genClient pm _ (q,qs) val = + let p = toUrlPiece val + q' = q { Req.queryPath = Req.queryPath q <> "/" <> p } + in genClient pm (Proxy :: Proxy api) (q', qs) -- | Data is Nothing iff Raw includes a non-0 response value data ClientResponse a = ClientResponse @@ -64,16 +106,39 @@ data ClientResponse a = ClientResponse , clientResponseRaw :: Resp.Query } -instance (RunClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasClient m (Leaf a) where +addQueryParamsToPath + :: QueryStringList + -> Text + -> Text +addQueryParamsToPath qs path = + let qParams = intercalate "&" $ map (\(n,v) -> n <> "=" <> v) qs + in case qs of + [] -> path + _ -> path <> "?" <> qParams + +instance (HasCodec a, RunClient m) => HasClient m (Leaf a) where type ClientT m (Leaf a) = m (ClientResponse a) - genClient _ _ q = + genClient _ _ = leafGenClient + +leafGenClient + :: HasCodec a + => RunClient m + => (Req.Query, QueryStringList) + -> m (ClientResponse a) +leafGenClient (q,qs) = do + let reqPath = addQueryParamsToPath qs $ Req.queryPath q + r@Resp.Query{..} <- runQuery q { Req.queryPath = reqPath } + -- anything other than 0 code is a failure: https://tendermint.readthedocs.io/en/latest/abci-spec.html + -- and will result in queryValue decoding to a "empty/default" object + return $ case queryCode of + 0 -> case decode $ Base64.toBytes queryValue of + Left err -> error $ "Impossible parse error: " <> cs err + Right a -> ClientResponse (Just a) r + _ -> ClientResponse Nothing r + +instance (RunClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasClient m (StoreLeaf a) where + type ClientT m (StoreLeaf a) = m (ClientResponse a) + genClient _ _ (q,qs) = let leaf = symbolVal (Proxy @(Name a)) - in do - r@Resp.Query{..} <- runQuery q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } - -- anything other than 0 code is a failure: https://tendermint.readthedocs.io/en/latest/abci-spec.html - -- and will result in queryValue decoding to a "empty/default" object - return $ case queryCode of - 0 -> case decodeQueryResult queryValue of - Left err -> error $ "Impossible parse error: " <> cs err - Right a -> ClientResponse (Just a) r - _ -> ClientResponse Nothing r + q' = q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } + in leafGenClient (q', qs) From d9d251da11301abbfac02f6cd7b0850e79820144 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Tue, 21 Jan 2020 12:06:09 -0500 Subject: [PATCH 47/70] WIP: Plain travis (#195) * try travis stages * proper nesting in stage * include both e2es * fix travis.yml * Update .travis.yml * Update .travis.yml * Removed failed docker rm step * Trying a flat travis file for benchmarking * remove iavl container after tests Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- .travis.yml | 58 +++++++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/.travis.yml b/.travis.yml index 284f734e..52d14ccc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -34,43 +34,49 @@ install: jobs: include: - - name: "Test Libraries" - script: make test-libraries - - name: "Test Libraries with Nix" + + - stage: Cleanliness is next to Godliness + name: "Ensure that code matches style guidelines for PRs to master" + # When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty + if: branch = master script: - - echo "This stage is currently disabled as Nix doesn't seem to play well with libsecp256k1" - #- curl https://nixos.org/nix/install | sh - #- source $HOME/.nix-profile/etc/profile.d/nix.sh && stack --nix test hs-abci-types hs-abci-server hs-abci-sdk - - name: "Test IAVL Client" + - echo "test formatting" + - travis_wait 120 stack --skip-ghc-check install hlint-2.1.26 stylish-haskell-0.9.4.3 weeder-1.0.8 + - make stylish && git diff-index --quiet HEAD + - make hlint + - make weeder + + - echo "test tutorial" + - travis_wait 120 stack --skip-ghc-check install markdown-unlit-0.5.0 + - make test-tutorial + + - stage: Core Tests + name: "Test Core and Client Libraries" script: - - docker run -p 8090:8090 -p 8091:8091 -d foamspace/iavl:latest /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + - echo "Test libraries" + - make test-libraries + - echo "Test Libraries with Nix" + - echo "This stage is currently disabled as Nix doesn't seem to play well with libsecp256k1" + + - echo "Test IAVL Client" + - docker run --name iavl -p 8090:8090 -p 8091:8091 -d foamspace/iavl:latest /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" - make test-iavl-client - - name: "Test Tendermint Client" - script: + - docker rm -f iavl + + - echo "Test Tendermint Client" - docker-compose -f hs-tendermint-client/docker-compose.yaml -p test-hs-tendermint-client up -d - make test-kv-store - docker-compose -f hs-tendermint-client/docker-compose.yaml -p test-hs-tendermint-client down -v --rmi local - - name: "Test SimpleStorage Example E2E" - script: + + - echo "Test simple-storage" - make docker-test-prebake - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e up -d - make test-simple-storage - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e down -v --rmi local - - name: "Test Nameservice Example E2E" - script: + + - echo "Test nameservice" - make docker-test-prebake - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e up -d - make test-nameservice - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local - - name: "Test Nameservice Tutorial" - script: - - travis_wait 120 stack --skip-ghc-check install markdown-unlit-0.5.0 - - make test-tutorial - - name: "Ensure that code matches style guidelines" - # When branch is `master` we run `haskell-stylish` and fail if git working directory becomes dirty - if: branch = master - script: - - travis_wait 120 stack --skip-ghc-check install hlint-2.1.26 stylish-haskell-0.9.4.3 weeder-1.0.8 - - make stylish && git diff-index --quiet HEAD - - make hlint - - make weeder + From 6bd251056f4cbe7d88f6a38f7731c3c77bcd9499 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Tue, 21 Jan 2020 11:39:15 -0800 Subject: [PATCH 48/70] Generalized router (#199) * compiles with generalized types, todo: move query specific types * cleanup imports, make router top level module --- hs-abci-sdk/package.yaml | 5 +- .../src/Tendermint/SDK/BaseApp/Query.hs | 43 +++--- .../src/Tendermint/SDK/BaseApp/Query/Class.hs | 58 ++++---- .../Tendermint/SDK/BaseApp/Query/Delayed.hs | 124 ----------------- .../Tendermint/SDK/BaseApp/Query/Router.hs | 100 -------------- .../src/Tendermint/SDK/BaseApp/Query/Store.hs | 40 +++--- .../src/Tendermint/SDK/BaseApp/Query/Types.hs | 86 +----------- .../src/Tendermint/SDK/BaseApp/Router.hs | 9 ++ .../Tendermint/SDK/BaseApp/Router/Delayed.hs | 128 ++++++++++++++++++ .../Tendermint/SDK/BaseApp/Router/Router.hs | 95 +++++++++++++ .../Tendermint/SDK/BaseApp/Router/Types.hs | 94 +++++++++++++ 11 files changed, 407 insertions(+), 375 deletions(-) delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Router.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Types.hs diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 5b722021..d6760493 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -105,10 +105,11 @@ library: - Tendermint.SDK.BaseApp.Metrics.Prometheus - Tendermint.SDK.BaseApp.Query - Tendermint.SDK.BaseApp.Query.Class - - Tendermint.SDK.BaseApp.Query.Delayed - - Tendermint.SDK.BaseApp.Query.Router - Tendermint.SDK.BaseApp.Query.Store - Tendermint.SDK.BaseApp.Query.Types + - Tendermint.SDK.BaseApp.Router.Delayed + - Tendermint.SDK.BaseApp.Router.Types + - Tendermint.SDK.BaseApp.Router.Router - Tendermint.SDK.BaseApp.Store - Tendermint.SDK.BaseApp.Store.AuthTreeStore - Tendermint.SDK.BaseApp.Store.RawStore diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs index 8800fe41..18cd132b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs @@ -1,30 +1,24 @@ module Tendermint.SDK.BaseApp.Query ( serve - , serveRouter - , module Tendermint.SDK.BaseApp.Query.Class - , module Tendermint.SDK.BaseApp.Query.Router + , HasRouter(..) + , StoreQueryHandlers(..) , module Tendermint.SDK.BaseApp.Query.Types - , module Tendermint.SDK.BaseApp.Query.Delayed - , module Tendermint.SDK.BaseApp.Query.Store ) where -import Control.Lens ((&), (.~)) -import Data.Default.Class (def) +import Control.Lens ((&), (.~)) +import Data.Default.Class (def) import Data.Proxy -import Network.ABCI.Types.Messages.Request () -import Polysemy (Sem) -import Tendermint.SDK.BaseApp.Errors (makeAppError, - queryAppError) -import Tendermint.SDK.BaseApp.Query.Class -import Tendermint.SDK.BaseApp.Query.Delayed -import Tendermint.SDK.BaseApp.Query.Router -import Tendermint.SDK.BaseApp.Query.Store +import qualified Network.ABCI.Types.Messages.Response as Response +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Errors (makeAppError, + queryAppError) +import Tendermint.SDK.BaseApp.Query.Class (HasRouter (..)) +import Tendermint.SDK.BaseApp.Query.Store (StoreQueryHandlers (..)) import Tendermint.SDK.BaseApp.Query.Types - -serveRouter - :: Router () r - -> QueryApplication (Sem r) -serveRouter rtr = toApplication $ runRouter rtr () +import Tendermint.SDK.BaseApp.Router.Delayed (emptyDelayed) +import Tendermint.SDK.BaseApp.Router.Router (runRouter) +import Tendermint.SDK.BaseApp.Router.Types (Application, + RouteResult (..)) serve :: HasRouter layout r @@ -33,11 +27,12 @@ serve -> RouteT layout r -> QueryApplication (Sem r) serve pl pr server = - toApplication (runRouter (route pl pr (emptyDelayed (Route server))) ()) + toQueryApplication (runRouter (route pl pr (emptyDelayed (Route server))) ()) -toApplication - :: RoutingApplication r -> QueryApplication (Sem r) -toApplication ra query = do +toQueryApplication + :: Application (Sem r) QueryRequest Response.Query + -> QueryApplication (Sem r) +toQueryApplication ra query = do res <- ra $ parseQueryRequest query case res of Fail e -> pure $ def & queryAppError .~ makeAppError e diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs index c4c7ed6d..697f9ff6 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs @@ -1,11 +1,15 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.BaseApp.Query.Class where +import Control.Lens ((&), (.~)) import Control.Monad (join) +import Data.ByteArray.Base64String (fromBytes) +import Data.Default.Class (def) import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text) import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.ABCI.Types.Messages.Response as Response import Network.HTTP.Types.URI (QueryText, parseQueryText) import Polysemy (Sem) @@ -14,23 +18,16 @@ import Servant.API.Modifiers (FoldLenient, FoldRequired, RequestArgument, unfoldRequestArgument) -import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, DelayedM, - addCapture, addParameter, - addQueryArgs, - delayedFail, withQuery) -import Tendermint.SDK.BaseApp.Query.Router (Router, - Router' (CaptureRouter), - choice, methodRouter, - pathRouter) import Tendermint.SDK.BaseApp.Query.Types (FromQueryData (..), Leaf, QA, QueryArgs (..), - QueryError (..), QueryRequest (..), - QueryResult) -import Tendermint.SDK.Codec (HasCodec) + QueryResult (..)) +import qualified Tendermint.SDK.BaseApp.Router as R +import Tendermint.SDK.Codec (HasCodec (..)) import Web.HttpApiData (FromHttpApiData (..), parseUrlPieceMaybe) + -------------------------------------------------------------------------------- -- | This class is used to construct a router given a 'layout' type. The layout @@ -40,12 +37,13 @@ class HasRouter layout r where -- | A route handler. type RouteT layout r :: * -- | Transform a route handler into a 'Router'. - route :: Proxy layout -> Proxy r -> Delayed (Sem r) env (RouteT layout r) -> Router env r + route :: Proxy layout -> Proxy r -> R.Delayed (Sem r) env QueryRequest (RouteT layout r) + -> R.Router env r QueryRequest Response.Query instance (HasRouter a r, HasRouter b r) => HasRouter (a :<|> b) r where type RouteT (a :<|> b) r = RouteT a r :<|> RouteT b r - route _ pr server = choice (route pa pr ((\ (a :<|> _) -> a) <$> server)) + route _ pr server = R.choice (route pa pr ((\ (a :<|> _) -> a) <$> server)) (route pb pr ((\ (_ :<|> b) -> b) <$> server)) where pa = Proxy :: Proxy a pb = Proxy :: Proxy b @@ -55,7 +53,7 @@ instance (HasRouter sublayout r, KnownSymbol path) => HasRouter (path :> sublayo type RouteT (path :> sublayout) r = RouteT sublayout r route _ pr subserver = - pathRouter (cs (symbolVal proxyPath)) (route (Proxy :: Proxy sublayout) pr subserver) + R.pathRouter (cs (symbolVal proxyPath)) (route (Proxy :: Proxy sublayout) pr subserver) where proxyPath = Proxy :: Proxy path instance ( HasRouter sublayout r, KnownSymbol sym, FromHttpApiData a @@ -68,14 +66,13 @@ instance ( HasRouter sublayout r, KnownSymbol sym, FromHttpApiData a let querytext :: QueryRequest -> Network.HTTP.Types.URI.QueryText querytext q = parseQueryText . cs $ queryRequestParamString q paramname = cs $ symbolVal (Proxy :: Proxy sym) - parseParam :: Monad m => QueryRequest -> DelayedM m (RequestArgument mods a) parseParam q = unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev where mev :: Maybe (Either Text a) mev = fmap parseQueryParam $ join $ lookup paramname $ querytext q - errReq = delayedFail $ InvalidQuery ("Query parameter " <> cs paramname <> " is required.") - errSt e = delayedFail $ InvalidQuery ("Error parsing query param " <> cs paramname <> " " <> cs e <> ".") - delayed = addParameter subserver $ withQuery parseParam + errReq = R.delayedFail $ R.InvalidRequest ("Query parameter " <> cs paramname <> " is required.") + errSt e = R.delayedFail $ R.InvalidRequest ("Error parsing query param " <> cs paramname <> " " <> cs e <> ".") + delayed = R.addParameter subserver $ R.withRequest parseParam in route (Proxy :: Proxy sublayout) pr delayed instance (FromHttpApiData a, HasRouter sublayout r) => HasRouter (Capture' mods capture a :> sublayout) r where @@ -83,11 +80,11 @@ instance (FromHttpApiData a, HasRouter sublayout r) => HasRouter (Capture' mods type RouteT (Capture' mods capture a :> sublayout) r = a -> RouteT sublayout r route _ pr subserver = - CaptureRouter $ + R.CaptureRouter $ route (Proxy :: Proxy sublayout) pr - (addCapture subserver $ \ txt -> case parseUrlPieceMaybe txt of - Nothing -> delayedFail PathNotFound + (R.addCapture subserver $ \ txt -> case parseUrlPieceMaybe txt of + Nothing -> R.delayedFail R.PathNotFound Just v -> return v ) @@ -103,11 +100,26 @@ instance (FromQueryData a, HasRouter sublayout r) route _ pr subserver = let parseQueryArgs QueryRequest{..} = case fromQueryData queryRequestData of - Left e -> delayedFail $ InvalidQuery ("Error parsing query data, " <> cs e <> ".") + Left e -> R.delayedFail $ R.InvalidRequest ("Error parsing query data, " <> cs e <> ".") Right a -> pure QueryArgs { queryArgsData = a , queryArgsHeight = queryRequestHeight , queryArgsProve = queryRequestProve } - delayed = addQueryArgs subserver $ withQuery parseQueryArgs + delayed = R.addBody subserver $ R.withRequest parseQueryArgs in route (Proxy :: Proxy sublayout) pr delayed + +-------------------------------------------------------------------------------- + +methodRouter + :: HasCodec b + => R.Delayed (Sem r) env req (Sem r (QueryResult b)) + -> R.Router env r req Response.Query +methodRouter action = R.leafRouter route' + where + route' env query = R.runAction action env query $ \QueryResult{..} -> + R.Route $ def & Response._queryIndex .~ queryResultIndex + & Response._queryKey .~ queryResultKey + & Response._queryValue .~ fromBytes (encode queryResultData) + & Response._queryProof .~ queryResultProof + & Response._queryHeight .~ queryResultHeight diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs deleted file mode 100644 index f8f7d3a5..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Delayed.hs +++ /dev/null @@ -1,124 +0,0 @@ -module Tendermint.SDK.BaseApp.Query.Delayed where - -import Control.Monad.Reader (MonadReader, ReaderT, - ask, runReaderT) -import Control.Monad.Trans (MonadTrans (..)) -import qualified Network.ABCI.Types.Messages.Response as Response -import Polysemy (Sem) -import Tendermint.SDK.BaseApp.Query.Types (QueryArgs (..), - QueryError (..), - QueryRequest (..), - RouteResult (..), - RouteResultT (..), - defaultQueryWithData) - --------------------------------------------------------------------------------- --- NOTE: most of this was vendored and repurposed from servant - - -newtype DelayedM m a = - DelayedM { runDelayedM' :: ReaderT QueryRequest (RouteResultT m) a } - deriving (Functor, Applicative, Monad, MonadReader QueryRequest) - -liftRouteResult :: Monad m => RouteResult a -> DelayedM m a -liftRouteResult x = DelayedM $ lift $ RouteResultT . return $ x - -runDelayedM :: DelayedM m a -> QueryRequest -> m (RouteResult a) -runDelayedM m req = runRouteResultT $ runReaderT (runDelayedM' m) req - --------------------------------------------------------------------------------- - -data Delayed m env a where - Delayed :: { delayedCaptures :: env -> DelayedM m captures - , delayedQueryArgs :: DelayedM m qa - , delayedParams :: DelayedM m params - , delayedHandler :: captures -> qa -> params -> QueryRequest -> RouteResult a - } -> Delayed m env a - -instance Functor m => Functor (Delayed m env) where - fmap f Delayed{..} = - Delayed { delayedHandler = \captures qa params q -> f <$> delayedHandler captures qa params q - , .. - } - -runDelayed - :: Monad m - => Delayed m env a - -> env - -> QueryRequest - -> m (RouteResult a) -runDelayed Delayed{..} env = runDelayedM (do - q <- ask - captures <- delayedCaptures env - qa <- delayedQueryArgs - params <- delayedParams - liftRouteResult $ delayedHandler captures qa params q - ) - -runAction - :: Delayed (Sem r) env (Sem r a) - -> env - -> QueryRequest - -> (a -> RouteResult Response.Query) - -> Sem r (RouteResult Response.Query) -runAction action env query k = do - res <- runDelayed action env query - case res of - Route a -> k <$> a - Fail e -> pure $ Fail e - FailFatal e -> pure $ FailFatal e - --- | Fail with the option to recover. -delayedFail :: Monad m => QueryError -> DelayedM m a -delayedFail err = liftRouteResult $ Fail err - -addQueryArgs - :: Monad m - => Delayed m env (QueryArgs a -> b) - -> DelayedM m (QueryArgs a) - -> Delayed m env b -addQueryArgs Delayed{..} new = - Delayed - { delayedQueryArgs = (,) <$> delayedQueryArgs <*> new - , delayedHandler = \caps (qa, qaNew) p query -> ($ qaNew) <$> delayedHandler caps qa p query - , .. - } - -addCapture - :: Monad m - => Delayed m env (a -> b) - -> (captured -> DelayedM m a) - -> Delayed m (captured, env) b -addCapture Delayed{..} new = - Delayed - { delayedCaptures = \ (txt, env) -> (,) <$> delayedCaptures env <*> new txt - , delayedHandler = \ (x, v) qa p query -> ($ v) <$> delayedHandler x qa p query - , .. - } -- Note [Existential Record Update] - -addParameter - :: Monad m - => Delayed m env (a -> b) - -> DelayedM m a - -> Delayed m env b -addParameter Delayed {..} new = - Delayed - { delayedParams = (,) <$> delayedParams <*> new - , delayedHandler = \caps qa (p, pNew) query -> ($ pNew) <$> delayedHandler caps qa p query - , .. - } - -emptyDelayed :: Monad m => RouteResult a -> Delayed m b a -emptyDelayed response = - let r = pure () - qa = pure $ defaultQueryWithData () - in Delayed (const r) qa r $ \_ _ _ _ -> response - --- | Gain access to the incoming request. -withQuery - :: Monad m - => (QueryRequest -> DelayedM m a) - -> DelayedM m a -withQuery f = do - req <- ask - f req diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs deleted file mode 100644 index 83108299..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs +++ /dev/null @@ -1,100 +0,0 @@ -module Tendermint.SDK.BaseApp.Query.Router where - -import Control.Lens ((&), (.~)) -import Data.ByteArray.Base64String (fromBytes) -import Data.Default.Class (def) -import Data.Map (Map) -import qualified Data.Map as M -import Data.Text (Text) -import qualified Data.Text as T -import qualified Data.Text.Encoding as T -import qualified Network.ABCI.Types.Messages.Response as Response -import Network.HTTP.Types (decodePathSegments) -import Polysemy (Sem) -import Tendermint.SDK.BaseApp.Query.Delayed (Delayed, runAction) -import Tendermint.SDK.BaseApp.Query.Types (QueryError (..), - QueryRequest (..), - QueryResult (..), - RouteResult (..)) -import Tendermint.SDK.Codec (HasCodec (..)) - - --- NOTE: most of this was vendored and repurposed from servant - -data Router' env a = - StaticRouter (Map Text (Router' env a)) [env -> a] - | CaptureRouter (Router' (Text, env) a) - | Choice (Router' env a) (Router' env a) - - -type RoutingApplication r = QueryRequest -> Sem r (RouteResult Response.Query) - -type Router env r = Router' env (RoutingApplication r) - -pathRouter :: Text -> Router' env a -> Router' env a -pathRouter t r = StaticRouter (M.singleton t r) [] - -leafRouter :: (env -> a) -> Router' env a -leafRouter l = StaticRouter M.empty [l] - -choice :: Router' env a -> Router' env a -> Router' env a -choice (StaticRouter table1 ls1) (StaticRouter table2 ls2) = - StaticRouter (M.unionWith choice table1 table2) (ls1 ++ ls2) -choice (CaptureRouter router1) (CaptureRouter router2) = - CaptureRouter (choice router1 router2) -choice router1 (Choice router2 router3) = Choice (choice router1 router2) router3 -choice router1 router2 = Choice router1 router2 - - -methodRouter - :: HasCodec b - => Delayed (Sem r) env (Sem r (QueryResult b)) - -> Router env r -methodRouter action = leafRouter route' - where - route' env query = runAction action env query $ \QueryResult{..} -> - Route $ def & Response._queryIndex .~ queryResultIndex - & Response._queryKey .~ queryResultKey - & Response._queryValue .~ fromBytes (encode queryResultData) - & Response._queryProof .~ queryResultProof - & Response._queryHeight .~ queryResultHeight - -runRouter - :: Router env r - -> env - -> RoutingApplication r -runRouter router env query = - case router of - StaticRouter table ls -> - let path = decodePathSegments . T.encodeUtf8 $ queryRequestPath query - in case path of - [] -> runChoice ls env query - -- This case is to handle trailing slashes. - [""] -> runChoice ls env query - first : rest | Just router' <- M.lookup first table - -> let query' = query { queryRequestPath = T.intercalate "/" rest } - in runRouter router' env query' - _ -> pure $ Fail PathNotFound - CaptureRouter router' -> - let path = decodePathSegments . T.encodeUtf8 $ queryRequestPath query - in case path of - [] -> pure $ Fail PathNotFound - -- This case is to handle trailing slashes. - [""] -> pure $ Fail PathNotFound - first : rest - -> let query' = query { queryRequestPath = T.intercalate "/" rest } - in runRouter router' (first, env) query' - Choice r1 r2 -> - runChoice [runRouter r1, runRouter r2] env query - -runChoice :: [env -> RoutingApplication r] -> env -> RoutingApplication r -runChoice ls = - case ls of - [] -> \ _ _ -> pure $ Fail PathNotFound - [r] -> r - (r : rs) -> - \ env query -> do - response1 <- r env query - case response1 of - Fail _ -> runChoice rs env query - _ -> pure response1 diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index 682fc276..3b76b751 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -2,22 +2,25 @@ module Tendermint.SDK.BaseApp.Query.Store where -import Control.Lens (to, (^.)) -import Data.ByteArray.Base64String (fromBytes) +import Control.Lens (to, (^.)) +import Data.ByteArray.Base64String (fromBytes) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, Symbol, - symbolVal) -import Polysemy (Members, Sem) -import Polysemy.Error (Error, throw) -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, Symbol, + symbolVal) +import Polysemy (Members, Sem) +import Polysemy.Error (Error, throw) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) import Tendermint.SDK.BaseApp.Query.Class -import Tendermint.SDK.BaseApp.Query.Router (methodRouter, pathRouter) -import Tendermint.SDK.BaseApp.Query.Types -import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), - RawStore, StoreKey, get) -import Tendermint.SDK.Codec (HasCodec) +import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), + QueryResult (..), + Queryable (..)) +import Tendermint.SDK.BaseApp.Router (RouterError (..), + pathRouter) +import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), + RawStore, StoreKey, get) +import Tendermint.SDK.Codec (HasCodec) data StoreLeaf a @@ -75,12 +78,3 @@ instance storeQueryHandlers _ storeKey pr = storeQueryHandler (Proxy :: Proxy a) storeKey :<|> storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pr - -allStoreHandlers - :: forall (contents :: [*]) ns r. - StoreQueryHandlers contents ns r - => Proxy contents - -> StoreKey ns - -> Proxy r - -> RouteT (QueryApi contents) r -allStoreHandlers pcs storeKey pr = storeQueryHandlers pcs storeKey pr diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs index d211a49b..96dfa1f1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs @@ -1,8 +1,6 @@ module Tendermint.SDK.BaseApp.Query.Types where -import Control.Lens (from, (^.)) -import Control.Monad (ap) -import Control.Monad.Trans (MonadTrans (..)) +import Control.Lens (from, lens, (^.)) import Data.ByteArray.Base64String (Base64String, toBytes) import Data.Int (Int64) import Data.Text (Text, breakOn, uncons) @@ -10,8 +8,7 @@ import GHC.TypeLits (Symbol) import Network.ABCI.Types.Messages.FieldTypes (Proof, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response -import Tendermint.SDK.BaseApp.Errors (AppError (..), - IsAppError (..)) +import Tendermint.SDK.BaseApp.Router.Types (HasPath (..)) import Tendermint.SDK.BaseApp.Store (RawKey (..)) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) @@ -38,53 +35,23 @@ parseQueryRequest :: Request.Query -> QueryRequest parseQueryRequest Request.Query{..} = - let (path, queryStrQ) = breakOn "?" queryPath + let (p, queryStrQ) = breakOn "?" queryPath queryStr = case Data.Text.uncons queryStrQ of Nothing -> "" Just ('?', rest) -> rest _ -> error "Impossible result parsing query string from path." in QueryRequest - { queryRequestPath = path + { queryRequestPath = p , queryRequestParamString = queryStr , queryRequestData = queryData , queryRequestProve = queryProve , queryRequestHeight = unWrappedVal queryHeight } --------------------------------------------------------------------------------- +instance HasPath QueryRequest where + path = lens queryRequestPath (\q p -> q {queryRequestPath = p}) -data QueryError = - PathNotFound - | ResourceNotFound - | InvalidQuery Text - | InternalError Text - deriving (Show) - -instance IsAppError QueryError where - makeAppError PathNotFound = - AppError - { appErrorCode = 1 - , appErrorCodespace = "query" - , appErrorMessage = "Path not found." - } - makeAppError ResourceNotFound = - AppError - { appErrorCode = 2 - , appErrorCodespace = "query" - , appErrorMessage = "Resource not found." - } - makeAppError (InvalidQuery msg) = - AppError - { appErrorCode = 3 - , appErrorCodespace = "query" - , appErrorMessage = "Invalid query: " <> msg - } - makeAppError (InternalError _) = - AppError - { appErrorCode = 4 - , appErrorCodespace = "query" - , appErrorMessage = "Internal error." - } +-------------------------------------------------------------------------------- data QueryArgs a = QueryArgs { queryArgsProve :: Bool @@ -126,42 +93,3 @@ class FromQueryData a where fromQueryData bs = Right (toBytes bs ^. from rawKey) instance FromQueryData Address - --------------------------------------------------------------------------------- --- NOTE: most of this was vendored and repurposed from servant. - -data RouteResult a = - Fail QueryError - | FailFatal QueryError - | Route a - deriving (Functor) - -instance Applicative RouteResult where - pure = return - (<*>) = ap - -instance Monad RouteResult where - return = Route - (>>=) m f = case m of - Route a -> f a - Fail e -> Fail e - FailFatal e -> FailFatal e - -data RouteResultT m a = RouteResultT { runRouteResultT :: m (RouteResult a) } - deriving (Functor) - -instance MonadTrans RouteResultT where - lift m = RouteResultT $ fmap Route m - -instance Monad m => Applicative (RouteResultT m) where - pure = return - (<*>) = ap - -instance Monad m => Monad (RouteResultT m) where - return = RouteResultT . return . Route - (>>=) m f = RouteResultT $ do - a <- runRouteResultT m - case a of - Route a' -> runRouteResultT $ f a' - Fail e -> return $ Fail e - FailFatal e -> return $ FailFatal e diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router.hs new file mode 100644 index 00000000..8bbca1a6 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router.hs @@ -0,0 +1,9 @@ +module Tendermint.SDK.BaseApp.Router + ( module Tendermint.SDK.BaseApp.Router.Types + , module Tendermint.SDK.BaseApp.Router.Router + , module Tendermint.SDK.BaseApp.Router.Delayed + ) where + +import Tendermint.SDK.BaseApp.Router.Delayed +import Tendermint.SDK.BaseApp.Router.Router +import Tendermint.SDK.BaseApp.Router.Types diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs new file mode 100644 index 00000000..361d4b3c --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs @@ -0,0 +1,128 @@ +module Tendermint.SDK.BaseApp.Router.Delayed + ( Delayed + , runAction + , delayedFail + , addBody + , addCapture + , addParameter + , emptyDelayed + , withRequest + ) where + +import Control.Monad.Reader (MonadReader, ReaderT, ask, + runReaderT) +import Control.Monad.Trans (MonadTrans (..)) +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Router.Types (RouteResult (..), + RouteResultT (..), + RouterError (..)) + +-------------------------------------------------------------------------------- +-- NOTE: most of this was vendored and repurposed from servant + + +newtype DelayedM m req a = + DelayedM { runDelayedM' :: ReaderT req (RouteResultT m) a } + deriving (Functor, Applicative, Monad, MonadReader req) + +liftRouteResult :: Monad m => RouteResult a -> DelayedM m req a +liftRouteResult x = DelayedM $ lift $ RouteResultT . return $ x + +runDelayedM :: DelayedM m req a -> req -> m (RouteResult a) +runDelayedM m req = runRouteResultT $ runReaderT (runDelayedM' m) req + +-------------------------------------------------------------------------------- + +data Delayed m env req a where + Delayed :: { delayedCaptures :: env -> DelayedM m req captures + , delayedBody :: DelayedM m req body + , delayedParams :: DelayedM m req params + , delayedHandler :: captures -> body -> params -> req -> RouteResult a + } -> Delayed m env req a + +instance Functor m => Functor (Delayed m env req) where + fmap f Delayed{..} = + Delayed { delayedHandler = \captures body params req -> f <$> delayedHandler captures body params req + , .. + } + +runDelayed + :: Monad m + => Delayed m env req a + -> env + -> req + -> m (RouteResult a) +runDelayed Delayed{..} env = runDelayedM (do + req <- ask + captures <- delayedCaptures env + body <- delayedBody + params <- delayedParams + liftRouteResult $ delayedHandler captures body params req + ) + +runAction + :: Delayed (Sem r) env req (Sem r a) + -> env + -> req + -> (a -> RouteResult b) + -> Sem r (RouteResult b) +runAction action env req k = do + res <- runDelayed action env req + case res of + Route a -> k <$> a + Fail e -> pure $ Fail e + FailFatal e -> pure $ FailFatal e + +-- | Fail with the option to recover. +delayedFail :: Monad m => RouterError -> DelayedM m req a +delayedFail err = liftRouteResult $ Fail err + +addBody + :: Monad m + => Delayed m env req (a -> b) + -> DelayedM m req a + -> Delayed m env req b +addBody Delayed{..} newBody = + Delayed + { delayedBody = (,) <$> delayedBody <*> newBody + , delayedHandler = \caps (body, bodyNew) p req -> ($ bodyNew) <$> delayedHandler caps body p req + , .. + } + +addCapture + :: Monad m + => Delayed m env req (a -> b) + -> (captured -> DelayedM m req a) + -> Delayed m (captured, env) req b +addCapture Delayed{..} new = + Delayed + { delayedCaptures = \ (txt, env) -> (,) <$> delayedCaptures env <*> new txt + , delayedHandler = \ (x, v) body p query -> ($ v) <$> delayedHandler x body p query + , .. + } -- Note [Existential Record Update] + +addParameter + :: Monad m + => Delayed m env req (a -> b) + -> DelayedM m req a + -> Delayed m env req b +addParameter Delayed {..} new = + Delayed + { delayedParams = (,) <$> delayedParams <*> new + , delayedHandler = \caps body (p, pNew) query -> ($ pNew) <$> delayedHandler caps body p query + , .. + } + +emptyDelayed :: Monad m => RouteResult a -> Delayed m b req a +emptyDelayed response = + let r = pure () + in Delayed (const r) r r $ \_ _ _ _ -> response + +-- | Gain access to the incoming request. +withRequest + :: Monad m + => (req -> DelayedM m req a) + -> DelayedM m req a +withRequest f = do + req <- ask + f req diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Router.hs new file mode 100644 index 00000000..32689af3 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Router.hs @@ -0,0 +1,95 @@ +module Tendermint.SDK.BaseApp.Router.Router + ( Router + , Router'(..) + , runRouter + , pathRouter + , leafRouter + , choice + ) where + +import Control.Lens ((&), (.~), (^.)) +import Data.Map (Map) +import qualified Data.Map as M +import Data.Text (Text) +import qualified Data.Text as T +import qualified Data.Text.Encoding as T +import Network.HTTP.Types (decodePathSegments) +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Router.Types (Application, HasPath (..), + RouteResult (..), + RouterError (..)) + + +-- NOTE: most of this was vendored and repurposed from servant + +data Router' env a = + StaticRouter (Map Text (Router' env a)) [env -> a] + | CaptureRouter (Router' (Text, env) a) + | Choice (Router' env a) (Router' env a) + + +type Router env r req res = Router' env (Application (Sem r) req res) + +pathRouter + :: Text + -> Router' env a + -> Router' env a +pathRouter t r = StaticRouter (M.singleton t r) [] + +leafRouter + :: (env -> a) + -> Router' env a +leafRouter l = StaticRouter M.empty [l] + +choice + :: Router' env a + -> Router' env a + -> Router' env a +choice (StaticRouter table1 ls1) (StaticRouter table2 ls2) = + StaticRouter (M.unionWith choice table1 table2) (ls1 ++ ls2) +choice (CaptureRouter router1) (CaptureRouter router2) = + CaptureRouter (choice router1 router2) +choice router1 (Choice router2 router3) = Choice (choice router1 router2) router3 +choice router1 router2 = Choice router1 router2 + +runRouter + :: HasPath req + => Router env r req res + -> env + -> Application (Sem r) req res +runRouter router env req = + case router of + StaticRouter table ls -> + case decodePathSegments . T.encodeUtf8 $ req ^. path of + [] -> runChoice ls env req + -- This case is to handle trailing slashes. + [""] -> runChoice ls env req + first : rest | Just router' <- M.lookup first table + -> let req' = req & path .~ T.intercalate "/" rest + in runRouter router' env req' + _ -> pure $ Fail PathNotFound + CaptureRouter router' -> + case decodePathSegments . T.encodeUtf8 $ req ^. path of + [] -> pure $ Fail PathNotFound + -- This case is to handle trailing slashes. + [""] -> pure $ Fail PathNotFound + first : rest + -> let req' = req & path .~ T.intercalate "/" rest + in runRouter router' (first, env) req' + Choice r1 r2 -> + runChoice [runRouter r1, runRouter r2] env req + +runChoice + :: [env -> Application (Sem r) req res] + -> env + -> Application (Sem r) req res +runChoice ls = + case ls of + [] -> \ _ _ -> pure $ Fail PathNotFound + [r] -> r + (r : rs) -> + \ env query -> do + response1 <- r env query + case response1 of + Fail _ -> runChoice rs env query + _ -> pure response1 diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Types.hs new file mode 100644 index 00000000..ea18cd7d --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Types.hs @@ -0,0 +1,94 @@ +module Tendermint.SDK.BaseApp.Router.Types + ( Application + , RouterError(..) + , RouteResult(..) + , RouteResultT(..) + , HasPath(..) + ) where + +import Control.Lens (Lens') +import Control.Monad (ap) +import Control.Monad.Trans (MonadTrans (..)) +import Data.Text (Text) +import Tendermint.SDK.BaseApp.Errors (AppError (..), IsAppError (..)) + +-------------------------------------------------------------------------------- + +type Application m req res = req -> m (RouteResult res) + +-------------------------------------------------------------------------------- + +data RouterError = + PathNotFound + | ResourceNotFound + | InvalidRequest Text + | InternalError Text + deriving (Show) + +instance IsAppError RouterError where + makeAppError PathNotFound = + AppError + { appErrorCode = 1 + , appErrorCodespace = "router" + , appErrorMessage = "Path not found." + } + makeAppError ResourceNotFound = + AppError + { appErrorCode = 2 + , appErrorCodespace = "router" + , appErrorMessage = "Resource not found." + } + makeAppError (InvalidRequest msg) = + AppError + { appErrorCode = 3 + , appErrorCodespace = "router" + , appErrorMessage = "Invalid request: " <> msg + } + makeAppError (InternalError _) = + AppError + { appErrorCode = 4 + , appErrorCodespace = "router" + , appErrorMessage = "Internal error." + } + +-------------------------------------------------------------------------------- +-- NOTE: most of this was vendored and repurposed from servant. + +data RouteResult a = + Fail RouterError + | FailFatal RouterError + | Route a + deriving (Functor) + +instance Applicative RouteResult where + pure = return + (<*>) = ap + +instance Monad RouteResult where + return = Route + (>>=) m f = case m of + Route a -> f a + Fail e -> Fail e + FailFatal e -> FailFatal e + +data RouteResultT m a = RouteResultT { runRouteResultT :: m (RouteResult a) } + deriving (Functor) + +instance MonadTrans RouteResultT where + lift m = RouteResultT $ fmap Route m + +instance Monad m => Applicative (RouteResultT m) where + pure = return + (<*>) = ap + +instance Monad m => Monad (RouteResultT m) where + return = RouteResultT . return . Route + (>>=) m f = RouteResultT $ do + a <- runRouteResultT m + case a of + Route a' -> runRouteResultT $ f a' + Fail e -> return $ Fail e + FailFatal e -> return $ FailFatal e + +class HasPath t where + path :: Lens' t Text From 5897ebd195b06f978e6360018f4fd68005011997 Mon Sep 17 00:00:00 2001 From: martyall Date: Mon, 27 Jan 2020 10:07:02 -0800 Subject: [PATCH 49/70] fix compose --- hs-abci-examples/nameservice/docker-compose.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hs-abci-examples/nameservice/docker-compose.yaml b/hs-abci-examples/nameservice/docker-compose.yaml index 51efa18c..4b05e929 100644 --- a/hs-abci-examples/nameservice/docker-compose.yaml +++ b/hs-abci-examples/nameservice/docker-compose.yaml @@ -29,7 +29,7 @@ services: restart: always depends_on: - elk - expose: + ports: - "26658" - "5555:5555" datadog: @@ -52,4 +52,4 @@ services: - "9200" - "5601" volumes: - tendermint-storage: \ No newline at end of file + tendermint-storage: From aba99147416f67cf577bb5382ef1a76961020f48 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 27 Jan 2020 13:11:52 -0800 Subject: [PATCH 50/70] Module router refactor (#200) * Transaction router module * message type field added , typed message added to protobuf * added transaction router class * made router class for transactions, instances * finished transaction voodoo, wip putting it into module stuff * empty server * sdk compiles * simple storage comiles * rename PreRoutedTx * nameservice * all e2e tests compile * added TypedMessage to protobuf file * added separate query and server empty serve * added serialize method to crypto sig * Added tx client class and signer stuff * last instance compiles * client runner instance * split out modules * clients compiling, need to update test utils * updated client utils * simple storage tests compile * fix case when account isn't present * wip nameservice tests * more ergonomic query result types * nameservice tests compile * hlint * remove burn signature * updated test for response code * try to fix test * tutorial wip * tutorial compiles --- hs-abci-examples/nameservice/package.yaml | 8 +- .../src/Nameservice/Modules/Nameservice.hs | 19 +- .../Modules/Nameservice/Messages.hs | 88 ++- .../Nameservice/Modules/Nameservice/Query.hs | 4 +- .../Nameservice/Modules/Nameservice/Router.hs | 72 ++- .../src/Nameservice/Modules/Token.hs | 24 +- .../src/Nameservice/Modules/Token/Messages.hs | 87 ++- .../src/Nameservice/Modules/Token/Query.hs | 4 +- .../src/Nameservice/Modules/Token/Router.hs | 61 +- .../src/Nameservice/Modules/TypedMessage.hs | 25 - .../test/Nameservice/Test/E2ESpec.hs | 526 +++++++++++++----- .../tutorial/Foundations/Modules.md | 13 +- .../tutorial/Tutorial/Nameservice/Message.md | 60 +- .../tutorial/Tutorial/Nameservice/Module.md | 37 +- .../tutorial/Tutorial/Nameservice/Query.md | 10 +- .../tutorial/Tutorial/Nameservice/Router.md | 120 ++++ .../tutorial/Tutorial/Nameservice/Types.md | 12 +- hs-abci-examples/simple-storage/package.yaml | 3 +- .../SimpleStorage/Modules/SimpleStorage.hs | 18 +- .../Modules/SimpleStorage/Message.hs | 33 +- .../Modules/SimpleStorage/Query.hs | 6 +- .../Modules/SimpleStorage/Router.hs | 32 +- .../test/SimpleStorage/Test/E2ESpec.hs | 140 +++-- hs-abci-sdk/package.yaml | 8 +- hs-abci-sdk/protos/types/transaction.proto | 7 +- hs-abci-sdk/src/Tendermint/SDK/Application.hs | 1 - .../Tendermint/SDK/Application/AnteHandler.hs | 60 +- .../Tendermint/SDK/Application/Handlers.hs | 49 +- .../src/Tendermint/SDK/Application/Module.hs | 183 +++--- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 16 +- .../src/Tendermint/SDK/BaseApp/Errors.hs | 2 +- .../src/Tendermint/SDK/BaseApp/Query.hs | 18 +- .../src/Tendermint/SDK/BaseApp/Query/Class.hs | 125 ----- .../Tendermint/SDK/BaseApp/Query/Router.hs | 139 +++++ .../src/Tendermint/SDK/BaseApp/Query/Store.hs | 45 +- .../src/Tendermint/SDK/BaseApp/Query/Types.hs | 16 +- .../src/Tendermint/SDK/BaseApp/Transaction.hs | 119 ++-- .../SDK/BaseApp/Transaction/Checker.hs | 51 ++ .../SDK/BaseApp/Transaction/Effect.hs | 78 +++ .../SDK/BaseApp/Transaction/Modifier.hs | 11 + .../SDK/BaseApp/Transaction/Router.hs | 87 +++ .../SDK/BaseApp/Transaction/Types.hs | 37 ++ hs-abci-sdk/src/Tendermint/SDK/Crypto.hs | 9 +- .../src/Tendermint/SDK/Modules/Auth.hs | 12 +- .../src/Tendermint/SDK/Modules/Auth/Query.hs | 2 +- .../src/Tendermint/SDK/Types/Message.hs | 47 +- .../src/Tendermint/SDK/Types/Transaction.hs | 22 +- .../test/Tendermint/SDK/Test/CryptoSpec.hs | 3 +- .../test/Tendermint/SDK/Test/QuerySpec.hs | 18 +- .../test/Tendermint/SDK/Test/SimpleStorage.hs | 55 +- hs-abci-test-utils/package.yaml | 4 +- .../src/Tendermint/Utils/Client.hs | 163 +----- .../src/Tendermint/Utils/ClientUtils.hs | 131 +++++ .../src/Tendermint/Utils/QueryClient/Class.hs | 150 +++++ .../src/Tendermint/Utils/QueryClient/Types.hs | 10 + .../src/Tendermint/Utils/Request.hs | 59 -- .../src/Tendermint/Utils/Response.hs | 36 -- .../src/Tendermint/Utils/TxClient/Class.hs | 113 ++++ .../src/Tendermint/Utils/TxClient/Types.hs | 97 ++++ .../src/Tendermint/Utils/User.hs | 71 +-- 60 files changed, 2203 insertions(+), 1253 deletions(-) delete mode 100644 hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs create mode 100644 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Router.md delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Types.hs delete mode 100644 hs-abci-test-utils/src/Tendermint/Utils/Request.hs delete mode 100644 hs-abci-test-utils/src/Tendermint/Utils/Response.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs create mode 100644 hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index f6494519..d1481d96 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -52,7 +52,6 @@ library: - aeson-casing - base >= 4.7 && < 5 - bloodhound - - bytestring - errors - hs-abci-extra - hs-abci-server @@ -66,6 +65,7 @@ library: - polysemy-plugin - proto3-suite - proto3-wire + - servant - string-conversions - text - validation @@ -83,14 +83,13 @@ library: - Nameservice.Config - Nameservice.Aeson - Nameservice.Server - - Nameservice.Modules.TypedMessage - - Nameservice.Modules.Nameservice - Nameservice.Modules.Token - Nameservice.Modules.Token.Messages - Nameservice.Modules.Token.Types - Nameservice.Modules.Token.Keeper - Nameservice.Modules.Token.Query - Nameservice.Modules.Token.Router + - Nameservice.Modules.Nameservice - Nameservice.Modules.Nameservice.Messages - Nameservice.Modules.Nameservice.Types - Nameservice.Modules.Nameservice.Keeper @@ -170,10 +169,9 @@ tests: - base >= 4.7 && < 5 - data-default-class - hs-abci-sdk - - hs-abci-types - hs-abci-test-utils - hs-tendermint-client - hspec - - lens + - mtl - nameservice - servant diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs index c9cccbae..440f860a 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -9,7 +9,6 @@ module Nameservice.Modules.Nameservice , Name(..) , Whois (..) , NameserviceError(..) - , NameserviceMessage(..) , NameClaimed(..) , NameRemapped(..) , NameDeleted(..) @@ -28,14 +27,16 @@ module Nameservice.Modules.Nameservice , eval -- * message router - , router + , MessageApi + , messageHandlers -- * query API - , Api + , QueryApi , server ) where +import Data.Proxy import Nameservice.Modules.Nameservice.Keeper import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query @@ -43,12 +44,12 @@ import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) -import Tendermint.SDK.Application (Module (..), - defaultTxChecker) -import Tendermint.SDK.BaseApp (BaseAppEffs) +import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.BaseApp (BaseAppEffs, + DefaultCheckTx (..)) type NameserviceM r = - Module "nameservice" NameserviceMessage () Api NameserviceEffs r + Module "nameservice" MessageApi QueryApi NameserviceEffs r nameserviceModule :: Members BaseAppEffs r @@ -56,8 +57,8 @@ nameserviceModule => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module - { moduleTxDeliverer = router - , moduleTxChecker = defaultTxChecker + { moduleTxDeliverer = messageHandlers + , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) , moduleQueryServer = server , moduleEval = eval } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index d25aa00e..a08d88ff 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -1,4 +1,8 @@ -module Nameservice.Modules.Nameservice.Messages where +module Nameservice.Modules.Nameservice.Messages + ( SetName(..) + , BuyName(..) + , DeleteName(..) + ) where import Data.Bifunctor (first) import Data.Foldable (sequenceA_) @@ -7,25 +11,19 @@ import Data.Text (Text) import GHC.Generics (Generic) import Nameservice.Modules.Nameservice.Types (Name (..)) import Nameservice.Modules.Token (Amount (..)) -import Nameservice.Modules.TypedMessage (TypedMessage (..)) import Proto3.Suite (Message, Named, fromByteString, toLazyByteString) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address (..)) -import Tendermint.SDK.Types.Message (Msg (..), +import Tendermint.SDK.Types.Message (HasMessageType (..), + Msg (..), ValidateMessage (..), coerceProto3Error, formatMessageParseError, isAuthorCheck, nonEmptyCheck) -data NameserviceMessage = - NSetName SetName - | NBuyName BuyName - | NDeleteName DeleteName - deriving (Eq, Show, Generic) - -- @NOTE: .proto genration will use these type names as is -- only field names stripped of prefixes during generation data SetName = SetName @@ -37,10 +35,26 @@ data SetName = SetName instance Message SetName instance Named SetName +instance HasMessageType SetName where + messageType _ = "SetName" + instance HasCodec SetName where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +-- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg +instance ValidateMessage SetName where + validateMessage msg@Msg{..} = + let SetName{setNameName, setNameValue} = msgData + Name name = setNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , nonEmptyCheck "Value" setNameValue + , isAuthorCheck "Owner" msg setNameOwner + ] + +-------------------------------------------------------------------------------- + data DeleteName = DeleteName { deleteNameOwner :: Address , deleteNameName :: Name @@ -49,10 +63,24 @@ data DeleteName = DeleteName instance Message DeleteName instance Named DeleteName +instance HasMessageType DeleteName where + messageType _ = "DeleteName" + instance HasCodec DeleteName where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +instance ValidateMessage DeleteName where + validateMessage msg@Msg{..} = + let DeleteName{deleteNameName} = msgData + Name name = deleteNameName + in sequenceA_ + [ nonEmptyCheck "Name" name + , isAuthorCheck "Owner" msg deleteNameOwner + ] + +-------------------------------------------------------------------------------- + data BuyName = BuyName { buyNameBid :: Amount , buyNameName :: Name @@ -63,49 +91,13 @@ data BuyName = BuyName instance Message BuyName instance Named BuyName +instance HasMessageType BuyName where + messageType _ = "BuyName" + instance HasCodec BuyName where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -instance HasCodec NameserviceMessage where - decode bs = do - TypedMessage{..} <- decode bs - case typedMessageType of - "SetName" -> NSetName <$> decode typedMessageContents - "DeleteName" -> NDeleteName <$> decode typedMessageContents - "BuyName" -> NBuyName <$> decode typedMessageContents - _ -> Left . cs $ "Unknown Nameservice message type " ++ cs typedMessageType - encode = \case - NSetName msg -> encode msg - NBuyName msg -> encode msg - NDeleteName msg -> encode msg - -instance ValidateMessage NameserviceMessage where - validateMessage m@Msg{msgData} = case msgData of - NBuyName msg -> validateMessage m {msgData = msg} - NSetName msg -> validateMessage m {msgData = msg} - NDeleteName msg -> validateMessage m {msgData = msg} - --- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg -instance ValidateMessage SetName where - validateMessage msg@Msg{..} = - let SetName{setNameName, setNameValue} = msgData - Name name = setNameName - in sequenceA_ - [ nonEmptyCheck "Name" name - , nonEmptyCheck "Value" setNameValue - , isAuthorCheck "Owner" msg setNameOwner - ] - -instance ValidateMessage DeleteName where - validateMessage msg@Msg{..} = - let DeleteName{deleteNameName} = msgData - Name name = deleteNameName - in sequenceA_ - [ nonEmptyCheck "Name" name - , isAuthorCheck "Owner" msg deleteNameOwner - ] - instance ValidateMessage BuyName where validateMessage msg@Msg{..} = let BuyName{buyNameName, buyNameValue} = msgData diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index 1786db0e..bc5864dc 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -13,10 +13,10 @@ import qualified Tendermint.SDK.BaseApp as BaseApp type NameserviceContents = '[(Name, Whois)] -type Api = BaseApp.QueryApi NameserviceContents +type QueryApi = BaseApp.QueryApi NameserviceContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api r + => BaseApp.RouteQ QueryApi r server = BaseApp.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 4a274226..8f292bbb 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -3,31 +3,63 @@ module Nameservice.Modules.Nameservice.Router where import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, buyName, deleteName, setName) -import Nameservice.Modules.Nameservice.Messages (NameserviceMessage (..)) +import Nameservice.Modules.Nameservice.Messages (BuyName, DeleteName, + SetName) import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members, Sem) -import Tendermint.SDK.BaseApp (BaseAppEffs, TxEffs, +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.BaseApp ((:~>), BaseAppEffs, + Return, + RouteContext (..), + RouteTx, + RoutingTx (..), + TxEffs, TypedMessage, incCount, withTimer) import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), - Tx (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) -router - :: Members TokenEffs r + + +type MessageApi = + TypedMessage BuyName :~> Return () + :<|> TypedMessage SetName :~> Return () + :<|> TypedMessage DeleteName :~> Return () + +messageHandlers + :: Members BaseAppEffs r + => Members TokenEffs r => Members NameserviceEffs r - => Members BaseAppEffs r + => RouteTx MessageApi r 'DeliverTx +messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH + +buyNameH + :: Members BaseAppEffs r => Members TxEffs r - => PreRoutedTx NameserviceMessage + => Members TokenEffs r + => Members NameserviceEffs r + => RoutingTx BuyName + -> Sem r () +buyNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do + incCount "buy_total" + withTimer "buy_duration_seconds" $ buyName msgData + +setNameH + :: Members BaseAppEffs r + => Members TxEffs r + => Members NameserviceEffs r + => RoutingTx SetName + -> Sem r () +setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do + incCount "set_total" + withTimer "set_duration_seconds" $ setName msgData + +deleteNameH + :: Members BaseAppEffs r + => Members TxEffs r + => Members TokenEffs r + => Members NameserviceEffs r + => RoutingTx DeleteName -> Sem r () -router (PreRoutedTx Tx{txMsg}) = - let Msg{msgData} = txMsg - in case msgData of - NSetName msg -> do - incCount "set_total" - withTimer "set_duration_seconds" $ setName msg - NBuyName msg -> do - incCount "buy_total" - withTimer "buy_duration_seconds" $ buyName msg - NDeleteName msg -> do - incCount "delete_total" - withTimer "delete_duration_seconds" $ deleteName msg +deleteNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do + incCount "delete_total" + withTimer "delete_duration_seconds" $ deleteName msgData diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs index c42d4b64..7e9fafbd 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs @@ -8,14 +8,16 @@ module Nameservice.Modules.Token , Address , Amount(..) , TokenError(..) + , Transfer(..) + , Burn(..) + , FaucetAccount(..) -- * effects , Token , TokenEffs , Faucetted(..) , TransferEvent(..) - , FaucetAccount(..) , getBalance , faucetAccount , transfer @@ -25,35 +27,37 @@ module Nameservice.Modules.Token -- * interpreter , eval - -- * router - , router + -- * Transaction + , MessageApi + , messageHandlers -- * Query Api - , Api + , QueryApi , server ) where +import Data.Proxy import Nameservice.Modules.Token.Keeper import Nameservice.Modules.Token.Messages import Nameservice.Modules.Token.Query import Nameservice.Modules.Token.Router import Nameservice.Modules.Token.Types import Polysemy (Members) -import Tendermint.SDK.Application (Module (..), - defaultTxChecker) -import Tendermint.SDK.BaseApp (BaseAppEffs) +import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.BaseApp (BaseAppEffs, + DefaultCheckTx (..)) import Tendermint.SDK.Types.Address (Address) -type TokenM r = Module "token" TokenMessage () Api TokenEffs r +type TokenM r = Module "token" MessageApi QueryApi TokenEffs r tokenModule :: Members BaseAppEffs r => Members TokenEffs r => TokenM r tokenModule = Module - { moduleTxDeliverer = router - , moduleTxChecker = defaultTxChecker + { moduleTxDeliverer = messageHandlers + , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) , moduleQueryServer = server , moduleEval = eval } diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs index 9ce3c885..592cd38c 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs @@ -1,26 +1,23 @@ -module Nameservice.Modules.Token.Messages where - -import Data.Bifunctor (first) -import Data.String.Conversions (cs) -import Data.Validation (Validation (..)) -import GHC.Generics (Generic) -import Nameservice.Modules.Token.Types (Amount) -import Nameservice.Modules.TypedMessage (TypedMessage (..)) -import Proto3.Suite (Message, Named, - fromByteString, - toLazyByteString) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (Msg (..), - ValidateMessage (..), - coerceProto3Error, - formatMessageParseError) - -data TokenMessage = - TTransfer Transfer - | TFaucetAccount FaucetAccount - | TBurn Burn - deriving (Eq, Show, Generic) +module Nameservice.Modules.Token.Messages + ( Transfer(..) + , Burn(..) + , FaucetAccount(..) + ) where + +import Data.Bifunctor (first) +import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) +import GHC.Generics (Generic) +import Nameservice.Modules.Token.Types (Amount) +import Proto3.Suite (Message, Named, + fromByteString, + toLazyByteString) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (HasMessageType (..), + ValidateMessage (..), + coerceProto3Error, + formatMessageParseError) data FaucetAccount = FaucetAccount { faucetAccountTo :: Address @@ -30,10 +27,18 @@ data FaucetAccount = FaucetAccount instance Message FaucetAccount instance Named FaucetAccount +instance HasMessageType FaucetAccount where + messageType _ = "FaucetAccount" + instance HasCodec FaucetAccount where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +instance ValidateMessage FaucetAccount where + validateMessage _ = Success () + +-------------------------------------------------------------------------------- + data Transfer = Transfer { transferTo :: Address , transferFrom :: Address @@ -43,10 +48,18 @@ data Transfer = Transfer instance Message Transfer instance Named Transfer +instance HasMessageType Transfer where + messageType _ = "Transfer" + instance HasCodec Transfer where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +instance ValidateMessage Transfer where + validateMessage _ = Success () + +-------------------------------------------------------------------------------- + data Burn = Burn { burnAddress :: Address , burnAmount :: Amount @@ -55,34 +68,12 @@ data Burn = Burn instance Message Burn instance Named Burn +instance HasMessageType Burn where + messageType _ = "Burn" + instance HasCodec Burn where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -instance HasCodec TokenMessage where - decode bs = do - TypedMessage{..} <- decode bs - case typedMessageType of - "Transfer" -> TTransfer <$> decode typedMessageContents - "Burn" -> TBurn <$> decode typedMessageContents - "FaucetAccount" -> TFaucetAccount <$> decode typedMessageContents - _ -> Left . cs $ "Unknown Token message type " ++ cs typedMessageType - encode = \case - TTransfer msg -> encode msg - TBurn msg -> encode msg - TFaucetAccount msg -> encode msg - -instance ValidateMessage TokenMessage where - validateMessage m@Msg{msgData} = case msgData of - TTransfer msg -> validateMessage m {msgData = msg} - TFaucetAccount msg -> validateMessage m {msgData = msg} - TBurn msg -> validateMessage m {msgData = msg} - -instance ValidateMessage Transfer where - validateMessage _ = Success () - -instance ValidateMessage FaucetAccount where - validateMessage _ = Success () - instance ValidateMessage Burn where validateMessage _ = Success () diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs index fd22193c..5685c852 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs @@ -14,10 +14,10 @@ import Tendermint.SDK.Types.Address (Address) type TokenContents = '[(Address, Amount)] -type Api = BaseApp.QueryApi TokenContents +type QueryApi = BaseApp.QueryApi TokenContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api r + => BaseApp.RouteQ QueryApi r server = BaseApp.storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs index b427a342..3c5de02d 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs @@ -1,27 +1,56 @@ -module Nameservice.Modules.Token.Router where +module Nameservice.Modules.Token.Router + ( MessageApi + , messageHandlers + + ) where import Nameservice.Modules.Token.Keeper (TokenEffs, burn, faucetAccount, transfer) -import Nameservice.Modules.Token.Messages (Burn (..), - TokenMessage (..), +import Nameservice.Modules.Token.Messages (Burn (..), FaucetAccount, Transfer (..)) import Polysemy (Members, Sem) -import Tendermint.SDK.BaseApp (BaseAppEffs, TxEffs) +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.BaseApp ((:~>), BaseAppEffs, Return, + RouteContext (..), RouteTx, + RoutingTx (..), TxEffs, + TypedMessage) import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) + +type MessageApi = + TypedMessage Burn :~> Return () + :<|> TypedMessage Transfer :~> Return () + :<|> TypedMessage FaucetAccount :~> Return () -router +messageHandlers :: Members TokenEffs r => Members BaseAppEffs r + => RouteTx MessageApi r 'DeliverTx +messageHandlers = burnH :<|> transferH :<|> faucetH + +transferH + :: Members TokenEffs r => Members TxEffs r - => PreRoutedTx TokenMessage + => Members BaseAppEffs r + => RoutingTx Transfer + -> Sem r () +transferH (RoutingTx Tx{txMsg=Msg{msgData}}) = + let Transfer{..} = msgData + in transfer transferFrom transferAmount transferTo + +burnH + :: Members TokenEffs r + => RoutingTx Burn + -> Sem r () +burnH (RoutingTx Tx{txMsg=Msg{msgData}}) = + let Burn{..} = msgData + in burn burnAddress burnAmount + +faucetH + :: Members TokenEffs r + => Members TxEffs r + => Members BaseAppEffs r + => RoutingTx FaucetAccount -> Sem r () -router (PreRoutedTx Tx{txMsg}) = - let Msg{msgData} = txMsg - in case msgData of - TFaucetAccount faucet -> - faucetAccount faucet - TTransfer Transfer{..} -> - transfer transferFrom transferAmount transferTo - TBurn Burn{..} -> - burn burnAddress burnAmount +faucetH (RoutingTx Tx{txMsg=Msg{msgData}}) = + faucetAccount msgData diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs deleted file mode 100644 index e7bdb768..00000000 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/TypedMessage.hs +++ /dev/null @@ -1,25 +0,0 @@ -module Nameservice.Modules.TypedMessage where - -import Data.Bifunctor (first) -import qualified Data.ByteString as BS -import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.Generics (Generic) -import Proto3.Suite (Message, Named, fromByteString, - toLazyByteString) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Message (coerceProto3Error, - formatMessageParseError) - --- Tags messages to disambiguate decoding instances -data TypedMessage = TypedMessage - { typedMessageType :: Text - , typedMessageContents :: BS.ByteString - } deriving (Eq, Show, Generic) - -instance Message TypedMessage -instance Named TypedMessage - -instance HasCodec TypedMessage where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs index c6a388a6..ebacc1b4 100644 --- a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,214 +1,432 @@ module Nameservice.Test.E2ESpec (spec) where -import Control.Lens ((^.)) -import Control.Monad (void) -import Data.Default.Class (def) +import Control.Monad (forM_, void) +import Control.Monad.Reader (ReaderT, runReaderT) +import Data.Default.Class (def) import Data.Proxy -import Nameservice.Modules.Nameservice (BuyName (..), - DeleteName (..), - Name (..), - NameClaimed (..), - NameDeleted (..), - NameRemapped (..), - SetName (..), Whois (..)) -import qualified Nameservice.Modules.Nameservice as N (Api) -import Nameservice.Modules.Token (Amount (..), - FaucetAccount (..), - Faucetted (..), - Transfer (..), - TransferEvent (..)) -import qualified Nameservice.Modules.Token as T (Api) -import Nameservice.Modules.TypedMessage (TypedMessage (..)) -import Nameservice.Test.EventOrphans () -import qualified Network.ABCI.Types.Messages.Response as Response -import qualified Network.Tendermint.Client as RPC -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Query (QueryArgs (..), - defaultQueryWithData) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address (..)) -import Tendermint.Utils.Client (ClientResponse (..), - HasClient (..)) -import Tendermint.Utils.Request (ensureCheckAndDeliverResponseCodes, - getDeliverTxResponse, - getQueryResponseSuccess, - runRPC) -import Tendermint.Utils.Response (ensureDeliverResponseCode, - ensureEventLogged) -import Tendermint.Utils.User (User (..), makeUser, mkSignedRawTransactionWithRoute) +import Nameservice.Application +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import Nameservice.Test.EventOrphans () +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.Application.Module (AppQueryRouter (QApi), + AppTxRouter (TApi)) +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + QueryResult (..), + defaultQueryArgs) +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Types.Address (Address) +import Tendermint.Utils.Client (ClientConfig (..), + EmptyTxClient (..), + HasQueryClient (..), + HasTxClient (..), + QueryClientResponse (..), + Signer (..), + TxClientResponse (..), + TxOpts (..), + defaultClientTxOpts) +import Tendermint.Utils.ClientUtils (assertQuery, assertTx, + deliverTxEvents, + ensureQueryResponseCode, + ensureResponseCodes, + rpcConfig) +import Tendermint.Utils.User (makeSignerFromUser, + makeUser) import Test.Hspec spec :: Spec spec = do - let satoshi = Name "satoshi" - addr1 = userAddress user1 - addr2 = userAddress user2 + let satoshi = N.Name "satoshi" faucetAmount = 1000 - beforeAll (do faucetAccount user1 faucetAmount; faucetAccount user2 faucetAmount) $ + beforeAll (forM_ [user1, user2] $ faucetUser faucetAmount) $ do + describe "Nameservice Spec" $ do it "Can query /health to make sure the node is alive" $ do - resp <- runRPC RPC.health + resp <- RPC.runTendermintM rpcConfig $ RPC.health resp `shouldBe` RPC.ResultHealth it "Can query account balances" $ do - let queryReq = defaultQueryWithData addr1 - void $ getQueryResponseSuccess $ getBalance queryReq + void . assertQuery . RPC.runTendermintM rpcConfig $ + getBalance defaultQueryArgs { queryArgsData = signerAddress user1 } - it "Can create a name (success 0)" $ do + it "Can create a name" $ do let val = "hello world" - msg = TypedMessage "BuyName" (encode $ BuyName 0 satoshi val addr1) - claimedLog = NameClaimed addr1 satoshi val 0 - deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user1 msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "NameClaimed" claimedLog + msg = N.BuyName + { buyNameBid = 0 + , buyNameName = satoshi + , buyNameValue = val + , buyNameBuyer = signerAddress user1 + } + claimedLog = N.NameClaimed + { nameClaimedOwner = signerAddress user1 + , nameClaimedName = satoshi + , nameClaimedValue = val + , nameClaimedBid = 0 + } + opts = TxOpts + { txOptsSigner = user1 + , txOptsGas = 0 + } + resp <- assertTx . runTxClientM $ buyName opts msg + ensureResponseCodes (0,0) resp + (errs, es) <- deliverTxEvents (Proxy @N.NameClaimed) resp + errs `shouldBe` [] + filter (claimedLog ==) es `shouldBe` [claimedLog] it "Can query for a name" $ do - let queryReq = defaultQueryWithData satoshi - foundWhois <- getQueryResponseSuccess $ getWhois queryReq - foundWhois `shouldBe` Whois "hello world" addr1 0 + let expected = N.Whois + { whoisValue = "hello world" + , whoisOwner = signerAddress user1 + , whoisPrice = 0 + } + foundWhois <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ + getWhois defaultQueryArgs { queryArgsData = satoshi } + foundWhois `shouldBe` expected it "Can query for a name that doesn't exist" $ do - let nope = Name "nope" - queryReq = defaultQueryWithData nope - ClientResponse{ clientResponseData, clientResponseRaw } <- runRPC $ getWhois queryReq - let queryRespCode = clientResponseRaw ^. Response._queryCode - -- storage failure - queryRespCode `shouldBe` 2 - clientResponseData `shouldBe` Nothing - - it "Can set a name value (success 0)" $ do + let nope = N.Name "nope" + resp <- RPC.runTendermintM rpcConfig $ + getWhois defaultQueryArgs { queryArgsData = nope } + ensureQueryResponseCode 2 resp + + it "Can set a name value" $ do let oldVal = "hello world" newVal = "goodbye to a world" - msg = TypedMessage "SetName" (encode $ SetName satoshi addr1 newVal) - remappedLog = NameRemapped satoshi oldVal newVal - deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user1 msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "NameRemapped" remappedLog - -- check for changes - let queryReq = defaultQueryWithData satoshi - foundWhois <- getQueryResponseSuccess $ getWhois queryReq - foundWhois `shouldBe` Whois "goodbye to a world" addr1 0 - - it "Can fail to set a name (failure 2)" $ do + msg = N.SetName + { setNameName = satoshi + , setNameOwner = signerAddress user1 + , setNameValue = newVal + } + remappedLog = N.NameRemapped + { nameRemappedName = satoshi + , nameRemappedOldValue = oldVal + , nameRemappedNewValue = newVal + } + opts = TxOpts + { txOptsSigner = user1 + , txOptsGas = 0 + } + resp <- assertTx . runTxClientM $ setName opts msg + ensureResponseCodes (0,0) resp + (errs, es) <- deliverTxEvents (Proxy @N.NameRemapped) resp + errs `shouldBe` [] + filter (remappedLog ==) es `shouldBe` [remappedLog] + + let expected = N.Whois + { whoisValue = "goodbye to a world" + , whoisOwner = signerAddress user1 + , whoisPrice = 0 + } + foundWhois <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ + getWhois defaultQueryArgs { queryArgsData = satoshi } + foundWhois `shouldBe` expected + + it "Can fail to set a name" $ do -- try to set a name without being the owner - let msg = TypedMessage "SetName" (encode $ SetName satoshi addr2 "goodbye to a world") - ensureCheckAndDeliverResponseCodes (0,2) =<< mkSignedRawTransactionWithRoute "nameservice" user2 msg + let msg = N.SetName + { setNameName = satoshi + , setNameOwner = signerAddress user2 + , setNameValue = "goodbye to a world" + } + opts = TxOpts + { txOptsSigner = user2 + , txOptsGas = 0 + } + resp <- assertTx . runTxClientM $ setName opts msg + ensureResponseCodes (0,2) resp - it "Can buy an existing name (success 0)" $ do - balance1 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 - balance2 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr2 - Whois{whoisPrice} <- getQueryResponseSuccess $ getWhois $ defaultQueryWithData satoshi + it "Can buy an existing name" $ do + balance1 <- getUserBalance user1 + balance2 <- getUserBalance user2 + N.Whois{whoisPrice} <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ + getWhois defaultQueryArgs { queryArgsData = satoshi } let purchaseAmount = whoisPrice + 1 newVal = "hello (again) world" - msg = TypedMessage "BuyName" (encode $ BuyName purchaseAmount satoshi newVal addr2) - claimedLog = NameClaimed addr2 satoshi newVal purchaseAmount - deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user2 msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "NameClaimed" claimedLog + msg = N.BuyName + { buyNameBid = purchaseAmount + , buyNameName = satoshi + , buyNameValue = newVal + , buyNameBuyer = signerAddress user2 + } + claimedLog = N.NameClaimed + { nameClaimedOwner = signerAddress user2 + , nameClaimedName = satoshi + , nameClaimedValue = newVal + , nameClaimedBid = purchaseAmount + } + opts = TxOpts + { txOptsSigner = user2 + , txOptsGas = 0 + } + + resp <- assertTx . runTxClientM $ buyName opts msg + ensureResponseCodes (0,0) resp + (errs, es) <- deliverTxEvents (Proxy @N.NameClaimed) resp + errs `shouldBe` [] + filter (claimedLog ==) es `shouldBe` [claimedLog] + -- check for updated balances - seller: addr1, buyer: addr2 - let sellerQueryReq = defaultQueryWithData addr1 - sellerFoundAmount <- getQueryResponseSuccess $ getBalance sellerQueryReq + sellerFoundAmount <- getUserBalance user1 sellerFoundAmount `shouldBe` (balance1 + purchaseAmount) - let buyerQueryReq = defaultQueryWithData addr2 - buyerFoundAmount <- getQueryResponseSuccess $ getBalance buyerQueryReq + buyerFoundAmount <- getUserBalance user2 buyerFoundAmount `shouldBe` (balance2 - purchaseAmount) - -- check for ownership changes - let queryReq = defaultQueryWithData satoshi - foundWhois <- getQueryResponseSuccess $ getWhois queryReq - foundWhois `shouldBe` Whois "hello (again) world" addr2 purchaseAmount + + let expected = N.Whois + { whoisValue = "hello (again) world" + , whoisOwner = signerAddress user2 + , whoisPrice = purchaseAmount + } + foundWhois <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ + getWhois defaultQueryArgs { queryArgsData = satoshi } + foundWhois `shouldBe` expected -- @NOTE: this is possibly a problem with the go application too -- https://cosmos.network/docs/tutorial/buy-name.html#msg - it "Can buy self-owned names and make a profit (success 0)" $ do + it "Can buy self-owned names and make a profit " $ do -- check balance before - let queryReq = defaultQueryWithData addr2 - beforeBuyAmount <- getQueryResponseSuccess $ getBalance queryReq + beforeBuyAmount <- getUserBalance user2 -- buy let val = "hello (again) world" - msg = TypedMessage "BuyName" (encode $ BuyName 500 satoshi val addr2) - claimedLog = NameClaimed addr2 satoshi val 500 - deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user2 msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "NameClaimed" claimedLog + msg = N.BuyName + { buyNameBid = 500 + , buyNameName = satoshi + , buyNameValue = val + , buyNameBuyer = signerAddress user2 + } + claimedLog = N.NameClaimed + { nameClaimedOwner = signerAddress user2 + , nameClaimedName = satoshi + , nameClaimedValue = val + , nameClaimedBid = 500 + } + opts = TxOpts + { txOptsSigner = user2 + , txOptsGas = 0 + } + + resp <- assertTx . runTxClientM $ buyName opts msg + ensureResponseCodes (0,0) resp + (errs, es) <- deliverTxEvents (Proxy @N.NameClaimed) resp + errs `shouldBe` [] + filter (claimedLog ==) es `shouldBe` [claimedLog] + -- check balance after - afterBuyAmount <- getQueryResponseSuccess $ getBalance queryReq + afterBuyAmount <- getUserBalance user2 -- owner/buyer still profits afterBuyAmount `shouldSatisfy` (> beforeBuyAmount) - it "Can fail to buy a name (failure 1)" $ do + it "Can fail to buy a name" $ do -- try to buy at a lower price - let msg = TypedMessage "BuyName" (encode $ BuyName 100 satoshi "hello (again) world" addr1) - mkSignedRawTransactionWithRoute "nameservice" user1 msg >>= ensureCheckAndDeliverResponseCodes (0,1) - - it "Can delete names (success 0)" $ do - let msg = TypedMessage "DeleteName" (encode $ DeleteName addr2 satoshi) - deletedLog = NameDeleted satoshi - deliverResp <- mkSignedRawTransactionWithRoute "nameservice" user2 msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "NameDeleted" deletedLog - -- name shouldn't exist - let queryReq = defaultQueryWithData satoshi - ClientResponse{ clientResponseData, clientResponseRaw } <- runRPC $ getWhois queryReq - let queryRespCode = clientResponseRaw ^. Response._queryCode - -- storage failure - queryRespCode `shouldBe` 2 - clientResponseData `shouldBe` Nothing - - it "Can fail a transfer (failure 1)" $ do - let senderBeforeQueryReq = defaultQueryWithData addr2 - addr2Balance <- getQueryResponseSuccess $ getBalance senderBeforeQueryReq + let msg = N.BuyName + { buyNameBid = 100 + , buyNameName = satoshi + , buyNameValue = "hello (again) world" + , buyNameBuyer = signerAddress user1 + } + opts = TxOpts + { txOptsSigner = user1 + , txOptsGas = 0 + } + + resp <- assertTx . runTxClientM $ buyName opts msg + ensureResponseCodes (0,1) resp + + it "Can delete names" $ do + let msg = N.DeleteName + { deleteNameOwner = signerAddress user2 + , deleteNameName = satoshi + } + deletedLog = N.NameDeleted + { nameDeletedName = satoshi + } + + opts = TxOpts + { txOptsSigner = user2 + , txOptsGas = 0 + } + + resp <- assertTx . runTxClientM $ deleteName opts msg + ensureResponseCodes (0,0) resp + (errs, es) <- deliverTxEvents (Proxy @N.NameDeleted) resp + errs `shouldBe` [] + filter (deletedLog ==) es `shouldBe` [deletedLog] + + respQ <- RPC.runTendermintM rpcConfig $ + getWhois defaultQueryArgs { queryArgsData = satoshi } + ensureQueryResponseCode 2 respQ + + + it "Can fail a transfer" $ do + addr2Balance <- getUserBalance user2 let tooMuchToTransfer = addr2Balance + 1 - msg = TypedMessage "Transfer" (encode $ Transfer addr2 addr1 tooMuchToTransfer) - ensureCheckAndDeliverResponseCodes (0,1) =<< mkSignedRawTransactionWithRoute "token" user2 msg + msg = T.Transfer + { transferFrom = signerAddress user2 + , transferTo = signerAddress user1 + , transferAmount = tooMuchToTransfer + } + opts = TxOpts + { txOptsSigner = user2 + , txOptsGas = 0 + } - it "Can transfer (success 0)" $ do - balance1 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 - balance2 <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr2 + resp <- assertTx . runTxClientM $ transfer opts msg + ensureResponseCodes (0,1) resp + + it "Can transfer" $ do + balance1 <- getUserBalance user1 + balance2 <- getUserBalance user2 let transferAmount = 1 - msg = TypedMessage "Transfer" $ encode - Transfer - { transferFrom = addr1 - , transferTo = addr2 + msg = + T.Transfer + { transferFrom = signerAddress user1 + , transferTo = signerAddress user2 , transferAmount = transferAmount } - transferEvent = TransferEvent + transferLog = T.TransferEvent { transferEventAmount = transferAmount - , transferEventTo = addr2 - , transferEventFrom = addr1 + , transferEventTo = signerAddress user2 + , transferEventFrom = signerAddress user1 + } + opts = TxOpts + { txOptsSigner = user1 + , txOptsGas = 0 } - deliverResp <- mkSignedRawTransactionWithRoute "token" user1 msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "TransferEvent" transferEvent + + resp <- assertTx . runTxClientM $ transfer opts msg + ensureResponseCodes (0,0) resp + (errs, es) <- deliverTxEvents (Proxy @T.TransferEvent) resp + errs `shouldBe` [] + filter (transferLog ==) es `shouldBe` [transferLog] + -- check balances - balance1' <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr1 + balance1' <- getUserBalance user1 balance1' `shouldBe` balance1 - transferAmount - balance2' <- getQueryResponseSuccess $ getBalance $ defaultQueryWithData addr2 + balance2' <- getUserBalance user2 balance2' `shouldBe` balance2 + transferAmount +faucetUser + :: T.Amount + -> Signer + -> IO () +faucetUser amount s@(Signer addr _) = + void . assertTx .runTxClientM $ + let msg = T.FaucetAccount addr amount + opts = TxOpts + { txOptsGas = 0 + , txOptsSigner = s + } + in faucet opts msg + +getUserBalance + :: Signer + -> IO T.Amount +getUserBalance usr = fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ + getBalance defaultQueryArgs { queryArgsData = signerAddress usr } + -------------------------------------------------------------------------------- -user1 :: User -user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" +user1 :: Signer +user1 = makeSignerFromUser $ + makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" -user2 :: User -user2 = makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" +user2 :: Signer +user2 = makeSignerFromUser $ + makeUser "f65242094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" -------------------------------------------------------------------------------- +-- Query Client +-------------------------------------------------------------------------------- + +getAccount + :: QueryArgs Address + -> RPC.TendermintM (QueryClientResponse Auth.Account) + +getWhois + :: QueryArgs N.Name + -> RPC.TendermintM (QueryClientResponse N.Whois) + +getBalance + :: QueryArgs Address + -> RPC.TendermintM (QueryClientResponse T.Amount) + +getWhois :<|> getBalance :<|> getAccount = + genClientQ (Proxy :: Proxy m) queryApiP def + where + queryApiP :: Proxy (QApi NameserviceModules) + queryApiP = Proxy + + +-------------------------------------------------------------------------------- +-- Tx Client +-------------------------------------------------------------------------------- + +txClientConfig :: ClientConfig +txClientConfig = + let getNonce addr = do + resp <- RPC.runTendermintM rpcConfig $ getAccount $ + QueryArgs + { queryArgsHeight = -1 + , queryArgsProve = False + , queryArgsData = addr + } + case resp of + QueryError e -> + if appErrorCode e == 2 + then pure 0 + else error $ "Unknown nonce error: " <> show (appErrorMessage e) + QueryResponse QueryResult {queryResultData} -> + pure $ Auth.accountNonce queryResultData + + in ClientConfig + { clientGetNonce = getNonce + , clientRPC = rpcConfig + } + +type TxClientM = ReaderT ClientConfig IO + +runTxClientM :: TxClientM a -> IO a +runTxClientM m = runReaderT m txClientConfig + + +-- Nameservice Client +buyName + :: TxOpts + -> N.BuyName + -> TxClientM (TxClientResponse () ()) + +setName + :: TxOpts + -> N.SetName + -> TxClientM (TxClientResponse () ()) + +deleteName + :: TxOpts + -> N.DeleteName + -> TxClientM (TxClientResponse () ()) -faucetAccount :: User -> Amount -> IO () -faucetAccount user@User{userAddress} amount = do - let msg = TypedMessage "FaucetAccount" (encode $ FaucetAccount userAddress amount) - faucetEvent = Faucetted userAddress amount - deliverResp <- mkSignedRawTransactionWithRoute "token" user msg >>= getDeliverTxResponse - ensureDeliverResponseCode deliverResp 0 - ensureEventLogged deliverResp "Faucetted" faucetEvent +-- Token Client +--burn +-- :: TxOpts +-- -> T.Burn +-- -> TxClientM (TxClientResponse () ()) -getWhois :: QueryArgs Name -> RPC.TendermintM (ClientResponse Whois) -getBalance :: QueryArgs Address -> RPC.TendermintM (ClientResponse Amount) +transfer + :: TxOpts + -> T.Transfer + -> TxClientM (TxClientResponse () ()) -apiP :: Proxy ("token" :> T.Api :<|> ("nameservice" :> N.Api)) -apiP = Proxy +faucet + :: TxOpts + -> T.FaucetAccount + -> TxClientM (TxClientResponse () ()) -(getBalance :<|> getWhois) = - genClient (Proxy :: Proxy RPC.TendermintM) apiP def +(buyName :<|> setName :<|> deleteName) :<|> + (_ :<|> transfer :<|> faucet) :<|> + EmptyTxClient = + genClientT (Proxy @TxClientM) txApiP defaultClientTxOpts + where + txApiP :: Proxy (TApi NameserviceModules) + txApiP = Proxy diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md b/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md index 2473573b..c2b33654 100644 --- a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md +++ b/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md @@ -5,10 +5,10 @@ A `Module` has a very specific meaning in the context of this SDK. A `Module` is something between a library and a small state machine. It is built on top of the `BaseApp` abstraction in the sense that all `Module`s must be explicitly interpeted in terms of `BaseApp` in order to compile the application. The full type definition is ~~~ haskell ignore -data Module (name :: Symbol) msg (api :: *) (val :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: RoutedTx msg -> Sem r val - , moduleTxChecker :: RoutedTx msg -> Sem r val - , moduleQueryServer :: RouteT api (Sem r) +data Module (name :: Symbol) (h :: *) (q :: *) (s :: EffectRow) (r :: EffectRow) = Module + { moduleTxDeliverer :: T.RouteTx h r 'DeliverTx + , moduleTxChecker :: T.RouteTx h r 'CheckTx + , moduleQueryServer :: Q.RouteQ q r , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } ~~~ @@ -16,9 +16,8 @@ data Module (name :: Symbol) msg (api :: *) (val :: *) (s :: EffectRow) (r :: Ef where the type parameters - `name` is the name of the module, e.g. `"bank"`. -- `msg` is the type of the incoming messages the module must handle. -- `val` is the type of the return value (must be common across all messages the module receives). -- `api` is the query api for querying state in the url format (more on this later). +- `h` is the transaction router api type. +- `q` is the query api type for querying state in the url format (more on this later). - `s` is the set of effects introduced by this module. - `r` is the global set of effects that this module will run in when part of a larger application (more on this later). diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md index 8cd0aa37..39220d24 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md @@ -2,7 +2,7 @@ ## Message Types -The `Message` module is ultimately a small state machine used for processing messages. Each module must define what messages it accepts, if any. Like many other types found in the SDK, this message class must implement the `HasCodec` class. We recommend using a protobuf serialization format for messages using either the `proto3-suite` or `proto-lens` libraries, though in theory you could use anything (e.g. `JSON`). +Each module is ultimately a small state machine used for processing messages. Each module must define what messages it accepts, if any. Like many other types found in the SDK, this message class must implement the `HasCodec` class. We recommend using a protobuf serialization format for messages using either the `proto3-suite` or `proto-lens` libraries, though in theory you could use anything (e.g. `JSON`). ### `proto3-suite` The advantages of using the `proto3-suite` library are that it has support for generics and that you can generate a `.proto` file from your haskell code for export to other applications. This is particularly useful when prototyping or when you have control over the message specification. @@ -26,10 +26,9 @@ import Data.Text (Text) import GHC.Generics (Generic) import Nameservice.Modules.Nameservice.Types (Name(..)) import Nameservice.Modules.Token (Amount) -import Nameservice.Modules.TypedMessage (TypedMessage(..)) import Proto3.Suite (Named, Message, fromByteString, toLazyByteString) import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (Msg(..), ValidateMessage(..), +import Tendermint.SDK.Types.Message (Msg(..), ValidateMessage(..), HasMessageType(..), isAuthorCheck, nonEmptyCheck, coerceProto3Error, formatMessageParseError) import Tendermint.SDK.Codec (HasCodec(..)) @@ -81,45 +80,28 @@ instance HasCodec BuyName where decode = first (formatMessageParseError . coerceProto3Error) . fromByteString ~~~ -We want a sum type that covers all possible messages the module can receive. As `protobuf` is a schemaless format, parsing is sometimes ambiguous if two types are the same up to field names, or one is a subset of the other. For this reason we defined a type called `TypedMessage`: +As `protobuf` is a schemaless format, parsing is sometimes ambiguous if two types are the same up to field names, or one is a subset of the other. For this reason we use the type class `HasTypedMessage` ~~~ haskell ignore -data TypedMessage = TypedMessage - { typedMessageType :: Text - , typedMessageContents :: BS.ByteString - } deriving (Eq, Show, Generic) - -instance Message TypedMessage -instance Named TypedMessage - -instance HasCodec TypedMessage where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString +class HasMessageType msg where + messageType :: Proxy msg -> Text ~~~ -This allows us to disambiguated messages based on the `type` field, so that for example we can distinguish `DeleteName` from a submessage of `BuyName`. With that out of the way, we can define the module level (sum) message type: +to associate each message to a tag to assist in parsing. So for example, we can implement this class for our message types as ~~~ haskell -data NameserviceMessage = - NSetName SetName - | NBuyName BuyName - | NDeleteName DeleteName - deriving (Eq, Show, Generic) - -instance HasCodec NameserviceMessage where - decode bs = do - TypedMessage{..} <- decode bs - case typedMessageType of - "SetName" -> NSetName <$> decode typedMessageContents - "DeleteName" -> NDeleteName <$> decode typedMessageContents - "BuyName" -> NBuyName <$> decode typedMessageContents - _ -> Left . cs $ "Unknown Nameservice message type " ++ cs typedMessageType - encode = \case - NSetName msg -> encode msg - NBuyName msg -> encode msg - NDeleteName msg -> encode msg + +instance HasMessageType SetName where + messageType _ = "SetName" + +instance HasMessageType DeleteName where + messageType _ = "DeleteName" + +instance HasMessageType BuyName where + messageType _ = "BuyName" ~~~ + ## Message Validation Message validation is an important part of the transaction life cycle. When a `checkTx` message comes in, Tendermint is asking whether a transaction bytestring from the mempool is potentially runnable. At the very least this means that @@ -192,14 +174,4 @@ instance ValidateMessage BuyName where ] ~~~ -Finally we can define a `ValidateMessage` instance for our top level message type by dispatching on the message type: - -~~~ haskell -instance ValidateMessage NameserviceMessage where - validateMessage m@Msg{msgData} = case msgData of - NBuyName msg -> validateMessage m {msgData = msg} - NSetName msg -> validateMessage m {msgData = msg} - NDeleteName msg -> validateMessage m {msgData = msg} -~~~ - [Next: Keeper](Keeper.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md index 00a9622a..92f77a8b 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md @@ -8,19 +8,19 @@ At this point we can collect the relevant pieces to form the Nameservice module: module Tutorial.Nameservice.Module where import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, eval) -import Nameservice.Modules.Nameservice.Messages (NameserviceMessage) -import Nameservice.Modules.Nameservice.Query (Api, server) -import Nameservice.Modules.Nameservice.Router (router) +import Nameservice.Modules.Nameservice.Query (QueryApi, server) +import Nameservice.Modules.Nameservice.Router (MessageApi, messageHandlers) import Nameservice.Modules.Nameservice.Types (NameserviceModuleName) import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) -import Tendermint.SDK.Application (Module (..), - defaultTxChecker) -import Tendermint.SDK.BaseApp (BaseAppEffs) +import Data.Proxy +import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.BaseApp (BaseAppEffs, + DefaultCheckTx (..)) -- a convenient type alias type NameserviceM r = - Module NameserviceModuleName NameserviceMessage () Api NameserviceEffs r + Module NameserviceModuleName MessageApi QueryApi NameserviceEffs r nameserviceModule :: Members BaseAppEffs r @@ -28,31 +28,38 @@ nameserviceModule => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module - { moduleTxDeliverer = router - , moduleTxChecker = defaultTxChecker + { moduleTxDeliverer = messageHandlers + , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) , moduleQueryServer = server , moduleEval = eval } + ~~~ -We are using `defaultTxChecker` as our transaction checker, which is a static message validator defined as +Here We are using `defaultCheckTx` as our transaction checker, which is a static message validator defined that respons to any message with the following handler: ~~~ haskell ignore -defaultTxChecker + +defaultCheckTxHandler :: Member (Error AppError) r => ValidateMessage msg - => RoutedTx msg + => RoutingTx msg -> Sem r () -defaultTxChecker (RoutedTx Tx{txMsg}) = +defaultCheckTxHandler(RoutingTx Tx{txMsg}) = case validateMessage txMsg of V.Failure err -> throwSDKError . MessageValidation . map formatMessageSemanticError $ err V.Success _ -> pure () + ~~~ -This means that we are only doing static validation, meaning that we're not interested in checking message validitity against the database. This is reflected in the return type for the checker `Sem r ()`. If you want to add custom checking, you may write a custom checker for your module. +Note that this checker can be used to implement any transaction for which +1. The message accepted by the router has a `ValidateMessage` instance +2. The return type is marked with `OnCheckUnit`, meaning that `()` is returned for any `checkTx` ABCI message. + +To generate a router for which every transaction has these properties, we used the `defaultCheckTx` type class method -Note the constraints on the module's effects `r`: +Note the constraints on `r`: ~~~ haskell ignore ... diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md index ecf78502..8f8d8bc1 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md @@ -10,7 +10,7 @@ import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Whois, Name) import Polysemy (Members) import Polysemy.Error (Error) -import Tendermint.SDK.BaseApp (RawStore, AppError, RouteT, QueryApi, storeQueryHandlers) +import Tendermint.SDK.BaseApp (RawStore, AppError, RouteQ, QueryApi, storeQueryHandlers) ~~~ The way to query application state is via the `query` message which uses a `url` like format. The SDK tries to abstract as much of this away as possible. For example, if you want to only serve state that you have registered with the store via the `IsKey` class, then things are very easy. If you need to make joins to serve requests, we support this as well and it's not hard, but we will skip this for now. @@ -31,11 +31,13 @@ To serve all the data registered with the `IsKey` class, we can use the `storeQu ~~~ haskell server :: Members [RawStore, Error AppError] r - => RouteT Api r + => RouteQ Api r server = storeQueryHandlers (Proxy @NameserviceContents) storeKey (Proxy :: Proxy r) ~~~ -Here `RouteT` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely copy-pasted from that codebase. +Here `RouteT` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely vendored from that codebase. -[Next: Module](Module.md) +Note that more advanced queries are possible other than just serving what is in storage. For example you might want to use joins to fulfill requests or use query parameters in the url. These are all possible, but we won't go into details here as they are not used in the app. + +[Next: Router](Router.md) diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Router.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Router.md new file mode 100644 index 00000000..dda61f7b --- /dev/null +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Router.md @@ -0,0 +1,120 @@ + +# Router + +## Tutorial.Nameservice.Router + +The Router is where you specifiy the handlers for the messages that the module accepts. The router is typed in a [servant](https://hackage.haskell.org/package/servant) style, using combinators and primitives to declare a very precise type for the router. + +~~~ haskell +module Tutorial.Nameservice.Router where + +import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, + buyName, deleteName, + setName) +import Nameservice.Modules.Nameservice.Messages (BuyName, DeleteName, + SetName) +import Nameservice.Modules.Token (TokenEffs) +import Polysemy (Members, Sem) +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.BaseApp ((:~>), BaseAppEffs, + Return, + RouteContext (..), + RouteTx, + RoutingTx (..), + TxEffs, TypedMessage, + incCount, withTimer) +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) + +~~~ + +## Typing the Router + +First we declare the type for our router + +~~~ haskell +type MessageApi = + TypedMessage BuyName :~> Return () + :<|> TypedMessage SetName :~> Return () + :<|> TypedMessage DeleteName :~> Return () +~~~ + +Lets break it down: + +- `(:<|>)` is the operator which denotes alternative, so our router is composed of 3 handlers in this case. +- `TypedMessage` is a combinator that speficies that message type we want to accept. We requre that whatever the message type is, it implements the `HasTypedMessage` class. +- `(:~>)` is a combinator that allows us to connect a message type with a response +- `Return` is used to specify the return type. + +Since there are two possible ABCI messages that the router has to accomodate, `checkTx` and `deliverTx`, the router may return different values depending on the ABCI message type. For example, it's possible that the `checkTx` does not fully mimic the transaction and simply returns `()`, while the `deliverTx` message returns a value of type `Whois`. Concretely you would write + +~~~ haskell ignore +type BuyNameHandler = TypeMessage BuyName :~> Return' 'OnCheckUnit Whois +~~~ + +or equivalently using the alias + +~~~ haskell ignore +type BuyNameHandler = TypeMessage BuyName :~> Return Whois +~~~ + + Alternatively, you could write the application so that each `checkTx` ABCI message is handled in the same way as the `deliverTx` message, e.g. the both return a value of type `Whois`. + +~~~ haskell ignore +type BuyNameHandler = TypeMessage BuyName :~> Return' 'OnCheckEval Whois +~~~ + + +In the case of our actual application, all the transactions return `()` for both `checkTx` and `deliverTx` + +## Implementing the Handlers + +Similar to the servant style, the types for the handlers must be computed from the type of the router. This requires that you understand what each of the combinators corresponds to, and again this ultimately depends on which `RouteContext` we're in, either `CheckTx` or `DeliverTx`. + +Rather than cover all possible cases, we just note that in the case of the Nameservice app we end up with the following server type for the `DeliverTx` context: + +~~~ haskell + +messageHandlers + :: Members BaseAppEffs r + => Members TokenEffs r + => Members NameserviceEffs r + => RouteTx MessageApi r 'DeliverTx +messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH + +buyNameH + :: Members BaseAppEffs r + => Members TxEffs r + => Members TokenEffs r + => Members NameserviceEffs r + => RoutingTx BuyName + -> Sem r () +buyNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do + incCount "buy_total" + withTimer "buy_duration_seconds" $ buyName msgData + +setNameH + :: Members BaseAppEffs r + => Members TxEffs r + => Members NameserviceEffs r + => RoutingTx SetName + -> Sem r () +setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do + incCount "set_total" + withTimer "set_duration_seconds" $ setName msgData + +deleteNameH + :: Members BaseAppEffs r + => Members TxEffs r + => Members TokenEffs r + => Members NameserviceEffs r + => RoutingTx DeleteName + -> Sem r () +deleteNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do + incCount "delete_total" + withTimer "delete_duration_seconds" $ deleteName msgData + + ~~~ + + +[Next: Module](Module.md) \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md index 603cad91..b698a019 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md +++ b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md @@ -133,19 +133,11 @@ The [`cosmos-sdk`](https://github.com/cosmos/cosmos-sdk) assumes that you use `u In order to register the `Whois` type with the query service, you must implement the `Queryable` typeclass: ~~~ haskell ignore -class Queryable a where +class HasCodec a => Queryable a where type Name a :: Symbol - encodeQueryResult :: a -> Base64String - decodeQueryResult :: Base64String -> Either Text a - - default encodeQueryResult :: HasCodec a => a -> Base64String - encodeQueryResult = fromBytes . encode - - default decodeQueryResult :: HasCodec a => Base64String -> Either Text a - decodeQueryResult = decode . toByte ~~~ -What this means is that you need to supply codecs for the type to query, with the default using the `HasCodec` class. You also need to name the type, as this will match the leaf of the `url` used for querying. So for example, in the Nameservice app we have +This means that any item which is queryable needs to have codecs via the `HasCodec` class. You also need to name the type, as this will match the leaf of the `url` used for querying. So for example, in the Nameservice app we have ~~~ haskell instance BA.Queryable Whois where diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-examples/simple-storage/package.yaml index a68c990b..38cddd95 100644 --- a/hs-abci-examples/simple-storage/package.yaml +++ b/hs-abci-examples/simple-storage/package.yaml @@ -129,9 +129,8 @@ tests: - data-default-class - hs-abci-sdk - simple-storage - - hs-abci-types - hs-abci-test-utils - hs-tendermint-client - hspec - - lens + - mtl - servant diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index bfd694b4..35e529d6 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -1,32 +1,34 @@ module SimpleStorage.Modules.SimpleStorage ( SimpleStorageM - , Api + , QueryApi + , MessageApi , simpleStorageModule , module SimpleStorage.Modules.SimpleStorage.Keeper , module SimpleStorage.Modules.SimpleStorage.Message , module SimpleStorage.Modules.SimpleStorage.Types ) where +import Data.Proxy import Polysemy (Member, Members) import SimpleStorage.Modules.SimpleStorage.Keeper hiding (storeKey) import SimpleStorage.Modules.SimpleStorage.Message -import SimpleStorage.Modules.SimpleStorage.Query (Api, server) -import SimpleStorage.Modules.SimpleStorage.Router (router) +import SimpleStorage.Modules.SimpleStorage.Query (QueryApi, server) +import SimpleStorage.Modules.SimpleStorage.Router (MessageApi, + messageHandlers) import SimpleStorage.Modules.SimpleStorage.Types -import Tendermint.SDK.Application (Module (..), - defaultTxChecker) +import Tendermint.SDK.Application (Module (..)) import qualified Tendermint.SDK.BaseApp as BaseApp type SimpleStorageM r = - Module "simple_storage" SimpleStorageMessage () Api SimpleStorageEffs r + Module "simple_storage" MessageApi QueryApi SimpleStorageEffs r simpleStorageModule :: Member SimpleStorage r => Members BaseApp.BaseAppEffs r => SimpleStorageM r simpleStorageModule = Module - { moduleTxDeliverer = router - , moduleTxChecker = defaultTxChecker + { moduleTxDeliverer = messageHandlers + , moduleTxChecker = BaseApp.defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) , moduleQueryServer = server , moduleEval = eval } diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs index 0d99be75..fe54cc93 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs @@ -1,14 +1,13 @@ module SimpleStorage.Modules.SimpleStorage.Message - ( SimpleStorageMessage(..) - , UpdateCountTx(..) + ( UpdateCountTx(..) )where import Control.Lens (from, iso, view, (&), - (.~), (^.)) -import Control.Lens.Wrapped (Wrapped (..), _Unwrapped') -import Data.Bifunctor (first) + (.~), (^.), _Wrapped') +import Control.Lens.Wrapped (Wrapped (..)) +import Data.Bifunctor (bimap) import Data.Int (Int32) -import qualified Data.ProtoLens as PL +import qualified Data.ProtoLens as P import Data.ProtoLens.Message (Message (..)) import Data.Serialize.Text () import Data.String.Conversions (cs) @@ -18,28 +17,18 @@ import GHC.Generics (Generic) import Proto.SimpleStorage.Messages as M import Proto.SimpleStorage.Messages_Fields as M import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Message (Msg (..), +import Tendermint.SDK.Types.Message (HasMessageType (..), ValidateMessage (..)) -data SimpleStorageMessage = - UpdateCount UpdateCountTx - -instance ValidateMessage SimpleStorageMessage where - validateMessage m@Msg{msgData} = case msgData of - UpdateCount msg -> validateMessage m {msgData = msg} - -instance HasCodec SimpleStorageMessage where - decode = first cs . fmap (UpdateCount . view _Unwrapped') . PL.decodeMessage - encode = \case - UpdateCount a -> PL.encodeMessage $ a ^. from _Unwrapped' - --------------------------------------------------------------------------------- data UpdateCountTx = UpdateCountTx { updateCountTxUsername :: Text , updateCountTxCount :: Int32 } deriving (Show, Eq, Generic) +instance HasMessageType UpdateCountTx where + messageType _ = "update_count" + instance ValidateMessage UpdateCountTx where validateMessage _ = Success () @@ -56,3 +45,7 @@ instance Wrapped UpdateCountTx where UpdateCountTx { updateCountTxUsername = msg ^. M.username , updateCountTxCount = msg ^. M.count } + +instance HasCodec UpdateCountTx where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs index 57d09fcb..788730b2 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs @@ -1,6 +1,6 @@ module SimpleStorage.Modules.SimpleStorage.Query ( CountStoreContents - , Api + , QueryApi , server ) where @@ -14,11 +14,11 @@ import qualified Tendermint.SDK.BaseApp as BaseApp type CountStoreContents = '[(CountKey, Count)] -type Api = BaseApp.QueryApi CountStoreContents +type QueryApi = BaseApp.QueryApi CountStoreContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api r + => BaseApp.RouteQ QueryApi r server = BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs index 817363df..57a9e9a6 100644 --- a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs +++ b/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -1,5 +1,6 @@ module SimpleStorage.Modules.SimpleStorage.Router - ( router + ( MessageApi + , messageHandlers ) where import Polysemy (Member, Members, @@ -8,19 +9,30 @@ import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, updateCount) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) -import Tendermint.SDK.BaseApp (TxEffs) +import Tendermint.SDK.BaseApp ((:~>), Return, + RouteContext (..), + RouteTx, + RoutingTx (..), + TxEffs, + TypedMessage) import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), - Tx (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) -router +type MessageApi = + TypedMessage UpdateCountTx :~> Return () + +messageHandlers + :: Member SimpleStorage r + => RouteTx MessageApi r 'DeliverTx +messageHandlers = updateCountH + +updateCountH :: Member SimpleStorage r => Members TxEffs r - => PreRoutedTx SimpleStorageMessage + => RoutingTx UpdateCountTx -> Sem r () -router (PreRoutedTx Tx{txMsg}) = +updateCountH (RoutingTx Tx{txMsg}) = let Msg{msgData} = txMsg - in case msgData of - UpdateCount UpdateCountTx{updateCountTxCount} -> - updateCount (Count updateCountTxCount) + UpdateCountTx{updateCountTxCount} = msgData + in updateCount (Count updateCountTxCount) diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index cc31c2d4..401df1ec 100644 --- a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -1,19 +1,34 @@ module SimpleStorage.Test.E2ESpec (spec) where -import Control.Lens ((^.)) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Default.Class (def) +import Control.Monad.Reader (ReaderT, runReaderT) +import Data.Default.Class (def) import Data.Proxy -import qualified Network.ABCI.Types.Messages.Response as Resp -import qualified Network.Tendermint.Client as RPC -import Servant.API ((:>)) -import qualified SimpleStorage.Modules.SimpleStorage as SS -import Tendermint.SDK.BaseApp.Query (QueryArgs (..)) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.Utils.Client (ClientResponse (..), - HasClient (..)) -import Tendermint.Utils.Request (runRPC) -import Tendermint.Utils.User (User (..), makeUser, mkSignedRawTransactionWithRoute) +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..)) +import SimpleStorage.Application +import qualified SimpleStorage.Modules.SimpleStorage as SS +import Tendermint.SDK.Application.Module (AppQueryRouter (QApi), + AppTxRouter (TApi)) +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + QueryResult (..), + defaultQueryArgs) +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Types.Address (Address) +import Tendermint.Utils.Client (ClientConfig (..), + EmptyTxClient (..), + HasQueryClient (..), + HasTxClient (..), + QueryClientResponse (..), + TxClientResponse (..), + TxOpts (..), + defaultClientTxOpts) +import Tendermint.Utils.ClientUtils (assertQuery, assertTx, + ensureResponseCodes, + rpcConfig) +import Tendermint.Utils.User (User (..), + makeSignerFromUser, + makeUser) import Test.Hspec spec :: Spec @@ -21,43 +36,86 @@ spec = do describe "SimpleStorage E2E - via hs-tendermint-client" $ do it "Can query /health to make sure the node is alive" $ do - resp <- runRPC RPC.health + resp <- RPC.runTendermintM rpcConfig RPC.health resp `shouldBe` RPC.ResultHealth - --it "Can query the count and make sure its initialized to 0" $ do - -- let queryReq = QueryArgs - -- { queryArgsData = SS.CountKey - -- , queryArgsHeight = 0 - -- , queryArgsProve = False - -- } - -- ClientResponse{clientResponseData = Just foundCount} <- runQueryRunner $ getCount queryReq - -- foundCount `shouldBe` SS.Count 0 - it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do - let txMsg = SS.UpdateCount $ SS.UpdateCountTx "irakli" 4 - tx <- mkSignedRawTransactionWithRoute "simple_storage" user1 txMsg - let txReq = RPC.RequestBroadcastTxCommit - { RPC.requestBroadcastTxCommitTx = Base64.fromBytes . encode $ tx - } - deliverResp <- fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ RPC.broadcastTxCommit txReq - let deliverRespCode = deliverResp ^. Resp._deliverTxCode - deliverRespCode `shouldBe` 0 + let txOpts = TxOpts + { txOptsGas = 0 + , txOptsSigner = makeSignerFromUser user1 + } + tx = SS.UpdateCountTx + { SS.updateCountTxUsername = "charles" + , SS.updateCountTxCount = 4 + } + resp <- assertTx . runTxClientM $ updateCount txOpts tx + ensureResponseCodes (0,0) resp it "can make sure the synchronous tx transaction worked and the count is now 4" $ do - let queryReq = QueryArgs - { queryArgsData = SS.CountKey - , queryArgsHeight = 0 - , queryArgsProve = False - } - ClientResponse{clientResponseData = Just foundCount} <- runRPC $ getCount queryReq + resp <- assertQuery . RPC.runTendermintM rpcConfig $ + getCount defaultQueryArgs { queryArgsData = SS.CountKey } + let foundCount = queryResultData resp foundCount `shouldBe` SS.Count 4 -------------------------------------------------------------------------------- +-- Query Client +-------------------------------------------------------------------------------- + +getCount + :: QueryArgs SS.CountKey + -> RPC.TendermintM (QueryClientResponse SS.Count) -getCount :: QueryArgs SS.CountKey -> RPC.TendermintM (ClientResponse SS.Count) -getCount = - let apiP = Proxy :: Proxy ("simple_storage" :> SS.Api) - in genClient (Proxy :: Proxy RPC.TendermintM) apiP def +getAccount + :: QueryArgs Address + -> RPC.TendermintM (QueryClientResponse Auth.Account) + +getCount :<|> getAccount = + genClientQ (Proxy :: Proxy m) queryApiP def + where + queryApiP :: Proxy (QApi SimpleStorageModules) + queryApiP = Proxy + + +-------------------------------------------------------------------------------- +-- Tx Client +-------------------------------------------------------------------------------- + +txClientConfig :: ClientConfig +txClientConfig = + let getNonce addr = do + resp <- RPC.runTendermintM rpcConfig $ getAccount $ + defaultQueryArgs { queryArgsData = addr } + case resp of + QueryError e -> + if appErrorCode e == 2 + then pure 0 + else error $ "Unknown nonce error: " <> show (appErrorMessage e) + QueryResponse QueryResult{queryResultData} -> + pure $ Auth.accountNonce queryResultData + + in ClientConfig + { clientGetNonce = getNonce + , clientRPC = rpcConfig + } + +type TxClientM = ReaderT ClientConfig IO + +runTxClientM :: TxClientM a -> IO a +runTxClientM m = runReaderT m txClientConfig + +updateCount + :: TxOpts + -> SS.UpdateCountTx + -> TxClientM (TxClientResponse () ()) + +updateCount :<|> EmptyTxClient = + genClientT (Proxy @TxClientM) txApiP defaultClientTxOpts + where + txApiP :: Proxy (TApi SimpleStorageModules) + txApiP = Proxy + + +-------------------------------------------------------------------------------- user1 :: User user1 = makeUser "f65255094d7773ed8dd417badc9fc045c1f80fdc5b2d25172b031ce6933e039a" diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index d6760493..068fdfad 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -104,7 +104,7 @@ library: - Tendermint.SDK.BaseApp.Metrics - Tendermint.SDK.BaseApp.Metrics.Prometheus - Tendermint.SDK.BaseApp.Query - - Tendermint.SDK.BaseApp.Query.Class + - Tendermint.SDK.BaseApp.Query.Router - Tendermint.SDK.BaseApp.Query.Store - Tendermint.SDK.BaseApp.Query.Types - Tendermint.SDK.BaseApp.Router.Delayed @@ -115,6 +115,11 @@ library: - Tendermint.SDK.BaseApp.Store.RawStore - Tendermint.SDK.BaseApp.Store.Scope - Tendermint.SDK.BaseApp.Transaction + - Tendermint.SDK.BaseApp.Transaction.Checker + - Tendermint.SDK.BaseApp.Transaction.Effect + - Tendermint.SDK.BaseApp.Transaction.Modifier + - Tendermint.SDK.BaseApp.Transaction.Router + - Tendermint.SDK.BaseApp.Transaction.Types - Tendermint.SDK.Codec - Tendermint.SDK.Crypto - Tendermint.SDK.Modules.Auth @@ -173,3 +178,4 @@ tests: - servant - string-conversions - text + - validation diff --git a/hs-abci-sdk/protos/types/transaction.proto b/hs-abci-sdk/protos/types/transaction.proto index d7c19d4e..f069828b 100644 --- a/hs-abci-sdk/protos/types/transaction.proto +++ b/hs-abci-sdk/protos/types/transaction.proto @@ -2,9 +2,14 @@ syntax = "proto3"; package Transaction; message RawTransaction { - bytes data = 1; + TypedMessage data = 1; int64 gas = 2; bytes signature = 3; string route = 4; uint64 nonce = 5; +} + +message TypedMessage { + string type = 1; + bytes data = 2; } \ No newline at end of file diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application.hs b/hs-abci-sdk/src/Tendermint/SDK/Application.hs index 2205f5e3..5f90a229 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application.hs @@ -1,7 +1,6 @@ module Tendermint.SDK.Application ( Modules(..) , Module(..) - , defaultTxChecker , HandlersContext(..) , AnteHandler(..) , baseAppAnteHandler diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs index 98f63600..443ffa46 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs @@ -4,18 +4,19 @@ module Tendermint.SDK.Application.AnteHandler , baseAppAnteHandler ) where -import Control.Monad (unless) +import Control.Monad (unless) import Polysemy -import Polysemy.Error (Error) -import qualified Tendermint.SDK.Application.Module as M -import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), - throwSDKError) -import qualified Tendermint.SDK.Modules.Auth as A -import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) +import Polysemy.Error (Error) +import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), + throwSDKError) +import Tendermint.SDK.BaseApp.Transaction (RoutingTx (..), + TransactionApplication) +import qualified Tendermint.SDK.Modules.Auth as A +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) -data AnteHandler r where - AnteHandler :: (forall msg. M.Router r msg -> M.Router r msg) -> AnteHandler r +data AnteHandler r = AnteHandler + ( TransactionApplication (Sem r) -> TransactionApplication (Sem r)) instance Semigroup (AnteHandler r) where (<>) (AnteHandler h1) (AnteHandler h2) = @@ -24,30 +25,33 @@ instance Semigroup (AnteHandler r) where instance Monoid (AnteHandler r) where mempty = AnteHandler id -applyAnteHandler :: AnteHandler r -> M.Router r msg -> M.Router r msg +applyAnteHandler + :: AnteHandler r + -> TransactionApplication (Sem r) + -> TransactionApplication (Sem r) applyAnteHandler (AnteHandler ah) = ($) ah nonceAnteHandler :: Members A.AuthEffs r => Member (Error AppError) r => AnteHandler r -nonceAnteHandler = AnteHandler $ \(M.Router router) -> - M.Router $ \tx@(PreRoutedTx Tx{..}) -> do - let Msg{msgAuthor} = txMsg - mAcnt <- A.getAccount msgAuthor - account <- case mAcnt of - Just a@A.Account{accountNonce} -> do - unless (accountNonce <= txNonce) $ - throwSDKError (NonceException accountNonce txNonce) - pure a - Nothing -> do - unless (txNonce == 0) $ - throwSDKError (NonceException 0 txNonce) - A.createAccount msgAuthor - result <- router tx - A.putAccount msgAuthor $ - account { A.accountNonce = A.accountNonce account + 1} - pure result +nonceAnteHandler = AnteHandler $ + \txApplication tx@(RoutingTx Tx{..}) -> do + let Msg{msgAuthor} = txMsg + mAcnt <- A.getAccount msgAuthor + account <- case mAcnt of + Just a@A.Account{accountNonce} -> do + unless (accountNonce <= txNonce) $ + throwSDKError (NonceException accountNonce txNonce) + pure a + Nothing -> do + unless (txNonce == 0) $ + throwSDKError (NonceException 0 txNonce) + A.createAccount msgAuthor + result <- txApplication tx + A.putAccount msgAuthor $ + account { A.accountNonce = A.accountNonce account + 1} + pure result baseAppAnteHandler :: Members A.AuthEffs r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index ec6d707a..a14c3322 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -29,14 +29,13 @@ import Tendermint.SDK.BaseApp.Errors (AppError, queryAppError, throwSDKError, txResultAppError) -import Tendermint.SDK.BaseApp.Query (HasRouter) +import qualified Tendermint.SDK.BaseApp.Query as Q import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) import qualified Tendermint.SDK.BaseApp.Store as Store +import Tendermint.SDK.BaseApp.Transaction as T import Tendermint.SDK.Crypto (RecoverableSignatureSchema, SignatureSchema (..)) -import qualified Tendermint.SDK.Modules.Auth as A -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), - parseTx) +import Tendermint.SDK.Types.Transaction (parseTx) import Tendermint.SDK.Types.TxResult (checkTxTxResult, deliverTxTxResult) @@ -85,13 +84,15 @@ data HandlersContext alg ms r core = HandlersContext -- Common function between checkTx and deliverTx makeHandlers :: forall alg ms r core. - Members A.AuthEffs r - => Member (Error AppError) r + Member (Error AppError) r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 - => M.TxRouter ms r - => M.QueryRouter ms r - => HasRouter (M.Api ms) r + => M.AppTxRouter ms r 'T.DeliverTx + => M.AppTxRouter ms r 'T.CheckTx + => M.AppQueryRouter ms r + => Q.HasQueryRouter (M.QApi ms) r + => T.HasTxRouter (M.TApi ms) r 'T.DeliverTx + => T.HasTxRouter (M.TApi ms) r 'T.CheckTx => Members CoreEffs core => M.Eval ms core => M.Effs ms core ~ r @@ -101,11 +102,17 @@ makeHandlers HandlersContext{..} = let compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a compileToBaseApp = M.eval modules - routerWithAH context = applyAnteHandler anteHandler $ M.txRouter context modules - txRouter context bs = case parseTx signatureAlgP bs of + + queryRouter = compileToBaseApp . M.appQueryRouter modules + + txParser bs = case parseTx signatureAlgP bs of Left err -> throwSDKError $ ParseError err - Right tx -> compileToBaseApp $ M.runRouter (routerWithAH context) (PreRoutedTx tx) - queryRouter = compileToBaseApp . M.queryRouter modules + Right tx -> pure $ T.RoutingTx tx + + txRouter ctx bs = compileToBaseApp $ do + let router = applyAnteHandler anteHandler $ M.appTxRouter modules ctx + tx <- txParser bs + router tx query (RequestQuery q) = Store.applyScope $ catch @@ -122,7 +129,7 @@ makeHandlers HandlersContext{..} = checkTx (RequestCheckTx _checkTx) = Store.applyScope $ do res <- catch ( let txBytes = _checkTx ^. Req._checkTxTx . to Base64.toBytes - in txRouter M.CheckTxContext txBytes + in txRouter T.CheckTx txBytes ) (\(err :: AppError) -> return $ def & txResultAppError .~ err @@ -132,7 +139,7 @@ makeHandlers HandlersContext{..} = deliverTx (RequestDeliverTx _deliverTx) = Store.applyScope $ do res <- catch @AppError ( let txBytes = _deliverTx ^. Req._deliverTxTx . to Base64.toBytes - in txRouter M.DeliverTxContext txBytes + in txRouter T.DeliverTx txBytes ) (\(err :: AppError) -> return $ def & txResultAppError .~ err @@ -157,13 +164,15 @@ makeHandlers HandlersContext{..} = makeApp :: forall alg ms r core. - Members A.AuthEffs r - => Members [Error AppError, Embed IO] r + Members [Error AppError, Embed IO] r => RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 - => M.TxRouter ms r - => M.QueryRouter ms r - => HasRouter (M.Api ms) r + => M.AppTxRouter ms r 'DeliverTx + => M.AppTxRouter ms r 'CheckTx + => M.AppQueryRouter ms r + => Q.HasQueryRouter (M.QApi ms) r + => T.HasTxRouter (M.TApi ms) r 'T.DeliverTx + => T.HasTxRouter (M.TApi ms) r 'T.CheckTx => Members CoreEffs core => M.Eval ms core => M.Effs ms core ~ r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 6cb0f3b0..444d6ee7 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -1,142 +1,97 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.Application.Module ( Module(..) - , voidModuleMessages - , defaultTxChecker , Modules(..) - , QueryRouter(Api) - , queryRouter - , RoutingContext(..) - , Router(..) - , TxRouter - , txRouter - , voidRouter + , AppQueryRouter(..) + , appQueryRouter + , AppTxRouter(..) + , appTxRouter , Eval(..) ) where -import Control.Monad.IO.Class (liftIO) -import Data.ByteString (ByteString) import Data.Proxy -import Data.String.Conversions (cs) -import qualified Data.Validation as V -import Data.Void -import GHC.TypeLits (KnownSymbol, Symbol, - symbolVal) -import Polysemy (EffectRow, Embed, Member, - Members, Sem) -import Polysemy.Error (Error) +import GHC.TypeLits (Symbol) +import Polysemy (EffectRow, Members, Sem) import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp ((:&), AppError, BaseApp, - BaseAppEffs, SDKError (..), - throwSDKError) +import Tendermint.SDK.BaseApp ((:&), BaseApp, BaseAppEffs) import qualified Tendermint.SDK.BaseApp.Query as Q import qualified Tendermint.SDK.BaseApp.Transaction as T -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Message (Msg (..), - ValidateMessage (..), - formatMessageSemanticError) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) -import Tendermint.SDK.Types.TxResult (TxResult) - -data Module (name :: Symbol) msg val (api :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: PreRoutedTx msg -> Sem (T.TxEffs :& r) val - , moduleTxChecker :: PreRoutedTx msg -> Sem (T.TxEffs :& r) val - , moduleQueryServer :: Q.RouteT api r + +data Module (name :: Symbol) (h :: *) (q :: *) (s :: EffectRow) (r :: EffectRow) = Module + { moduleTxDeliverer :: T.RouteTx h r 'T.DeliverTx + , moduleTxChecker :: T.RouteTx h r 'T.CheckTx + , moduleQueryServer :: Q.RouteQ q r , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a } -voidModuleMessages :: Module name msg val api s r -> Module name Void Void api s r -voidModuleMessages m = - m { moduleTxDeliverer = voidRouter - , moduleTxChecker = voidRouter - } - -defaultTxChecker - :: Member (Error AppError) r - => ValidateMessage msg - => PreRoutedTx msg - -> Sem r () -defaultTxChecker (PreRoutedTx Tx{txMsg}) = - case validateMessage txMsg of - V.Failure err -> - throwSDKError . MessageValidation . map formatMessageSemanticError $ err - V.Success _ -> pure () - data Modules (ms :: [*]) r where NilModules :: Modules '[] r - (:+) :: Module name msg val api s r -> Modules ms r -> Modules (Module name msg val api s r ': ms) r + (:+) :: Module name h q s r -> Modules ms r -> Modules (Module name h q s r ': ms) r infixr 5 :+ -------------------------------------------------------------------------------- -queryRouter - :: QueryRouter ms r - => Q.HasRouter (Api ms) r +appQueryRouter + :: AppQueryRouter ms r + => Q.HasQueryRouter (QApi ms) r => Modules ms r -> Q.QueryApplication (Sem r) -queryRouter (ms :: Modules ms r) = Q.serve (Proxy :: Proxy (Api ms)) (Proxy :: Proxy r) (routeQuery ms) +appQueryRouter (ms :: Modules ms r) = + Q.serveQueryApplication (Proxy :: Proxy (QApi ms)) (Proxy :: Proxy r) (routeAppQuery ms) -class QueryRouter ms r where - type Api ms :: * - routeQuery :: Modules ms r -> Q.RouteT (Api ms) r +class AppQueryRouter ms r where + type QApi ms :: * + routeAppQuery :: Modules ms r -> Q.RouteQ (QApi ms) r -instance QueryRouter '[Module name msg val api s r] r where - type Api '[Module name msg val api s r] = name :> api - routeQuery (m :+ NilModules) = moduleQueryServer m +instance AppQueryRouter '[Module name h q s r] r where + type QApi '[Module name h q s r] = name :> q + routeAppQuery (m :+ NilModules) = moduleQueryServer m -instance QueryRouter (m' ': ms) r => QueryRouter (Module name msg val api s r ': m' ': ms) r where - type Api (Module name msg val api s r ': m' ': ms) = (name :> api) :<|> Api (m' ': ms) - routeQuery (m :+ rest) = moduleQueryServer m :<|> routeQuery rest +instance AppQueryRouter (m' ': ms) r => AppQueryRouter (Module name h q s r ': m' ': ms) r where + type QApi (Module name h q s r ': m' ': ms) = (name :> q) :<|> QApi (m' ': ms) + routeAppQuery (m :+ rest) = moduleQueryServer m :<|> routeAppQuery rest -------------------------------------------------------------------------------- -data RoutingContext = CheckTxContext | DeliverTxContext - -data Router r msg = Router { runRouter :: PreRoutedTx msg -> Sem r TxResult } - -txRouter - :: TxRouter ms r - => RoutingContext - -> Modules ms r - -> Router r ByteString -txRouter routeContext ms = Router $ \(PreRoutedTx tx) -> - routeTx routeContext ms tx - -class TxRouter ms r where - routeTx :: forall alg. RoutingContext -> Modules ms r -> Tx alg ByteString -> Sem r TxResult - -instance (Member (Error AppError) r) => TxRouter '[] r where - routeTx _ NilModules Tx{txRoute} = - throwSDKError $ UnmatchedRoute txRoute - -instance {-# OVERLAPPING #-} (Member (Error AppError) r, TxRouter ms r, KnownSymbol name) => TxRouter (Module name Void val api s r ': ms) r where - routeTx routeContext (_ :+ rest) tx@Tx{txRoute} - | symbolVal (Proxy :: Proxy name) == cs txRoute = throwSDKError $ UnmatchedRoute txRoute - | otherwise = routeTx routeContext rest tx - -instance {-# OVERLAPPABLE #-} (Member (Error AppError) r, TxRouter ms r, HasCodec msg, HasCodec val, Member (Embed IO) r, KnownSymbol name) => TxRouter (Module name msg val api s r ': ms) r where - routeTx routeContext (m :+ rest) tx@Tx{..} - | symbolVal (Proxy :: Proxy name) == cs txRoute = do - msg <- case decode $ msgData txMsg of - Left err -> throwSDKError $ ParseError err - Right (msg :: msg) -> return msg - let msg' = txMsg {msgData = msg} - tx' = PreRoutedTx $ tx {txMsg = msg'} - ctx <- liftIO $ T.newTransactionContext tx' - T.eval ctx $ case routeContext of - CheckTxContext -> moduleTxChecker m tx' - DeliverTxContext -> moduleTxDeliverer m tx' - | otherwise = routeTx routeContext rest tx - -voidRouter - :: forall a r. - PreRoutedTx Void - -> Sem r a -voidRouter (PreRoutedTx tx) = - let Tx{txMsg} = tx - Msg{msgData} = txMsg - in pure $ absurd msgData +appTxRouter + :: AppTxRouter ms r 'T.DeliverTx + => AppTxRouter ms r 'T.CheckTx + => T.HasTxRouter (TApi ms) r 'T.DeliverTx + => T.HasTxRouter (TApi ms) r 'T.CheckTx + => Modules ms r + -> T.RouteContext + -> T.TransactionApplication (Sem r) +appTxRouter (ms :: Modules ms r) ctx = + case ctx of + T.CheckTx -> + let checkTxP = Proxy :: Proxy 'T.CheckTx + in T.serveTxApplication (Proxy :: Proxy (TApi ms)) (Proxy :: Proxy r) + checkTxP (routeAppTx checkTxP ms) + T.DeliverTx -> + let deliverTxP = Proxy :: Proxy 'T.DeliverTx + in T.serveTxApplication (Proxy :: Proxy (TApi ms)) (Proxy :: Proxy r) + deliverTxP (routeAppTx deliverTxP ms) + +class AppTxRouter ms r (c :: T.RouteContext) where + type TApi ms :: * + routeAppTx :: Proxy c -> Modules ms r -> T.RouteTx (TApi ms) r c + +instance AppTxRouter '[Module name h q s r] r 'T.CheckTx where + type TApi '[Module name h q s r] = name :> h + routeAppTx _ (m :+ NilModules) = moduleTxChecker m + +instance AppTxRouter (m' ': ms) r 'T.CheckTx => AppTxRouter (Module name h q s r ': m' ': ms) r 'T.CheckTx where + type TApi (Module name h q s r ': m' ': ms) = (name :> h) :<|> TApi (m' ': ms) + routeAppTx pc (m :+ rest) = moduleTxChecker m :<|> routeAppTx pc rest + +instance AppTxRouter '[Module name h q s r] r 'T.DeliverTx where + type TApi '[Module name h q s r] = name :> h + routeAppTx _ (m :+ NilModules) = moduleTxDeliverer m + +instance AppTxRouter (m' ': ms) r 'T.DeliverTx => AppTxRouter (Module name h q s r ': m' ': ms) r 'T.DeliverTx where + type TApi (Module name h q s r ': m' ': ms) = (name :> h) :<|> TApi (m' ': ms) + routeAppTx pc (m :+ rest) = moduleTxDeliverer m :<|> routeAppTx pc rest -------------------------------------------------------------------------------- @@ -146,10 +101,10 @@ class Eval ms core where -> forall a. Sem (Effs ms core) a -> Sem (BaseApp core) a -instance Eval '[Module name msg val api s r] core where - type Effs '[Module name msg val api s r] core = s :& BaseApp core +instance Eval '[Module name h q s r] core where + type Effs '[Module name h q s r] core = s :& BaseApp core eval (m :+ NilModules) = moduleEval m -instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name msg val api s r ': m' ': ms) core where - type Effs (Module name msg val api s r ': m' ': ms) core = s :& (Effs (m': ms)) core +instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name h q s r ': m' ': ms) core where + type Effs (Module name h q s r ': m' ': ms) core = s :& (Effs (m': ms)) core eval (m :+ rest) = eval rest . moduleEval m diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index c25795ca..282cf7bb 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -66,15 +66,29 @@ module Tendermint.SDK.BaseApp , HistogramName(..) -- * Transaction + , TransactionApplication + , RoutingTx(..) + , RouteContext(..) + , RouteTx + , Return + , (:~>) + , TypedMessage , TxEffs + , EmptyTxServer + , emptyTxServer + , serveTxApplication + , DefaultCheckTx(..) -- * Query , Queryable(..) , FromQueryData(..) , QueryApi - , RouteT + , RouteQ , QueryResult(..) , storeQueryHandlers + , serveQueryApplication + , EmptyQueryServer + , emptyQueryServer ) where diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index c643f77a..e89659bc 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -23,7 +23,7 @@ data AppError = AppError { appErrorCode :: Word32 , appErrorCodespace :: Text , appErrorMessage :: Text - } deriving Show + } deriving (Eq, Show) instance Exception AppError diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs index 18cd132b..b06dfcf7 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs @@ -1,8 +1,9 @@ module Tendermint.SDK.BaseApp.Query - ( serve - , HasRouter(..) + ( serveQueryApplication + , HasQueryRouter(..) , StoreQueryHandlers(..) , module Tendermint.SDK.BaseApp.Query.Types + , emptyQueryServer ) where import Control.Lens ((&), (.~)) @@ -12,7 +13,8 @@ import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy (Sem) import Tendermint.SDK.BaseApp.Errors (makeAppError, queryAppError) -import Tendermint.SDK.BaseApp.Query.Class (HasRouter (..)) +import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..), + emptyQueryServer) import Tendermint.SDK.BaseApp.Query.Store (StoreQueryHandlers (..)) import Tendermint.SDK.BaseApp.Query.Types import Tendermint.SDK.BaseApp.Router.Delayed (emptyDelayed) @@ -20,14 +22,14 @@ import Tendermint.SDK.BaseApp.Router.Router (runRouter) import Tendermint.SDK.BaseApp.Router.Types (Application, RouteResult (..)) -serve - :: HasRouter layout r +serveQueryApplication + :: HasQueryRouter layout r => Proxy layout -> Proxy r - -> RouteT layout r + -> RouteQ layout r -> QueryApplication (Sem r) -serve pl pr server = - toQueryApplication (runRouter (route pl pr (emptyDelayed (Route server))) ()) +serveQueryApplication pl pr server = + toQueryApplication (runRouter (routeQ pl pr (emptyDelayed (Route server))) ()) toQueryApplication :: Application (Sem r) QueryRequest Response.Query diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs deleted file mode 100644 index 697f9ff6..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Class.hs +++ /dev/null @@ -1,125 +0,0 @@ -{-# LANGUAGE UndecidableInstances #-} -module Tendermint.SDK.BaseApp.Query.Class where - -import Control.Lens ((&), (.~)) -import Control.Monad (join) -import Data.ByteArray.Base64String (fromBytes) -import Data.Default.Class (def) -import Data.Proxy -import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.TypeLits (KnownSymbol, symbolVal) -import Network.ABCI.Types.Messages.Response as Response -import Network.HTTP.Types.URI (QueryText, - parseQueryText) -import Polysemy (Sem) -import Servant.API -import Servant.API.Modifiers (FoldLenient, - FoldRequired, - RequestArgument, - unfoldRequestArgument) -import Tendermint.SDK.BaseApp.Query.Types (FromQueryData (..), Leaf, - QA, QueryArgs (..), - QueryRequest (..), - QueryResult (..)) -import qualified Tendermint.SDK.BaseApp.Router as R -import Tendermint.SDK.Codec (HasCodec (..)) -import Web.HttpApiData (FromHttpApiData (..), - parseUrlPieceMaybe) - - --------------------------------------------------------------------------------- - --- | This class is used to construct a router given a 'layout' type. The layout --- | is constructed using the combinators that appear in the instances here, no other --- | Servant combinators are recognized. -class HasRouter layout r where - -- | A route handler. - type RouteT layout r :: * - -- | Transform a route handler into a 'Router'. - route :: Proxy layout -> Proxy r -> R.Delayed (Sem r) env QueryRequest (RouteT layout r) - -> R.Router env r QueryRequest Response.Query - -instance (HasRouter a r, HasRouter b r) => HasRouter (a :<|> b) r where - type RouteT (a :<|> b) r = RouteT a r :<|> RouteT b r - - route _ pr server = R.choice (route pa pr ((\ (a :<|> _) -> a) <$> server)) - (route pb pr ((\ (_ :<|> b) -> b) <$> server)) - where pa = Proxy :: Proxy a - pb = Proxy :: Proxy b - -instance (HasRouter sublayout r, KnownSymbol path) => HasRouter (path :> sublayout) r where - - type RouteT (path :> sublayout) r = RouteT sublayout r - - route _ pr subserver = - R.pathRouter (cs (symbolVal proxyPath)) (route (Proxy :: Proxy sublayout) pr subserver) - where proxyPath = Proxy :: Proxy path - -instance ( HasRouter sublayout r, KnownSymbol sym, FromHttpApiData a - , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods) - ) => HasRouter (QueryParam' mods sym a :> sublayout) r where - - type RouteT (QueryParam' mods sym a :> sublayout) r = RequestArgument mods a -> RouteT sublayout r - - route _ pr subserver = - let querytext :: QueryRequest -> Network.HTTP.Types.URI.QueryText - querytext q = parseQueryText . cs $ queryRequestParamString q - paramname = cs $ symbolVal (Proxy :: Proxy sym) - parseParam q = unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev - where - mev :: Maybe (Either Text a) - mev = fmap parseQueryParam $ join $ lookup paramname $ querytext q - errReq = R.delayedFail $ R.InvalidRequest ("Query parameter " <> cs paramname <> " is required.") - errSt e = R.delayedFail $ R.InvalidRequest ("Error parsing query param " <> cs paramname <> " " <> cs e <> ".") - delayed = R.addParameter subserver $ R.withRequest parseParam - in route (Proxy :: Proxy sublayout) pr delayed - -instance (FromHttpApiData a, HasRouter sublayout r) => HasRouter (Capture' mods capture a :> sublayout) r where - - type RouteT (Capture' mods capture a :> sublayout) r = a -> RouteT sublayout r - - route _ pr subserver = - R.CaptureRouter $ - route (Proxy :: Proxy sublayout) - pr - (R.addCapture subserver $ \ txt -> case parseUrlPieceMaybe txt of - Nothing -> R.delayedFail R.PathNotFound - Just v -> return v - ) - -instance HasCodec a => HasRouter (Leaf a) r where - - type RouteT (Leaf a) r = Sem r (QueryResult a) - route _ _ = methodRouter - -instance (FromQueryData a, HasRouter sublayout r) - => HasRouter (QA a :> sublayout) r where - - type RouteT (QA a :> sublayout) r = QueryArgs a -> RouteT sublayout r - - route _ pr subserver = - let parseQueryArgs QueryRequest{..} = case fromQueryData queryRequestData of - Left e -> R.delayedFail $ R.InvalidRequest ("Error parsing query data, " <> cs e <> ".") - Right a -> pure QueryArgs - { queryArgsData = a - , queryArgsHeight = queryRequestHeight - , queryArgsProve = queryRequestProve - } - delayed = R.addBody subserver $ R.withRequest parseQueryArgs - in route (Proxy :: Proxy sublayout) pr delayed - --------------------------------------------------------------------------------- - -methodRouter - :: HasCodec b - => R.Delayed (Sem r) env req (Sem r (QueryResult b)) - -> R.Router env r req Response.Query -methodRouter action = R.leafRouter route' - where - route' env query = R.runAction action env query $ \QueryResult{..} -> - R.Route $ def & Response._queryIndex .~ queryResultIndex - & Response._queryKey .~ queryResultKey - & Response._queryValue .~ fromBytes (encode queryResultData) - & Response._queryProof .~ queryResultProof - & Response._queryHeight .~ queryResultHeight diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs new file mode 100644 index 00000000..917f35de --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs @@ -0,0 +1,139 @@ +{-# LANGUAGE UndecidableInstances #-} +module Tendermint.SDK.BaseApp.Query.Router + ( HasQueryRouter(..) + , emptyQueryServer + , methodRouter + ) where + +import Control.Lens ((&), (.~)) +import Control.Monad (join) +import Data.ByteArray.Base64String (fromBytes) +import Data.Default.Class (def) +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) +import Network.ABCI.Types.Messages.Response as Response +import Network.HTTP.Types.URI (QueryText, + parseQueryText) +import Polysemy (Sem) +import Servant.API +import Servant.API.Modifiers (FoldLenient, + FoldRequired, + RequestArgument, + unfoldRequestArgument) +import Tendermint.SDK.BaseApp.Query.Types (EmptyQueryServer (..), + FromQueryData (..), + Leaf, QA, + QueryArgs (..), + QueryRequest (..), + QueryResult (..)) +import qualified Tendermint.SDK.BaseApp.Router as R +import Tendermint.SDK.Codec (HasCodec (..)) +import Web.HttpApiData (FromHttpApiData (..), + parseUrlPieceMaybe) + + +-------------------------------------------------------------------------------- + +-- | This class is used to construct a router given a 'layout' type. The layout +-- | is constructed using the combinators that appear in the instances here, no other +-- | Servant combinators are recognized. +class HasQueryRouter layout r where + -- | A routeQ handler. + type RouteQ layout r :: * + -- | Transform a routeQ handler into a 'Router'. + routeQ :: Proxy layout -> Proxy r -> R.Delayed (Sem r) env QueryRequest (RouteQ layout r) + -> R.Router env r QueryRequest Response.Query + +instance (HasQueryRouter a r, HasQueryRouter b r) => HasQueryRouter (a :<|> b) r where + type RouteQ (a :<|> b) r = RouteQ a r :<|> RouteQ b r + + routeQ _ pr server = R.choice (routeQ pa pr ((\ (a :<|> _) -> a) <$> server)) + (routeQ pb pr ((\ (_ :<|> b) -> b) <$> server)) + where pa = Proxy :: Proxy a + pb = Proxy :: Proxy b + +instance (HasQueryRouter sublayout r, KnownSymbol path) => HasQueryRouter (path :> sublayout) r where + + type RouteQ (path :> sublayout) r = RouteQ sublayout r + + routeQ _ pr subserver = + R.pathRouter (cs (symbolVal proxyPath)) (routeQ (Proxy :: Proxy sublayout) pr subserver) + where proxyPath = Proxy :: Proxy path + +instance ( HasQueryRouter sublayout r, KnownSymbol sym, FromHttpApiData a + , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods) + ) => HasQueryRouter (QueryParam' mods sym a :> sublayout) r where + + type RouteQ (QueryParam' mods sym a :> sublayout) r = RequestArgument mods a -> RouteQ sublayout r + + routeQ _ pr subserver = + let querytext :: QueryRequest -> Network.HTTP.Types.URI.QueryText + querytext q = parseQueryText . cs $ queryRequestParamString q + paramname = cs $ symbolVal (Proxy :: Proxy sym) + parseParam q = unfoldRequestArgument (Proxy :: Proxy mods) errReq errSt mev + where + mev :: Maybe (Either Text a) + mev = fmap parseQueryParam $ join $ lookup paramname $ querytext q + errReq = R.delayedFail $ R.InvalidRequest ("Query parameter " <> cs paramname <> " is required.") + errSt e = R.delayedFail $ R.InvalidRequest ("Error parsing query param " <> cs paramname <> " " <> cs e <> ".") + delayed = R.addParameter subserver $ R.withRequest parseParam + in routeQ (Proxy :: Proxy sublayout) pr delayed + +instance (FromHttpApiData a, HasQueryRouter sublayout r) => HasQueryRouter (Capture' mods capture a :> sublayout) r where + + type RouteQ (Capture' mods capture a :> sublayout) r = a -> RouteQ sublayout r + + routeQ _ pr subserver = + R.CaptureRouter $ + routeQ (Proxy :: Proxy sublayout) + pr + (R.addCapture subserver $ \ txt -> case parseUrlPieceMaybe txt of + Nothing -> R.delayedFail R.PathNotFound + Just v -> return v + ) + +instance HasCodec a => HasQueryRouter (Leaf a) r where + + type RouteQ (Leaf a) r = Sem r (QueryResult a) + routeQ _ _ = methodRouter + +instance (FromQueryData a, HasQueryRouter sublayout r) + => HasQueryRouter (QA a :> sublayout) r where + + type RouteQ (QA a :> sublayout) r = QueryArgs a -> RouteQ sublayout r + + routeQ _ pr subserver = + let parseQueryArgs QueryRequest{..} = case fromQueryData queryRequestData of + Left e -> R.delayedFail $ R.InvalidRequest ("Error parsing query data, " <> cs e <> ".") + Right a -> pure QueryArgs + { queryArgsData = a + , queryArgsHeight = queryRequestHeight + , queryArgsProve = queryRequestProve + } + delayed = R.addBody subserver $ R.withRequest parseQueryArgs + in routeQ (Proxy :: Proxy sublayout) pr delayed + +emptyQueryServer :: RouteQ EmptyQueryServer r +emptyQueryServer = EmptyQueryServer + +instance HasQueryRouter EmptyQueryServer r where + type RouteQ EmptyQueryServer r = EmptyQueryServer + routeQ _ _ _ = R.StaticRouter mempty mempty + +-------------------------------------------------------------------------------- + +methodRouter + :: HasCodec b + => R.Delayed (Sem r) env req (Sem r (QueryResult b)) + -> R.Router env r req Response.Query +methodRouter action = R.leafRouter route' + where + route' env query = R.runAction action env query $ \QueryResult{..} -> + R.Route $ def & Response._queryIndex .~ WrappedVal queryResultIndex + & Response._queryKey .~ queryResultKey + & Response._queryValue .~ fromBytes (encode queryResultData) + & Response._queryProof .~ queryResultProof + & Response._queryHeight .~ WrappedVal queryResultHeight diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index 3b76b751..a3e39af1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -2,32 +2,33 @@ module Tendermint.SDK.BaseApp.Query.Store where -import Control.Lens (to, (^.)) -import Data.ByteArray.Base64String (fromBytes) +import Control.Lens (to, (^.)) +import Data.ByteArray.Base64String (fromBytes) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, Symbol, - symbolVal) -import Polysemy (Members, Sem) -import Polysemy.Error (Error, throw) -import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) -import Tendermint.SDK.BaseApp.Query.Class -import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), - QueryResult (..), - Queryable (..)) -import Tendermint.SDK.BaseApp.Router (RouterError (..), - pathRouter) -import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), - RawStore, StoreKey, get) -import Tendermint.SDK.Codec (HasCodec) +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, Symbol, + symbolVal) +import Polysemy (Members, Sem) +import Polysemy.Error (Error, throw) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) +import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..), + methodRouter) +import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), + QueryResult (..), + Queryable (..)) +import Tendermint.SDK.BaseApp.Router (RouterError (..), + pathRouter) +import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), + RawStore, StoreKey, get) +import Tendermint.SDK.Codec (HasCodec) data StoreLeaf a -instance (Queryable a, KnownSymbol (Name a)) => HasRouter (StoreLeaf a) r where +instance (Queryable a, KnownSymbol (Name a)) => HasQueryRouter (StoreLeaf a) r where - type RouteT (StoreLeaf a) r = Sem r (QueryResult a) - route _ _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter + type RouteQ (StoreLeaf a) r = Sem r (QueryResult a) + routeQ _ _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter where proxyPath = Proxy :: Proxy (Name a) class StoreQueryHandler a (ns :: Symbol) h where @@ -56,7 +57,7 @@ instance class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) r where type QueryApi kvs :: * - storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy r -> RouteT (QueryApi kvs) r + storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy r -> RouteQ (QueryApi kvs) r instance ( IsKey k ns diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs index 96dfa1f1..6e2cf6d1 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs @@ -60,20 +60,20 @@ data QueryArgs a = QueryArgs } deriving Functor -- wrap data with default query fields -defaultQueryWithData :: a -> QueryArgs a -defaultQueryWithData x = QueryArgs - { queryArgsData = x - , queryArgsHeight = 0 +defaultQueryArgs :: QueryArgs () +defaultQueryArgs = QueryArgs + { queryArgsData = () + , queryArgsHeight = -1 , queryArgsProve = False } data QueryResult a = QueryResult { queryResultData :: a - , queryResultIndex :: WrappedVal Int64 + , queryResultIndex :: Int64 , queryResultKey :: Base64String , queryResultProof :: Maybe Proof - , queryResultHeight :: WrappedVal Int64 - } deriving Functor + , queryResultHeight :: Int64 + } deriving (Eq, Show, Functor) -------------------------------------------------------------------------------- @@ -93,3 +93,5 @@ class FromQueryData a where fromQueryData bs = Right (toBytes bs ^. from rawKey) instance FromQueryData Address + +data EmptyQueryServer = EmptyQueryServer diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs index 6e5008bf..be9a9507 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs @@ -1,74 +1,59 @@ module Tendermint.SDK.BaseApp.Transaction - ( TxEffs - , TransactionContext(..) - , newTransactionContext - , eval + ( serveTxApplication + , serveDefaultTxChecker + -- * Re-Exports + , module Tendermint.SDK.BaseApp.Transaction.Types + , HasTxRouter(..) + , emptyTxServer + , DefaultCheckTx(..) + , TxEffs ) where -import Control.Lens ((&), (.~)) -import Control.Monad.IO.Class (liftIO) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Default.Class (def) -import Data.IORef (IORef, newIORef, readIORef) -import Polysemy (Embed, Member, Sem, - raiseUnder) -import Polysemy.Error (Error, runError) -import Polysemy.Output (Output, - runOutputMonoidAssocR) -import Polysemy.State (State, runStateIORef) -import Tendermint.SDK.BaseApp.Errors (AppError, txResultAppError) -import qualified Tendermint.SDK.BaseApp.Events as E -import qualified Tendermint.SDK.BaseApp.Gas as G -import Tendermint.SDK.Codec (HasCodec (encode)) -import Tendermint.SDK.Types.Effects ((:&)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) -import Tendermint.SDK.Types.TxResult (TxResult, txResultData, - txResultEvents, - txResultGasUsed, - txResultGasWanted) +import Control.Lens ((&), (.~)) +import Data.Proxy +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Errors (makeAppError, + txResultAppError) +import Tendermint.SDK.BaseApp.Router (Application, + RouteResult (..), + emptyDelayed, + runRouter) +import Tendermint.SDK.BaseApp.Transaction.Checker (DefaultCheckTx (..)) +import Tendermint.SDK.BaseApp.Transaction.Effect (TxEffs) +import Tendermint.SDK.BaseApp.Transaction.Router +import Tendermint.SDK.BaseApp.Transaction.Types +import Tendermint.SDK.Types.TxResult (TxResult) -type TxEffs = - [ Output E.Event - , G.GasMeter - , Error AppError - ] +import Data.ByteString (ByteString) +import Data.Default.Class (def) -data TransactionContext = TransactionContext - { gas :: IORef G.GasAmount - } +serveTxApplication + :: HasTxRouter layout r c + => Proxy layout + -> Proxy r + -> Proxy (c :: RouteContext) + -> RouteTx layout r c + -> TransactionApplication (Sem r) +serveTxApplication pl pr pc server = + toTxApplication (runRouter (routeTx pl pr pc (emptyDelayed (Route server))) ()) -newTransactionContext - :: PreRoutedTx msg - -> IO TransactionContext -newTransactionContext (PreRoutedTx Tx{txGas}) = do - initialGas <- newIORef $ G.GasAmount txGas - pure TransactionContext - { gas = initialGas - } +toTxApplication + :: Application (Sem r) (RoutingTx ByteString) TxResult + -> TransactionApplication (Sem r) +toTxApplication ra tx = do + res <- ra tx + case res of + Fail e -> pure $ def & txResultAppError .~ makeAppError e + FailFatal e -> pure $ def & txResultAppError .~ makeAppError e + Route a -> pure a -eval - :: forall r a. - HasCodec a - => Member (Embed IO) r - => TransactionContext - -> Sem (TxEffs :& r) a - -> Sem r TxResult -eval TransactionContext{..} action = do - initialGas <- liftIO $ readIORef gas - eRes <- - runError . - runStateIORef gas . - G.eval . - raiseUnder @(State G.GasAmount) $ - runOutputMonoidAssocR (pure @[]) action - gasRemaining <- liftIO $ readIORef gas - let gasUsed = initialGas - gasRemaining - baseResponse = - def & txResultGasWanted .~ G.unGasAmount initialGas - & txResultGasUsed .~ G.unGasAmount gasUsed - return $ case eRes of - Left e -> - baseResponse & txResultAppError .~ e - Right (events, a) -> - baseResponse & txResultEvents .~ events - & txResultData .~ Base64.fromBytes (encode a) + +serveDefaultTxChecker + :: HasTxRouter layout r 'CheckTx + => DefaultCheckTx layout r + => RouteTx layout r 'CheckTx ~ DefaultCheckTxT layout r + => Proxy layout + -> Proxy r + -> TransactionApplication (Sem r) +serveDefaultTxChecker pl pr = + serveTxApplication pl pr (Proxy :: Proxy 'CheckTx) (defaultCheckTx pl pr) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs new file mode 100644 index 00000000..f1859567 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs @@ -0,0 +1,51 @@ +module Tendermint.SDK.BaseApp.Transaction.Checker + ( DefaultCheckTx(..) + ) where + +import Data.Proxy +import qualified Data.Validation as V +import Polysemy (EffectRow, Member, + Sem) +import Polysemy.Error (Error) +import Servant.API ((:<|>) (..), (:>)) +import Tendermint.SDK.BaseApp.Errors (AppError, + SDKError (..), + throwSDKError) +import Tendermint.SDK.BaseApp.Transaction.Types +import Tendermint.SDK.Types.Message (ValidateMessage (..), formatMessageSemanticError) + +defaultCheckTxHandler + :: Member (Error AppError) r + => ValidateMessage msg + => RoutingTx msg + -> Sem r () +defaultCheckTxHandler(RoutingTx Tx{txMsg}) = + case validateMessage txMsg of + V.Failure err -> + throwSDKError . MessageValidation . map formatMessageSemanticError $ err + V.Success _ -> pure () + +class DefaultCheckTx api (r :: EffectRow) where + type DefaultCheckTxT api r :: * + defaultCheckTx :: Proxy api -> Proxy r -> DefaultCheckTxT api r + +instance (DefaultCheckTx a r, DefaultCheckTx b r) => DefaultCheckTx (a :<|> b) r where + type DefaultCheckTxT (a :<|> b) r = DefaultCheckTxT a r :<|> DefaultCheckTxT b r + + defaultCheckTx _ pr = + defaultCheckTx (Proxy :: Proxy a) pr :<|> defaultCheckTx (Proxy :: Proxy b) pr + +instance DefaultCheckTx rest r => DefaultCheckTx (path :> rest) r where + type DefaultCheckTxT (path :> rest) r = DefaultCheckTxT rest r + + defaultCheckTx _ = defaultCheckTx (Proxy :: Proxy rest) + +instance (Member (Error AppError) r, ValidateMessage msg) => DefaultCheckTx (TypedMessage msg :~> Return a) r where + type DefaultCheckTxT (TypedMessage msg :~> Return a) r = RoutingTx msg -> Sem r () + + defaultCheckTx _ _ = defaultCheckTxHandler + +instance DefaultCheckTx EmptyTxServer r where + type DefaultCheckTxT EmptyTxServer r = EmptyTxServer + + defaultCheckTx _ _ = EmptyTxServer diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs new file mode 100644 index 00000000..0f068ba7 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs @@ -0,0 +1,78 @@ +module Tendermint.SDK.BaseApp.Transaction.Effect + ( TxEffs + , TransactionContext(..) + , newTransactionContext + , eval + ) where + +import Control.Lens ((&), (.~)) +import Control.Monad.IO.Class (liftIO) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Default.Class (def) +import Data.IORef (IORef, newIORef, + readIORef) +import Polysemy (Embed, Member, Sem, + raiseUnder) +import Polysemy.Error (Error, runError) +import Polysemy.Output (Output, + runOutputMonoidAssocR) +import Polysemy.State (State, runStateIORef) +import Tendermint.SDK.BaseApp.Errors (AppError, + txResultAppError) +import qualified Tendermint.SDK.BaseApp.Events as E +import qualified Tendermint.SDK.BaseApp.Gas as G +import Tendermint.SDK.BaseApp.Transaction.Types (RoutingTx (..), + Tx (..)) +import Tendermint.SDK.Codec (HasCodec (encode)) +import Tendermint.SDK.Types.Effects ((:&)) +import Tendermint.SDK.Types.TxResult (TxResult, + txResultData, + txResultEvents, + txResultGasUsed, + txResultGasWanted) + +type TxEffs = + [ Output E.Event + , G.GasMeter + , Error AppError + ] + +data TransactionContext = TransactionContext + { gas :: IORef G.GasAmount + } + +newTransactionContext + :: RoutingTx msg + -> IO TransactionContext +newTransactionContext (RoutingTx Tx{txGas}) = do + initialGas <- newIORef $ G.GasAmount txGas + pure TransactionContext + { gas = initialGas + } + +eval + :: forall r a. + HasCodec a + => Member (Embed IO) r + => TransactionContext + -> Sem (TxEffs :& r) a + -> Sem r TxResult +eval TransactionContext{..} action = do + initialGas <- liftIO $ readIORef gas + eRes <- + runError . + runStateIORef gas . + G.eval . + raiseUnder @(State G.GasAmount) $ + runOutputMonoidAssocR (pure @[]) action + gasRemaining <- liftIO $ readIORef gas + let gasUsed = initialGas - gasRemaining + baseResponse = + def & txResultGasWanted .~ G.unGasAmount initialGas + & txResultGasUsed .~ G.unGasAmount gasUsed + return $ case eRes of + Left e -> + baseResponse & txResultAppError .~ e + Right (events, a) -> + baseResponse & txResultEvents .~ events + & txResultData .~ Base64.fromBytes (encode a) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs new file mode 100644 index 00000000..5bf84a80 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs @@ -0,0 +1,11 @@ +module Tendermint.SDK.BaseApp.Transaction.Modifier + ( OnCheck(..) + , OnCheckReturn + ) where + +import Tendermint.SDK.BaseApp.Transaction.Types + +type family OnCheckReturn (ctx :: RouteContext) (oc :: OnCheck) a where + OnCheckReturn 'CheckTx 'OnCheckEval a = a + OnCheckReturn 'CheckTx 'OnCheckUnit a = () + OnCheckReturn 'DeliverTx _ a = a diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs new file mode 100644 index 00000000..e91a00a0 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE UndecidableInstances #-} +module Tendermint.SDK.BaseApp.Transaction.Router + ( HasTxRouter(..) + , emptyTxServer + ) where + +import Control.Monad.IO.Class (liftIO) +import Data.ByteString (ByteString) +import Data.Proxy +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, + symbolVal) +import Polysemy (Embed, Member, + Sem) +import Servant.API +import qualified Tendermint.SDK.BaseApp.Router as R +import Tendermint.SDK.BaseApp.Transaction.Effect (TxEffs, eval, newTransactionContext) +import Tendermint.SDK.BaseApp.Transaction.Modifier +import Tendermint.SDK.BaseApp.Transaction.Types +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Effects ((:&)) +import Tendermint.SDK.Types.Message (HasMessageType (..), + Msg (..)) +import Tendermint.SDK.Types.TxResult (TxResult) + +-------------------------------------------------------------------------------- + +class HasTxRouter layout r (c :: RouteContext) where + type RouteTx layout r c :: * + routeTx + :: Proxy layout + -> Proxy r + -> Proxy c + -> R.Delayed (Sem r) env (RoutingTx ByteString) (RouteTx layout r c) + -> R.Router env r (RoutingTx ByteString) TxResult + +instance (HasTxRouter a r c, HasTxRouter b r c) => HasTxRouter (a :<|> b) r c where + type RouteTx (a :<|> b) r c = RouteTx a r c :<|> RouteTx b r c + + routeTx _ pr pc server = + R.choice (routeTx pa pr pc ((\ (a :<|> _) -> a) <$> server)) + (routeTx pb pr pc ((\ (_ :<|> b) -> b) <$> server)) + where pa = Proxy :: Proxy a + pb = Proxy :: Proxy b + +instance (HasTxRouter sublayout r c, KnownSymbol path) => HasTxRouter (path :> sublayout) r c where + + type RouteTx (path :> sublayout) r c = RouteTx sublayout r c + + routeTx _ pr pc subserver = + R.pathRouter (cs (symbolVal proxyPath)) (routeTx (Proxy :: Proxy sublayout) pr pc subserver) + where proxyPath = Proxy :: Proxy path + +methodRouter + :: HasCodec a + => Member (Embed IO) r + => R.Delayed (Sem r) env (RoutingTx msg) (Sem (TxEffs :& r) a) + -> R.Router env r (RoutingTx msg) TxResult +methodRouter action = R.leafRouter route' + where + route' env tx = do + ctx <- liftIO $ newTransactionContext tx + let action' = eval ctx <$> action + R.runAction action' env tx R.Route + +instance ( HasMessageType msg, HasCodec msg, HasCodec (OnCheckReturn c oc a), Member (Embed IO) r) => HasTxRouter (TypedMessage msg :~> Return' oc a) r c where + + type RouteTx (TypedMessage msg :~> Return' oc a) r c = RoutingTx msg -> Sem (TxEffs :& r) (OnCheckReturn c oc a) + + routeTx _ _ _ subserver = + let f (RoutingTx tx@Tx{txMsg}) = + if msgType txMsg == mt + then case decode $ msgData txMsg of + Left e -> R.delayedFail $ + R.InvalidRequest ("Failed to parse message of type " <> mt <> ": " <> e <> ".") + Right a -> pure . RoutingTx $ tx {txMsg = txMsg {msgData = a}} + else R.delayedFail R.PathNotFound + in methodRouter $ + R.addBody subserver $ R.withRequest f + where mt = messageType (Proxy :: Proxy msg) + +emptyTxServer :: RouteTx EmptyTxServer r c +emptyTxServer = EmptyTxServer + +instance HasTxRouter EmptyTxServer r c where + type RouteTx EmptyTxServer r c = EmptyTxServer + routeTx _ _ _ _ = R.StaticRouter mempty mempty diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs new file mode 100644 index 00000000..160e2308 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs @@ -0,0 +1,37 @@ +module Tendermint.SDK.BaseApp.Transaction.Types + ( module Tendermint.SDK.BaseApp.Transaction.Types + -- * Re-Exports + , Tx(..) + ) where + +import Control.Lens (lens) +import Data.ByteString (ByteString) +import Tendermint.SDK.BaseApp.Router (HasPath (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) +import Tendermint.SDK.Types.TxResult (TxResult) + +data msg :~> a + +data TypedMessage msg + +data OnCheck = OnCheckEval | OnCheckUnit + +data Return' (c :: OnCheck) a + +type Return = Return' 'OnCheckUnit + +data RouteContext = CheckTx | DeliverTx deriving (Eq, Show) + +type TransactionApplication m = RoutingTx ByteString -> m TxResult + +data EmptyTxServer = EmptyTxServer + +data RoutingTx msg where + RoutingTx :: Tx alg msg -> RoutingTx msg + +instance Functor RoutingTx where + fmap f (RoutingTx tx) = RoutingTx $ fmap f tx + +instance HasPath (RoutingTx msg) where + path = lens (\(RoutingTx tx) -> txRoute tx) + (\(RoutingTx tx) r -> RoutingTx tx {txRoute = r}) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs b/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs index 40461f20..0a1e779a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Crypto.hs @@ -39,6 +39,7 @@ class SignatureSchema alg where makePubKey :: Proxy alg -> B.ByteString -> Maybe (PubKey alg) makeSignature :: Proxy alg -> B.ByteString -> Maybe (Signature alg) + derivePubKey :: Proxy alg -> PrivateKey alg -> PubKey alg addressFromPubKey :: Proxy alg -> PubKey alg -> Address -- | Class allowing for signing and recovering signatures for messages. @@ -47,7 +48,7 @@ class SignatureSchema alg => RecoverableSignatureSchema alg where signRecoverableMessage :: Proxy alg -> PrivateKey alg -> Message alg -> RecoverableSignature alg recover :: Proxy alg -> RecoverableSignature alg -> Message alg -> Maybe (PubKey alg) - + serializeRecoverableSignature :: Proxy alg -> RecoverableSignature alg -> B.ByteString makeRecoverableSignature :: Proxy alg -> B.ByteString -> Maybe (RecoverableSignature alg) data Secp256k1 @@ -69,6 +70,7 @@ instance SignatureSchema Secp256k1 where makePubKey _ = Secp256k1.importPubKey makeSignature _ = Secp256k1.importSig -- For lack of a better idea, we're just going to use the Ethereum style here + derivePubKey _ = Secp256k1.derivePubKey addressFromPubKey _ = addressFromBytes . B.drop 12 . convert . hashWith Keccak_256 . Secp256k1.exportPubKey False @@ -77,6 +79,11 @@ instance RecoverableSignatureSchema Secp256k1 where signRecoverableMessage _ priv dig = Secp256k1.signRecMsg priv (msgFromSHA256 dig) recover _ sig dig = Secp256k1.recover sig (msgFromSHA256 dig) + serializeRecoverableSignature _ sig = + let csr = Secp256k1.exportCompactRecSig sig + in Short.fromShort (Secp256k1.getCompactRecSigR csr) <> + Short.fromShort (Secp256k1.getCompactRecSigS csr) <> + B.pack [Secp256k1.getCompactRecSigV csr] makeRecoverableSignature _ bs = let (r,rest) = B.splitAt 32 bs (s,v) = B.splitAt 32 rest diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 0cb7d980..060e901f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -16,22 +16,22 @@ module Tendermint.SDK.Modules.Auth , module Tendermint.SDK.Modules.Auth.Types ) where -import Data.Void import Polysemy (Members) -import Tendermint.SDK.Application.Module (Module (..), voidRouter) -import Tendermint.SDK.BaseApp (BaseAppEffs) +import Tendermint.SDK.Application.Module (Module (..)) +import Tendermint.SDK.BaseApp (BaseAppEffs, EmptyTxServer, + emptyTxServer) import Tendermint.SDK.Modules.Auth.Keeper import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types -type AuthM r = Module AuthModule Void Void Api AuthEffs r +type AuthM r = Module AuthModule EmptyTxServer Api AuthEffs r authModule :: Members BaseAppEffs r => AuthM r authModule = Module - { moduleTxDeliverer = voidRouter - , moduleTxChecker = voidRouter + { moduleTxDeliverer = emptyTxServer + , moduleTxChecker = emptyTxServer , moduleQueryServer = server , moduleEval = eval } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs index 197bcf35..8b40e4d9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs @@ -21,6 +21,6 @@ type Api = BaseApp.QueryApi AuthContents server :: Members [BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api r + => BaseApp.RouteQ Api r server = BaseApp.storeQueryHandlers (Proxy :: Proxy AuthContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs index a80127f1..bdd203b5 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Message.hs @@ -1,21 +1,56 @@ module Tendermint.SDK.Types.Message where -import Control.Lens (( # )) -import Data.String.Conversions (cs) -import Data.Text (Text) -import qualified Data.Validation as V -import qualified Proto3.Wire.Decode as Wire -import Tendermint.SDK.Types.Address (Address) +import Control.Lens (Wrapped (..), from, iso, view, + ( # ), (&), (.~), (^.)) +import Data.Bifunctor (bimap) +import Data.ByteString (ByteString) +import qualified Data.ProtoLens as P +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Data.Validation as V +import qualified Proto.Types.Transaction as T +import qualified Proto.Types.Transaction_Fields as T +import qualified Proto3.Wire.Decode as Wire +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) -- | The basic message format embedded in any transaction. data Msg msg = Msg { msgAuthor :: Address , msgData :: msg + , msgType :: Text } instance Functor Msg where fmap f msg@Msg{msgData} = msg {msgData = f msgData} +class HasMessageType msg where + messageType :: Proxy msg -> Text + +data TypedMessage = TypedMessage + { typedMsgData :: ByteString + , typedMsgType :: Text + } + +instance Wrapped TypedMessage where + type Unwrapped TypedMessage = T.TypedMessage + + _Wrapped' = iso t f + where + t TypedMessage {..} = + P.defMessage + & T.data' .~ typedMsgData + & T.type' .~ typedMsgType + f message = TypedMessage + { typedMsgData = message ^. T.data' + , typedMsgType = message ^. T.type' + } + +instance HasCodec TypedMessage where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + -- | This is a general error type, primarily accomodating protobuf messages being parsed -- | by either the [proto3-wire](https://hackage.haskell.org/package/proto3-wire) -- | or the [proto-lens](https://hackage.haskell.org/package/proto-lens) libraries. diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs index babe57f4..4c255815 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Transaction.hs @@ -2,7 +2,7 @@ module Tendermint.SDK.Types.Transaction where import Control.Error (note) import Control.Lens (Wrapped (..), from, iso, view, - (&), (.~), (^.)) + (&), (.~), (^.), _Unwrapped') import Crypto.Hash (Digest, hashWith) import Crypto.Hash.Algorithms (SHA256 (..)) import Data.Bifunctor (bimap) @@ -20,7 +20,8 @@ import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Crypto (MakeDigest (..), RecoverableSignatureSchema (..), SignatureSchema (..)) -import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Message (Msg (..), TypedMessage (..)) + -- Our standard transaction type parameterized by the signature schema 'alg' -- and an underlying message type 'msg'. data Tx alg msg = Tx @@ -43,7 +44,7 @@ instance Functor (Tx alg) where -- | Raw transaction type coming in over the wire data RawTransaction = RawTransaction - { rawTransactionData :: ByteString + { rawTransactionData :: TypedMessage -- ^ the encoded message via protobuf encoding , rawTransactionGas :: Int64 , rawTransactionRoute :: Text @@ -59,13 +60,13 @@ instance Wrapped RawTransaction where where t RawTransaction {..} = P.defMessage - & T.data' .~ rawTransactionData + & T.data' .~ (rawTransactionData ^. _Wrapped') & T.gas .~ rawTransactionGas - & T.route .~ cs rawTransactionRoute + & T.route .~ rawTransactionRoute & T.signature .~ rawTransactionSignature & T.nonce .~ rawTransactionNonce f message = RawTransaction - { rawTransactionData = message ^. T.data' + { rawTransactionData = message ^. T.data' . _Unwrapped' , rawTransactionGas = message ^. T.gas , rawTransactionRoute = message ^. T.route , rawTransactionSignature = message ^. T.signature @@ -109,8 +110,9 @@ parseTx p bs = do signerPubKey <- note "Signature recovery failed." $ recover p recSig signBytes return $ Tx { txMsg = Msg - { msgData = rawTransactionData + { msgData = typedMsgData rawTransactionData , msgAuthor = addressFromPubKey p signerPubKey + , msgType = typedMsgType rawTransactionData } , txRoute = cs rawTransactionRoute , txGas = rawTransactionGas @@ -119,9 +121,3 @@ parseTx p bs = do , txSigner = signerPubKey , txNonce = rawTransactionNonce } - -data PreRoutedTx msg where - PreRoutedTx :: Tx alg msg -> PreRoutedTx msg - -instance Functor PreRoutedTx where - fmap f (PreRoutedTx tx) = PreRoutedTx $ fmap f tx diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs index 139a46d2..2d2cbaff 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/CryptoSpec.hs @@ -11,6 +11,7 @@ import Data.Proxy import Data.String (fromString) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Types.Message import Tendermint.SDK.Types.Transaction import Test.Hspec @@ -18,7 +19,7 @@ spec :: Spec spec = describe "Crypto Tests" $ do it "Can sign a transaction and recover the signature" $ do let rawTxWithoutSig = RawTransaction - { rawTransactionData = "abcd" + { rawTransactionData = TypedMessage "abcd" "foo_msg" , rawTransactionSignature = "" , rawTransactionRoute= "dog" , rawTransactionGas = 10 diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs index ce9191b0..e9b0dc25 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs @@ -8,12 +8,10 @@ import qualified Tendermint.SDK.Application as App import qualified Tendermint.SDK.Application.Module as M import qualified Tendermint.SDK.BaseApp as BA import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Transaction as T import Tendermint.SDK.Codec (HasCodec (..)) import qualified Tendermint.SDK.Test.SimpleStorage as SS import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), - Tx (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) import Test.Hspec type Effs = SS.SimpleStorage ': BA.BaseApp BA.CoreEffs @@ -23,24 +21,24 @@ spec = beforeAll (BA.makeContext (KL.InitialLogNamespace "test" "spec") Nothing) describe "Query tests" $ do let modules :: App.Modules '[SS.SimpleStorageM Effs] Effs modules = SS.simpleStorageModule App.:+ App.NilModules - ssServer = M.queryRouter modules - handler = App.moduleTxDeliverer SS.simpleStorageModule + ssServer = M.appQueryRouter modules + handler = M.appTxRouter modules BA.DeliverTx it "Can make a new count and query it with a multiplier" $ \ctx -> do let increaseCountMsg = Msg { msgAuthor = undefined - , msgData = SS.UpdateCount $ SS.UpdateCountTx 1 + , msgType = "update_count" + , msgData = encode $ SS.UpdateCountTx 1 } - tx = PreRoutedTx $ Tx + tx = BA.RoutingTx $ Tx { txMsg = increaseCountMsg - , txRoute = undefined + , txRoute = "simple_storage" , txGas = 0 , txSignature = undefined , txSignBytes = undefined , txSigner = undefined , txNonce = undefined } - txContext <- T.newTransactionContext tx - _ <- SS.evalToIO ctx . T.eval txContext $ handler tx + _ <- SS.evalToIO ctx $ handler tx let q = Req.Query -- TODO -- this shouldn't require / count { queryPath = "/simple_storage/manipulated/1?factor=4" diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index 454c892c..ae20c0fa 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -1,8 +1,7 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.Test.SimpleStorage - ( SimpleStorageMessage(..) - , SimpleStorageM + ( SimpleStorageM , SimpleStorage , UpdateCountTx(..) , simpleStorageModule @@ -21,8 +20,8 @@ import Data.Maybe (fromJust) import Data.Proxy import qualified Data.Serialize as Serialize import Data.Serialize.Text () -import qualified Data.Serialize.Text () import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) import GHC.Generics (Generic) import Polysemy import Polysemy.Error (Error) @@ -30,8 +29,10 @@ import Servant.API import Tendermint.SDK.Application (Module (..)) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (PreRoutedTx (..), Tx (..)) +import Tendermint.SDK.Types.Message (HasMessageType (..), + Msg (..), + ValidateMessage (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) -------------------------------------------------------------------------------- -- Types @@ -63,13 +64,22 @@ instance BaseApp.Queryable Count where -- Message Types -------------------------------------------------------------------------------- -data SimpleStorageMessage = - UpdateCount UpdateCountTx - data UpdateCountTx = UpdateCountTx { updateCountTxCount :: Int32 } deriving (Show, Eq, Generic) +instance Serialize.Serialize UpdateCountTx + +instance HasMessageType UpdateCountTx where + messageType _ = "update_count" + +instance HasCodec UpdateCountTx where + encode = Serialize.encode + decode = first cs . Serialize.decode + +instance ValidateMessage UpdateCountTx where + validateMessage _ = Success () + -------------------------------------------------------------------------------- -- Keeper -------------------------------------------------------------------------------- @@ -104,16 +114,23 @@ eval = interpret (\case -- Router -------------------------------------------------------------------------------- -router +type MessageApi = + BaseApp.TypedMessage UpdateCountTx BaseApp.:~> BaseApp.Return () + +messageHandlers + :: Member SimpleStorage r + => BaseApp.RouteTx MessageApi r 'BaseApp.DeliverTx +messageHandlers = updateCountH + +updateCountH :: Member SimpleStorage r => Members BaseApp.TxEffs r - => PreRoutedTx SimpleStorageMessage + => BaseApp.RoutingTx UpdateCountTx -> Sem r () -router (PreRoutedTx Tx{txMsg}) = +updateCountH (BaseApp.RoutingTx Tx{txMsg}) = let Msg{msgData} = txMsg - in case msgData of - UpdateCount UpdateCountTx{updateCountTxCount} -> - updateCount (Count updateCountTxCount) + UpdateCountTx{updateCountTxCount} = msgData + in updateCount (Count updateCountTxCount) -------------------------------------------------------------------------------- -- Server @@ -144,12 +161,12 @@ getMultipliedCount subtractor multiplier = do , queryResultHeight = 0 } -type Api = GetMultipliedCount :<|> BaseApp.QueryApi CountStoreContents +type QueryApi = GetMultipliedCount :<|> BaseApp.QueryApi CountStoreContents server :: forall r. Members [SimpleStorage, BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteT Api r + => BaseApp.RouteQ QueryApi r server = let storeHandlers = BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy r) @@ -160,15 +177,15 @@ server = -------------------------------------------------------------------------------- type SimpleStorageM r = - Module "simple_storage" SimpleStorageMessage () Api SimpleStorageEffs r + Module "simple_storage" MessageApi QueryApi SimpleStorageEffs r simpleStorageModule :: Member SimpleStorage r => Members BaseApp.BaseAppEffs r => SimpleStorageM r simpleStorageModule = Module - { moduleTxDeliverer = router - , moduleTxChecker = router + { moduleTxDeliverer = messageHandlers + , moduleTxChecker = BaseApp.defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) , moduleQueryServer = server , moduleEval = eval } diff --git a/hs-abci-test-utils/package.yaml b/hs-abci-test-utils/package.yaml index 0e90e704..b3486172 100644 --- a/hs-abci-test-utils/package.yaml +++ b/hs-abci-test-utils/package.yaml @@ -31,6 +31,7 @@ default-extensions: - RecordWildCards - ScopedTypeVariables - TypeOperators + - FlexibleContexts library: source-dirs: src @@ -46,9 +47,8 @@ library: - aeson-pretty - base >= 4.7 && < 5 - bytestring - - data-default-class + - cryptonite - errors - - hspec - http-api-data - lens - mtl diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Client.hs b/hs-abci-test-utils/src/Tendermint/Utils/Client.hs index 0a2b77a7..fcc871e1 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/Client.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/Client.hs @@ -1,144 +1,25 @@ -{-# LANGUAGE UndecidableInstances #-} - module Tendermint.Utils.Client - ( RunClient(..) - , HasClient(..) - , ClientResponse(..) - ) where - -import Control.Lens (to, (^.)) -import Control.Monad.Reader (ReaderT) -import qualified Data.ByteArray.Base64String as Base64 -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString) -import Data.Proxy -import Data.String.Conversions (cs) -import Data.Text (Text, intercalate) -import GHC.TypeLits (KnownSymbol, symbolVal) -import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp -import qualified Network.Tendermint.Client as RPC -import Servant.API -import Servant.API.Modifiers -import Tendermint.SDK.BaseApp.Query.Store (StoreLeaf) -import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, - QueryArgs (..), - Queryable (..)) -import Tendermint.SDK.BaseApp.Store (RawKey (..)) -import Tendermint.SDK.Codec (HasCodec (decode)) -import Web.Internal.HttpApiData (ToHttpApiData (..)) - -class Monad m => RunClient m where - -- | How to make a request. - runQuery :: Req.Query -> m Resp.Query - -instance RunClient (ReaderT RPC.Config IO) where - runQuery Req.Query{..} = - let rpcQ = RPC.RequestABCIQuery - { RPC.requestABCIQueryPath = Just queryPath - , RPC.requestABCIQueryData = Hex.fromBytes @ByteString . Base64.toBytes $ queryData - , RPC.requestABCIQueryHeight = Just $ queryHeight - , RPC.requestABCIQueryProve = queryProve - } - in RPC.resultABCIQueryResponse <$> RPC.abciQuery rpcQ - -type QueryStringList = [(Text, Text)] - -class HasClient m layout where - - type ClientT (m :: * -> *) layout :: * - genClient :: Proxy m -> Proxy layout -> (Req.Query, QueryStringList) -> ClientT m layout - -instance (HasClient m a, HasClient m b) => HasClient m (a :<|> b) where - type ClientT m (a :<|> b) = ClientT m a :<|> ClientT m b - genClient pm _ (q,qs) = genClient pm (Proxy @a) (q,qs) :<|> genClient pm (Proxy @b) (q,qs) - -instance (KnownSymbol path, HasClient m a) => HasClient m (path :> a) where - type ClientT m (path :> a) = ClientT m a - genClient pm _ (q,qs) = genClient pm (Proxy @a) - (q {Req.queryPath = Req.queryPath q <> "/" <> cs (symbolVal (Proxy @path))}, qs) - -appendToQueryString - :: Text -- ^ param name - -> Maybe Text -- ^ param value - -> QueryStringList - -> QueryStringList -appendToQueryString pname pvalue qs = - maybe qs (\v -> (pname, v) : qs) pvalue - -instance (KnownSymbol sym, ToHttpApiData a, HasClient m api, SBoolI (FoldRequired mods)) - => HasClient m (QueryParam' mods sym a :> api) where + ( RunQueryClient(..) + , HasQueryClient(..) + , QueryClientResponse(..) + , EmptyQueryClient(..) + + , HasTxClient(..) + , RunTxClient(..) + , EmptyTxClient(..) + , TxClientResponse(..) + , SynchronousResponse(..) + , TxResponse(..) + , ClientConfig(..) + , defaultClientTxOpts + + , Signer(..) + , TxOpts(..) + , makeSignerFromKey - type ClientT m (QueryParam' mods sym a :> api) = RequiredArgument mods a -> ClientT m api - - -- if mparam = Nothing, we don't add it to the query string - genClient pm Proxy (q,qs) mparam = - genClient pm (Proxy :: Proxy api) $ foldRequiredArgument - (Proxy :: Proxy mods) add (maybe (q,qs) add) mparam - where - add :: a -> (Req.Query, QueryStringList) - add param = (q, appendToQueryString pname (Just $ toQueryParam param) qs) - - pname :: Text - pname = cs $ symbolVal (Proxy :: Proxy sym) - -instance (RawKey k, HasClient m a) => HasClient m (QA k :> a) where - type ClientT m (QA k :> a) = QueryArgs k -> ClientT m a - genClient pm _ (q,qs) QueryArgs{..} = genClient pm (Proxy @a) - (q { Req.queryData = queryArgsData ^. rawKey . to Base64.fromBytes - , Req.queryHeight = WrappedVal queryArgsHeight - , Req.queryProve = queryArgsProve - }, qs) - -instance (ToHttpApiData a, HasClient m api) => HasClient m (Capture' mods capture a :> api) where - - type ClientT m (Capture' mods capture a :> api) = a -> ClientT m api - - genClient pm _ (q,qs) val = - let p = toUrlPiece val - q' = q { Req.queryPath = Req.queryPath q <> "/" <> p } - in genClient pm (Proxy :: Proxy api) (q', qs) - --- | Data is Nothing iff Raw includes a non-0 response value -data ClientResponse a = ClientResponse - { clientResponseData :: Maybe a - , clientResponseRaw :: Resp.Query - } - -addQueryParamsToPath - :: QueryStringList - -> Text - -> Text -addQueryParamsToPath qs path = - let qParams = intercalate "&" $ map (\(n,v) -> n <> "=" <> v) qs - in case qs of - [] -> path - _ -> path <> "?" <> qParams - -instance (HasCodec a, RunClient m) => HasClient m (Leaf a) where - type ClientT m (Leaf a) = m (ClientResponse a) - genClient _ _ = leafGenClient - -leafGenClient - :: HasCodec a - => RunClient m - => (Req.Query, QueryStringList) - -> m (ClientResponse a) -leafGenClient (q,qs) = do - let reqPath = addQueryParamsToPath qs $ Req.queryPath q - r@Resp.Query{..} <- runQuery q { Req.queryPath = reqPath } - -- anything other than 0 code is a failure: https://tendermint.readthedocs.io/en/latest/abci-spec.html - -- and will result in queryValue decoding to a "empty/default" object - return $ case queryCode of - 0 -> case decode $ Base64.toBytes queryValue of - Left err -> error $ "Impossible parse error: " <> cs err - Right a -> ClientResponse (Just a) r - _ -> ClientResponse Nothing r + ) where -instance (RunClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasClient m (StoreLeaf a) where - type ClientT m (StoreLeaf a) = m (ClientResponse a) - genClient _ _ (q,qs) = - let leaf = symbolVal (Proxy @(Name a)) - q' = q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } - in leafGenClient (q', qs) +import Tendermint.Utils.QueryClient.Class +import Tendermint.Utils.QueryClient.Types +import Tendermint.Utils.TxClient.Class +import Tendermint.Utils.TxClient.Types diff --git a/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs b/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs new file mode 100644 index 00000000..2b053047 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs @@ -0,0 +1,131 @@ +module Tendermint.Utils.ClientUtils where + +import Control.Monad (unless) +import Data.Aeson (ToJSON) +import Data.Aeson.Encode.Pretty (encodePretty) +import Data.Either (partitionEithers) +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word32) +import Network.ABCI.Types.Messages.FieldTypes (Event (..)) +import qualified Network.Tendermint.Client as RPC +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Events (ToEvent (..)) +import Tendermint.SDK.BaseApp.Query (QueryResult (..)) +import Tendermint.Utils.Client (QueryClientResponse (..), + SynchronousResponse (..), + TxClientResponse (..), + TxResponse (..)) +import Tendermint.Utils.Events (FromEvent (..)) + +-------------------------------------------------------------------------------- +-- | Tx helpers +-------------------------------------------------------------------------------- + +assertTx + :: Monad m + => m (TxClientResponse a b) + -> m (SynchronousResponse a b) +assertTx m = do + resp <- m + case resp of + Response r -> pure r + RPCError err -> fail $ "Expected Response, got RPCError " <> show err + ParseError ctx err -> fail $ "Expected Response, got ParseError in context " <> show ctx + <> ": " <> show err + +-- get the logged events from a deliver response, +deliverTxEvents + :: Monad m + => FromEvent e + => Proxy e + -> SynchronousResponse a b + -> m ([Text],[e]) +deliverTxEvents pE SynchronousResponse{deliverTxResponse} = + case deliverTxResponse of + TxResponse {txResponseEvents} -> + let eventName = cs $ makeEventType pE + es = filter ((== eventName) . eventType) txResponseEvents + in return . partitionEithers . map fromEvent $ es + TxError appError -> fail (show appError) + +-- check for a specific check response code +ensureCheckResponseCode + :: Monad m + => Word32 + -> SynchronousResponse a b + -> m () +ensureCheckResponseCode code SynchronousResponse{checkTxResponse} = + case checkTxResponse of + TxResponse _ _ -> + unless (code == 0) $ + fail $ "Couldn't match found checkTx response code 0 with expected code " <> show code <> "." + TxError appError -> + let errCode = appErrorCode appError + in unless (errCode == code) $ + fail $ "Couldn't match found checkTx response code " <> show errCode <> + " with expected code " <> show code <> "." + +-- check for a specific check response code +ensureDeliverResponseCode + :: Monad m + => Word32 + -> SynchronousResponse a b + -> m () +ensureDeliverResponseCode code SynchronousResponse{deliverTxResponse} = + case deliverTxResponse of + TxResponse _ _ -> + unless (code == 0) $ + fail $ "Couldn't match found deliverTx response code 0 with expected code " <> show code <> "." + TxError appError -> + let errCode = appErrorCode appError + in unless (errCode == code) $ + fail $ "Couldn't match found deliverTx response code " <> show errCode <> + " with expected code " <> show code <> "." + +ensureResponseCodes + :: Monad m + => (Word32, Word32) + -> SynchronousResponse a b + -> m () +ensureResponseCodes (checkCode, deliverCode) resp = do + ensureCheckResponseCode checkCode resp + ensureDeliverResponseCode deliverCode resp + +-------------------------------------------------------------------------------- +-- | Query helpers +-------------------------------------------------------------------------------- + +assertQuery + :: Monad m + => m (QueryClientResponse a) + -> m (QueryResult a) +assertQuery m = do + resp <- m + case resp of + QueryResponse r -> pure r + QueryError err -> fail $ show err + +ensureQueryResponseCode + :: Monad m + => Word32 + -> QueryClientResponse a + -> m () +ensureQueryResponseCode code resp = case resp of + QueryResponse _ -> + unless (code == 0) $ + fail $ "Couldn't match found query response code 0 with expected code " <> show code <> "." + QueryError AppError{appErrorCode} -> + unless (appErrorCode == code) $ + fail $ "Couldn't match found query response code " <> show appErrorCode <> + " with expected code " <> show code <> "." + +-------------------------------------------------------------------------------- + +rpcConfig :: RPC.Config +rpcConfig = + let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 + prettyPrint :: forall b. ToJSON b => String -> b -> IO () + prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) + in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") diff --git a/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs b/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs new file mode 100644 index 00000000..d4687e32 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs @@ -0,0 +1,150 @@ +{-# LANGUAGE UndecidableInstances #-} +module Tendermint.Utils.QueryClient.Class where + +import Control.Lens (to, (^.)) +import Control.Monad.Reader (ReaderT) +import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text, intercalate) +import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import qualified Network.Tendermint.Client as RPC +import Servant.API +import Servant.API.Modifiers +import Tendermint.SDK.BaseApp.Errors (queryAppError) +import Tendermint.SDK.BaseApp.Query.Store (StoreLeaf) +import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, + QueryArgs (..), + QueryResult (..), + Queryable (..)) +import Tendermint.SDK.BaseApp.Store (RawKey (..)) +import Tendermint.SDK.Codec (HasCodec (decode)) +import Tendermint.Utils.QueryClient.Types +import Web.Internal.HttpApiData (ToHttpApiData (..)) + +class Monad m => RunQueryClient m where + -- | How to make a request. + runQuery :: Req.Query -> m Resp.Query + +instance RunQueryClient (ReaderT RPC.Config IO) where + runQuery Req.Query{..} = + let rpcQ = RPC.RequestABCIQuery + { RPC.requestABCIQueryPath = Just queryPath + , RPC.requestABCIQueryData = Hex.fromBytes @ByteString . Base64.toBytes $ queryData + , RPC.requestABCIQueryHeight = Just $ queryHeight + , RPC.requestABCIQueryProve = queryProve + } + in RPC.resultABCIQueryResponse <$> RPC.abciQuery rpcQ + +type QueryStringList = [(Text, Text)] + +class HasQueryClient m layout where + + type ClientQ (m :: * -> *) layout :: * + genClientQ :: Proxy m -> Proxy layout -> (Req.Query, QueryStringList) -> ClientQ m layout + +instance (HasQueryClient m a, HasQueryClient m b) => HasQueryClient m (a :<|> b) where + type ClientQ m (a :<|> b) = ClientQ m a :<|> ClientQ m b + genClientQ pm _ (q,qs) = genClientQ pm (Proxy @a) (q,qs) :<|> genClientQ pm (Proxy @b) (q,qs) + +instance (KnownSymbol path, HasQueryClient m a) => HasQueryClient m (path :> a) where + type ClientQ m (path :> a) = ClientQ m a + genClientQ pm _ (q,qs) = genClientQ pm (Proxy @a) + (q {Req.queryPath = Req.queryPath q <> "/" <> cs (symbolVal (Proxy @path))}, qs) + +appendToQueryString + :: Text -- ^ param name + -> Maybe Text -- ^ param value + -> QueryStringList + -> QueryStringList +appendToQueryString pname pvalue qs = + maybe qs (\v -> (pname, v) : qs) pvalue + +instance (KnownSymbol sym, ToHttpApiData a, HasQueryClient m api, SBoolI (FoldRequired mods)) + => HasQueryClient m (QueryParam' mods sym a :> api) where + + type ClientQ m (QueryParam' mods sym a :> api) = RequiredArgument mods a -> ClientQ m api + + -- if mparam = Nothing, we don't add it to the query string + genClientQ pm Proxy (q,qs) mparam = + genClientQ pm (Proxy :: Proxy api) $ foldRequiredArgument + (Proxy :: Proxy mods) add (maybe (q,qs) add) mparam + where + add :: a -> (Req.Query, QueryStringList) + add param = (q, appendToQueryString pname (Just $ toQueryParam param) qs) + + pname :: Text + pname = cs $ symbolVal (Proxy :: Proxy sym) + +instance (RawKey k, HasQueryClient m a) => HasQueryClient m (QA k :> a) where + type ClientQ m (QA k :> a) = QueryArgs k -> ClientQ m a + genClientQ pm _ (q,qs) QueryArgs{..} = genClientQ pm (Proxy @a) + (q { Req.queryData = queryArgsData ^. rawKey . to Base64.fromBytes + , Req.queryHeight = WrappedVal queryArgsHeight + , Req.queryProve = queryArgsProve + }, qs) + +instance (ToHttpApiData a, HasQueryClient m api) => HasQueryClient m (Capture' mods capture a :> api) where + + type ClientQ m (Capture' mods capture a :> api) = a -> ClientQ m api + + genClientQ pm _ (q,qs) val = + let p = toUrlPiece val + q' = q { Req.queryPath = Req.queryPath q <> "/" <> p } + in genClientQ pm (Proxy :: Proxy api) (q', qs) + +addQueryParamsToPath + :: QueryStringList + -> Text + -> Text +addQueryParamsToPath qs path = + let qParams = intercalate "&" $ map (\(n,v) -> n <> "=" <> v) qs + in case qs of + [] -> path + _ -> path <> "?" <> qParams + +instance (HasCodec a, RunQueryClient m) => HasQueryClient m (Leaf a) where + type ClientQ m (Leaf a) = m (QueryClientResponse a) + genClientQ _ _ = leafGenClient + +leafGenClient + :: HasCodec a + => RunQueryClient m + => (Req.Query, QueryStringList) + -> m (QueryClientResponse a) +leafGenClient (q,qs) = do + let reqPath = addQueryParamsToPath qs $ Req.queryPath q + r@Resp.Query{..} <- runQuery q { Req.queryPath = reqPath } + -- anything other than 0 code is a failure: https://tendermint.readthedocs.io/en/latest/abci-spec.html + -- and will result in queryValue decoding to a "empty/default" object + return $ case queryCode of + 0 -> case decode $ Base64.toBytes queryValue of + Left err -> error $ "Impossible parse error: " <> cs err + Right a -> QueryResponse $ QueryResult + { queryResultData = a + , queryResultIndex = unWrappedVal queryIndex + , queryResultHeight = unWrappedVal queryHeight + , queryResultProof = queryProof + , queryResultKey = queryKey + } + _ -> QueryError $ r ^. queryAppError + +instance (RunQueryClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasQueryClient m (StoreLeaf a) where + type ClientQ m (StoreLeaf a) = m (QueryClientResponse a) + genClientQ _ _ (q,qs) = + let leaf = symbolVal (Proxy @(Name a)) + q' = q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } + in leafGenClient (q', qs) + +-- | Singleton type representing a client for an empty API. +data EmptyQueryClient = EmptyQueryClient deriving (Eq, Show, Bounded, Enum) + +instance HasQueryClient m EmptyQueryClient where + type ClientQ m EmptyQueryClient = EmptyQueryClient + + genClientQ _ _ _ = EmptyQueryClient diff --git a/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Types.hs b/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Types.hs new file mode 100644 index 00000000..ff65cf63 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Types.hs @@ -0,0 +1,10 @@ +module Tendermint.Utils.QueryClient.Types where + +import Tendermint.SDK.BaseApp.Errors (AppError) +import Tendermint.SDK.BaseApp.Query.Types (QueryResult) + +-- | Data is Nothing iff Raw includes a non-0 response value +data QueryClientResponse a = + QueryResponse (QueryResult a) + | QueryError AppError + deriving (Eq, Show) diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Request.hs b/hs-abci-test-utils/src/Tendermint/Utils/Request.hs deleted file mode 100644 index b2e61461..00000000 --- a/hs-abci-test-utils/src/Tendermint/Utils/Request.hs +++ /dev/null @@ -1,59 +0,0 @@ -module Tendermint.Utils.Request where - -import Control.Lens ((^.)) -import Data.Aeson (ToJSON) -import Data.Aeson.Encode.Pretty (encodePretty) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Maybe (fromJust) -import Data.String.Conversions (cs) -import Data.Word (Word32) -import qualified Network.ABCI.Types.Messages.Response as Response -import qualified Network.Tendermint.Client as RPC -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Transaction (RawTransaction (..)) -import Tendermint.Utils.Client (ClientResponse (..)) -import Test.Hspec - -runRPC :: forall a. RPC.TendermintM a -> IO a -runRPC = RPC.runTendermintM rpcConfig - where - rpcConfig :: RPC.Config - rpcConfig = - let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 - prettyPrint :: forall b. ToJSON b => String -> b -> IO () - prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) - in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") - --- executes a query and ensures a 0 response code -getQueryResponseSuccess :: RPC.TendermintM (ClientResponse a) -> IO a -getQueryResponseSuccess query = do - ClientResponse{clientResponseData,clientResponseRaw} <- runRPC query - let responseCode = clientResponseRaw ^. Response._queryCode - responseCode `shouldBe` 0 - return . fromJust $ clientResponseData - --- executes a request, then returns the checkTx response -getCheckTxResponse :: RawTransaction -> IO Response.CheckTx -getCheckTxResponse rawTx = do - let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - fmap RPC.resultBroadcastTxCommitCheckTx . runRPC $ - RPC.broadcastTxCommit txReq - --- executes a request, then returns the deliverTx response -getDeliverTxResponse :: RawTransaction -> IO Response.DeliverTx -getDeliverTxResponse rawTx = do - let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - fmap RPC.resultBroadcastTxCommitDeliverTx . runRPC $ - RPC.broadcastTxCommit txReq - --- executes a request, check deliver and response codes -ensureCheckAndDeliverResponseCodes :: (Word32, Word32) -> RawTransaction -> IO () -ensureCheckAndDeliverResponseCodes codes rawTx = do - let txReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeRawTx rawTx } - resp <- runRPC $ RPC.broadcastTxCommit txReq - let checkResp = RPC.resultBroadcastTxCommitCheckTx resp - deliverResp = RPC.resultBroadcastTxCommitDeliverTx resp - codes `shouldBe` (checkResp ^. Response._checkTxCode, deliverResp ^. Response._deliverTxCode) - -encodeRawTx :: RawTransaction -> Base64.Base64String -encodeRawTx = Base64.fromBytes . encode diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Response.hs b/hs-abci-test-utils/src/Tendermint/Utils/Response.hs deleted file mode 100644 index 21aad542..00000000 --- a/hs-abci-test-utils/src/Tendermint/Utils/Response.hs +++ /dev/null @@ -1,36 +0,0 @@ -module Tendermint.Utils.Response where - -import Control.Lens ((^.)) -import Data.Either (partitionEithers) -import Data.Text (Text) -import Data.Word (Word32) -import Network.ABCI.Types.Messages.FieldTypes (Event (..)) -import qualified Network.ABCI.Types.Messages.Response as Response -import Tendermint.Utils.Events (FromEvent (..)) -import Test.Hspec - --- get the logged events from a deliver response, -deliverTxEvents :: FromEvent e => Response.DeliverTx -> Text -> IO ([Text],[e]) -deliverTxEvents deliverResp eventName = do - let deliverEvents = deliverResp ^. Response._deliverTxEvents - filtered = filter ((== eventName) . eventType) deliverEvents - return . partitionEithers . map fromEvent $ filtered - --- ensures there are no errors when parsing event logs and contains the expectedEvent -ensureEventLogged :: (Eq e, Show e, FromEvent e) => Response.DeliverTx -> Text -> e -> IO () -ensureEventLogged deliverResp eventName expectedEvent = do - (errs, events) <- deliverTxEvents deliverResp eventName - errs `shouldBe` mempty - events `shouldSatisfy` elem expectedEvent - --- check for a specific check response code -ensureCheckResponseCode :: Response.CheckTx -> Word32 -> IO () -ensureCheckResponseCode checkResp code = do - let checkRespCode = checkResp ^. Response._checkTxCode - checkRespCode `shouldBe` code - --- check for a specific deliver response code -ensureDeliverResponseCode :: Response.DeliverTx -> Word32 -> IO () -ensureDeliverResponseCode deliverResp code = do - let deliverRespCode = deliverResp ^. Response._deliverTxCode - deliverRespCode `shouldBe` code diff --git a/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs new file mode 100644 index 00000000..55054eb5 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs @@ -0,0 +1,113 @@ +{-# LANGUAGE UndecidableInstances #-} + +module Tendermint.Utils.TxClient.Class + ( ClientConfig(..) + , RunTxClient(..) + , HasTxClient(..) + , EmptyTxClient(..) + , defaultClientTxOpts + ) where + +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Reader (ReaderT, ask) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word64) +import GHC.TypeLits (KnownSymbol, + symbolVal) +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..), (:>)) +import qualified Tendermint.SDK.BaseApp.Transaction as T +import qualified Tendermint.SDK.BaseApp.Transaction.Modifier as T +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (HasMessageType (..), + TypedMessage (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..)) +import Tendermint.Utils.TxClient.Types + +class Monad m => RunTxClient m where + -- | How to make a request. + runTx :: RawTransaction -> m RPC.ResultBroadcastTxCommit + getNonce :: Address -> m Word64 + +data ClientConfig = ClientConfig + { clientRPC :: RPC.Config + , clientGetNonce :: Address -> IO Word64 + } + + +instance RunTxClient (ReaderT ClientConfig IO) where + getNonce addr = do + nonceGetter <- clientGetNonce <$> ask + liftIO $ nonceGetter addr + runTx tx = do + let txReq = RPC.broadcastTxCommit . RPC.RequestBroadcastTxCommit . Base64.fromBytes . encode $ tx + rpc <- clientRPC <$> ask + liftIO . RPC.runTendermintM rpc $ txReq + +data ClientTxOpts = ClientTxOpts + { clientTxOptsRoute :: Text + , clientTxOptsNonce :: Word64 + } + +defaultClientTxOpts :: ClientTxOpts +defaultClientTxOpts = ClientTxOpts "" 0 + +class HasTxClient m layout where + + type ClientT (m :: * -> *) layout :: * + genClientT :: Proxy m -> Proxy layout -> ClientTxOpts -> ClientT m layout + +instance (HasTxClient m a, HasTxClient m b) => HasTxClient m (a :<|> b) where + type ClientT m (a :<|> b) = ClientT m a :<|> ClientT m b + genClientT pm _ opts = genClientT pm (Proxy @a) opts :<|> genClientT pm (Proxy @b) opts + +instance (KnownSymbol path, HasTxClient m a) => HasTxClient m (path :> a) where + type ClientT m (path :> a) = ClientT m a + genClientT pm _ clientOpts = + let clientOpts' = clientOpts { clientTxOptsRoute = cs $ symbolVal (Proxy @path) } + in genClientT pm (Proxy @a) clientOpts' + +makeRawTxForSigning + :: forall msg. + HasMessageType msg + => HasCodec msg + => ClientTxOpts + -> TxOpts + -> msg + -> RawTransaction +makeRawTxForSigning ClientTxOpts{..} TxOpts{..} msg = + RawTransaction + { rawTransactionData = TypedMessage (encode msg) (messageType $ Proxy @msg) + , rawTransactionGas = txOptsGas + , rawTransactionNonce = clientTxOptsNonce + , rawTransactionRoute = clientTxOptsRoute + , rawTransactionSignature = "" + } + +instance ( HasMessageType msg, HasCodec msg + , HasCodec a, HasCodec (T.OnCheckReturn 'T.CheckTx oc a) + , RunTxClient m + ) => HasTxClient m (T.TypedMessage msg T.:~> T.Return' oc a) where + type ClientT m (T.TypedMessage msg T.:~> T.Return' oc a) = TxOpts -> msg -> m (TxClientResponse (T.OnCheckReturn 'T.CheckTx oc a) a) + + genClientT _ _ clientOpts opts msg = do + let Signer signerAddress signer = txOptsSigner opts + nonce <- getNonce signerAddress + let clientOpts' = clientOpts {clientTxOptsNonce = nonce} + rawTxForSigning = makeRawTxForSigning clientOpts' opts msg + rawTxWithSig = signer rawTxForSigning + txRes <- runTx rawTxWithSig + pure $ parseRPCResponse (Proxy @a) (Proxy @oc) txRes + + +-- | Singleton type representing a client for an empty API. +data EmptyTxClient = EmptyTxClient deriving (Eq, Show, Bounded, Enum) + +instance HasTxClient m T.EmptyTxServer where + type ClientT m T.EmptyTxServer = EmptyTxClient + + genClientT _ _ _ = EmptyTxClient diff --git a/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs new file mode 100644 index 00000000..24d67ec6 --- /dev/null +++ b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs @@ -0,0 +1,97 @@ +module Tendermint.Utils.TxClient.Types where + +import Control.Lens ((^.)) +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import Data.Bifunctor (first) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Int (Int64) +import Data.Proxy +import Data.Text (Text) +import Network.ABCI.Types.Messages.FieldTypes (Event) +import qualified Network.ABCI.Types.Messages.Response as Response +import qualified Network.Tendermint.Client as RPC +import Tendermint.SDK.BaseApp.Errors (AppError, + txResultAppError) +import qualified Tendermint.SDK.BaseApp.Transaction as T +import qualified Tendermint.SDK.BaseApp.Transaction.Modifier as T +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (RecoverableSignatureSchema (..), + SignatureSchema (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Transaction (RawTransaction (..), + signRawTransaction) +import Tendermint.SDK.Types.TxResult (checkTxTxResult, + deliverTxTxResult) + +data TxOpts = TxOpts + { txOptsGas :: Int64 + , txOptsSigner :: Signer + } + +data Signer = Signer + { signerAddress :: Address + , signerSign :: RawTransaction -> RawTransaction + } + +makeSignerFromKey + :: RecoverableSignatureSchema alg + => Message alg ~ Digest SHA256 + => Proxy alg + -> PrivateKey alg + -> Signer +makeSignerFromKey pa privKey = Signer (addressFromPubKey pa . derivePubKey pa $ privKey) $ \r -> + let sig = serializeRecoverableSignature pa $ + signRawTransaction pa privKey $ r {rawTransactionSignature = ""} + in r {rawTransactionSignature = sig} + +data TxResponse a = + TxResponse + { txResponseResult :: a + , txResponseEvents :: [Event] + } + | TxError AppError + deriving (Eq, Show) + +data SynchronousResponse c d = SynchronousResponse + { checkTxResponse :: TxResponse c + , deliverTxResponse :: TxResponse d + } deriving (Eq, Show) + +data TxClientResponse c d = + RPCError Text + | ParseError T.RouteContext Text + | Response (SynchronousResponse c d) + deriving (Eq, Show) + +parseRPCResponse + :: HasCodec a + => HasCodec (T.OnCheckReturn 'T.CheckTx oc a) + => Proxy a + -> Proxy (oc :: T.OnCheck) + -> RPC.ResultBroadcastTxCommit + -> TxClientResponse (T.OnCheckReturn 'T.CheckTx oc a) a +parseRPCResponse _ _ RPC.ResultBroadcastTxCommit{..} = + let + makeCheckResp r@Response.CheckTx{..} = case checkTxCode of + 0 -> do + resp <- decode $ Base64.toBytes checkTxData + pure $ TxResponse resp $ checkTxEvents + _ -> Right . TxError $ r ^. checkTxTxResult . txResultAppError + + makeDeliverResp r@Response.DeliverTx{..} = case deliverTxCode of + 0 -> do + resp <- decode $ Base64.toBytes deliverTxData + pure $ TxResponse resp $ deliverTxEvents + _ -> Right . TxError $ r ^. deliverTxTxResult . txResultAppError + + eResponses = do + checkResp <- first (ParseError T.CheckTx) $ + makeCheckResp resultBroadcastTxCommitCheckTx + deliverResp <- first (ParseError T.DeliverTx) $ + makeDeliverResp resultBroadcastTxCommitDeliverTx + pure (checkResp, deliverResp) + + in case eResponses of + Left e -> e + Right (check, deliver) -> Response $ SynchronousResponse check deliver diff --git a/hs-abci-test-utils/src/Tendermint/Utils/User.hs b/hs-abci-test-utils/src/Tendermint/Utils/User.hs index 43578759..89f178b9 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/User.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/User.hs @@ -1,32 +1,13 @@ module Tendermint.Utils.User where -import Crypto.Secp256k1 (CompactRecSig (..), SecKey, - derivePubKey, - exportCompactRecSig, secKey) -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString, snoc) -import qualified Data.ByteString as BS -import Data.ByteString.Short (fromShort) -import Data.Default.Class (def) -import Data.Maybe (fromJust) +import Crypto.Secp256k1 (SecKey, derivePubKey, secKey) +import qualified Data.ByteArray.HexString as Hex +import Data.Maybe (fromJust) import Data.Proxy -import Data.String (fromString) -import Data.String.Conversions (cs) -import Data.Word (Word64) -import qualified Network.Tendermint.Client as RPC -import Servant.API ((:>)) -import Tendermint.SDK.BaseApp.Query (QueryArgs (..), - defaultQueryWithData) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (Secp256k1, addressFromPubKey) -import Tendermint.SDK.Modules.Auth (Account (..)) -import qualified Tendermint.SDK.Modules.Auth as Auth -import Tendermint.SDK.Types.Address (Address (..)) -import Tendermint.SDK.Types.Transaction (RawTransaction (..), - signRawTransaction) -import Tendermint.Utils.Client (ClientResponse (..), - HasClient (..)) -import Tendermint.Utils.Request (runRPC) +import Data.String (fromString) +import Tendermint.SDK.Crypto (Secp256k1, addressFromPubKey) +import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.Utils.TxClient.Types (Signer, makeSignerFromKey) data User = User { userPrivKey :: SecKey @@ -40,36 +21,8 @@ makeUser privKeyStr = address = addressFromPubKey (Proxy @Secp256k1) pubKey in User privateKey address -algProxy :: Proxy Secp256k1 -algProxy = Proxy - -getAccount :: QueryArgs Address -> RPC.TendermintM (ClientResponse Account) -getAccount = - let apiP = Proxy :: Proxy ("auth" :> Auth.Api) - in genClient (Proxy :: Proxy RPC.TendermintM) apiP def - -getAccountNonce :: Address -> IO Word64 -getAccountNonce userAddress = do - let query = getAccount $ defaultQueryWithData userAddress - ClientResponse{clientResponseData} <- runRPC query - case clientResponseData of - -- unitialized account = 0 nonce - Nothing -> return 0 - Just Account {accountNonce} -> return accountNonce - --- sign a trx with a user's private key and add the user's account nonce -mkSignedRawTransactionWithRoute :: HasCodec a => BS.ByteString -> User -> a -> IO RawTransaction -mkSignedRawTransactionWithRoute route User{userAddress, userPrivKey} msg = do - nonce <- getAccountNonce userAddress - let unsigned = RawTransaction { rawTransactionData = encode msg - , rawTransactionRoute = cs route - , rawTransactionSignature = "" - , rawTransactionGas = 0 - , rawTransactionNonce = nonce - } - sig = signRawTransaction algProxy userPrivKey unsigned - sign rt = rt { rawTransactionSignature = encodeCompactRecSig $ exportCompactRecSig sig } - return . sign $ unsigned - -encodeCompactRecSig :: CompactRecSig -> ByteString -encodeCompactRecSig (CompactRecSig r s v) = snoc (fromShort r <> fromShort s) v +makeSignerFromUser + :: User + -> Signer +makeSignerFromUser User{userPrivKey} = + makeSignerFromKey (Proxy @Secp256k1) userPrivKey From 3b13a569bcc77dcdec154552175f69bf4ea8b8ca Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Mon, 27 Jan 2020 22:04:35 -0500 Subject: [PATCH 51/70] Async nameservice interact (#179) * Init hs-abci-test-utils * import packages and use correct default extensions * use directory module hierarchy * update project files with new utils lib * nameservice tests passing * use utils in ss * use new user module in nameservice tests * delete special msg encoder * use user util module in simple storage tests * move response checkers to utils library * move request runners to new request module * Update readme * Add interact exe to nameservice * Add basic main for interact * add random name generator * Clean up action type signatures +Fix markdown-unlit issue * Async actionBlock * unused * unused from merge * kind of works * fix concurrency * WIP * Fix actions + lower default concurrency var value Some transactions are failing midway through the actionBlock due to an improper nonces. At this point, the entire actionBlock run should quit instead of continuing. Tx Cache errors are still possible. * Off by one error: allow transfer to 0 an account * Update script to generate random users for each thread * wip: compile with master changes * buyName * createName * deleteName * Works again --- Makefile | 5 + .../nameservice/interact/Interact.hs | 193 ++++++++++++++++++ hs-abci-examples/nameservice/interact/Main.hs | 17 ++ hs-abci-examples/nameservice/package.yaml | 24 +++ .../Nameservice/Modules/Nameservice/Types.hs | 3 + .../src/Nameservice/Modules/Token/Keeper.hs | 2 +- stack.yaml | 4 + stack.yaml.lock | 28 +++ 8 files changed, 275 insertions(+), 1 deletion(-) create mode 100644 hs-abci-examples/nameservice/interact/Interact.hs create mode 100644 hs-abci-examples/nameservice/interact/Main.hs diff --git a/Makefile b/Makefile index 9e757b0f..3a09ac83 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ STATS_PORT ?= 9200 +INTERACT_THREAD_COUNT ?= 5 export @@ -84,6 +85,10 @@ test-simple-storage: install ## Run the test suite for the simple-storage exampl test-nameservice: install ## Run the test suite for the nameservice example application stack test nameservice:nameservice-test +interact-nameservice: install ## Run nameservice interaction script + INTERACT_THREAD_COUNT=$(INTERACT_THREAD_COUNT) \ + stack exec interact + test-tutorial: install ## Make sure the tutorial builds stack test nameservice:tutorial diff --git a/hs-abci-examples/nameservice/interact/Interact.hs b/hs-abci-examples/nameservice/interact/Interact.hs new file mode 100644 index 00000000..7b590951 --- /dev/null +++ b/hs-abci-examples/nameservice/interact/Interact.hs @@ -0,0 +1,193 @@ +module Interact + ( actionBlock + , makeRandomUsers + ) where + +import Control.Monad (replicateM, void) +import Control.Monad.Reader (ReaderT, runReaderT) +import Data.Char (isHexDigit) +import Data.Default.Class (def) +import Data.Proxy +import Data.String (fromString) +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Faker.Lorem as Lorem +import qualified Faker.Name as Name +import qualified Faker.Utils as Utils +import Nameservice.Application +import qualified Nameservice.Modules.Nameservice as N +import qualified Nameservice.Modules.Token as T +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.Application.Module (AppQueryRouter (QApi), + AppTxRouter (TApi)) +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + QueryResult (..)) +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Types.Address (Address) +import Tendermint.Utils.Client (ClientConfig (..), + EmptyTxClient (..), + HasQueryClient (..), + HasTxClient (..), + QueryClientResponse (..), + Signer (..), + TxClientResponse (..), + TxOpts (..), + defaultClientTxOpts) +import Tendermint.Utils.ClientUtils (assertTx, rpcConfig) +import Tendermint.Utils.User (makeSignerFromUser, + makeUser) +import Test.RandomStrings (onlyWith, randomASCII, + randomString) + +-------------------------------------------------------------------------------- +-- Actions +-------------------------------------------------------------------------------- + +faucetAccount :: Signer -> T.Amount -> IO () +faucetAccount s@(Signer addr _) amount = + runAction_ s faucet $ T.FaucetAccount addr amount + +createName :: Signer -> N.Name -> Text -> IO () +createName s name val = buyName s name val 0 + +buyName :: Signer -> N.Name -> Text -> T.Amount -> IO () +buyName s@(Signer addr _) name newVal amount = + runAction_ s buy $ N.BuyName amount name newVal addr + +deleteName :: Signer -> N.Name -> IO () +deleteName s@(Signer addr _) name = + runAction_ s delete $ N.DeleteName addr name + +setName :: Signer -> N.Name -> Text -> IO () +setName s@(Signer addr _) name val = + runAction_ s set $ N.SetName name addr val + +runAction_ + :: Signer + -> (TxOpts -> msg -> TxClientM (TxClientResponse () ())) + -> msg + -> IO () +runAction_ s f = void . assertTx . runTxClientM . f (TxOpts 0 s) + +actionBlock :: (Signer, Signer) -> IO () +actionBlock (s1, s2) = do + name <- genName + genCVal <- genWords + genBVal <- genWords + genBAmt <- genAmount + genSVal <- genWords + faucetAccount s2 genBAmt + createName s1 name genCVal + buyName s2 name genBVal genBAmt + setName s2 name genSVal + deleteName s2 name + +-------------------------------------------------------------------------------- +-- Users +-------------------------------------------------------------------------------- + +makeRandomUsers :: IO (Signer, Signer) +makeRandomUsers = do + str1 <- randomString (onlyWith isHexDigit randomASCII) 64 + str2 <- randomString (onlyWith isHexDigit randomASCII) 64 + return $ (makeSignerFromUser . makeUser $ str1 + ,makeSignerFromUser . makeUser $ str2 + ) + +-------------------------------------------------------------------------------- +-- Query Client +-------------------------------------------------------------------------------- + +getAccount + :: QueryArgs Address + -> RPC.TendermintM (QueryClientResponse Auth.Account) + +_ :<|> _ :<|> getAccount = + genClientQ (Proxy :: Proxy m) queryApiP def + where + queryApiP :: Proxy (QApi NameserviceModules) + queryApiP = Proxy + + -------------------------------------------------------------------------------- +-- Tx Client +-------------------------------------------------------------------------------- + +txClientConfig :: ClientConfig +txClientConfig = + let getNonce addr = do + resp <- RPC.runTendermintM rpcConfig $ getAccount $ + QueryArgs + { queryArgsHeight = -1 + , queryArgsProve = False + , queryArgsData = addr + } + case resp of + QueryError e -> + if appErrorCode e == 2 + then pure 0 + else error $ "Unknown nonce error: " <> show (appErrorMessage e) + QueryResponse QueryResult {queryResultData} -> + pure $ Auth.accountNonce queryResultData + + in ClientConfig + { clientGetNonce = getNonce + , clientRPC = rpcConfig + } + +type TxClientM = ReaderT ClientConfig IO + +runTxClientM :: TxClientM a -> IO a +runTxClientM m = runReaderT m txClientConfig + +-- Nameservice Client +buy + :: TxOpts + -> N.BuyName + -> TxClientM (TxClientResponse () ()) + +set + :: TxOpts + -> N.SetName + -> TxClientM (TxClientResponse () ()) + +delete + :: TxOpts + -> N.DeleteName + -> TxClientM (TxClientResponse () ()) + +-- Token Client +faucet + :: TxOpts + -> T.FaucetAccount + -> TxClientM (TxClientResponse () ()) + +(buy :<|> set :<|> delete) :<|> + (_ :<|> _ :<|> faucet) :<|> + EmptyTxClient = + genClientT (Proxy @TxClientM) txApiP defaultClientTxOpts + where + txApiP :: Proxy (TApi NameserviceModules) + txApiP = Proxy + + +-------------------------------------------------------------------------------- +-- Generation +-------------------------------------------------------------------------------- + +genWords :: IO Text +genWords = do + numWords <- Utils.randomNum (1, 10) + ws <- replicateM numWords Lorem.word + return . cs . unwords $ ws + +genName :: IO N.Name +genName = do + name <- Name.name + return . fromString $ name + +genAmount :: IO T.Amount +genAmount = do + genAmt <- Utils.randomNum (1, 1000) + return . fromInteger . toInteger $ genAmt diff --git a/hs-abci-examples/nameservice/interact/Main.hs b/hs-abci-examples/nameservice/interact/Main.hs new file mode 100644 index 00000000..c0cb01e1 --- /dev/null +++ b/hs-abci-examples/nameservice/interact/Main.hs @@ -0,0 +1,17 @@ +module Main where + +import Control.Concurrent.Async (forConcurrently_) +import Control.Monad (forever, replicateM) +import Data.Maybe (maybe) +import Interact +import System.Environment (lookupEnv) +import Text.Read (read) + +main :: IO () +main = do + mThreads <- lookupEnv "INTERACT_THREAD_COUNT" + let threads = maybe 1 read mThreads :: Int + usersForThreads <- replicateM threads makeRandomUsers + putStrLn $ "Running nameservice interaction with #threads: " <> show threads + forever $ forConcurrently_ [0..(threads-1)] $ \i -> + actionBlock $ usersForThreads !! i diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-examples/nameservice/package.yaml index d1481d96..e57a0f25 100644 --- a/hs-abci-examples/nameservice/package.yaml +++ b/hs-abci-examples/nameservice/package.yaml @@ -128,6 +128,30 @@ executables: - proto3-suite - proto3-wire + interact: + main: Main.hs + source-dirs: interact + ghc-options: + - -threaded + - -rtsopts + - -with-rtsopts=-N + - -Werror + - -Wall + dependencies: + - async + - base + - data-default-class + - faker + - mtl + - nameservice + - hs-abci-sdk + - hs-abci-test-utils + - hs-tendermint-client + - random-strings + - servant + - string-conversions + - text + tests: tutorial: main: README.lhs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 90ac46bc..5c510eed 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -3,6 +3,7 @@ module Nameservice.Modules.Nameservice.Types where import Control.Lens (iso) import Data.Aeson as A import Data.Bifunctor (first) +import Data.String (IsString (..)) import Data.String.Conversions (cs) import Data.Text (Text) import qualified Data.Text.Lazy as TL @@ -33,6 +34,8 @@ instance Primitive Name where primType _ = DotProto.String instance HasDefault Name instance MessageField Name +instance IsString Name where + fromString = Name . fromString instance BaseApp.FromQueryData Name diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs index 258c2641..379d6b24 100644 --- a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs +++ b/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs @@ -75,7 +75,7 @@ transfer transfer addr1 amount addr2 = do -- check if addr1 has amt addr1Bal <- getBalance addr1 - if addr1Bal > amount + if addr1Bal >= amount then do addr2Bal <- getBalance addr2 let newBalance1 = addr1Bal - amount diff --git a/stack.yaml b/stack.yaml index f32754da..ebd00ff3 100644 --- a/stack.yaml +++ b/stack.yaml @@ -17,6 +17,10 @@ packages: - ./hs-iavl-client extra-deps: + - async-2.2.2 + - faker-0.0.0.2 + - gimlh-0.1.3.0 + - random-strings-0.1.1.0 - proto-lens-runtime-0.5.0.0 - proto-lens-setup-0.4.0.2 - lens-labels-0.3.0.1 diff --git a/stack.yaml.lock b/stack.yaml.lock index beccf8ec..4146e7a1 100644 --- a/stack.yaml.lock +++ b/stack.yaml.lock @@ -4,6 +4,34 @@ # https://docs.haskellstack.org/en/stable/lock_files packages: +- completed: + hackage: async-2.2.2@sha256:ed46f0f5be36cf8a3e3aebc6827d015e1f3bf9615c245e057b9e9bd35faddd21,2895 + pantry-tree: + size: 501 + sha256: dab5a4c2126fbce3f4a7c15ccf66e60de61d3eccae071f4bfbad036087399f32 + original: + hackage: async-2.2.2 +- completed: + hackage: faker-0.0.0.2@sha256:e181a9dba8022098d2cca9822b6a616a28d3013ee978076b7c7cd18b6e15c8eb,980 + pantry-tree: + size: 792 + sha256: d1fd5fcf4175f259b84f9036ab8d53d23457eb1ab9eab163dce83e8a8d7fca65 + original: + hackage: faker-0.0.0.2 +- completed: + hackage: gimlh-0.1.3.0@sha256:0cb3513ec36b7f935956b68875de40a05e934cf75499918e1db533b7d32dfc46,747 + pantry-tree: + size: 201 + sha256: 106e63ee076f0339ae5e15c599f5eb15d1e663f1f2303417000f26c9514f24c6 + original: + hackage: gimlh-0.1.3.0 +- completed: + hackage: random-strings-0.1.1.0@sha256:935a7a23dab45411960df77636a29b44ce42b89eeb15f2b1e809d771491fa677,2517 + pantry-tree: + size: 663 + sha256: 5a382966fdd8d5220b5791f3bff6db00d2ea29235e2716dadd52461b8a8beb97 + original: + hackage: random-strings-0.1.1.0 - completed: hackage: proto-lens-runtime-0.5.0.0@sha256:cb39cf13ce4f7dac5414f94a7afe0adc9b831312e6b60588a23bd816accc385f,3132 pantry-tree: From ab6477b4c0669db10b1828d397ce354726ee7717 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Sat, 1 Feb 2020 09:03:44 -0800 Subject: [PATCH 52/70] website for docs (#190) * Fixes #203 * Travis uploads haddocks * Link to local docs * add navigation structure * merge upstream --- .travis.yml | 26 +++++++++++++++--- Makefile | 21 ++++++++++++-- hs-abci-docs/.tintin.yml | 10 +++++++ hs-abci-docs/Makefile | 15 ++++++++++ hs-abci-docs/doc/0100-Tutorial.md | 1 + hs-abci-docs/doc/0310-Overview.md | 1 + hs-abci-docs/doc/0320-BaseApp.md | 1 + hs-abci-docs/doc/0330-Modules.md | 1 + hs-abci-docs/doc/0410-Overview.md | 1 + hs-abci-docs/doc/0420-Types.md | 1 + hs-abci-docs/doc/0430-Message.md | 1 + hs-abci-docs/doc/0440-Keeper.md | 1 + hs-abci-docs/doc/0450-Query.md | 1 + hs-abci-docs/doc/0460-Module.md | 1 + hs-abci-docs/doc/0470-Application.md | 1 + .../doc/98-Logging.md | 4 +++ .../doc/99-Metrics.md | 4 +++ hs-abci-docs/doc/index.md | 7 +++++ .../nameservice/.gitignore | 0 .../nameservice/README.md | 0 .../nameservice/Setup.hs | 0 .../nameservice/app/Main.hs | 0 .../conf.d/openmetrics.d/conf.yaml | 0 .../nameservice/docker-compose-test.yaml | 0 .../nameservice/docker-compose.yaml | 0 .../nameservice/images/kibana_discover.png | Bin .../images/kibana_discover_filter.png | Bin .../kibana_discover_filter_advanced.png | Bin .../nameservice/images/kibana_management.png | Bin .../images/kibana_management_2.png | Bin .../images/kibana_welcome_screen.png | Bin .../nameservice/interact/Interact.hs | 0 .../nameservice/interact/Main.hs | 0 .../nameservice/package.yaml | 0 .../nameservice/protogen/Main.hs | 0 .../nameservice/protogen/Protogen.hs | 0 .../nameservice/src/Nameservice/Aeson.hs | 0 .../src/Nameservice/Application.hs | 0 .../nameservice/src/Nameservice/Config.hs | 0 .../src/Nameservice/Modules/Nameservice.hs | 0 .../Nameservice/Modules/Nameservice/Keeper.hs | 0 .../Modules/Nameservice/Messages.hs | 0 .../Nameservice/Modules/Nameservice/Query.hs | 0 .../Nameservice/Modules/Nameservice/Router.hs | 0 .../Nameservice/Modules/Nameservice/Types.hs | 0 .../src/Nameservice/Modules/Token.hs | 0 .../src/Nameservice/Modules/Token/Keeper.hs | 0 .../src/Nameservice/Modules/Token/Messages.hs | 0 .../src/Nameservice/Modules/Token/Query.hs | 0 .../src/Nameservice/Modules/Token/Router.hs | 0 .../src/Nameservice/Modules/Token/Types.hs | 0 .../nameservice/src/Nameservice/Server.hs | 0 .../test/Nameservice/Test/E2ESpec.hs | 0 .../test/Nameservice/Test/EventOrphans.hs | 0 .../nameservice/test/Spec.hs | 0 .../tutorial/Foundations/01-Overview.md | 4 +++ .../tutorial/Foundations/02-BaseApp.md | 4 +++ .../tutorial/Foundations/03-Modules.md | 4 +++ .../nameservice/tutorial/README.lhs | 0 .../nameservice/tutorial/README.md | 4 +++ .../Tutorial/Nameservice/01-Overview.md | 6 +++- .../tutorial/Tutorial/Nameservice/02-Types.md | 4 +++ .../Tutorial/Nameservice/03-Message.md | 4 +++ .../Tutorial/Nameservice/04-Keeper.md | 4 +++ .../tutorial/Tutorial/Nameservice/05-Query.md | 4 +++ .../Tutorial/Nameservice/06-Module.md | 4 +++ .../Tutorial/Nameservice/07-Application.md | 4 +++ .../Tutorial/Nameservice/Application.lhs | 1 + .../tutorial/Tutorial/Nameservice/Keeper.lhs | 1 + .../tutorial/Tutorial/Nameservice/Message.lhs | 1 + .../tutorial/Tutorial/Nameservice/Module.lhs | 1 + .../tutorial/Tutorial/Nameservice/Query.lhs | 1 + .../tutorial/Tutorial/Nameservice/Router.md | 0 .../tutorial/Tutorial/Nameservice/Types.lhs | 1 + hs-abci-docs/package.yaml | 9 ++++++ .../simple-storage/README.md | 0 .../simple-storage/Setup.hs | 0 .../simple-storage/app/Main.hs | 0 .../simple-storage/docker-compose.yaml | 0 .../simple-storage/package.yaml | 0 .../protos/simple-storage/messages.proto | 0 .../src/SimpleStorage/Application.hs | 0 .../src/SimpleStorage/Config.hs | 0 .../SimpleStorage/Modules/SimpleStorage.hs | 0 .../Modules/SimpleStorage/Keeper.hs | 0 .../Modules/SimpleStorage/Message.hs | 0 .../Modules/SimpleStorage/Query.hs | 0 .../Modules/SimpleStorage/Router.hs | 0 .../Modules/SimpleStorage/Types.hs | 0 .../src/SimpleStorage/Server.hs | 0 .../test/SimpleStorage/Test/E2ESpec.hs | 0 .../simple-storage/test/Spec.hs | 0 .../Tutorial/Nameservice/Application.lhs | 1 - .../tutorial/Tutorial/Nameservice/Keeper.lhs | 1 - .../tutorial/Tutorial/Nameservice/Message.lhs | 1 - .../tutorial/Tutorial/Nameservice/Module.lhs | 1 - .../tutorial/Tutorial/Nameservice/Query.lhs | 1 - .../tutorial/Tutorial/Nameservice/Types.lhs | 1 - hs-abci-sdk/package.yaml | 2 +- .../SDK/BaseApp/Metrics/Prometheus.hs | 10 +++---- stack.yaml | 4 +-- 101 files changed, 160 insertions(+), 21 deletions(-) create mode 100644 hs-abci-docs/.tintin.yml create mode 100644 hs-abci-docs/Makefile create mode 120000 hs-abci-docs/doc/0100-Tutorial.md create mode 120000 hs-abci-docs/doc/0310-Overview.md create mode 120000 hs-abci-docs/doc/0320-BaseApp.md create mode 120000 hs-abci-docs/doc/0330-Modules.md create mode 120000 hs-abci-docs/doc/0410-Overview.md create mode 120000 hs-abci-docs/doc/0420-Types.md create mode 120000 hs-abci-docs/doc/0430-Message.md create mode 120000 hs-abci-docs/doc/0440-Keeper.md create mode 120000 hs-abci-docs/doc/0450-Query.md create mode 120000 hs-abci-docs/doc/0460-Module.md create mode 120000 hs-abci-docs/doc/0470-Application.md rename hs-abci-examples/nameservice/docs/Logging.md => hs-abci-docs/doc/98-Logging.md (99%) rename hs-abci-examples/nameservice/docs/Metrics.md => hs-abci-docs/doc/99-Metrics.md (98%) create mode 100644 hs-abci-docs/doc/index.md rename {hs-abci-examples => hs-abci-docs}/nameservice/.gitignore (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/README.md (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/Setup.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/app/Main.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/conf.d/openmetrics.d/conf.yaml (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/docker-compose-test.yaml (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/docker-compose.yaml (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/images/kibana_discover.png (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/images/kibana_discover_filter.png (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/images/kibana_discover_filter_advanced.png (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/images/kibana_management.png (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/images/kibana_management_2.png (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/images/kibana_welcome_screen.png (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/interact/Interact.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/interact/Main.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/package.yaml (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/protogen/Main.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/protogen/Protogen.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Aeson.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Application.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Config.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Nameservice.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Nameservice/Query.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Nameservice/Router.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Nameservice/Types.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Token.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Token/Keeper.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Token/Messages.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Token/Query.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Token/Router.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Modules/Token/Types.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/src/Nameservice/Server.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/test/Nameservice/Test/E2ESpec.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/test/Nameservice/Test/EventOrphans.hs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/test/Spec.hs (100%) rename hs-abci-examples/nameservice/tutorial/Foundations/Overview.md => hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md (97%) rename hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md => hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md (97%) rename hs-abci-examples/nameservice/tutorial/Foundations/Modules.md => hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md (98%) rename {hs-abci-examples => hs-abci-docs}/nameservice/tutorial/README.lhs (100%) rename {hs-abci-examples => hs-abci-docs}/nameservice/tutorial/README.md (99%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/01-Overview.md (96%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md (99%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md (99%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md (99%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md (98%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md (98%) rename hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md => hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md (98%) create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Message.lhs create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Query.lhs rename {hs-abci-examples => hs-abci-docs}/nameservice/tutorial/Tutorial/Nameservice/Router.md (100%) create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Types.lhs create mode 100644 hs-abci-docs/package.yaml rename {hs-abci-examples => hs-abci-docs}/simple-storage/README.md (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/Setup.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/app/Main.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/docker-compose.yaml (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/package.yaml (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/protos/simple-storage/messages.proto (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Application.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Config.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/src/SimpleStorage/Server.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/test/SimpleStorage/Test/E2ESpec.hs (100%) rename {hs-abci-examples => hs-abci-docs}/simple-storage/test/Spec.hs (100%) delete mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs delete mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs delete mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs delete mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs delete mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs delete mode 120000 hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs diff --git a/.travis.yml b/.travis.yml index 52d14ccc..13d685a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -70,13 +70,31 @@ jobs: - echo "Test simple-storage" - make docker-test-prebake - - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e up -d + - docker-compose -f hs-abci-docs/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e up -d - make test-simple-storage - - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e down -v --rmi local + - docker-compose -f hs-abci-docs/simple-storage/docker-compose.yaml -p test-hs-abci-examples-simple-storage-e2e down -v --rmi local - echo "Test nameservice" - make docker-test-prebake - - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e up -d + - docker-compose -f hs-abci-docs/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e up -d - make test-nameservice - - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local + - docker-compose -f hs-abci-docs/nameservice/docker-compose-test.yaml -p test-hs-abci-examples-nameservice-e2e down -v --rmi local + - stage: publish website + script: + - echo "building website" + - travis_wait 120 stack --skip-ghc-check install tintin + - make build-site + - make build-docs-local + - mkdir -p ./hs-abci-docs/.stack-work/tintin/rendered/haddocks + - find ./ -type f -name "index.html" | grep -v tintin | sed 's/index.html//g' | xargs -I {} cp -r {} hs-abci-docs/.stack-work/tintin/rendered/haddocks/ + deploy: + provider: pages + local-dir: ./hs-abci-docs/.stack-work/tintin/rendered + email: deploy@travis-ci.org + name: Deployment Bot + skip-cleanup: true + github-token: $GITHUB_TOKEN + keep-history: true + on: + branch: master diff --git a/Makefile b/Makefile index 3a09ac83..e4b74fe7 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,15 @@ SIMPLE_STORAGE_BINARY := $(shell stack exec -- which simple-storage) help: ## Ask for help! @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' +# Thank you Apple +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + SED=sed -i'' +endif +ifeq ($(UNAME_S),Darwin) + SED=sed -i '' +endif + ##################### # Linting and Styling ##################### @@ -22,8 +31,8 @@ hlint: ## Run hlint on all haskell projects hs-abci-extra \ hs-abci-sdk \ hs-abci-test-utils \ - hs-abci-examples/simple-storage \ - hs-abci-examples/nameservice \ + hs-abci-docs/simple-storage \ + hs-abci-docs/nameservice \ hs-iavl-client stylish: ## Run stylish-haskell over all haskell projects @@ -42,8 +51,16 @@ stylish: ## Run stylish-haskell over all haskell projects ################### build-docs-local: ## Build the haddocks documentation for just this project (no dependencies) + find . -type f -name "package.yaml" -exec $(SED) -e 's/- -fplugin=Polysemy.Plugin/- -fdefer-type-errors/g' {} + && \ + find . -type f -name "package.yaml" -exec $(SED) -e 's/- -Wall/- -fno-warn-deferred-type-errors/g' {} + && \ stack haddock --no-haddock-deps +build-site: ## Build the tintin site + find ./hs-abci-docs/ -type f -name "*.md" -exec $(SED) -e 's/~~~ haskell.*/```haskell/g' {} + && \ + find ./hs-abci-docs/ -type f -name "*.md" -exec $(SED) -e 's/~~~/```/g' {} + && \ + cd hs-abci-docs && \ + tintin run + ##################### # Core Libraries ##################### diff --git a/hs-abci-docs/.tintin.yml b/hs-abci-docs/.tintin.yml new file mode 100644 index 00000000..362ef2aa --- /dev/null +++ b/hs-abci-docs/.tintin.yml @@ -0,0 +1,10 @@ +name: kepler +synopsis: Haskell Cosmos SDK +github: f-o-a-m/kepler +author: Martin Allen +authorWebsite: https://foam.space +color: #5E5184 +#logo: https://pbs.twimg.com/profile_images/791467713956839424/pBRQn1wt_400x400.jpg +titleFont: Poiret One +titleFontWeight: 400 +bodyFont: PT Sans diff --git a/hs-abci-docs/Makefile b/hs-abci-docs/Makefile new file mode 100644 index 00000000..90f650dc --- /dev/null +++ b/hs-abci-docs/Makefile @@ -0,0 +1,15 @@ +# Thank you Apple +UNAME_S := $(shell uname -s) +ifeq ($(UNAME_S),Linux) + SED=sed -i'' +endif +ifeq ($(UNAME_S),Darwin) + SED=sed -i '' +endif + +pre-process: + find ./doc/ -type f,l -name "*.md" -exec $(SED) -e 's/~~~ haskell.*/```haskell/g' {} + && \ + find ./doc/ -type f,l -name "*.md" -exec $(SED) -e 's/~~~/```/g' {} + + +tintin: + tintin run diff --git a/hs-abci-docs/doc/0100-Tutorial.md b/hs-abci-docs/doc/0100-Tutorial.md new file mode 120000 index 00000000..b8f2d6e4 --- /dev/null +++ b/hs-abci-docs/doc/0100-Tutorial.md @@ -0,0 +1 @@ +../nameservice/tutorial/README.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0310-Overview.md b/hs-abci-docs/doc/0310-Overview.md new file mode 120000 index 00000000..57c4381d --- /dev/null +++ b/hs-abci-docs/doc/0310-Overview.md @@ -0,0 +1 @@ +../nameservice/tutorial/Foundations/01-Overview.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0320-BaseApp.md b/hs-abci-docs/doc/0320-BaseApp.md new file mode 120000 index 00000000..a496f4c5 --- /dev/null +++ b/hs-abci-docs/doc/0320-BaseApp.md @@ -0,0 +1 @@ +../nameservice/tutorial/Foundations/02-BaseApp.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0330-Modules.md b/hs-abci-docs/doc/0330-Modules.md new file mode 120000 index 00000000..dad0f248 --- /dev/null +++ b/hs-abci-docs/doc/0330-Modules.md @@ -0,0 +1 @@ +../nameservice/tutorial/Foundations/03-Modules.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0410-Overview.md b/hs-abci-docs/doc/0410-Overview.md new file mode 120000 index 00000000..c7f7ff49 --- /dev/null +++ b/hs-abci-docs/doc/0410-Overview.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/01-Overview.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0420-Types.md b/hs-abci-docs/doc/0420-Types.md new file mode 120000 index 00000000..19d081b0 --- /dev/null +++ b/hs-abci-docs/doc/0420-Types.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/02-Types.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0430-Message.md b/hs-abci-docs/doc/0430-Message.md new file mode 120000 index 00000000..7a036a3d --- /dev/null +++ b/hs-abci-docs/doc/0430-Message.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/03-Message.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0440-Keeper.md b/hs-abci-docs/doc/0440-Keeper.md new file mode 120000 index 00000000..c0f6820e --- /dev/null +++ b/hs-abci-docs/doc/0440-Keeper.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0450-Query.md b/hs-abci-docs/doc/0450-Query.md new file mode 120000 index 00000000..b9730e13 --- /dev/null +++ b/hs-abci-docs/doc/0450-Query.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/05-Query.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0460-Module.md b/hs-abci-docs/doc/0460-Module.md new file mode 120000 index 00000000..6168482b --- /dev/null +++ b/hs-abci-docs/doc/0460-Module.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/06-Module.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0470-Application.md b/hs-abci-docs/doc/0470-Application.md new file mode 120000 index 00000000..33990a65 --- /dev/null +++ b/hs-abci-docs/doc/0470-Application.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/07-Application.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/docs/Logging.md b/hs-abci-docs/doc/98-Logging.md similarity index 99% rename from hs-abci-examples/nameservice/docs/Logging.md rename to hs-abci-docs/doc/98-Logging.md index 23287796..eaf50c78 100644 --- a/hs-abci-examples/nameservice/docs/Logging.md +++ b/hs-abci-docs/doc/98-Logging.md @@ -1,3 +1,7 @@ +--- +title: Logging +--- + # Logging The SDK has built in support for structured logging via the [katip](https://hackage.haskell.org/package/katip) logging library. Even still, the SDK is agnostic to where you want your logs to go. Katip logs are managed by *scribes* whos job is precisely this, so the output depends on which scribes you use. The two most common scribes are the [console scribe](https://hackage.haskell.org/package/katip-0.8.3.0/docs/Katip-Scribes-Handle.html#v:mkHandleScribe) and the [Elasticsearch scribe](https://hackage.haskell.org/package/katip-elasticsearch). diff --git a/hs-abci-examples/nameservice/docs/Metrics.md b/hs-abci-docs/doc/99-Metrics.md similarity index 98% rename from hs-abci-examples/nameservice/docs/Metrics.md rename to hs-abci-docs/doc/99-Metrics.md index 128fa7bf..06efc9da 100644 --- a/hs-abci-examples/nameservice/docs/Metrics.md +++ b/hs-abci-docs/doc/99-Metrics.md @@ -1,3 +1,7 @@ +--- +title: Metrics +--- + # Metrics The SDK has some built in support for metrics via [prometheus](https://prometheus.io/), but ultimately you may choose a different runtime interpretation for the metrics, or even choose to ignore it entirely. diff --git a/hs-abci-docs/doc/index.md b/hs-abci-docs/doc/index.md new file mode 100644 index 00000000..1d6def92 --- /dev/null +++ b/hs-abci-docs/doc/index.md @@ -0,0 +1,7 @@ +--- +title: Home +--- + +The home of Kepler. +Link to [hackage](https://hackage.haskell.org/package/kepler) +Link to [hs-abci-sdk](haddocks/hs-abci-sdk/) diff --git a/hs-abci-examples/nameservice/.gitignore b/hs-abci-docs/nameservice/.gitignore similarity index 100% rename from hs-abci-examples/nameservice/.gitignore rename to hs-abci-docs/nameservice/.gitignore diff --git a/hs-abci-examples/nameservice/README.md b/hs-abci-docs/nameservice/README.md similarity index 100% rename from hs-abci-examples/nameservice/README.md rename to hs-abci-docs/nameservice/README.md diff --git a/hs-abci-examples/nameservice/Setup.hs b/hs-abci-docs/nameservice/Setup.hs similarity index 100% rename from hs-abci-examples/nameservice/Setup.hs rename to hs-abci-docs/nameservice/Setup.hs diff --git a/hs-abci-examples/nameservice/app/Main.hs b/hs-abci-docs/nameservice/app/Main.hs similarity index 100% rename from hs-abci-examples/nameservice/app/Main.hs rename to hs-abci-docs/nameservice/app/Main.hs diff --git a/hs-abci-examples/nameservice/conf.d/openmetrics.d/conf.yaml b/hs-abci-docs/nameservice/conf.d/openmetrics.d/conf.yaml similarity index 100% rename from hs-abci-examples/nameservice/conf.d/openmetrics.d/conf.yaml rename to hs-abci-docs/nameservice/conf.d/openmetrics.d/conf.yaml diff --git a/hs-abci-examples/nameservice/docker-compose-test.yaml b/hs-abci-docs/nameservice/docker-compose-test.yaml similarity index 100% rename from hs-abci-examples/nameservice/docker-compose-test.yaml rename to hs-abci-docs/nameservice/docker-compose-test.yaml diff --git a/hs-abci-examples/nameservice/docker-compose.yaml b/hs-abci-docs/nameservice/docker-compose.yaml similarity index 100% rename from hs-abci-examples/nameservice/docker-compose.yaml rename to hs-abci-docs/nameservice/docker-compose.yaml diff --git a/hs-abci-examples/nameservice/images/kibana_discover.png b/hs-abci-docs/nameservice/images/kibana_discover.png similarity index 100% rename from hs-abci-examples/nameservice/images/kibana_discover.png rename to hs-abci-docs/nameservice/images/kibana_discover.png diff --git a/hs-abci-examples/nameservice/images/kibana_discover_filter.png b/hs-abci-docs/nameservice/images/kibana_discover_filter.png similarity index 100% rename from hs-abci-examples/nameservice/images/kibana_discover_filter.png rename to hs-abci-docs/nameservice/images/kibana_discover_filter.png diff --git a/hs-abci-examples/nameservice/images/kibana_discover_filter_advanced.png b/hs-abci-docs/nameservice/images/kibana_discover_filter_advanced.png similarity index 100% rename from hs-abci-examples/nameservice/images/kibana_discover_filter_advanced.png rename to hs-abci-docs/nameservice/images/kibana_discover_filter_advanced.png diff --git a/hs-abci-examples/nameservice/images/kibana_management.png b/hs-abci-docs/nameservice/images/kibana_management.png similarity index 100% rename from hs-abci-examples/nameservice/images/kibana_management.png rename to hs-abci-docs/nameservice/images/kibana_management.png diff --git a/hs-abci-examples/nameservice/images/kibana_management_2.png b/hs-abci-docs/nameservice/images/kibana_management_2.png similarity index 100% rename from hs-abci-examples/nameservice/images/kibana_management_2.png rename to hs-abci-docs/nameservice/images/kibana_management_2.png diff --git a/hs-abci-examples/nameservice/images/kibana_welcome_screen.png b/hs-abci-docs/nameservice/images/kibana_welcome_screen.png similarity index 100% rename from hs-abci-examples/nameservice/images/kibana_welcome_screen.png rename to hs-abci-docs/nameservice/images/kibana_welcome_screen.png diff --git a/hs-abci-examples/nameservice/interact/Interact.hs b/hs-abci-docs/nameservice/interact/Interact.hs similarity index 100% rename from hs-abci-examples/nameservice/interact/Interact.hs rename to hs-abci-docs/nameservice/interact/Interact.hs diff --git a/hs-abci-examples/nameservice/interact/Main.hs b/hs-abci-docs/nameservice/interact/Main.hs similarity index 100% rename from hs-abci-examples/nameservice/interact/Main.hs rename to hs-abci-docs/nameservice/interact/Main.hs diff --git a/hs-abci-examples/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml similarity index 100% rename from hs-abci-examples/nameservice/package.yaml rename to hs-abci-docs/nameservice/package.yaml diff --git a/hs-abci-examples/nameservice/protogen/Main.hs b/hs-abci-docs/nameservice/protogen/Main.hs similarity index 100% rename from hs-abci-examples/nameservice/protogen/Main.hs rename to hs-abci-docs/nameservice/protogen/Main.hs diff --git a/hs-abci-examples/nameservice/protogen/Protogen.hs b/hs-abci-docs/nameservice/protogen/Protogen.hs similarity index 100% rename from hs-abci-examples/nameservice/protogen/Protogen.hs rename to hs-abci-docs/nameservice/protogen/Protogen.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Aeson.hs b/hs-abci-docs/nameservice/src/Nameservice/Aeson.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Aeson.hs rename to hs-abci-docs/nameservice/src/Nameservice/Aeson.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Application.hs b/hs-abci-docs/nameservice/src/Nameservice/Application.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Application.hs rename to hs-abci-docs/nameservice/src/Nameservice/Application.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Config.hs b/hs-abci-docs/nameservice/src/Nameservice/Config.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Config.hs rename to hs-abci-docs/nameservice/src/Nameservice/Config.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Query.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Router.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Nameservice/Types.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Token.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Keeper.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Messages.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Messages.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Messages.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Query.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Router.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Types.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Modules/Token/Types.hs rename to hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Types.hs diff --git a/hs-abci-examples/nameservice/src/Nameservice/Server.hs b/hs-abci-docs/nameservice/src/Nameservice/Server.hs similarity index 100% rename from hs-abci-examples/nameservice/src/Nameservice/Server.hs rename to hs-abci-docs/nameservice/src/Nameservice/Server.hs diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs similarity index 100% rename from hs-abci-examples/nameservice/test/Nameservice/Test/E2ESpec.hs rename to hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs diff --git a/hs-abci-examples/nameservice/test/Nameservice/Test/EventOrphans.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/EventOrphans.hs similarity index 100% rename from hs-abci-examples/nameservice/test/Nameservice/Test/EventOrphans.hs rename to hs-abci-docs/nameservice/test/Nameservice/Test/EventOrphans.hs diff --git a/hs-abci-examples/nameservice/test/Spec.hs b/hs-abci-docs/nameservice/test/Spec.hs similarity index 100% rename from hs-abci-examples/nameservice/test/Spec.hs rename to hs-abci-docs/nameservice/test/Spec.hs diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/Overview.md b/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md similarity index 97% rename from hs-abci-examples/nameservice/tutorial/Foundations/Overview.md rename to hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md index 145c9432..9d23bab5 100644 --- a/hs-abci-examples/nameservice/tutorial/Foundations/Overview.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md @@ -1,3 +1,7 @@ +--- +title: Foundations - Overview +--- + # Overview The SDK relies heavily on two abstractions to facilitate application development, effects systems and Modules. diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md similarity index 97% rename from hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md rename to hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md index 6c321ad7..163ac17f 100644 --- a/hs-abci-examples/nameservice/tutorial/Foundations/BaseApp.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md @@ -1,3 +1,7 @@ +--- +title: Foundations - BaseApp +--- + # BaseApp `BaseApp` is the set of effects that the SDK operates with and are freely available diff --git a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md similarity index 98% rename from hs-abci-examples/nameservice/tutorial/Foundations/Modules.md rename to hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md index c2b33654..9f18874f 100644 --- a/hs-abci-examples/nameservice/tutorial/Foundations/Modules.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md @@ -1,3 +1,7 @@ +--- +title: Foundations - Module +--- + # Modules ## Definition diff --git a/hs-abci-examples/nameservice/tutorial/README.lhs b/hs-abci-docs/nameservice/tutorial/README.lhs similarity index 100% rename from hs-abci-examples/nameservice/tutorial/README.lhs rename to hs-abci-docs/nameservice/tutorial/README.lhs diff --git a/hs-abci-examples/nameservice/tutorial/README.md b/hs-abci-docs/nameservice/tutorial/README.md similarity index 99% rename from hs-abci-examples/nameservice/tutorial/README.md rename to hs-abci-docs/nameservice/tutorial/README.md index 1ee84be8..4ce23e72 100644 --- a/hs-abci-examples/nameservice/tutorial/README.md +++ b/hs-abci-docs/nameservice/tutorial/README.md @@ -1,3 +1,7 @@ +--- +title: Overview +--- + ## Introduction We're going to build an example application that mirrors the `golang` [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) example application called [Nameservice](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). There is also a tutorial for that application which you can find [here](https://tutorials.cosmos.network/nameservice/tutorial/00-intro.html) for comparison. diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/01-Overview.md similarity index 96% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/01-Overview.md index c2925ec7..37a31aaa 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Overview.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/01-Overview.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Overview +--- + # Overview This section is where we sketch the definition of the Nameservice module and application. It's to everyones benefit if module structures follow a similar file heirachy as the Nameservice module, or any module found in the SDK. In the case of Nameservice this roughly translates to @@ -22,4 +26,4 @@ The contents of these modules are roughly as follows: - `Nameservice.Router` - Defines the transaction router for the module. - `Namervice` Defines the module itself and re-exports any types or utils necessary for using this module as a dependency. -The reason why we suggest this is that each of these haskell modules is buiding up one of the core components of our defition of a module, and it provides a nice logical split between these pieces. \ No newline at end of file +The reason why we suggest this is that each of these haskell modules is buiding up one of the core components of our defition of a module, and it provides a nice logical split between these pieces. diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md similarity index 99% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md index b698a019..f6c5aa82 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Types +--- + # Types The `Types` module is used to define the basic types that the module will make use of. This includes things like custom error types, event types, database types, etc. diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md similarity index 99% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md index 39220d24..5984b55e 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Message +--- + # Message ## Message Types diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md similarity index 99% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md index ad163891..5ae099f5 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Keeper +--- + # Keeper ## Definition diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md similarity index 98% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md index 8f8d8bc1..90325ebf 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Query +--- + # Query ## Tutorial.Nameservice.Query diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md similarity index 98% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md index 92f77a8b..94450f97 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Module +--- + # Module ## Tutorial.Nameservice.Module diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md similarity index 98% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md index 1eb05d17..8310470f 100644 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md @@ -1,3 +1,7 @@ +--- +title: Nameservice - Application +--- + # Application ## From Modules to App diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs new file mode 120000 index 00000000..a1b50380 --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs @@ -0,0 +1 @@ +07-Application.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs new file mode 120000 index 00000000..6262309b --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs @@ -0,0 +1 @@ +04-Keeper.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Message.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Message.lhs new file mode 120000 index 00000000..0330728d --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Message.lhs @@ -0,0 +1 @@ +03-Message.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs new file mode 120000 index 00000000..e6e3abfd --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs @@ -0,0 +1 @@ +06-Module.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Query.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Query.lhs new file mode 120000 index 00000000..ad79aed3 --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Query.lhs @@ -0,0 +1 @@ +05-Query.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Router.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md similarity index 100% rename from hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Router.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Types.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Types.lhs new file mode 120000 index 00000000..1265f50c --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Types.lhs @@ -0,0 +1 @@ +02-Types.md \ No newline at end of file diff --git a/hs-abci-docs/package.yaml b/hs-abci-docs/package.yaml new file mode 100644 index 00000000..d4c71d57 --- /dev/null +++ b/hs-abci-docs/package.yaml @@ -0,0 +1,9 @@ +name: kepler +version: 1.0.0.0 +github: "f-o-a-m/kepler" +license: BSD3 +author: "f-o-a-m" +maintainer: "martin@foam.space" +copyright: "2020 FOAM" +synopsis: "Haskell cosmos sdk" +description: Please see the README on GitHub at diff --git a/hs-abci-examples/simple-storage/README.md b/hs-abci-docs/simple-storage/README.md similarity index 100% rename from hs-abci-examples/simple-storage/README.md rename to hs-abci-docs/simple-storage/README.md diff --git a/hs-abci-examples/simple-storage/Setup.hs b/hs-abci-docs/simple-storage/Setup.hs similarity index 100% rename from hs-abci-examples/simple-storage/Setup.hs rename to hs-abci-docs/simple-storage/Setup.hs diff --git a/hs-abci-examples/simple-storage/app/Main.hs b/hs-abci-docs/simple-storage/app/Main.hs similarity index 100% rename from hs-abci-examples/simple-storage/app/Main.hs rename to hs-abci-docs/simple-storage/app/Main.hs diff --git a/hs-abci-examples/simple-storage/docker-compose.yaml b/hs-abci-docs/simple-storage/docker-compose.yaml similarity index 100% rename from hs-abci-examples/simple-storage/docker-compose.yaml rename to hs-abci-docs/simple-storage/docker-compose.yaml diff --git a/hs-abci-examples/simple-storage/package.yaml b/hs-abci-docs/simple-storage/package.yaml similarity index 100% rename from hs-abci-examples/simple-storage/package.yaml rename to hs-abci-docs/simple-storage/package.yaml diff --git a/hs-abci-examples/simple-storage/protos/simple-storage/messages.proto b/hs-abci-docs/simple-storage/protos/simple-storage/messages.proto similarity index 100% rename from hs-abci-examples/simple-storage/protos/simple-storage/messages.proto rename to hs-abci-docs/simple-storage/protos/simple-storage/messages.proto diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Application.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Config.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Config.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Config.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Message.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs diff --git a/hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Server.hs similarity index 100% rename from hs-abci-examples/simple-storage/src/SimpleStorage/Server.hs rename to hs-abci-docs/simple-storage/src/SimpleStorage/Server.hs diff --git a/hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs similarity index 100% rename from hs-abci-examples/simple-storage/test/SimpleStorage/Test/E2ESpec.hs rename to hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs diff --git a/hs-abci-examples/simple-storage/test/Spec.hs b/hs-abci-docs/simple-storage/test/Spec.hs similarity index 100% rename from hs-abci-examples/simple-storage/test/Spec.hs rename to hs-abci-docs/simple-storage/test/Spec.hs diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs deleted file mode 120000 index cdd5b2f1..00000000 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Application.lhs +++ /dev/null @@ -1 +0,0 @@ -Application.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs deleted file mode 120000 index 9e71f64f..00000000 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Keeper.lhs +++ /dev/null @@ -1 +0,0 @@ -Keeper.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs deleted file mode 120000 index c1b3cdd7..00000000 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Message.lhs +++ /dev/null @@ -1 +0,0 @@ -Message.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs deleted file mode 120000 index a2722172..00000000 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Module.lhs +++ /dev/null @@ -1 +0,0 @@ -Module.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs deleted file mode 120000 index dc7a9d6f..00000000 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Query.lhs +++ /dev/null @@ -1 +0,0 @@ -Query.md \ No newline at end of file diff --git a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs b/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs deleted file mode 120000 index 62d28166..00000000 --- a/hs-abci-examples/nameservice/tutorial/Tutorial/Nameservice/Types.lhs +++ /dev/null @@ -1 +0,0 @@ -Types.md \ No newline at end of file diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index 068fdfad..d6ca53bd 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -81,8 +81,8 @@ library: - validation ghc-options: - -fplugin=Polysemy.Plugin - - -Werror - -Wall + - -Werror - -Wcompat - -Widentities - -Wincomplete-uni-patterns diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs index 56d34285..0962f7fb 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -173,15 +173,15 @@ evalNothing = do raise $ evalNothing a ) +-- | Increments existing count, if it doesn't exist, creates a new +-- | counter and increments it. evalMetrics :: Member (Embed IO) r => MetricsState -> Sem (Metrics ': r) a -> Sem r a evalMetrics state@MetricsState{..} = do - interpretH (\case - -- | Increments existing count; if it doesn't exist, creates a new - -- | counter and increments it. + interpretH (\case IncCount ctrName -> do let c@MetricIdentifier{..} = countToIdentifier ctrName cid = metricIdStorable c @@ -199,8 +199,8 @@ evalMetrics state@MetricsState{..} = do pure counterMap pureT () - -- | Updates a histogram with the time it takes to do an action - -- | If histogram doesn't exist, creates a new one and observes it. + -- Updates a histogram with the time it takes to do an action + -- If histogram doesn't exist, creates a new one and observes it. WithTimer histName action -> do start <- liftIO $ getCurrentTime a <- runT action diff --git a/stack.yaml b/stack.yaml index ebd00ff3..921ef17e 100644 --- a/stack.yaml +++ b/stack.yaml @@ -12,8 +12,8 @@ packages: - ./hs-abci-extra - ./hs-abci-sdk - ./hs-abci-test-utils -- ./hs-abci-examples/simple-storage -- ./hs-abci-examples/nameservice +- ./hs-abci-docs/simple-storage +- ./hs-abci-docs/nameservice - ./hs-iavl-client extra-deps: From 601fd05779b190075aa95e335dce794e296773cc Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Tue, 4 Feb 2020 14:24:06 -0800 Subject: [PATCH 53/70] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b26809fc..1c6b53e7 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # hs-abci -![Travis Status](https://travis-ci.com/f-o-a-m/hs-abci.svg?branch=master) +[![Build Status](https://travis-ci.com/f-o-a-m/hs-abci.svg?branch=master)](https://travis-ci.com/f-o-a-m/hs-abci) ## Introduction This is the official repository for the Haskell implementation of the ABCI server and From 9a1c8afebe23c60af639b7953889819d7abf6ac5 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Fri, 7 Feb 2020 10:37:10 -0800 Subject: [PATCH 54/70] WIP: Betterdocs (#206) * Added all hackage links * Added installation instructions, symlinked to docs * Polish docs * Fixed licence and author and github locations * Renamed project on main README * Clean up docs * Updated readme links in haddocks * Fix broken link * Changed font --- INSTALL.md | 67 ++++++++++++++++++ Makefile | 8 +-- README.md | 68 ++----------------- hs-abci-docs/.tintin.yml | 6 +- hs-abci-docs/doc/0010-Overview.md | 5 ++ hs-abci-docs/doc/0020-Installation.md | 1 + .../{0100-Tutorial.md => 0400-Tutorial.md} | 0 hs-abci-docs/doc/98-Logging.md | 18 +++-- hs-abci-docs/doc/99-Metrics.md | 2 +- hs-abci-docs/doc/index.md | 20 +++++- hs-abci-docs/nameservice/package.yaml | 20 ++---- .../tutorial/Foundations/02-BaseApp.md | 2 - .../tutorial/Foundations/03-Modules.md | 2 - hs-abci-docs/nameservice/tutorial/README.md | 38 +---------- .../tutorial/Tutorial/Nameservice/02-Types.md | 4 +- .../Tutorial/Nameservice/03-Message.md | 2 - .../Tutorial/Nameservice/04-Keeper.md | 2 - .../tutorial/Tutorial/Nameservice/05-Query.md | 2 - .../Tutorial/Nameservice/06-Module.md | 2 - hs-abci-docs/package.yaml | 2 +- hs-abci-docs/simple-storage/README.md | 4 +- hs-abci-docs/simple-storage/package.yaml | 6 +- hs-abci-extra/README.md | 2 +- hs-abci-extra/package.yaml | 19 ++---- hs-abci-sdk/README.md | 10 +-- hs-abci-sdk/package.yaml | 6 +- hs-abci-server/README.md | 4 +- hs-abci-server/package.yaml | 6 +- hs-abci-test-utils/package.yaml | 19 ++---- hs-abci-types/README.md | 3 +- hs-abci-types/package.yaml | 6 +- hs-iavl-client/package.yaml | 6 +- hs-tendermint-client/package.yaml | 19 ++---- 33 files changed, 164 insertions(+), 217 deletions(-) create mode 100644 INSTALL.md create mode 100644 hs-abci-docs/doc/0010-Overview.md create mode 120000 hs-abci-docs/doc/0020-Installation.md rename hs-abci-docs/doc/{0100-Tutorial.md => 0400-Tutorial.md} (100%) diff --git a/INSTALL.md b/INSTALL.md new file mode 100644 index 00000000..b818511f --- /dev/null +++ b/INSTALL.md @@ -0,0 +1,67 @@ +--- +title: Installation +--- + +## Build + +### Prerequisites + +#### stack +At the moment the project's build is managed by `stack`. You can find everything you need regarding how to install stack on your machine [here](https://docs.haskellstack.org/en/stable/README/). + +#### protoc +We use a custom setup to generate Haskell bindings to the protobuf files, using the proto-lens library from Google. In order for this to work you need to have the protobuf compiler `protoc` on your machine. You can get installation instructions [here](https://google.github.io/proto-lens/installing-protoc.html) + +#### libsecp256k1 +In order to build with stack you will need this. On MacOS you can use brew: + +``` +> brew tap cuber/homebrew-libsecp256k1 +> brew install libsecp256k1 +``` + +On linux: + +``` +> sudo add-apt-repository ppa:tah83/secp256k1 +> sudo apt-get update +> sudo apt-get install libsecp256k1 +``` + +#### style +You will also need to install `hlint` and `stylish-haskell` for code hygiene during development. In the project root simply run + +```bash +> stack install hlint stylish-haskell +``` + +### Commands +There is a `Makefile` for this project where you can find all of the options for building, testing etc. The `Makefile` +is documented and there is a help menu which you can access via the commands `make` or `make help`. + +## Protobuf Files and Generated Modules +The protobuf files are all in the `/protos` directory, and we use a custom setup in order +to generate the corresponding `Proto.*` Haskell modules. If you want to view all of these +generated modules, you can run + +```bash +> find hs-abci-types/.stack-work -path '*autogen/Proto' +``` + +to find the root directory. + +## Style Guide +There is a `.stylish-haskell.yaml` file that controls some of the style guide, particularly +around import statements and some indentation rules. There is also a small guide for things that +fall outside of this in the [style wiki](https://github.com/f-o-a-m/kepler/wiki/code-style-guide). +If it's not covered by either of these but you think it's really important, mention it in an issue. + +## Building documentation +You can build the haddocks for the library code only using + +```bash +make build-docs-local +``` + +This does not build and link documentation for dependencies, useful mostly for testing +documentation formatting. diff --git a/Makefile b/Makefile index e4b74fe7..876ac36e 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ stylish: ## Run stylish-haskell over all haskell projects find ./hs-abci-types \ ./hs-abci-extra \ ./hs-tendermint-client \ - ./hs-abci-examples \ + ./hs-abci-docs \ ./hs-abci-sdk \ ./hs-abci-test-utils \ ./hs-abci-server \ @@ -80,13 +80,13 @@ test-iavl-client: ## test the iavl client library basic operation (requires grpc ##################### deploy-simple-storage-docker: install ## run the simple storage docker network - docker-compose -f hs-abci-examples/simple-storage/docker-compose.yaml up --build + docker-compose -f hs-abci-docs/simple-storage/docker-compose.yaml up --build deploy-nameservice: install ## run the nameservice docker network with elk stack for logging - docker-compose -f hs-abci-examples/nameservice/docker-compose.yaml up --build + docker-compose -f hs-abci-docs/nameservice/docker-compose.yaml up --build deploy-nameservice-test: install ## run the nameservice docker network for testing - docker-compose -f hs-abci-examples/nameservice/docker-compose-test.yaml up --build + docker-compose -f hs-abci-docs/nameservice/docker-compose-test.yaml up --build ##################### diff --git a/README.md b/README.md index 1c6b53e7..aab1665b 100644 --- a/README.md +++ b/README.md @@ -1,74 +1,14 @@ -# hs-abci +# kepler -[![Build Status](https://travis-ci.com/f-o-a-m/hs-abci.svg?branch=master)](https://travis-ci.com/f-o-a-m/hs-abci) +[![Build Status](https://travis-ci.com/f-o-a-m/kepler.svg?branch=master)](https://travis-ci.com/f-o-a-m/kepler) ## Introduction This is the official repository for the Haskell implementation of the ABCI server and SDK for developing applications backed by the Tendermint replication engine. You can read more about Tendermint and the ABCI specs in their [documentation](https://tendermint.com/docs/spec/abci/). -To understand how to build a simple application using this library, see the literate haskell [walkthrough](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-examples/nameservice/README.md). +To understand how to build a simple application using this library, see the literate haskell [walkthrough](https://github.com/f-o-a-m/kepler/blob/master/hs-abci-docs/nameservice/README.md). ## Build -### Prerequisites - -#### stack -At the moment the project's build is managed by `stack`. You can find everything you need regarding how to install stack on your machine [here](https://docs.haskellstack.org/en/stable/README/). - -#### protoc -We use a custom setup to generate Haskell bindings to the protobuf files, using the proto-lens library from Google. In order for this to work you need to have the protobuf compiler `protoc` on your machine. You can get installation instructions [here](https://google.github.io/proto-lens/installing-protoc.html) - -#### libsecp256k1 -In order to build with stack you will need this. On MacOS you can use brew: - -``` -> brew tap cuber/homebrew-libsecp256k1 -> brew install libsecp256k1 -``` - -On linux: - -``` -> sudo add-apt-repository ppa:tah83/secp256k1 -> sudo apt-get update -> sudo apt-get install libsecp256k1 -``` - -#### style -You will also need to install `hlint` and `stylish-haskell` for code hygiene during development. In the project root simply run - -```bash -> stack install hlint stylish-haskell -``` - -### Commands -There is a `Makefile` for this project where you can find all of the options for building, testing etc. The `Makefile` -is documented and there is a help menu which you can access via the commands `make` or `make help`. - -## Protobuf Files and Generated Modules -The protobuf files are all in the `/protos` directory, and we use a custom setup in order -to generate the corresponding `Proto.*` Haskell modules. If you want to view all of these -generated modules, you can run - -```bash -> find hs-abci-types/.stack-work -path '*autogen/Proto' -``` - -to find the root directory. - -## Style Guide -There is a `.stylish-haskell.yaml` file that controls some of the style guide, particularly -around import statements and some indentation rules. There is also a small guide for things that -fall outside of this in the [style wiki](https://github.com/f-o-a-m/hs-abci/wiki/code-style-guide). -If it's not covered by either of these but you think it's really important, mention it in an issue. - -## Building documentation -You can build the haddocks for the library code only using - -```bash -make build-docs-local -``` - -This does not build and link documentation for dependencies, useful mostly for testing -documentation formatting. +See [installation instructions](https://github.com/f-o-a-m/kepler/blob/master/INSTALL.md) diff --git a/hs-abci-docs/.tintin.yml b/hs-abci-docs/.tintin.yml index 362ef2aa..1b0cd520 100644 --- a/hs-abci-docs/.tintin.yml +++ b/hs-abci-docs/.tintin.yml @@ -1,10 +1,10 @@ name: kepler synopsis: Haskell Cosmos SDK github: f-o-a-m/kepler -author: Martin Allen +author: f-o-a-m authorWebsite: https://foam.space color: #5E5184 #logo: https://pbs.twimg.com/profile_images/791467713956839424/pBRQn1wt_400x400.jpg -titleFont: Poiret One +titleFont: Lora titleFontWeight: 400 -bodyFont: PT Sans +bodyFont: Open Sans diff --git a/hs-abci-docs/doc/0010-Overview.md b/hs-abci-docs/doc/0010-Overview.md new file mode 100644 index 00000000..03d97d9f --- /dev/null +++ b/hs-abci-docs/doc/0010-Overview.md @@ -0,0 +1,5 @@ +--- +title: Overview +--- + +The documentation consists of an overview, foundations, a tutorial called ` nameservice` as well as documentation of logging and metrics. You can navigate to the relevant sections on the left side. diff --git a/hs-abci-docs/doc/0020-Installation.md b/hs-abci-docs/doc/0020-Installation.md new file mode 120000 index 00000000..5f52b2be --- /dev/null +++ b/hs-abci-docs/doc/0020-Installation.md @@ -0,0 +1 @@ +../../INSTALL.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0100-Tutorial.md b/hs-abci-docs/doc/0400-Tutorial.md similarity index 100% rename from hs-abci-docs/doc/0100-Tutorial.md rename to hs-abci-docs/doc/0400-Tutorial.md diff --git a/hs-abci-docs/doc/98-Logging.md b/hs-abci-docs/doc/98-Logging.md index eaf50c78..9ef0ac80 100644 --- a/hs-abci-docs/doc/98-Logging.md +++ b/hs-abci-docs/doc/98-Logging.md @@ -4,7 +4,7 @@ title: Logging # Logging -The SDK has built in support for structured logging via the [katip](https://hackage.haskell.org/package/katip) logging library. Even still, the SDK is agnostic to where you want your logs to go. Katip logs are managed by *scribes* whos job is precisely this, so the output depends on which scribes you use. The two most common scribes are the [console scribe](https://hackage.haskell.org/package/katip-0.8.3.0/docs/Katip-Scribes-Handle.html#v:mkHandleScribe) and the [Elasticsearch scribe](https://hackage.haskell.org/package/katip-elasticsearch). +The SDK has built in support for structured logging via the [katip](https://hackage.haskell.org/package/katip) logging library. Even still, the SDK is agnostic to where you want your logs to go. Katip logs are managed by *scribes* whose job is precisely this, so the output depends on which scribes you use. The two most common scribes are the [console scribe](https://hackage.haskell.org/package/katip-0.8.3.0/docs/Katip-Scribes-Handle.html#v:mkHandleScribe) and the [Elasticsearch scribe](https://hackage.haskell.org/package/katip-elasticsearch). The `Nameservice` application has support for either scribe -- it will use Elasticsearch if you provide the `ES_HOST` and `ES_PORT` environment variables or otherwise will default to console logging. The docker deployment is configured to use Elasticsearch. @@ -14,19 +14,19 @@ The docker network includes an `elk` image (Elasticsearch, Logstash, Kibana) for When logging to Elasticsearch, you can use the Kibana dashboard for creating queries and visualizations. We will cover the basics here. If you have already launched the docker network, you can view the Kibana dashboard by going to http://localhost:5601/app/kibana. You should see something like - + To create an index (a searchable pattern), click on the *Management* tab, click *Create Index*, and enter `nameservice` as the pattern. You should see something like this: - + You can ignore the advanced options, e.g. time filter, for now: - + To view and search the logs, you can click the `Discover` tab. You should see all of the logs in the resulting search, from both server and application: - + ## Searching a Log Index @@ -38,19 +38,19 @@ The log structure is effectively a JSON object (with nesting). There are a few f - `message_hash`: the SHA256 of the protobuf encoded bytes for the abci message that caused the logs. - `ns` (namespace): a list of increasingly specific scopes for where the log originated. In this case, `nameservice` is the root namespace, `server` or `application` is the next scope. -Remember that the basic lifescycle of an `ABCI` message is that it first comes to the ABCI-server from tendermint, is then handed off to your application for processing, and finally the response is sent from the ABCI-server back to tendermint. In order to better track this lifecycle, we highly recommend you use the [logging middleware](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs). This middleware will attach the `message_type` and `message_hash` to the context for every single log that is produced, meaning that you can get a trace for a given message by simply searching its hash. +Remember that the basic lifescycle of an `ABCI` message is that it first comes to the ABCI-server from tendermint, is then handed off to your application for processing, and finally the response is sent from the ABCI-server back to tendermint. In order to better track this lifecycle, we highly recommend you use the [logging middleware](https://github.com/f-o-a-m/kepler/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Logger.hs). This middleware will attach the `message_type` and `message_hash` to the context for every single log that is produced, meaning that you can get a trace for a given message by simply searching its hash. ### Querying the Logs You can create custom search filters in the *Discover* tab, just click the *Add a filter* button near the search bar. For example, we can filter all of the logs for those that correspond to a *deliverTx* message: - + (**NOTE**: If you run the e2e tests against the docker network, you should see search results corresponding to the transactions created by the test suite. ) Similarly, you can compose multiple filters to obtain only those logs emitted by the application itself during a *deliverTx* context, i.e. by filtering for `application` on the `ns` namespace field: - + ### Indexing Transaction Events @@ -85,5 +85,3 @@ This log corresponds to an event emitted by the `Nameservice` module during tran ``` In this way the log index serves as a rudimentary event indexer for transaction events as well. - -## [Next: Metrics](./Metrics.md) diff --git a/hs-abci-docs/doc/99-Metrics.md b/hs-abci-docs/doc/99-Metrics.md index 06efc9da..b0e600e5 100644 --- a/hs-abci-docs/doc/99-Metrics.md +++ b/hs-abci-docs/doc/99-Metrics.md @@ -6,7 +6,7 @@ title: Metrics The SDK has some built in support for metrics via [prometheus](https://prometheus.io/), but ultimately you may choose a different runtime interpretation for the metrics, or even choose to ignore it entirely. -The `Nameservice` application uses application specific metrics, for instance increasing the message counters for module level messages, or for timing module responses. It also uses the server metrics via the [metrics middleware](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs) to count ABCI messages and to time server responses. This middleware is highly recommended for any production system. +The `Nameservice` application uses application specific metrics, for instance increasing the message counters for module level messages, or for timing module responses. It also uses the server metrics via the [metrics middleware](https://github.com/f-o-a-m/kepler/blob/master/hs-abci-extra/src/Network/ABCI/Server/Middleware/Metrics.hs) to count ABCI messages and to time server responses. This middleware is highly recommended for any production system. ## Setting up metrics diff --git a/hs-abci-docs/doc/index.md b/hs-abci-docs/doc/index.md index 1d6def92..7da17c6f 100644 --- a/hs-abci-docs/doc/index.md +++ b/hs-abci-docs/doc/index.md @@ -2,6 +2,20 @@ title: Home --- -The home of Kepler. -Link to [hackage](https://hackage.haskell.org/package/kepler) -Link to [hs-abci-sdk](haddocks/hs-abci-sdk/) +## Introduction + +This is the official repository for the Haskell implementation of the ABCI server and SDK for developing applications backed by the Tendermint replication engine. + +This site contains a [tutorial](0010-Overview.html) as well as haddocks documentation below. + +You can read more about Tendermint and the ABCI specs in the [documentation](https://tendermint.com/docs/spec/abci/) hosted on their website. + +## Documentation + +- [hs-abci-sdk](haddocks/hs-abci-sdk/) +- [hs-abci-extra](haddocks/hs-abci-extra/) +- [hs-abci-server](haddocks/hs-abci-server/) +- [hs-abci-test-utils](haddocks/hs-abci-test-utils/) +- [hs-abci-types](haddocks/hs-abci-types/) +- [hs-iavl-client](haddocks/hs-iavl-client/) +- [hs-tendermint-client](haddocks/hs-tendermint-client/) diff --git a/hs-abci-docs/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml index e57a0f25..f0e8750d 100644 --- a/hs-abci-docs/nameservice/package.yaml +++ b/hs-abci-docs/nameservice/package.yaml @@ -1,20 +1,12 @@ name: nameservice version: 0.1.0.0 -github: "githubuser/nameservice" -license: BSD3 -author: "Author name here" -maintainer: "example@example.com" -copyright: "2019 Author name here" +github: "f-o-a-m/kepler/hs-abci-docs/nameservice" +license: Apache +author: "Martin Allen" +maintainer: "martin@foam.space" +copyright: "2020 Martin Allen" - -# Metadata used when publishing your package -# synopsis: Short description of your package -# category: Web - -# To avoid duplicated efforts in documentation and dealing with the -# complications of embedding Haddock markup inside cabal files, it is -# common to point users to the README.md file. -description: Please see the README on GitHub at +description: Please see the README on GitHub at extra-source-files: - README.md diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md index 163ac17f..31a5bc28 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md @@ -30,5 +30,3 @@ These effects are: `BaseApp` acts as an intermediate effect system for specifying applications, it does not make any assumptions about how these effects will be interpreted at runtime. For example, `RawStore` could eventualy be interpeted by any persistent or in-memory storage capable of handling the commands `Put`, `Get` etc. Most of the work in writing modules involves plugging into `BaseApp` at various points. For example, your module can create custom errors to throw or catch, but you must tell the SDK how to translate this custom error into an `AppError`. Likewise your module can define custom events to log during transaction execution, but you must describe to the SDK how to translate these custom events types into the type `Event`. - -[Next: Modules](Modules.md) diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md index 9f18874f..b6b2eea2 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md @@ -45,5 +45,3 @@ data Modules (ms :: [*]) r where ~~~ When you are ready to create your application, you simply specify a value of type `Modules` and some other configuration data, and the SDK will create an `App` for you. - -[Next: Types](../Tutorial/Nameservice/Types.md) diff --git a/hs-abci-docs/nameservice/tutorial/README.md b/hs-abci-docs/nameservice/tutorial/README.md index 4ce23e72..dcf218b7 100644 --- a/hs-abci-docs/nameservice/tutorial/README.md +++ b/hs-abci-docs/nameservice/tutorial/README.md @@ -1,43 +1,11 @@ --- -title: Overview +title: Tutorial --- ## Introduction We're going to build an example application that mirrors the `golang` [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) example application called [Nameservice](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). There is also a tutorial for that application which you can find [here](https://tutorials.cosmos.network/nameservice/tutorial/00-intro.html) for comparison. -## Contents -1. [Introduction](README.md) - - [Application Specification](README.md#application-specification) - - [How to Read this Tutorial](README.md#how-to-read-this-tutorial) - - [Tutorial Goals](README.md#tutorial-goals) -2. Foundations - - [Overview](Foundations/Overview.md) - - [BaseApp](Foundations/BaseApp.md) - - [Modules](Foundations/Modules.md) - 1. [Definition](Foundations/Modules.md#definition) - 2. [Composition](Foundations/Modules.md#composition) -3. Nameservice - - [Overview](Tutorial/Nameservice/Overview.md) - - [Types](Tutorial/Nameservice/Types.md) - 1. [Using A Typed Key Value Store](Tutorial/Nameservice/Types.md#using-a-typed-key-value-store) - 2. [Tutorial.Nameservice.Types](Tutorial/Nameservice/Types.md#tutorialnameservicetypes) - - [Message](Tutorial/Nameservice/Message.md) - 1. [Message Types](Tutorial/Nameservice/Message.md#message-types) - 2. [Tutorial.Nameservice.Message](Tutorial/Nameservice/Message.md#tutorialnameservicemessage) - - [Keeper](Tutorial/Nameservice/Keeper.md) - 1. [Definition](Tutorial/Nameservice/Keeper.md#definition) - 2. [Tutorial.Nameservice.Keeper](Tutorial/Nameservice/Keeper.md#tutorialnameservicekeeper) - - [Query](Tutorial/Nameservice/Query.md) - 1. [Definition](Tutorial/Nameservice/Query.md#definition) - 2. [Tutorial.Nameservice.Query](Tutorial/Nameservice/Query.md#tutorialnameservicequery) - - [Module](Tutorial/Nameservice/Module.md) - 1. [Tutorial.Nameservice.Module](Tutorial/Nameservice/Module.md#tutorialnameservicemodule) - - [Application](Tutorial/Nameservice/Application.md) - 1. [From Modules To App](Tutorial/Nameservice/Application.md#from-modules-to-app) - 2. [Tutorial.Nameservice.Application](Tutorial/Nameservice/Application.md#tutorialnameserviceapplication) - - ## Application Specification The Nameservice application is a simple marketplace for a name resolution service. Let us say that a `Name` resolves to type called `Whois` where @@ -67,7 +35,7 @@ The benefit of this is that we don't have to develop the entire application in t ## Tutorial Goals The goal of this tutorial is to explain how the Nameservice app is constructed using the `hs-abci-sdk` package. Nameservice is a relatively simple but still non-trivial application. -If you would like to start with something simpler, you can view the tutorial for the [simple-storage](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/simple-storage) example application. +If you would like to start with something simpler, you can view the tutorial for the [simple-storage](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-docs/simple-storage) example application. This tutorial should teach you: 1. How to construct application specific modules. @@ -85,5 +53,3 @@ It is also allows the application developer to construct modules without much re main :: IO () main = pure () ~~~ - -[Next: BaseApp](Foundations/Overview.md) diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md index f6c5aa82..48da5dc3 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md @@ -167,7 +167,7 @@ class IsAppError e where makeAppError :: e -> AppError ~~~ -The fields for `AppError` correspond to tendermint message fields for messages that support error return types, such as `checkTx`, `deliverTx`, and `query`. Typically we use the module name as the codespace, like in the definition of `NamespaceError`: +The fields for `AppError` correspond to tendermint message fields for messages that support error return types, such as `checkTx`, `deliverTx`, and `query`. Typically we use the module name as the codespace, like in the definition of `NameserviceError`: ~~~ haskell data NameserviceError = @@ -254,5 +254,3 @@ instance A.FromJSON NameClaimed where instance BA.ToEvent NameClaimed where makeEventType _ = "NameClaimed" ~~~ - -[Next: Message](Message.md) diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md index 5984b55e..d48a7029 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md @@ -177,5 +177,3 @@ instance ValidateMessage BuyName where , isAuthorCheck "Owner" msg buyNameBuyer ] ~~~ - -[Next: Keeper](Keeper.md) diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md index 5ae099f5..c3d3c707 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md @@ -120,5 +120,3 @@ eval = mapError BA.makeAppError . evalNameservice DeleteWhois name -> BA.delete storeKey name ) ~~~ - -[Next: Query](Query.md) diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md index 90325ebf..278c97cd 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md @@ -43,5 +43,3 @@ server = Here `RouteT` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely vendored from that codebase. Note that more advanced queries are possible other than just serving what is in storage. For example you might want to use joins to fulfill requests or use query parameters in the url. These are all possible, but we won't go into details here as they are not used in the app. - -[Next: Router](Router.md) diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md index 94450f97..ef4f01de 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md @@ -76,5 +76,3 @@ Note the constraints on `r`: This is saying that we can run this module in any context for which `r` has the effects from `BaseApp`, `Token`, and `Nameservice`. This is how we explicitly declare module dependencies, by using the constraint system. Other than that, there is nothing really to note. We are just collecting the pieces we have already defined in one place. - -[Next: Application](Application.md) diff --git a/hs-abci-docs/package.yaml b/hs-abci-docs/package.yaml index d4c71d57..43e5f156 100644 --- a/hs-abci-docs/package.yaml +++ b/hs-abci-docs/package.yaml @@ -1,7 +1,7 @@ name: kepler version: 1.0.0.0 github: "f-o-a-m/kepler" -license: BSD3 +license: Apache author: "f-o-a-m" maintainer: "martin@foam.space" copyright: "2020 FOAM" diff --git a/hs-abci-docs/simple-storage/README.md b/hs-abci-docs/simple-storage/README.md index da7e6542..9ffd955b 100644 --- a/hs-abci-docs/simple-storage/README.md +++ b/hs-abci-docs/simple-storage/README.md @@ -19,5 +19,5 @@ it will build an image for simple-storage and launch it in a docker network with a tendermint-core node. ## Application Messages -The application uses a protobuf file to define its [transaction messages](https://github.com/f-o-a-m/hs-abci/blob/master/hs-abci-examples/simple-storage/protos/simple-storage/messages.proto). Thus if you would like to post transactions to this application via RPC, you will need to first consume -this profobuf file. You can follow the pattern in the test suite using hs-tendermint-client. \ No newline at end of file +The application uses a protobuf file to define its [transaction messages](https://github.com/f-o-a-m/kepler/blob/master/hs-abci-docs/simple-storage/protos/simple-storage/messages.proto). Thus if you would like to post transactions to this application via RPC, you will need to first consume +this profobuf file. You can follow the pattern in the test suite using hs-tendermint-client. diff --git a/hs-abci-docs/simple-storage/package.yaml b/hs-abci-docs/simple-storage/package.yaml index 38cddd95..23ab2344 100644 --- a/hs-abci-docs/simple-storage/package.yaml +++ b/hs-abci-docs/simple-storage/package.yaml @@ -1,15 +1,15 @@ name: simple-storage version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-abci-examples/simple-storage" +github: "f-o-a-m/kepler/hs-abci-docs/simple-storage" license: Apache author: Martin Allen maintainer: "martin@foam.space" -copyright: "2019 Martin Allen" +copyright: "2020 Martin Allen" extra-source-files: - protos/**/*.proto -description: Please see the README on GitHub at +description: Please see the README on GitHub at default-extensions: - DeriveGeneric diff --git a/hs-abci-extra/README.md b/hs-abci-extra/README.md index fc5ae47e..589cb793 100644 --- a/hs-abci-extra/README.md +++ b/hs-abci-extra/README.md @@ -4,4 +4,4 @@ The goal here is to provide common features and example middleware implementatio ## Example using middleware -There is a small example using the Logger middleware [here](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/simple-storage). \ No newline at end of file +There is a small example using the Logger middleware [here](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-docs/simple-storage). diff --git a/hs-abci-extra/package.yaml b/hs-abci-extra/package.yaml index 224ff151..48c320e6 100644 --- a/hs-abci-extra/package.yaml +++ b/hs-abci-extra/package.yaml @@ -1,23 +1,16 @@ name: hs-abci-extra version: 0.1.0.0 -github: "https://github.com/f-o-a-m/hs-abci-server" -license: BSD3 -author: "Author name here" -maintainer: "example@example.com" -copyright: "2019 Author name here" +github: "https://github.com/f-o-a-m/kepler/hs-abci-server" +license: Apache +author: "Martin Allen" +maintainer: "martin@foam.space" +copyright: "2020 Martin Allen" extra-source-files: - README.md - ChangeLog.md -# Metadata used when publishing your package -# synopsis: Short description of your package -# category: Web - -# To avoid duplicated efforts in documentation and dealing with the -# complications of embedding Haddock markup inside cabal files, it is -# common to point users to the README.md file. -description: Please see the README on GitHub at +description: Please see the README on GitHub at dependencies: - base >= 4.7 && < 5 diff --git a/hs-abci-sdk/README.md b/hs-abci-sdk/README.md index 7f86b20a..894308fc 100644 --- a/hs-abci-sdk/README.md +++ b/hs-abci-sdk/README.md @@ -1,13 +1,13 @@ # hs-abci-sdk ## Introduction -This package lays out an SDK for rapidly developing blockchain applications in haskell backed by the Tendermint replication engine. It relies on the [hs-abci-server](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-server) to communicate to Tendermint core via the ABCI protocol. +This package lays out an SDK for rapidly developing blockchain applications in haskell backed by the Tendermint replication engine. It relies on the [hs-abci-server](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-server) to communicate to Tendermint core via the ABCI protocol. ## Requirements ### libsecp256k You will need to have the `libsecp256k` `C` library installed on your machine to build this package, or anything depedning on it, as it is not statically linked to its haskell wrapper. You -can find instructions for this [here](https://github.com/f-o-a-m/hs-abci#libsecp256k1). +can find instructions for this [here](https://github.com/f-o-a-m/kepler/tree/master/INSTALL.md#libsecp256k1). ## Architecture @@ -20,7 +20,7 @@ The SDK makes heavy use of an effects system to separate different components of ### Application Specific Effects It is assumed that you will want to define your own application specific effects, for example -in the way that the [Nameservice example app](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/nameservice) does. Application specific effects are useful for defining module level storage capabilities, custom errors and handling, and explicit dependencies on other modules' effects. There are many hooks in this SDK to facilitate compiling application effects +in the way that the [Nameservice example app](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-docs/nameservice) does. Application specific effects are useful for defining module level storage capabilities, custom errors and handling, and explicit dependencies on other modules' effects. There are many hooks in this SDK to facilitate compiling application effects to `BaseApp`. For examples, see `Tendermint.SDK.Errors` or `Tendermint.SDK.Store`. ### Core Effects @@ -31,6 +31,6 @@ a context for executing database transactions, and various buffers and vars to f ## Example Applications There are currenlty two official example applications -1. [Simple Storage](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/simple-storage): This is a trivial application developed around a single module that allows get and set operations on an integer value. +1. [Simple Storage](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-docs/simple-storage): This is a trivial application developed around a single module that allows get and set operations on an integer value. -2. [Nameservice](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-examples/nameservice): This is an implementation of the official example application for the [cosmos-sdk](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). It is built to support a simple name resolution market place. +2. [Nameservice](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-docs/nameservice): This is an implementation of the official example application for the [cosmos-sdk](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). It is built to support a simple name resolution market place. diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index d6ca53bd..f8eb57e2 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -1,12 +1,12 @@ name: hs-abci-sdk version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-abci-sdk" +github: "f-o-a-m/kepler/hs-abci-sdk" license: Apache author: Martin Allen maintainer: "martin@foam.space" -copyright: "2019 Martin Allen" +copyright: "2020 Martin Allen" -description: Please see the README on GitHub at +description: Please see the README on GitHub at extra-source-files: - protos/**/*.proto diff --git a/hs-abci-server/README.md b/hs-abci-server/README.md index 41f83cea..9ea6bb48 100644 --- a/hs-abci-server/README.md +++ b/hs-abci-server/README.md @@ -1,7 +1,7 @@ # hs-abci-server The `hs-abci-server` package defines the types and methods for serving an ABCI application. See -the [example application](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-example) as a guide for how you can use this package. +the [example application](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-example) as a guide for how you can use this package. ## Application types The application type `App m` is defined as a newtype @@ -39,4 +39,4 @@ type Middleware m = App m -> App m ``` This is useful for hooking in things like metrics, loggers, etc. You can find some out-of-the-box middleware solutions in -the [hs-abci-extra](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-extra) package. +the [hs-abci-extra](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-extra) package. diff --git a/hs-abci-server/package.yaml b/hs-abci-server/package.yaml index 802b70c6..6c86099a 100644 --- a/hs-abci-server/package.yaml +++ b/hs-abci-server/package.yaml @@ -1,12 +1,12 @@ name: hs-abci-server version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-abci-server" +github: "f-o-a-m/kepler/hs-abci-server" license: Apache author: "Martin Allen" maintainer: "martin@foam.space" -copyright: "2019 Martin Allen" +copyright: "2020 Martin Allen" -description: Please see the README on GitHub at +description: Please see the README on GitHub at default-extensions: - NamedFieldPuns diff --git a/hs-abci-test-utils/package.yaml b/hs-abci-test-utils/package.yaml index b3486172..aaeb5f0f 100644 --- a/hs-abci-test-utils/package.yaml +++ b/hs-abci-test-utils/package.yaml @@ -1,22 +1,15 @@ name: hs-abci-test-utils version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-abci-test-utils" -license: BSD3 -author: "Author name here" -maintainer: "example@example.com" -copyright: "2019 Author name here" +github: "f-o-a-m/kepler/hs-abci-test-utils" +license: Apache +author: "Martin Allen" +maintainer: "martin@foam.space" +copyright: "2020 Martin Allen" extra-source-files: - README.md -# Metadata used when publishing your package -# synopsis: Short description of your package -# category: Web - -# To avoid duplicated efforts in documentation and dealing with the -# complications of embedding Haddock markup inside cabal files, it is -# common to point users to the README.md file. -description: Please see the README on GitHub at +description: Please see the README on GitHub at default-extensions: - DataKinds diff --git a/hs-abci-types/README.md b/hs-abci-types/README.md index b5166717..a4ff944e 100644 --- a/hs-abci-types/README.md +++ b/hs-abci-types/README.md @@ -2,9 +2,8 @@ This module provides haskell bindings for the Tendermint ABCI message types defined in [tendermint/tendermint/abci/types/types.proto](https://github.com/tendermint/tendermint/blob/v0.32.2/abci/types/types.proto#L3). - ## Under the hood -We use [proto-lens](https://github.com/google/proto-lens) to generate all the types from files in the [protos](https://github.com/f-o-a-m/hs-abci/tree/master/hs-abci-types/protos) directory, but the generated code is a bit brutal to use directly. This package defines a more user friendly version using Control.Lens.Wrapped. This way we still use proto-lens for encoding/decoding protocol messages while users can work with nicer types defined here. +We use [proto-lens](https://github.com/google/proto-lens) to generate all the types from files in the [protos](https://github.com/f-o-a-m/kepler/tree/master/hs-abci-types/protos) directory, but the generated code is a bit brutal to use directly. This package defines a more user friendly version using Control.Lens.Wrapped. This way we still use proto-lens for encoding/decoding protocol messages while users can work with nicer types defined here. ## JSON Again, Tendermint protocol messages are defined with protobuf files and use protobuf codecs. However, we still supply `ToJSON`/`FromJSON` instances of the types for communicating with tendermint json-rpc server and because they are generally useful. diff --git a/hs-abci-types/package.yaml b/hs-abci-types/package.yaml index 8cec6e5c..ffd03e8b 100644 --- a/hs-abci-types/package.yaml +++ b/hs-abci-types/package.yaml @@ -1,15 +1,15 @@ name: hs-abci-types version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-abci-types" +github: "f-o-a-m/kepler//hs-abci-types" license: Apache author: "Martin Allen" maintainer: "martin@foam.space" -copyright: "2019 Martin Allen" +copyright: "2020 Martin Allen" extra-source-files: - protos/**/*.proto -description: Please see the README on GitHub at +description: Please see the README on GitHub at custom-setup: dependencies: diff --git a/hs-iavl-client/package.yaml b/hs-iavl-client/package.yaml index e2f32258..bbb2149a 100644 --- a/hs-iavl-client/package.yaml +++ b/hs-iavl-client/package.yaml @@ -1,15 +1,15 @@ name: hs-iavl-client version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-iavl-client" +github: "f-o-a-m/kepler/hs-iavl-client" license: Apache author: "Martin Allen" maintainer: "martin@foam.space" -copyright: "2019 Martin Allen" +copyright: "2020 Martin Allen" extra-source-files: - protos/**/*.proto -description: Please see the README on GitHub at +description: Please see the README on GitHub at custom-setup: dependencies: diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index 08df8389..c904a58e 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -1,19 +1,12 @@ name: hs-tendermint-client version: 0.1.0.0 -github: "f-o-a-m/hs-abci/hs-tendermint-client" -license: BSD3 -author: "Author name here" -maintainer: "example@example.com" -copyright: "2019 Author name here" +github: "f-o-a-m/kepler/hs-tendermint-client" +license: Apache +author: "Martin Allen" +maintainer: "martin@foam.spacem" +copyright: "2020 FOAM" -# Metadata used when publishing your package -# synopsis: Short description of your package -# category: Web - -# To avoid duplicated efforts in documentation and dealing with the -# complications of embedding Haddock markup inside cabal files, it is -# common to point users to the README.md file. -description: Please see the README on GitHub at +description: Please see the README on GitHub at default-extensions: - NamedFieldPuns From 91b1146b1978cea3965fbb9058a739447e90a208 Mon Sep 17 00:00:00 2001 From: Charles Crain Date: Fri, 7 Feb 2020 15:52:31 -0800 Subject: [PATCH 55/70] subscribe (#205) * subscribe * removed containers * fixed broken test --- .../src/Tendermint/Utils/ClientUtils.hs | 4 +- .../kv-test/KVStore/Test/KVSpec.hs | 4 +- hs-tendermint-client/package.yaml | 2 + .../src/Network/Tendermint/Client.hs | 30 +++++++++++- .../Tendermint/Client/Internal/RPCClient.hs | 49 ++++++++++++++++++- 5 files changed, 82 insertions(+), 7 deletions(-) diff --git a/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs b/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs index 2b053047..05a586ec 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs @@ -125,7 +125,7 @@ ensureQueryResponseCode code resp = case resp of rpcConfig :: RPC.Config rpcConfig = - let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 + let RPC.Config baseReq _ _ host port tls = RPC.defaultConfig "localhost" 26657 False prettyPrint :: forall b. ToJSON b => String -> b -> IO () prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) - in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") + in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") host port tls diff --git a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs index c4ddbef3..06c97dc8 100644 --- a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs +++ b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs @@ -93,7 +93,7 @@ runRPC = RPC.runTendermintM rpcConfig where rpcConfig :: RPC.Config rpcConfig = - let RPC.Config baseReq _ _ = RPC.defaultConfig "localhost" 26657 + let RPC.Config baseReq _ _ host port tls = RPC.defaultConfig "localhost" 26657 False prettyPrint :: forall b. ToJSON b => String -> b -> IO () prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) - in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") + in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") host port tls diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index c904a58e..3e62b1bc 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -42,6 +42,8 @@ library: - mtl - random - text + - websockets + - wuss ghc-options: - -Werror - -Wall diff --git a/hs-tendermint-client/src/Network/Tendermint/Client.hs b/hs-tendermint-client/src/Network/Tendermint/Client.hs index ae0219bc..8df188bd 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client.hs @@ -43,13 +43,15 @@ defaultConfig -- ^ Hostname or IP (e.g. "localhost", "127.0.0.1", "151.101.208.68") -> Int -- ^ Port + -> Bool + -- ^ TLS True/False -> RPC.Config -defaultConfig host port = +defaultConfig host port tls = let baseReq = HTTP.setRequestHost host $ HTTP.setRequestPort port $ HTTP.defaultRequest - in RPC.Config baseReq mempty mempty + in RPC.Config baseReq mempty mempty host port tls -------------------------------------------------------------------------------- -- ABCI Query @@ -236,6 +238,30 @@ data ResultABCIInfo = ResultABCIInfo instance FromJSON ResultABCIInfo where parseJSON = genericParseJSON $ defaultRPCOptions "resultABCIInfo" +-------------------------------------------------------------------------------- +-- Subscribe +-------------------------------------------------------------------------------- + +-- | invokes [/subscribe](https://tendermint.com/rpc/#subscribe) rpc call +-- https://github.com/tendermint/tendermint/blob/master/rpc/core/events.go#L17 +subscribe :: RequestSubscribe -> (Aeson.Value -> IO ()) -> TendermintM ResultSubscribe +subscribe req handler = do + RPC.remoteWS (RPC.MethodName "subscribe") req handler + pure ResultSubscribe + + +newtype RequestSubscribe = RequestSubscribe + { requestSubscribeQuery :: Text + } deriving (Eq, Show, Generic) +instance ToJSON RequestSubscribe where + toJSON = genericToJSON $ defaultRPCOptions "requestSubscribe" + +-- https://github.com/tendermint/tendermint/blob/v0.32.2/rpc/core/types/responses.go#L208 +data ResultSubscribe = ResultSubscribe deriving (Eq, Show) + +instance FromJSON ResultSubscribe where + parseJSON = Aeson.withObject "Expected emptyObject" $ \_ -> pure ResultSubscribe + -------------------------------------------------------------------------------- -- https://github.com/tendermint/tendermint/blob/v0.32.2/rpc/core/types/responses.go#L147 diff --git a/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs b/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs index 3e592dd8..19a91b77 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs @@ -2,6 +2,7 @@ module Network.Tendermint.Client.Internal.RPCClient where import Control.Applicative ((<|>)) import Control.Exception (Exception) +import Control.Monad (forever, void) import Control.Monad.Catch (throwM) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (MonadReader, ask) @@ -9,9 +10,13 @@ import Data.Aeson (FromJSON (..), Result (..), ToJSON (..), Value (..), fromJSON, (.:), (.:?), (.=)) import qualified Data.Aeson as Aeson +import Data.ByteString (ByteString) +import qualified Data.ByteString.Char8 as BS import Data.Text (Text, unpack) import qualified Network.HTTP.Simple as HTTP +import qualified Network.WebSockets as WS import System.Random (randomIO) +import Wuss (runSecureClient) -- | JSON-RPC request. data Request = Request @@ -95,8 +100,50 @@ data Config = Config -- ^ An acion to perform before sending the 'HTTP.Request' , withResponse :: Response -> IO () -- ^ An acion to perform before handling the 'HTTP.Response' + , cHost :: ByteString + -- ^ The host for client to connect + , cPort :: Int + -- ^ Port for client to use + , tlsEnabled :: Bool + -- ^ Whether to use TLS or not } +remoteWS :: + ( MonadIO m + , MonadReader Config m + , FromJSON output + , ToJSON input + ) + => MethodName + -> input + -> (output -> IO ()) + -> m () +{-# INLINE remoteWS #-} +remoteWS method input handler = do + Config {..} <- ask + let host = BS.unpack cHost + port = fromInteger $ toInteger cPort + tlsPort = fromInteger $ toInteger port + path = "/websocket" + if tlsEnabled + then void . liftIO $ runSecureClient host tlsPort path ws + else void . liftIO $ WS.runClient host port path ws + where + ws c = do + rid <- abs <$> liftIO randomIO + let rpcParams = Aeson.toJSON input + rpcRequest = Request method rid rpcParams + msg = WS.Binary $ Aeson.encode rpcRequest + WS.sendDataMessage c msg + void . forever $ do + message <- WS.receiveData c >>= decodeRPCResponse + handler message + decodeRPCResponse bs = case Aeson.eitherDecodeStrict bs of + Left err -> throwM $ ParsingException err + Right response -> pure response + + + remote :: ( MonadIO m , MonadReader Config m @@ -109,7 +156,7 @@ remote :: {-# INLINE remote #-} remote method input = do rid <- abs <$> liftIO randomIO - Config baseHTTPRequest withReq withResp <- ask + Config baseHTTPRequest withReq withResp _ _ _ <- ask let req = Request method rid (toJSON input) httpReq = HTTP.setRequestBodyJSON req $ HTTP.setRequestHeaders [("Content-Type", "application/json")] From 7bac3363f02ecc307a76359d7ea9c272dd265bd1 Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Mon, 10 Feb 2020 08:43:58 -0800 Subject: [PATCH 56/70] Update .travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 13d685a3..6c59547a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,6 +88,7 @@ jobs: - make build-docs-local - mkdir -p ./hs-abci-docs/.stack-work/tintin/rendered/haddocks - find ./ -type f -name "index.html" | grep -v tintin | sed 's/index.html//g' | xargs -I {} cp -r {} hs-abci-docs/.stack-work/tintin/rendered/haddocks/ + - cat "kepler.dev" > ./hs-abci-docs/.stack-work/tintin/rendered/CNAME deploy: provider: pages local-dir: ./hs-abci-docs/.stack-work/tintin/rendered From d11b5966907a2e5297cbca46553b5b81cea46beb Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Mon, 10 Feb 2020 09:23:08 -0800 Subject: [PATCH 57/70] Update .travis.yml --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6c59547a..af180257 100644 --- a/.travis.yml +++ b/.travis.yml @@ -88,7 +88,7 @@ jobs: - make build-docs-local - mkdir -p ./hs-abci-docs/.stack-work/tintin/rendered/haddocks - find ./ -type f -name "index.html" | grep -v tintin | sed 's/index.html//g' | xargs -I {} cp -r {} hs-abci-docs/.stack-work/tintin/rendered/haddocks/ - - cat "kepler.dev" > ./hs-abci-docs/.stack-work/tintin/rendered/CNAME + - echo "kepler.dev" > ./hs-abci-docs/.stack-work/tintin/rendered/CNAME deploy: provider: pages local-dir: ./hs-abci-docs/.stack-work/tintin/rendered From 2fe6f271908ba86fc40ebd750caecfc0fd1a46f3 Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Fri, 14 Feb 2020 16:39:08 -0500 Subject: [PATCH 58/70] Fix store bug (#211) Closes #10. --- hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index a3e39af1..3040649e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -14,7 +14,7 @@ import Servant.API ((:<|>) (..), (:>)) import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..), methodRouter) -import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), +import Tendermint.SDK.BaseApp.Query.Types (QA, QueryArgs (..), QueryResult (..), Queryable (..)) import Tendermint.SDK.BaseApp.Router (RouterError (..), @@ -75,7 +75,7 @@ instance , StoreQueryHandlers ((k', a') ': as) ns r , Members [RawStore, Error AppError] r ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns r where - type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> Leaf a) :<|> QueryApi ((k', a') ': as) + type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> StoreLeaf a) :<|> QueryApi ((k', a') ': as) storeQueryHandlers _ storeKey pr = storeQueryHandler (Proxy :: Proxy a) storeKey :<|> storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pr From 0af053732a2ff34d7e604da618c335057fbcc31b Mon Sep 17 00:00:00 2001 From: Charles Crain Date: Fri, 14 Feb 2020 15:03:19 -0800 Subject: [PATCH 59/70] IAVL RawStore Bindings (#188) * WIP | transaction cache store interpreter * lower level raw store key * MVP IAVLStore implementation * added index check to tests, updated test rootHash, store with genesis * new auth tree store mimicking iavlstore, new and updated store specs * stylish * Tx effects module compiles * core effs compiles, stylish * module compiles * query and transaction routers compile * transaction compiles * wip * transaction module compileS * query compiles * wip * remove routecontext param * auth module compiles * handlers compiles * sdk compiles * iavl compiles: * sdk compiles * separate store effs wip * wip * compiles with scopes * added back in commit and transactional handlers * compiling to core * about to add antehandler * simple-storage compiles * applying antehandler * nameservice compiles * client compiles * simple storage in sdk tests compiles * all e2es compile * wip * configuratble grpc host port * sdk tests passing * added a bunch of trace statements, seems to work locally * configure iavl host and port through env var * docker files for iavl * added in memory db * memory store simplification * compile to Core pure * run pure with no metrics * iavlspec passes in sdk * update travis * fix env var in dockerfile * expose ports to docker network * fix iavl test checking has * Remove unused singleton * error compiling * fix interact * stylish * fix store leaf * more sane imports * organize imports * updated tutorial * remove trace statements * fix build error Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- .travis.yml | 3 + .../nameservice/docker-compose-test.yaml | 12 + hs-abci-docs/nameservice/docker-compose.yaml | 14 ++ hs-abci-docs/nameservice/interact/Interact.hs | 14 +- .../src/Nameservice/Application.hs | 20 +- .../nameservice/src/Nameservice/Config.hs | 9 +- .../src/Nameservice/Modules/Nameservice.hs | 48 ++-- .../Nameservice/Modules/Nameservice/Keeper.hs | 14 +- .../Nameservice/Modules/Nameservice/Query.hs | 12 +- .../Nameservice/Modules/Nameservice/Router.hs | 22 +- .../src/Nameservice/Modules/Token.hs | 14 +- .../src/Nameservice/Modules/Token/Keeper.hs | 4 +- .../src/Nameservice/Modules/Token/Query.hs | 7 +- .../src/Nameservice/Modules/Token/Router.hs | 16 +- .../test/Nameservice/Test/E2ESpec.hs | 13 +- .../tutorial/Foundations/01-Overview.md | 9 +- .../tutorial/Foundations/02-BaseApp.md | 92 ++++++-- .../tutorial/Foundations/03-Modules.md | 33 +-- .../Tutorial/Nameservice/04-Keeper.md | 6 +- .../tutorial/Tutorial/Nameservice/05-Query.md | 9 +- .../Tutorial/Nameservice/06-Module.md | 22 +- .../Tutorial/Nameservice/07-Application.md | 49 ++-- .../simple-storage/docker-compose.yaml | 12 + hs-abci-docs/simple-storage/package.yaml | 1 + .../src/SimpleStorage/Application.hs | 14 +- .../src/SimpleStorage/Config.hs | 25 +- .../SimpleStorage/Modules/SimpleStorage.hs | 11 +- .../Modules/SimpleStorage/Keeper.hs | 19 +- .../Modules/SimpleStorage/Query.hs | 9 +- .../Modules/SimpleStorage/Router.hs | 10 +- .../Modules/SimpleStorage/Types.hs | 2 + .../test/SimpleStorage/Test/E2ESpec.hs | 62 ++--- hs-abci-sdk/package.yaml | 22 +- hs-abci-sdk/src/Tendermint/SDK/Application.hs | 3 +- .../Tendermint/SDK/Application/AnteHandler.hs | 25 +- .../Tendermint/SDK/Application/Handlers.hs | 221 +++++++++--------- .../src/Tendermint/SDK/Application/Module.hs | 204 +++++++++------- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 56 ++--- .../src/Tendermint/SDK/BaseApp/BaseApp.hs | 63 ----- .../src/Tendermint/SDK/BaseApp/CoreEff.hs | 97 -------- .../src/Tendermint/SDK/BaseApp/Effects.hs | 32 +++ .../SDK/BaseApp/Effects/BaseEffs.hs | 56 +++++ .../SDK/BaseApp/Effects/CoreEffs.hs | 82 +++++++ .../SDK/BaseApp/Effects/PureCoreEffs.hs | 76 ++++++ .../src/Tendermint/SDK/BaseApp/Errors.hs | 16 ++ .../src/Tendermint/SDK/BaseApp/Events.hs | 12 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs | 17 +- .../src/Tendermint/SDK/BaseApp/Logger.hs | 8 +- .../Tendermint/SDK/BaseApp/Logger/Katip.hs | 26 ++- .../SDK/BaseApp/Metrics/Prometheus.hs | 9 +- .../src/Tendermint/SDK/BaseApp/Query.hs | 10 +- .../Tendermint/SDK/BaseApp/Query/Effect.hs | 47 ++++ .../Tendermint/SDK/BaseApp/Query/Router.hs | 109 ++++----- .../src/Tendermint/SDK/BaseApp/Query/Store.hs | 27 ++- .../src/Tendermint/SDK/BaseApp/Query/Types.hs | 20 +- .../Tendermint/SDK/BaseApp/Router/Delayed.hs | 4 +- .../src/Tendermint/SDK/BaseApp/Store.hs | 2 - .../SDK/BaseApp/Store/AuthTreeStore.hs | 125 ---------- .../Tendermint/SDK/BaseApp/Store/IAVLStore.hs | 158 +++++++++++++ .../SDK/BaseApp/Store/MemoryStore.hs | 197 ++++++++++++++++ .../Tendermint/SDK/BaseApp/Store/RawStore.hs | 181 +++++++++----- .../src/Tendermint/SDK/BaseApp/Store/Scope.hs | 34 --- .../src/Tendermint/SDK/BaseApp/Transaction.hs | 59 ++--- .../SDK/BaseApp/Transaction/AnteHandler.hs | 16 ++ .../SDK/BaseApp/Transaction/Cache.hs | 66 ++++++ .../SDK/BaseApp/Transaction/Checker.hs | 6 + .../SDK/BaseApp/Transaction/Effect.hs | 153 ++++++++---- .../SDK/BaseApp/Transaction/Modifier.hs | 11 - .../SDK/BaseApp/Transaction/Router.hs | 139 +++++++---- .../SDK/BaseApp/Transaction/Types.hs | 57 ++++- .../src/Tendermint/SDK/Modules/Auth.hs | 20 +- .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 10 +- .../src/Tendermint/SDK/Modules/Auth/Query.hs | 10 +- .../Tendermint/SDK/Test/AuthTreeStoreSpec.hs | 171 -------------- .../test/Tendermint/SDK/Test/IAVLStoreSpec.hs | 214 +++++++++++++++++ .../test/Tendermint/SDK/Test/QuerySpec.hs | 57 +++-- .../test/Tendermint/SDK/Test/SimpleStorage.hs | 99 ++++---- .../src/Tendermint/Utils/TxClient/Class.hs | 70 +++--- .../src/Tendermint/Utils/TxClient/Types.hs | 56 +++-- hs-iavl-client/package.yaml | 2 + hs-iavl-client/src/Database/IAVL/RPC/Types.hs | 37 +++ hs-iavl-client/test/Database/IAVL/RPCSpec.hs | 78 +++++-- 82 files changed, 2438 insertions(+), 1439 deletions(-) delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/BaseEffs.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/CoreEffs.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/PureCoreEffs.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Effect.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs delete mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs delete mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs create mode 100644 hs-iavl-client/src/Database/IAVL/RPC/Types.hs diff --git a/.travis.yml b/.travis.yml index af180257..2058ec98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -54,7 +54,10 @@ jobs: name: "Test Core and Client Libraries" script: - echo "Test libraries" + # this image is needed for the sdk IAVLSpec + - docker run --name iavl -p 8090:8090 -p 8091:8091 -d foamspace/iavl:latest /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" - make test-libraries + - docker rm -f iavl - echo "Test Libraries with Nix" - echo "This stage is currently disabled as Nix doesn't seem to play well with libsecp256k1" diff --git a/hs-abci-docs/nameservice/docker-compose-test.yaml b/hs-abci-docs/nameservice/docker-compose-test.yaml index b7f15d9d..5703df46 100644 --- a/hs-abci-docs/nameservice/docker-compose-test.yaml +++ b/hs-abci-docs/nameservice/docker-compose-test.yaml @@ -21,12 +21,24 @@ services: context: ../../. dockerfile: Dockerfile image: hs-abci:test + depends_on: + - iavl environment: - STATS_PORT=9200 + - IAVL_HOST=iavl + - IAVL_PORT=8090 restart: always entrypoint: /usr/local/bin/nameservice expose: - "26658" - "9200" + iavl: + image: foamspace/iavl:latest + command: /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + ports: + - "8090-8091:8091-8091" + expose: + - "8090" + - "8091" volumes: tendermint-storage: diff --git a/hs-abci-docs/nameservice/docker-compose.yaml b/hs-abci-docs/nameservice/docker-compose.yaml index 4b05e929..ee4d3891 100644 --- a/hs-abci-docs/nameservice/docker-compose.yaml +++ b/hs-abci-docs/nameservice/docker-compose.yaml @@ -26,9 +26,12 @@ services: - ES_HOST=elk - ES_PORT=9200 - STATS_PORT=5555 + - IAVL_HOST=iavl + - IAVL_port=8090 restart: always depends_on: - elk + - iavl ports: - "26658" - "5555:5555" @@ -39,6 +42,8 @@ services: restart: always environment: - DD_API_KEY=${DD_API_KEY} + - IAVL_HOST=iavl + - IAVL_PORT=8090 volumes: - /proc/:/host/proc/:ro - ./conf.d/openmetrics.d:/etc/datadog-agent/conf.d/openmetrics.d @@ -51,5 +56,14 @@ services: expose: - "9200" - "5601" + iavl: + image: foamspace/iavl:latest + command: /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + ports: + - "8090-8091:8091-8091" + expose: + - "8090" + - "8091" + volumes: tendermint-storage: diff --git a/hs-abci-docs/nameservice/interact/Interact.hs b/hs-abci-docs/nameservice/interact/Interact.hs index 7b590951..a3f43347 100644 --- a/hs-abci-docs/nameservice/interact/Interact.hs +++ b/hs-abci-docs/nameservice/interact/Interact.hs @@ -19,8 +19,8 @@ import qualified Nameservice.Modules.Nameservice as N import qualified Nameservice.Modules.Token as T import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..)) -import Tendermint.SDK.Application.Module (AppQueryRouter (QApi), - AppTxRouter (TApi)) +import Tendermint.SDK.Application.Module (ApplicationC, ApplicationD, + ApplicationQ) import Tendermint.SDK.BaseApp.Errors (AppError (..)) import Tendermint.SDK.BaseApp.Query (QueryArgs (..), QueryResult (..)) @@ -107,7 +107,7 @@ getAccount _ :<|> _ :<|> getAccount = genClientQ (Proxy :: Proxy m) queryApiP def where - queryApiP :: Proxy (QApi NameserviceModules) + queryApiP :: Proxy (ApplicationQ NameserviceModules) queryApiP = Proxy -------------------------------------------------------------------------------- @@ -166,10 +166,12 @@ faucet (buy :<|> set :<|> delete) :<|> (_ :<|> _ :<|> faucet) :<|> EmptyTxClient = - genClientT (Proxy @TxClientM) txApiP defaultClientTxOpts + genClientT (Proxy @TxClientM) txApiCP txApiDP defaultClientTxOpts where - txApiP :: Proxy (TApi NameserviceModules) - txApiP = Proxy + txApiCP :: Proxy (ApplicationC NameserviceModules) + txApiCP = Proxy + txApiDP :: Proxy (ApplicationD NameserviceModules) + txApiDP = Proxy -------------------------------------------------------------------------------- diff --git a/hs-abci-docs/nameservice/src/Nameservice/Application.hs b/hs-abci-docs/nameservice/src/Nameservice/Application.hs index 6fa25729..84b88b30 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Application.hs @@ -8,18 +8,18 @@ import Data.Proxy import qualified Nameservice.Modules.Nameservice as N import qualified Nameservice.Modules.Token as T import Tendermint.SDK.Application (HandlersContext (..), - Modules (..), + ModuleList (..), baseAppAnteHandler) -import Tendermint.SDK.BaseApp ((:&)) -import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A type EffR = - N.NameserviceEffs :& - T.TokenEffs :& - A.AuthEffs :& - BaseApp.BaseApp BaseApp.CoreEffs + N.NameserviceEffs BA.:& + T.TokenEffs BA.:& + A.AuthEffs BA.:& + BA.TxEffs BA.:& + BA.BaseApp BA.CoreEffs type NameserviceModules = '[ N.NameserviceM EffR @@ -27,15 +27,15 @@ type NameserviceModules = , A.AuthM EffR ] -handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR BaseApp.CoreEffs +handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR BA.CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules - , compileToCore = BaseApp.compileScopedEff + , compileToCore = BA.defaultCompileToCore , anteHandler = baseAppAnteHandler } where - nameserviceModules :: Modules NameserviceModules EffR + nameserviceModules :: ModuleList NameserviceModules EffR nameserviceModules = N.nameserviceModule :+ T.tokenModule diff --git a/hs-abci-docs/nameservice/src/Nameservice/Config.hs b/hs-abci-docs/nameservice/src/Nameservice/Config.hs index 10ecb41d..437c1cfe 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Config.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Config.hs @@ -24,6 +24,8 @@ import System.IO (stdout) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.BaseApp.Logger.Katip as KL import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P +import Tendermint.SDK.BaseApp.Store.IAVLStore (GrpcConfig (..), + initIAVLVersions) import Text.Read (read) @@ -35,10 +37,15 @@ makeLenses ''AppConfig makeAppConfig :: IO AppConfig makeAppConfig = do + versions <- initIAVLVersions + grpcConfig <- do + host <- getEnv "IAVL_HOST" + port <- read <$> getEnv "IAVL_PORT" + pure $ GrpcConfig host port prometheusEnv <- runMaybeT $ do prometheusPort <- read <$> MaybeT (lookupEnv "STATS_PORT") pure $ P.MetricsScrapingConfig prometheusPort - c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "nameservice") prometheusEnv + c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "nameservice") prometheusEnv versions grpcConfig prometheusServer <- newIORef Nothing addScribesToLogEnv $ AppConfig { _baseAppContext = c diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs index 440f860a..191aa0dd 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -4,40 +4,16 @@ module Nameservice.Modules.Nameservice -- * Module NameserviceM , nameserviceModule - - -- * types - , Name(..) - , Whois (..) - , NameserviceError(..) - , NameClaimed(..) - , NameRemapped(..) - , NameDeleted(..) - , BuyName(..) - , SetName(..) - , DeleteName(..) - - -- * effects - , NameserviceEffs - , getWhois - , buyName - , setName - , deleteName - - -- * interpreter - , eval - - -- * message router - , MessageApi - , messageHandlers - - -- * query API - , QueryApi - , server + , module Nameservice.Modules.Nameservice.Keeper + , module Nameservice.Modules.Nameservice.Messages + , module Nameservice.Modules.Nameservice.Query + , module Nameservice.Modules.Nameservice.Router + , module Nameservice.Modules.Nameservice.Types ) where import Data.Proxy -import Nameservice.Modules.Nameservice.Keeper +import Nameservice.Modules.Nameservice.Keeper hiding (storeKey) import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router @@ -45,20 +21,22 @@ import Nameservice.Modules.Nameservice.Types import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseAppEffs, - DefaultCheckTx (..)) +import Tendermint.SDK.BaseApp (BaseEffs, + DefaultCheckTx (..), + TxEffs) type NameserviceM r = - Module "nameservice" MessageApi QueryApi NameserviceEffs r + Module "nameservice" MessageApi MessageApi QueryApi NameserviceEffs r nameserviceModule - :: Members BaseAppEffs r + :: Members BaseEffs r + => Members TxEffs r => Members TokenEffs r => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) - , moduleQueryServer = server + , moduleQuerier = querier , moduleEval = eval } diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 0292da47..04f95c05 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -1,6 +1,14 @@ {-# LANGUAGE TemplateHaskell #-} -module Nameservice.Modules.Nameservice.Keeper where +module Nameservice.Modules.Nameservice.Keeper + ( NameserviceKeeper + , NameserviceEffs + , setName + , deleteName + , buyName + , storeKey + , eval + ) where import Data.Proxy import Data.String.Conversions (cs) @@ -29,13 +37,13 @@ storeKey :: BaseApp.StoreKey NameserviceModuleName storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @NameserviceModuleName eval - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + :: Members BaseApp.TxEffs r => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a -> Sem r a eval = mapError BaseApp.makeAppError . evalNameservice where evalNameservice - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + :: Members BaseApp.TxEffs r => Sem (NameserviceKeeper ': r) a -> Sem r a evalNameservice = interpret (\case diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index bc5864dc..d3d59ac4 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -1,10 +1,12 @@ -module Nameservice.Modules.Nameservice.Query where +module Nameservice.Modules.Nameservice.Query + ( QueryApi + , querier + ) where import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Name, Whois) import Polysemy (Members) -import Polysemy.Error (Error) import qualified Tendermint.SDK.BaseApp as BaseApp -------------------------------------------------------------------------------- @@ -15,8 +17,8 @@ type NameserviceContents = '[(Name, Whois)] type QueryApi = BaseApp.QueryApi NameserviceContents -server - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r +querier + :: Members BaseApp.QueryEffs r => BaseApp.RouteQ QueryApi r -server = +querier = BaseApp.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 8f292bbb..8f7632f9 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -1,4 +1,7 @@ -module Nameservice.Modules.Nameservice.Router where +module Nameservice.Modules.Nameservice.Router + ( MessageApi + , messageHandlers + ) where import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, buyName, deleteName, @@ -8,10 +11,8 @@ import Nameservice.Modules.Nameservice.Messages (BuyName, DeleteName, import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members, Sem) import Servant.API ((:<|>) (..)) -import Tendermint.SDK.BaseApp ((:~>), BaseAppEffs, - Return, - RouteContext (..), - RouteTx, +import Tendermint.SDK.BaseApp ((:~>), BaseEffs, + Return, RouteTx, RoutingTx (..), TxEffs, TypedMessage, incCount, withTimer) @@ -26,14 +27,15 @@ type MessageApi = :<|> TypedMessage DeleteName :~> Return () messageHandlers - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TokenEffs r + => Members TxEffs r => Members NameserviceEffs r - => RouteTx MessageApi r 'DeliverTx + => RouteTx MessageApi r messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH buyNameH - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TxEffs r => Members TokenEffs r => Members NameserviceEffs r @@ -44,7 +46,7 @@ buyNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do withTimer "buy_duration_seconds" $ buyName msgData setNameH - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TxEffs r => Members NameserviceEffs r => RoutingTx SetName @@ -54,7 +56,7 @@ setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do withTimer "set_duration_seconds" $ setName msgData deleteNameH - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TxEffs r => Members TokenEffs r => Members NameserviceEffs r diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs index 7e9fafbd..9b1bc699 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs @@ -33,7 +33,7 @@ module Nameservice.Modules.Token -- * Query Api , QueryApi - , server + , querier ) where @@ -45,19 +45,21 @@ import Nameservice.Modules.Token.Router import Nameservice.Modules.Token.Types import Polysemy (Members) import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseAppEffs, - DefaultCheckTx (..)) +import Tendermint.SDK.BaseApp (BaseEffs, + DefaultCheckTx (..), + TxEffs) import Tendermint.SDK.Types.Address (Address) -type TokenM r = Module "token" MessageApi QueryApi TokenEffs r +type TokenM r = Module "token" MessageApi MessageApi QueryApi TokenEffs r tokenModule - :: Members BaseAppEffs r + :: Members BaseEffs r + => Members TxEffs r => Members TokenEffs r => TokenM r tokenModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) - , moduleQueryServer = server + , moduleQuerier = querier , moduleEval = eval } diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs index 379d6b24..b8e9747d 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs @@ -26,12 +26,12 @@ storeKey :: BaseApp.StoreKey "token" storeKey = BaseApp.StoreKey "token" eval - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + :: Members BaseApp.TxEffs r => forall a. Sem (Token ': Error TokenError ': r) a -> Sem r a eval = mapError BaseApp.makeAppError . evalToken where evalToken - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r + :: Members BaseApp.TxEffs r => forall a. Sem (Token ': r) a -> Sem r a evalToken = interpret diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs index 5685c852..1585d6b1 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs @@ -4,7 +4,6 @@ import Data.Proxy import Nameservice.Modules.Token.Keeper (storeKey) import Nameservice.Modules.Token.Types (Amount) import Polysemy -import Polysemy.Error (Error) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Types.Address (Address) @@ -16,8 +15,8 @@ type TokenContents = '[(Address, Amount)] type QueryApi = BaseApp.QueryApi TokenContents -server - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r +querier + :: Members BaseApp.QueryEffs r => BaseApp.RouteQ QueryApi r -server = +querier = BaseApp.storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs index 3c5de02d..240bd4b6 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs @@ -10,10 +10,9 @@ import Nameservice.Modules.Token.Messages (Burn (..), FaucetAccount, Transfer (..)) import Polysemy (Members, Sem) import Servant.API ((:<|>) (..)) -import Tendermint.SDK.BaseApp ((:~>), BaseAppEffs, Return, - RouteContext (..), RouteTx, - RoutingTx (..), TxEffs, - TypedMessage) +import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, + RouteTx, RoutingTx (..), + TxEffs, TypedMessage) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) @@ -24,14 +23,15 @@ type MessageApi = messageHandlers :: Members TokenEffs r - => Members BaseAppEffs r - => RouteTx MessageApi r 'DeliverTx + => Members BaseEffs r + => Members TxEffs r + => RouteTx MessageApi r messageHandlers = burnH :<|> transferH :<|> faucetH transferH :: Members TokenEffs r => Members TxEffs r - => Members BaseAppEffs r + => Members BaseEffs r => RoutingTx Transfer -> Sem r () transferH (RoutingTx Tx{txMsg=Msg{msgData}}) = @@ -49,7 +49,7 @@ burnH (RoutingTx Tx{txMsg=Msg{msgData}}) = faucetH :: Members TokenEffs r => Members TxEffs r - => Members BaseAppEffs r + => Members BaseEffs r => RoutingTx FaucetAccount -> Sem r () faucetH (RoutingTx Tx{txMsg=Msg{msgData}}) = diff --git a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs index ebacc1b4..7c2d67bc 100644 --- a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -10,8 +10,7 @@ import qualified Nameservice.Modules.Token as T import Nameservice.Test.EventOrphans () import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..)) -import Tendermint.SDK.Application.Module (AppQueryRouter (QApi), - AppTxRouter (TApi)) +import qualified Tendermint.SDK.Application.Module as M import Tendermint.SDK.BaseApp.Errors (AppError (..)) import Tendermint.SDK.BaseApp.Query (QueryArgs (..), QueryResult (..), @@ -355,7 +354,7 @@ getBalance getWhois :<|> getBalance :<|> getAccount = genClientQ (Proxy :: Proxy m) queryApiP def where - queryApiP :: Proxy (QApi NameserviceModules) + queryApiP :: Proxy (M.ApplicationQ NameserviceModules) queryApiP = Proxy @@ -426,7 +425,9 @@ faucet (buyName :<|> setName :<|> deleteName) :<|> (_ :<|> transfer :<|> faucet) :<|> EmptyTxClient = - genClientT (Proxy @TxClientM) txApiP defaultClientTxOpts + genClientT (Proxy @TxClientM) txApiCP txApiDP defaultClientTxOpts where - txApiP :: Proxy (TApi NameserviceModules) - txApiP = Proxy + txApiCP :: Proxy (M.ApplicationC NameserviceModules) + txApiCP = Proxy + txApiDP :: Proxy (M.ApplicationD NameserviceModules) + txApiDP = Proxy diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md b/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md index 9d23bab5..1955824f 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md @@ -11,13 +11,14 @@ The SDK relies heavily on two abstractions to facilitate application development The effects system is backed by a library called `polysemy` which we mentioned in the introduction. An application basically has three layers of effects 1. **Application level effects**: These are introduced by the application developer in order to customize application behavior. -2. **BaseApp effects**: These are effects into which you must interpret your application in order for it to be runnable by the SDK. -3. **Core effects**: These are largely internal and used to interpet the BaseApp effects to `IO`. There are a few different core options available in the SDK, but the more advanced developer might wish to use their own. +2. **Transaction effects**: These are the effects that allow you to interpret transactions, emit events, meter gas, and handle storage requests. +3. **Base effects**: These include things like logging, metrics, exception handling, and some error handling. +4. **Core effects**: These are largely internal and used to interpet the other effects to `IO`. There are a two different core options available in the SDK (distinguished by a an in-memory versus production database), but the more advanced developer might wish to write their own. The tutorial explains the multiple points at which you can hook your application specific effects and types into the SDK. ## Modules -The core building block of an application is a Module. There are some modules that ship with the SDK and make up a kind of standard library. These modules are of general utility, like dealing with things like authentication or tokens, and are considered to be safe. +The core building block of an application is a `Module`. There are some modules that ship with the SDK and make up a kind of standard library. These modules are of general utility, like dealing with things like authentication or tokens, and are considered to be safe. -The most useful part of the SDK is that you are free to define your own modules, or depend on other third party modules outside the SDK. Since they all have the same type, they all easily fit into larger applications as standalone components or dependencies. +The most useful part of the SDK is that you are free to define your own modules, or depend on other third party modules outside the SDK. Since they all have the same type, they all easily compose into larger applications as standalone components or dependencies. diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md index 31a5bc28..319f5d6b 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md @@ -1,18 +1,16 @@ --- -title: Foundations - BaseApp +title: Foundations - BaseEffs, TxEffs, and QueryEffs --- -# BaseApp +# BaseEffs -`BaseApp` is the set of effects that the SDK operates with and are freely available -for an application developer to make use of in any part of their application code. It is expected -(in fact required) that any application code can be rewritten in terms of the `BaseApp` -effects. Let's look at the `BaseApp` type: +`BaseEffs` is a set of effects that the SDK operates with and are freely available +for an application developer to make use of in any part of their application code. They +are defined as: ~~~ haskell ignore -type BaseAppEffs = - [ RawStore - , Output Event +type BaseEffs = + [ Metrics , Logger , Resource , Error AppError @@ -21,12 +19,74 @@ type BaseAppEffs = These effects are: -1. `RawStore` - allows for basic storage operations, e.g. get, put, delete, prove etc. -2. `Output Event` - allows for emitting events in the course of transaction processing. -3. `Logger` - allows for console loging with log levels. -4. `Resource` - allows for bracketing and resource management in the presence of exeptions. -5. `Error AppError` -- allows for errors of type `AppError` to be thrown or caught. +1. `Metrics` - creates and manages custom counters and timers. +2. `Logger` - allows for structured logging with log levels. +3. `Resource` - allows for bracketing and resource management in the presence of exeptions. +4. `Error AppError` -- allows for errors of type `AppError` to be thrown or caught. + +The SDK does not make any assumptions about how `BaseEffs` will be interpreted at runtime, it only assumes that the developer might want use one of the provided core effects systems to interpret them. For example, the standard `CoreEffs` uses a prometheus metrics server to interpret the `Metrics` effect while `PureCoreEffs` just ignores the effect entirely. + +# TxEffs + +`TxEffs` are the effects used to interpret transactions and are defined as + +~~~ haskell ignore +type TxEffs = + [ Output Event + , GasMeter + , WriteStore + , ReadStore + , Error AppError + ] +~~~ + +where + +1. `Output Event` - allows for emitting events during transaction execution. +2. `GasMeter` - allows for gas costs to be levied at any place during transaction code. +3. `WriteStore` - allows for put/delete operations on the database. +4. `ReadStore` - allows for get/prove operations on the database. +5. `Error AppError` - allows for throwing and catching errors raised during transactions. + +`TxEffs` effects are available any time during transactions and are interpreted at the time of transaction routing. It's worth noting that the interpreters take care of finalizing writes to the database when it's appropriate (i.e. during a `deliverTx` message) and not otherwise (e.g. during a `checkTx` message). + +# QueryEffs + +`QueryEffs` are used to interpret queries and are defined as + +~~~ haskell ignore +type QueryEffs = + [ ReadStore + , Error AppError + ] +~~~ + +where + +1. `ReadStore` allows for get/prove operations on the database. +2. `Error AppError` allows for throwing/catching errors when performing database queries. + +`QueryEffs` are available any time you are writing handlers for a module's query api. The SDK manages a separate connection for reading from committed (i.e. via blocks) state when `QueryEffs` are present. + +# BaseApp + +There is a type alias in the SDK called `BaseApp` with the definition + +~~~ haskell ignore +type BaseApp core = BaseEffs :& StoreEffs :& core +~~~ + +where `StoreEffs` is given by + +~~~ haskell ignore +type StoreEffs = + [ Tagged 'Consensus ReadStore + , Tagged 'QueryAndMempool ReadStore + , Tagged 'Consensus WriteStore + , Transaction + , CommitBlock + ] +~~~ -`BaseApp` acts as an intermediate effect system for specifying applications, it does not make any assumptions about how these effects will be interpreted at runtime. For example, `RawStore` could eventualy be interpeted by any persistent or in-memory storage capable of handling the commands `Put`, `Get` etc. -Most of the work in writing modules involves plugging into `BaseApp` at various points. For example, your module can create custom errors to throw or catch, but you must tell the SDK how to translate this custom error into an `AppError`. Likewise your module can define custom events to log during transaction execution, but you must describe to the SDK how to translate these custom events types into the type `Event`. +It sits at the bottom of any applications effects list and ultimately everything is interpreted through these effects before being run, e.g. `TxEffs` and `QueryEffs`. This effects list is pretty much the only place where the application developer needs to decide how the interpretation should be done. There are two options in the SDK, using `PureCoreEffs` or `CoreEffs` depending on whether you want to rely on an external or in-memory database. \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md index b6b2eea2..e89b4be4 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md @@ -6,42 +6,47 @@ title: Foundations - Module ## Definition -A `Module` has a very specific meaning in the context of this SDK. A `Module` is something between a library and a small state machine. It is built on top of the `BaseApp` abstraction in the sense that all `Module`s must be explicitly interpeted in terms of `BaseApp` in order to compile the application. The full type definition is +A `Module` has a very specific meaning in the context of this SDK. A `Module` is something between a library and a small state machine. Here is the type: ~~~ haskell ignore -data Module (name :: Symbol) (h :: *) (q :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: T.RouteTx h r 'DeliverTx - , moduleTxChecker :: T.RouteTx h r 'CheckTx - , moduleQueryServer :: Q.RouteQ q r - , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a +data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: EffectRow) (r :: EffectRow) = Module + { moduleTxChecker :: T.RouteTx check r + , moduleTxDeliverer :: T.RouteTx deliver r + , moduleQuerier :: Q.RouteQ query r + , moduleEval :: forall deps. Members T.TxEffs deps => forall a. Sem (es :& deps) a -> Sem deps a } ~~~ where the type parameters - `name` is the name of the module, e.g. `"bank"`. -- `h` is the transaction router api type. -- `q` is the query api type for querying state in the url format (more on this later). -- `s` is the set of effects introduced by this module. +- `check` is the transaction router api type for `checkTx` messages. +- `deliver` is the transaction router api type for `checkTx` messages. +- `query` is the query router api type for `query` messages +- `es` is the set of effects introduced by this module. - `r` is the global set of effects that this module will run in when part of a larger application (more on this later). Below that line we see the fields for the `Module` data type, where - `moduleTxDeliverer` specifies how the module processes transactions in order to update the application state during `deliverTx` messages. - `moduleTxChecker` is used during `checkTx` messages to check if a transaction in the mempool is a valid transaction. - - `moduleQueryServer` is responsible for handling queries for application state from the `query` message. + - `moduleQuerier` is responsible for handling queries for application state from the `query` message. - `moduleEval` is the natural transformation that specifies how to interpet the `Module` in terms of `BaseApp`. +If you have ever used the `servant` library for specifying rest apis, then the type families `T.RouteTx` and `Q.RouteQ` may look familiar to you, they play a similar role as `ServerT`. + Note that in the event that a `Module` is _abstract_, meaning it doesn't have any messages to respond to, then we have `msg ~ Void`. ## Composition -`Module`s are meant to be composed to create larger applications. We will see examples of this with the `Nameservice` application. The way to do this is easy, as the `Modules` data type allows you to simply combine them in a heterogeneous list: +`Module`s are meant to be composed to create larger applications. We will see examples of this with the `Nameservice` application. The way to do this is easy, as the `ModuleList` data type allows you to simply combine them in a heterogeneous list: ~~~ haskell ignore -data Modules (ms :: [*]) r where +data ModuleList (ms :: [*]) r where NilModules :: Modules '[] r - (:+) :: Module name msg api s r -> Modules ms r -> Modules (Module name msg api s r ': ms) r + (:+) :: Module name check deliver query es r + -> Modules ms r + -> Modules (Module name check deliver query es r ': ms) r ~~~ -When you are ready to create your application, you simply specify a value of type `Modules` and some other configuration data, and the SDK will create an `App` for you. +When you are ready to create your application, you simply specify a value of type `ModuleList` and some other configuration data, and the SDK will create an `App` for you. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md index c3d3c707..c602eb7e 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md @@ -91,7 +91,7 @@ Taking a look at the class constraints, we see ### Evaluating Module Effects -Like we said before, all modules must ultimately compile to the set of effects belonging to `BaseApp`. For effects interpreted to `RawStore`, this means that you will need to define something called a `StoreKey`. +Like we said before, all transactions must ultimately compile to the set of effects belonging to `TxEffs` and `BaseEffs`. For effects interpreted to `ReadStore` and `WriteStore`, this means that you will need to define something called a `StoreKey`. A `StoreKey` is effectively a namespacing inside the database, and is unique for a given module. In theory it could be any `ByteString`, but the natural definition in the case of Nameservice is would be something like @@ -105,13 +105,13 @@ With this `storeKey` it is possible to write the `eval` function to resolve the ~~~ haskell eval - :: Members [BA.RawStore, Error BA.AppError] r + :: Members BA.TxEffs r => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a -> Sem r a eval = mapError BA.makeAppError . evalNameservice where evalNameservice - :: Members [BA.RawStore, Error BA.AppError] r + :: Members BA.TxEffs r => Sem (NameserviceKeeper ': r) a -> Sem r a evalNameservice = interpret (\case diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md index 278c97cd..706bb96e 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md @@ -13,8 +13,7 @@ import Data.Proxy import Nameservice.Modules.Nameservice.Keeper (storeKey) import Nameservice.Modules.Nameservice.Types (Whois, Name) import Polysemy (Members) -import Polysemy.Error (Error) -import Tendermint.SDK.BaseApp (RawStore, AppError, RouteQ, QueryApi, storeQueryHandlers) +import Tendermint.SDK.BaseApp (QueryEffs, RouteQ, QueryApi, storeQueryHandlers) ~~~ The way to query application state is via the `query` message which uses a `url` like format. The SDK tries to abstract as much of this away as possible. For example, if you want to only serve state that you have registered with the store via the `IsKey` class, then things are very easy. If you need to make joins to serve requests, we support this as well and it's not hard, but we will skip this for now. @@ -33,10 +32,10 @@ type Api = QueryApi NameserviceContents To serve all the data registered with the `IsKey` class, we can use the `storeQueryHandlers` function, supplying a proxy for the store contents, the `storeKey` and a proxy for the effects used in serving requests. In this case because we are serving only types registered with the store, we will need to assume the `RawStore` and `Error AppError` effects. ~~~ haskell -server - :: Members [RawStore, Error AppError] r +querier + :: Members QueryEffs r => RouteQ Api r -server = +querier = storeQueryHandlers (Proxy @NameserviceContents) storeKey (Proxy :: Proxy r) ~~~ diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md index ef4f01de..e65a1e17 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md @@ -12,38 +12,37 @@ At this point we can collect the relevant pieces to form the Nameservice module: module Tutorial.Nameservice.Module where import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, eval) -import Nameservice.Modules.Nameservice.Query (QueryApi, server) +import Nameservice.Modules.Nameservice.Query (QueryApi, querier) import Nameservice.Modules.Nameservice.Router (MessageApi, messageHandlers) import Nameservice.Modules.Nameservice.Types (NameserviceModuleName) import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) import Data.Proxy import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseAppEffs, - DefaultCheckTx (..)) +import Tendermint.SDK.BaseApp (BaseEffs, TxEffs, DefaultCheckTx (..)) -- a convenient type alias type NameserviceM r = - Module NameserviceModuleName MessageApi QueryApi NameserviceEffs r + Module NameserviceModuleName MessageApi MessageApi QueryApi NameserviceEffs r nameserviceModule - :: Members BaseAppEffs r + :: Members BaseEffs r + => Members TxEffs r => Members TokenEffs r => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) - , moduleQueryServer = server + , moduleQuerier = querier , moduleEval = eval } ~~~ -Here We are using `defaultCheckTx` as our transaction checker, which is a static message validator defined that respons to any message with the following handler: +Here We are using `defaultCheckTx` as our transaction checker, which is a static, message validating handler defined as: ~~~ haskell ignore - defaultCheckTxHandler :: Member (Error AppError) r => ValidateMessage msg @@ -54,16 +53,15 @@ defaultCheckTxHandler(RoutingTx Tx{txMsg}) = V.Failure err -> throwSDKError . MessageValidation . map formatMessageSemanticError $ err V.Success _ -> pure () - ~~~ Note that this checker can be used to implement any transaction for which 1. The message accepted by the router has a `ValidateMessage` instance -2. The return type is marked with `OnCheckUnit`, meaning that `()` is returned for any `checkTx` ABCI message. +2. The return type in the serve type is `Return ()` -To generate a router for which every transaction has these properties, we used the `defaultCheckTx` type class method +To generate a server for which every transaction has these properties, we used the `defaultCheckTx` type class method on the `MessageApi` type. This will generate a server of type `VoidReturn MessageApi`, which has the exact same shape as `MessageApi` just will all the return values changed to `Return ()`. In this paricular case all handlers for `MessageApi` already return `()`, so we have `MessageApi ~ VoidReturn MessageApi` and there's no need to use the `VoidReturn` family in the module type. -Note the constraints on `r`: +Note the constraints on `r` in the Module's type: ~~~ haskell ignore ... diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md index 8310470f..d1c9d1f9 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md @@ -13,13 +13,14 @@ newtype App m = App { unApp :: forall (t :: MessageType). Request t -> m (Response t) } ~~~ -and ultimately our configuration of modules must be converted to this format. This is probably the most important part of the SDK, to provide the bridge between the list of modules - a heterogeneous list of type `Modules` - and the actual application. The type that provides the input for this bridge is `HandlersContext`: +and ultimately our configuration of modules must be converted to this format. This is probably the most important part of the SDK, to provide the bridge between the list of modules - a heterogeneous list of type `ModuleList` - and the actual application. The type that provides the input for this bridge is `HandlersContext`: ~~~ haskell ignore data HandlersContext alg ms r core = HandlersContext { signatureAlgP :: Proxy alg - , modules :: M.Modules ms r - , compileToCore :: forall a. ScopedEff core a -> Sem core a + , modules :: M.ModuleList ms r + , anteHandler :: BA.AnteHandler r + , compileToCore :: forall a. Sem (BA.BaseApp core) a -> Sem core a } ~~~ @@ -29,11 +30,23 @@ where - `r` is the global effects list for the application - `core` is the set of core effects that are used to interpet `BaseApp` to `IO`. -We should say a few words on this `compileToCore` field. The application developer has access to any effects in `BaseApp`, -but `BaseApp` itself still needs to be interpreted in order to run the application. In other words, `BaseApp` is still just a -list of free effects. The set of effects capable of interpreting `BaseApp` is called `core`, and while the developer is free to provide any `core` they want, we have a standard set of them in the SDK - e.g. in memory, production, etc. +We should say a few words on this `compileToCore` field. The application developer has to, at the end of the day, specify how the entire effects system for the application will be interpreted to `IO`. Luckily most of these decisions are abstracted away, but the one that remains is dealing with `BaseApp core`. The sdk provides two default methods for two different types of `core`: -The `ScopedEff` type is more complicated and not relevant to the discussion of application development. Long story short, tendermint core requests three connections to the application's state - `Consensus`, `Mempool` and `Query`. The `ScopedEff` type is used to abstract this concern away from the developer, and as long as you are using one of the `core` effects provided in the SDK you don't need to worry about it. + +~~~ haskell ignore +defaultCompileToCore + :: forall a. + Sem (BaseApp CoreEffs) a + -> Sem CoreEffs a + +defaultCompileToPureCore + :: forall a. + Sem (BaseApp PureCoreEffs) a + -> Sem PureCoreEffs a +~~~ + + +The difference is that `defaultCompileToCore` uses the IAVL store external database and also allows for metrics, where `defaultCompileToPureCore` uses an in-memory db and treats all metrics operations as a no-op. ## Tutorial.Nameservice.Application @@ -46,14 +59,12 @@ import Nameservice.Modules.Token (tokenModule, TokenM, TokenEffs) import Network.ABCI.Server.App (App) import Polysemy (Sem) import Tendermint.SDK.Modules.Auth (authModule, AuthEffs, AuthM) -import Tendermint.SDK.Application (Modules(..), HandlersContext(..), baseAppAnteHandler, makeApp) -import Tendermint.SDK.BaseApp (BaseApp, CoreEffs, (:&), compileScopedEff) +import Tendermint.SDK.Application (ModuleList(..), HandlersContext(..), baseAppAnteHandler, makeApp, createIOApp) +import Tendermint.SDK.BaseApp (BaseApp, CoreEffs, Context, TxEffs, (:&), defaultCompileToCore, runCoreEffs) import Tendermint.SDK.Crypto (Secp256k1) ~~~ -This is the part of the application where the effects list must be given a monomorphic type. There is also a requirement -that the `Modules` type for the application be given the same _order_ as the effects introducted. This ordering problem is due -to the fact that type level lists are used to represent the effects in `polysemy`, and the order matters there. Still, it's only a small annoyance. +This is the part of the application where the effects list must be given a monomorphic type. The only requirement is that you list the effects in the same order that the corresponding modules appear in the `NameserviceModules` list: ~~~ haskell @@ -61,6 +72,7 @@ type EffR = NameserviceEffs :& TokenEffs :& AuthEffs :& + TxEffs :& BaseApp CoreEffs type NameserviceModules = @@ -70,7 +82,7 @@ type NameserviceModules = ] ~~~ -Notice that we've specified `EffR` as the effects list for each of the modules to run in, which trivially satisfies the constraints on each module at the definition site, since it is simply the union of all effects. +Also notice that `TxEffs :& BaseApp CoreEffs` appears at the end of the effects list, but doesn't strictly corrospond to a module. This needs to be here as ultimately all transactions and queries are intereted to `TxEffs :& BaseApp CoreEffs` before being run. We're now ready to define the `HandlersContext` for our application: @@ -79,11 +91,11 @@ handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules - , compileToCore = compileScopedEff + , compileToCore = defaultCompileToCore , anteHandler = baseAppAnteHandler } where - nameserviceModules :: Modules NameserviceModules EffR + nameserviceModules :: ModuleList NameserviceModules EffR nameserviceModules = nameserviceModule :+ tokenModule @@ -98,3 +110,10 @@ Finally we're able to define our application that runs in the `CoreEffs` context app :: App (Sem CoreEffs) app = makeApp handlersContext ~~~ + +Since the ABCI server requires you to pass a value of type `App IO`, we have one more transformation to perform to get replace the `Sem CoreEffs` in our app. We can simple use the `createIOApp` function: + +~~~ haskell +makeIOApp :: Context -> App IO +makeIOApp ctx = createIOApp (runCoreEffs ctx) app +~~~ diff --git a/hs-abci-docs/simple-storage/docker-compose.yaml b/hs-abci-docs/simple-storage/docker-compose.yaml index 6e3649d1..9750c6f3 100644 --- a/hs-abci-docs/simple-storage/docker-compose.yaml +++ b/hs-abci-docs/simple-storage/docker-compose.yaml @@ -23,9 +23,21 @@ services: image: hs-abci:test environment: - DD_API_KEY=${DD_API_KEY} + - IAVL_HOST=iavl + - IAVL_PORT=8090 restart: always entrypoint: /usr/local/bin/simple-storage expose: - "26658" + iavl: + image: foamspace/iavl:latest + command: /iavlserver -db-name "test" -datadir "." -grpc-endpoint "0.0.0.0:8090" -gateway-endpoint "0.0.0.0:8091" + ports: + - "8090:8090" + - "8091:8091" + expose: + - "8090" + - "8091" + volumes: tendermint-storage: diff --git a/hs-abci-docs/simple-storage/package.yaml b/hs-abci-docs/simple-storage/package.yaml index 23ab2344..52db0792 100644 --- a/hs-abci-docs/simple-storage/package.yaml +++ b/hs-abci-docs/simple-storage/package.yaml @@ -134,3 +134,4 @@ tests: - hspec - mtl - servant + - random diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs index bca6eeb5..c3052ffe 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs @@ -7,10 +7,10 @@ module SimpleStorage.Application import Data.Proxy import SimpleStorage.Modules.SimpleStorage as SimpleStorage import Tendermint.SDK.Application (HandlersContext (..), - Modules (..), + ModuleList (..), baseAppAnteHandler) import Tendermint.SDK.BaseApp ((:&)) -import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A @@ -19,22 +19,24 @@ import qualified Tendermint.SDK.Modules.Auth as A type EffR = SimpleStorage.SimpleStorageEffs :& A.AuthEffs :& - BaseApp.BaseApp BaseApp.CoreEffs + BA.TxEffs :& + BA.BaseApp BA.CoreEffs + type SimpleStorageModules = '[ SimpleStorage.SimpleStorageM EffR , A.AuthM EffR ] -handlersContext :: HandlersContext Secp256k1 SimpleStorageModules EffR BaseApp.CoreEffs +handlersContext :: HandlersContext Secp256k1 SimpleStorageModules EffR BA.CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = simpleStorageModules - , compileToCore = BaseApp.compileScopedEff + , compileToCore = BA.defaultCompileToCore , anteHandler = baseAppAnteHandler } where - simpleStorageModules :: Modules SimpleStorageModules EffR + simpleStorageModules :: ModuleList SimpleStorageModules EffR simpleStorageModules = SimpleStorage.simpleStorageModule :+ A.authModule diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Config.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Config.hs index 836886a9..a8c7a662 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Config.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Config.hs @@ -6,15 +6,17 @@ module SimpleStorage.Config , makeAppConfig ) where -import Control.Lens (makeLenses, (&), (.~), - (^.)) -import Data.Maybe (fromMaybe) -import Data.String.Conversions (cs) -import qualified Katip as K +import Control.Lens (makeLenses, (&), (.~), + (^.)) +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import qualified Katip as K import System.Environment -import System.IO (stdout) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.BaseApp.Logger.Katip as KL +import System.IO (stdout) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.BaseApp.Store.IAVLStore (GrpcConfig (..), + initIAVLVersions) data AppConfig = AppConfig @@ -24,7 +26,12 @@ makeLenses ''AppConfig makeAppConfig :: IO AppConfig makeAppConfig = do - c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") Nothing + versions <- initIAVLVersions + grpcConfig <- do + host <- getEnv "IAVL_HOST" + port <- read <$> getEnv "IAVL_PORT" + pure $ GrpcConfig host port + c <- BaseApp.makeContext (KL.InitialLogNamespace "dev" "simple-storage") Nothing versions grpcConfig addScribesToLogEnv $ AppConfig { _baseAppContext = c } diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index 35e529d6..c884d348 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -12,7 +12,7 @@ import Data.Proxy import Polysemy (Member, Members) import SimpleStorage.Modules.SimpleStorage.Keeper hiding (storeKey) import SimpleStorage.Modules.SimpleStorage.Message -import SimpleStorage.Modules.SimpleStorage.Query (QueryApi, server) +import SimpleStorage.Modules.SimpleStorage.Query (QueryApi, querier) import SimpleStorage.Modules.SimpleStorage.Router (MessageApi, messageHandlers) import SimpleStorage.Modules.SimpleStorage.Types @@ -20,15 +20,16 @@ import Tendermint.SDK.Application (Module (..)) import qualified Tendermint.SDK.BaseApp as BaseApp type SimpleStorageM r = - Module "simple_storage" MessageApi QueryApi SimpleStorageEffs r + Module "simple_storage" MessageApi MessageApi QueryApi SimpleStorageEffs r simpleStorageModule :: Member SimpleStorage r - => Members BaseApp.BaseAppEffs r + => Members BaseApp.TxEffs r + => Members BaseApp.BaseEffs r => SimpleStorageM r simpleStorageModule = Module { moduleTxDeliverer = messageHandlers - , moduleTxChecker = BaseApp.defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) - , moduleQueryServer = server + , moduleTxChecker = BaseApp.defaultCheckTx (Proxy @MessageApi) (Proxy :: Proxy r) + , moduleQuerier = querier , moduleEval = eval } diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs index 0339283a..65689b68 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -2,16 +2,14 @@ module SimpleStorage.Modules.SimpleStorage.Keeper ( SimpleStorage , SimpleStorageEffs - , storeKey , updateCount , getCount + , storeKey , eval ) where -import Data.Maybe (fromJust) import Polysemy (Members, Sem, interpret, makeSem) -import Polysemy.Error (Error) import Polysemy.Output (Output) import SimpleStorage.Modules.SimpleStorage.Types (Count, CountKey (..), @@ -23,25 +21,28 @@ storeKey = BaseApp.StoreKey "simple_storage" data SimpleStorage m a where PutCount :: Count -> SimpleStorage m () - GetCount :: SimpleStorage m Count + GetCount :: SimpleStorage m (Maybe Count) makeSem ''SimpleStorage type SimpleStorageEffs = '[SimpleStorage] updateCount - :: Members '[SimpleStorage, Output BaseApp.Event] r + :: Members '[SimpleStorage, Output BaseApp.Event, BaseApp.Logger] r => Count -> Sem r () updateCount count = do putCount count - BaseApp.emit $ CountSet count + let event = CountSet count + BaseApp.emit event + BaseApp.logEvent event eval :: forall r. - Members '[BaseApp.RawStore, Error BaseApp.AppError] r + Members BaseApp.TxEffs r => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) eval = interpret (\case - PutCount count -> BaseApp.put storeKey CountKey count - GetCount -> fromJust <$> BaseApp.get storeKey CountKey + PutCount count -> do + BaseApp.put storeKey CountKey count + GetCount -> BaseApp.get storeKey CountKey ) diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs index 788730b2..741d868a 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs @@ -1,12 +1,11 @@ module SimpleStorage.Modules.SimpleStorage.Query ( CountStoreContents , QueryApi - , server + , querier ) where import Data.Proxy import Polysemy (Members) -import Polysemy.Error (Error) import SimpleStorage.Modules.SimpleStorage.Keeper (storeKey) import SimpleStorage.Modules.SimpleStorage.Types (Count, CountKey) import qualified Tendermint.SDK.BaseApp as BaseApp @@ -16,9 +15,9 @@ type CountStoreContents = '[(CountKey, Count)] type QueryApi = BaseApp.QueryApi CountStoreContents -server - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r +querier + :: Members BaseApp.QueryEffs r => BaseApp.RouteQ QueryApi r -server = +querier = BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs index 57a9e9a6..86e12d82 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -9,9 +9,8 @@ import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, updateCount) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) -import Tendermint.SDK.BaseApp ((:~>), Return, - RouteContext (..), - RouteTx, +import Tendermint.SDK.BaseApp ((:~>), BaseEffs, + Return, RouteTx, RoutingTx (..), TxEffs, TypedMessage) @@ -24,12 +23,15 @@ type MessageApi = messageHandlers :: Member SimpleStorage r - => RouteTx MessageApi r 'DeliverTx + => Members TxEffs r + => Members BaseEffs r + => RouteTx MessageApi r messageHandlers = updateCountH updateCountH :: Member SimpleStorage r => Members TxEffs r + => Members BaseEffs r => RoutingTx UpdateCountTx -> Sem r () updateCountH (RoutingTx Tx{txMsg}) = diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs index 6d52ba79..8c7d53c0 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -53,3 +53,5 @@ instance A.FromJSON CountSet where instance BaseApp.ToEvent CountSet where makeEventType _ = "count_set" + +instance BaseApp.Select CountSet diff --git a/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 401df1ec..2b66d28b 100644 --- a/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -2,13 +2,14 @@ module SimpleStorage.Test.E2ESpec (spec) where import Control.Monad.Reader (ReaderT, runReaderT) import Data.Default.Class (def) +import Data.Int (Int32) import Data.Proxy import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..)) import SimpleStorage.Application import qualified SimpleStorage.Modules.SimpleStorage as SS -import Tendermint.SDK.Application.Module (AppQueryRouter (QApi), - AppTxRouter (TApi)) +import System.Random (randomIO) +import qualified Tendermint.SDK.Application.Module as M import Tendermint.SDK.BaseApp.Errors (AppError (..)) import Tendermint.SDK.BaseApp.Query (QueryArgs (..), QueryResult (..), @@ -33,29 +34,30 @@ import Test.Hspec spec :: Spec spec = do - describe "SimpleStorage E2E - via hs-tendermint-client" $ do - - it "Can query /health to make sure the node is alive" $ do - resp <- RPC.runTendermintM rpcConfig RPC.health - resp `shouldBe` RPC.ResultHealth - - it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ do - let txOpts = TxOpts - { txOptsGas = 0 - , txOptsSigner = makeSignerFromUser user1 - } - tx = SS.UpdateCountTx - { SS.updateCountTxUsername = "charles" - , SS.updateCountTxCount = 4 - } - resp <- assertTx . runTxClientM $ updateCount txOpts tx - ensureResponseCodes (0,0) resp - - it "can make sure the synchronous tx transaction worked and the count is now 4" $ do - resp <- assertQuery . RPC.runTendermintM rpcConfig $ - getCount defaultQueryArgs { queryArgsData = SS.CountKey } - let foundCount = queryResultData resp - foundCount `shouldBe` SS.Count 4 + beforeAll (abs <$> randomIO :: IO Int32) $ + describe "SimpleStorage E2E - via hs-tendermint-client" $ do + + it "Can query /health to make sure the node is alive" $ \_ -> do + resp <- RPC.runTendermintM rpcConfig RPC.health + resp `shouldBe` RPC.ResultHealth + + it "Can submit a tx synchronously and make sure that the response code is 0 (success)" $ \c -> do + let txOpts = TxOpts + { txOptsGas = 0 + , txOptsSigner = makeSignerFromUser user1 + } + tx = SS.UpdateCountTx + { SS.updateCountTxUsername = "charles" + , SS.updateCountTxCount = c + } + resp <- assertTx . runTxClientM $ updateCount txOpts tx + ensureResponseCodes (0,0) resp + + it "can make sure the synchronous tx transaction worked and the count is now 4" $ \c -> do + resp <- assertQuery . RPC.runTendermintM rpcConfig $ + getCount defaultQueryArgs { queryArgsData = SS.CountKey } + let foundCount = queryResultData resp + foundCount `shouldBe` SS.Count c -------------------------------------------------------------------------------- -- Query Client @@ -72,7 +74,7 @@ getAccount getCount :<|> getAccount = genClientQ (Proxy :: Proxy m) queryApiP def where - queryApiP :: Proxy (QApi SimpleStorageModules) + queryApiP :: Proxy (M.ApplicationQ SimpleStorageModules) queryApiP = Proxy @@ -109,10 +111,12 @@ updateCount -> TxClientM (TxClientResponse () ()) updateCount :<|> EmptyTxClient = - genClientT (Proxy @TxClientM) txApiP defaultClientTxOpts + genClientT (Proxy @TxClientM) txApiCP txApiDP defaultClientTxOpts where - txApiP :: Proxy (TApi SimpleStorageModules) - txApiP = Proxy + txApiCP :: Proxy (M.ApplicationC SimpleStorageModules) + txApiCP = Proxy + txApiDP :: Proxy (M.ApplicationD SimpleStorageModules) + txApiDP = Proxy -------------------------------------------------------------------------------- diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index f8eb57e2..cd92bea3 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -59,8 +59,11 @@ library: - errors - hs-abci-server - hs-abci-types + - hs-iavl-client - http-api-data - http-types + - http2-client + - http2-client-grpc - katip - lens - memory @@ -74,7 +77,6 @@ library: - proto3-wire - secp256k1-haskell - servant - - stm - string-conversions - text - time @@ -89,13 +91,15 @@ library: - -Wredundant-constraints exposed-modules: - Tendermint.SDK.Application - - Tendermint.SDK.Application.AnteHandler - Tendermint.SDK.Application.App - Tendermint.SDK.Application.Module - Tendermint.SDK.Application.Handlers + - Tendermint.SDK.Application.AnteHandler - Tendermint.SDK.BaseApp - - Tendermint.SDK.BaseApp.BaseApp - - Tendermint.SDK.BaseApp.CoreEff + - Tendermint.SDK.BaseApp.Effects + - Tendermint.SDK.BaseApp.Effects.BaseEffs + - Tendermint.SDK.BaseApp.Effects.CoreEffs + - Tendermint.SDK.BaseApp.Effects.PureCoreEffs - Tendermint.SDK.BaseApp.Errors - Tendermint.SDK.BaseApp.Events - Tendermint.SDK.BaseApp.Gas @@ -111,13 +115,14 @@ library: - Tendermint.SDK.BaseApp.Router.Types - Tendermint.SDK.BaseApp.Router.Router - Tendermint.SDK.BaseApp.Store - - Tendermint.SDK.BaseApp.Store.AuthTreeStore + - Tendermint.SDK.BaseApp.Store.IAVLStore + - Tendermint.SDK.BaseApp.Store.MemoryStore - Tendermint.SDK.BaseApp.Store.RawStore - - Tendermint.SDK.BaseApp.Store.Scope - Tendermint.SDK.BaseApp.Transaction + - Tendermint.SDK.BaseApp.Transaction.AnteHandler + - Tendermint.SDK.BaseApp.Transaction.Cache - Tendermint.SDK.BaseApp.Transaction.Checker - Tendermint.SDK.BaseApp.Transaction.Effect - - Tendermint.SDK.BaseApp.Transaction.Modifier - Tendermint.SDK.BaseApp.Transaction.Router - Tendermint.SDK.BaseApp.Transaction.Types - Tendermint.SDK.Codec @@ -140,7 +145,7 @@ tests: main: Spec.hs source-dirs: test other-modules: - - Tendermint.SDK.Test.AuthTreeStoreSpec + - Tendermint.SDK.Test.IAVLStoreSpec - Tendermint.SDK.Test.CryptoSpec - Tendermint.SDK.Test.GasSpec - Tendermint.SDK.Test.MetricsSpec @@ -168,6 +173,7 @@ tests: - hspec - hspec-core - hspec-discover + - http2-client-grpc - lens - memory - polysemy diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application.hs b/hs-abci-sdk/src/Tendermint/SDK/Application.hs index 5f90a229..6389e284 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application.hs @@ -1,8 +1,7 @@ module Tendermint.SDK.Application - ( Modules(..) + ( ModuleList(..) , Module(..) , HandlersContext(..) - , AnteHandler(..) , baseAppAnteHandler , createIOApp , makeApp diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs index 443ffa46..8b73474f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs @@ -1,7 +1,7 @@ module Tendermint.SDK.Application.AnteHandler - ( AnteHandler(..) - , applyAnteHandler - , baseAppAnteHandler + ( module Tendermint.SDK.Application.AnteHandler + -- Re-Exports + , AnteHandler(..) ) where import Control.Monad (unless) @@ -9,27 +9,12 @@ import Polysemy import Polysemy.Error (Error) import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), throwSDKError) -import Tendermint.SDK.BaseApp.Transaction (RoutingTx (..), - TransactionApplication) +import Tendermint.SDK.BaseApp.Transaction (AnteHandler (..), + RoutingTx (..)) import qualified Tendermint.SDK.Modules.Auth as A import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) -data AnteHandler r = AnteHandler - ( TransactionApplication (Sem r) -> TransactionApplication (Sem r)) - -instance Semigroup (AnteHandler r) where - (<>) (AnteHandler h1) (AnteHandler h2) = - AnteHandler $ h1 . h2 - -instance Monoid (AnteHandler r) where - mempty = AnteHandler id - -applyAnteHandler - :: AnteHandler r - -> TransactionApplication (Sem r) - -> TransactionApplication (Sem r) -applyAnteHandler (AnteHandler ah) = ($) ah nonceAnteHandler :: Members A.AuthEffs r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index a14c3322..3173f9e9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -4,57 +4,53 @@ module Tendermint.SDK.Application.Handlers , makeApp ) where -import Control.Lens (from, to, (&), (.~), - (^.)) -import Crypto.Hash (Digest) -import Crypto.Hash.Algorithms (SHA256) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Default.Class (Default (..)) +import Control.Lens (from, to, (&), (.~), + (^.)) +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Default.Class (Default (..)) import Data.Proxy -import Network.ABCI.Server.App (App (..), - MessageType (..), - Request (..), - Response (..)) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp +import Network.ABCI.Server.App (App (..), + MessageType (..), + Request (..), + Response (..), + transformApp) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp import Polysemy -import Polysemy.Error (Error, catch) -import Tendermint.SDK.Application.AnteHandler (AnteHandler, - applyAnteHandler) -import qualified Tendermint.SDK.Application.Module as M -import qualified Tendermint.SDK.BaseApp.BaseApp as BA -import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) -import Tendermint.SDK.BaseApp.Errors (AppError, - SDKError (..), - queryAppError, - throwSDKError, - txResultAppError) -import qualified Tendermint.SDK.BaseApp.Query as Q -import Tendermint.SDK.BaseApp.Store (ConnectionScope (..)) -import qualified Tendermint.SDK.BaseApp.Store as Store -import Tendermint.SDK.BaseApp.Transaction as T -import Tendermint.SDK.Crypto (RecoverableSignatureSchema, - SignatureSchema (..)) -import Tendermint.SDK.Types.Transaction (parseTx) -import Tendermint.SDK.Types.TxResult (checkTxTxResult, - deliverTxTxResult) - +import Polysemy.Error (catch) +import qualified Tendermint.SDK.Application.Module as M +import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.BaseApp.Errors (SDKError (..), + queryAppError, + throwSDKError, + txResultAppError) +import qualified Tendermint.SDK.BaseApp.Query as Q +import qualified Tendermint.SDK.BaseApp.Store as Store +import Tendermint.SDK.BaseApp.Transaction as T +import Tendermint.SDK.BaseApp.Transaction.Cache (writeCache) +import Tendermint.SDK.Crypto (RecoverableSignatureSchema, + SignatureSchema (..)) +import Tendermint.SDK.Types.Transaction (parseTx) +import Tendermint.SDK.Types.TxResult (checkTxTxResult, + deliverTxTxResult) type Handler mt r = Request mt -> Sem r (Response mt) -data Handlers core = Handlers - { info :: Handler 'MTInfo (BA.ScopedBaseApp 'Query core) - , setOption :: Handler 'MTSetOption (BA.ScopedBaseApp 'Query core) - , initChain :: Handler 'MTInitChain (BA.ScopedBaseApp 'Consensus core) - , query :: Handler 'MTQuery (BA.ScopedBaseApp 'Query core) - , checkTx :: Handler 'MTCheckTx (BA.ScopedBaseApp 'Mempool core) - , beginBlock :: Handler 'MTBeginBlock (BA.ScopedBaseApp 'Consensus core) - , deliverTx :: Handler 'MTDeliverTx (BA.ScopedBaseApp 'Consensus core) - , endBlock :: Handler 'MTEndBlock (BA.ScopedBaseApp 'Consensus core) - , commit :: Handler 'MTCommit (BA.ScopedBaseApp 'Consensus core) +data Handlers r = Handlers + { info :: Handler 'MTInfo r + , setOption :: Handler 'MTSetOption r + , initChain :: Handler 'MTInitChain r + , query :: Handler 'MTQuery r + , checkTx :: Handler 'MTCheckTx r + , beginBlock :: Handler 'MTBeginBlock r + , deliverTx :: Handler 'MTDeliverTx r + , endBlock :: Handler 'MTEndBlock r + , commit :: Handler 'MTCommit r } -defaultHandlers :: forall core. Handlers core +defaultHandlers :: forall r. Handlers r defaultHandlers = Handlers { info = defaultHandler , setOption = defaultHandler @@ -74,123 +70,134 @@ defaultHandlers = Handlers -> m a defaultHandler = const $ pure def + data HandlersContext alg ms r core = HandlersContext { signatureAlgP :: Proxy alg - , modules :: M.Modules ms r - , compileToCore :: forall a. BA.ScopedEff core a -> Sem core a - , anteHandler :: AnteHandler r + , modules :: M.ModuleList ms r + , anteHandler :: BA.AnteHandler r + , compileToCore :: forall a. Sem (BA.BaseApp core) a -> Sem core a } -- Common function between checkTx and deliverTx makeHandlers :: forall alg ms r core. - Member (Error AppError) r - => RecoverableSignatureSchema alg + RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 - => M.AppTxRouter ms r 'T.DeliverTx - => M.AppTxRouter ms r 'T.CheckTx - => M.AppQueryRouter ms r - => Q.HasQueryRouter (M.QApi ms) r - => T.HasTxRouter (M.TApi ms) r 'T.DeliverTx - => T.HasTxRouter (M.TApi ms) r 'T.CheckTx - => Members CoreEffs core - => M.Eval ms core - => M.Effs ms core ~ r + => M.ToApplication ms (M.Effs ms (BA.BaseApp core)) + => T.HasTxRouter (M.ApplicationC ms) (M.Effs ms (BA.BaseApp core)) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationC ms) (BA.BaseApp core) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationD ms) (M.Effs ms (BA.BaseApp core)) 'Store.Consensus + => T.HasTxRouter (M.ApplicationD ms) (BA.BaseApp core) 'Store.Consensus + => Q.HasQueryRouter (M.ApplicationQ ms) (M.Effs ms (BA.BaseApp core)) + => Q.HasQueryRouter (M.ApplicationQ ms) (BA.BaseApp core) + => M.Eval ms (BA.BaseApp core) + => M.Effs ms (BA.BaseApp core) ~ r => HandlersContext alg ms r core - -> Handlers core -makeHandlers HandlersContext{..} = + -> Handlers (BA.BaseApp core) +makeHandlers (HandlersContext{..} :: HandlersContext alg ms r core) = let - compileToBaseApp :: forall a. Sem r a -> Sem (BA.BaseApp core) a - compileToBaseApp = M.eval modules - queryRouter = compileToBaseApp . M.appQueryRouter modules + rProxy :: Proxy (BA.BaseApp core) + rProxy = Proxy + + app :: M.Application (M.ApplicationC ms) (M.ApplicationD ms) (M.ApplicationQ ms) + (T.TxEffs BA.:& BA.BaseApp core) (Q.QueryEffs BA.:& BA.BaseApp core) + app = M.makeApplication rProxy anteHandler modules txParser bs = case parseTx signatureAlgP bs of Left err -> throwSDKError $ ParseError err Right tx -> pure $ T.RoutingTx tx - txRouter ctx bs = compileToBaseApp $ do - let router = applyAnteHandler anteHandler $ M.appTxRouter modules ctx - tx <- txParser bs - router tx + checkServer :: T.TransactionApplication (Sem (BA.BaseApp core)) + checkServer = + T.serveTxApplication (Proxy @(M.ApplicationC ms)) rProxy (Proxy @'Store.QueryAndMempool) $ M.applicationTxChecker app + + deliverServer :: T.TransactionApplication (Sem (BA.BaseApp core)) + deliverServer = + T.serveTxApplication (Proxy @(M.ApplicationD ms)) rProxy (Proxy @'Store.Consensus) $ M.applicationTxDeliverer app - query (RequestQuery q) = Store.applyScope $ + queryServer :: Q.QueryApplication (Sem (BA.BaseApp core)) + queryServer = Q.serveQueryApplication (Proxy @(M.ApplicationQ ms)) rProxy $ M.applicationQuerier app + + query (RequestQuery q) = + --Store.applyScope $ catch (do - queryResp <- queryRouter q + queryResp <- queryServer q pure $ ResponseQuery queryResp ) - (\(err :: AppError) -> + (\(err :: BA.AppError) -> return . ResponseQuery $ def & queryAppError .~ err ) - beginBlock _ = Store.applyScope (def <$ Store.beginBlock) - - checkTx (RequestCheckTx _checkTx) = Store.applyScope $ do + checkTx (RequestCheckTx _checkTx) = do res <- catch ( let txBytes = _checkTx ^. Req._checkTxTx . to Base64.toBytes - in txRouter T.CheckTx txBytes + in do + (res, _) <- txParser txBytes >>= checkServer + pure res ) - (\(err :: AppError) -> + (\(err :: BA.AppError) -> return $ def & txResultAppError .~ err ) return . ResponseCheckTx $ res ^. from checkTxTxResult - deliverTx (RequestDeliverTx _deliverTx) = Store.applyScope $ do - res <- catch @AppError + deliverTx (RequestDeliverTx _deliverTx) = do + res <- catch @BA.AppError ( let txBytes = _deliverTx ^. Req._deliverTxTx . to Base64.toBytes - in txRouter T.DeliverTx txBytes + in do + (res, cache) <- txParser txBytes >>= deliverServer + maybe (pure ()) writeCache cache + pure res ) - (\(err :: AppError) -> + (\(err :: BA.AppError) -> return $ def & txResultAppError .~ err ) return . ResponseDeliverTx $ res ^. from deliverTxTxResult - commit :: Handler 'MTCommit (BA.ScopedBaseApp 'Consensus core) - commit _ = Store.applyScope $ do - Store.commitBlock - Store.mergeScopes - rootHash <- Store.storeRoot + commit :: Handler 'MTCommit (BA.BaseApp core) + commit _ = do + _ <- Store.commit + rootHash <- Store.commitBlock return . ResponseCommit $ def & Resp._commitData .~ Base64.fromBytes rootHash in defaultHandlers { query = query , checkTx = checkTx - , beginBlock = beginBlock , deliverTx = deliverTx , commit = commit } makeApp :: forall alg ms r core. - Members [Error AppError, Embed IO] r - => RecoverableSignatureSchema alg + + RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 - => M.AppTxRouter ms r 'DeliverTx - => M.AppTxRouter ms r 'CheckTx - => M.AppQueryRouter ms r - => Q.HasQueryRouter (M.QApi ms) r - => T.HasTxRouter (M.TApi ms) r 'T.DeliverTx - => T.HasTxRouter (M.TApi ms) r 'T.CheckTx - => Members CoreEffs core - => M.Eval ms core - => M.Effs ms core ~ r + => M.ToApplication ms (M.Effs ms (BA.BaseApp core)) + => T.HasTxRouter (M.ApplicationC ms) (M.Effs ms (BA.BaseApp core)) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationC ms) (BA.BaseApp core) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationD ms) (M.Effs ms (BA.BaseApp core)) 'Store.Consensus + => T.HasTxRouter (M.ApplicationD ms) (BA.BaseApp core) 'Store.Consensus + => Q.HasQueryRouter (M.ApplicationQ ms) (M.Effs ms (BA.BaseApp core)) + => Q.HasQueryRouter (M.ApplicationQ ms) (BA.BaseApp core) + => M.Eval ms (BA.BaseApp core) + => M.Effs ms (BA.BaseApp core) ~ r => HandlersContext alg ms r core -> App (Sem core) makeApp handlersContext@HandlersContext{compileToCore} = - let Handlers{..} = makeHandlers handlersContext - in App $ \case + let Handlers{..} = makeHandlers handlersContext :: Handlers (BA.BaseApp core) + in transformApp compileToCore $ App $ \case RequestEcho echo -> pure . ResponseEcho $ def & Resp._echoMessage .~ echo ^. Req._echoMessage RequestFlush _ -> pure def - msg@(RequestInfo _) -> compileToCore . BA.QueryScoped $ info msg - msg@(RequestSetOption _) -> compileToCore . BA.QueryScoped $ setOption msg - msg@(RequestInitChain _) -> compileToCore . BA.ConsensusScoped $ initChain msg - msg@(RequestQuery _) -> compileToCore . BA.QueryScoped $ query msg - msg@(RequestBeginBlock _) -> compileToCore . BA.ConsensusScoped $ beginBlock msg - msg@(RequestCheckTx _) -> compileToCore . BA.MempoolScoped $ checkTx msg - msg@(RequestDeliverTx _) -> compileToCore . BA.ConsensusScoped $ deliverTx msg - msg@(RequestEndBlock _) -> compileToCore . BA.ConsensusScoped $ endBlock msg - msg@(RequestCommit _) -> compileToCore . BA.ConsensusScoped $ commit msg + msg@(RequestInfo _) -> info msg + msg@(RequestSetOption _) -> setOption msg + msg@(RequestInitChain _) -> initChain msg + msg@(RequestQuery _) -> query msg + msg@(RequestBeginBlock _) -> beginBlock msg + msg@(RequestCheckTx _) -> checkTx msg + msg@(RequestDeliverTx _) -> deliverTx msg + msg@(RequestEndBlock _) -> endBlock msg + msg@(RequestCommit _) -> commit msg diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 444d6ee7..646867d0 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -1,110 +1,136 @@ {-# LANGUAGE UndecidableInstances #-} + module Tendermint.SDK.Application.Module ( Module(..) - , Modules(..) - , AppQueryRouter(..) - , appQueryRouter - , AppTxRouter(..) - , appTxRouter + , ModuleList(..) + , Application(..) + , ToApplication(..) + , hoistApplication , Eval(..) + , makeApplication + , applyAnteHandler + ) where import Data.Proxy import GHC.TypeLits (Symbol) import Polysemy (EffectRow, Members, Sem) import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp ((:&), BaseApp, BaseAppEffs) +import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp.Query as Q +import Tendermint.SDK.BaseApp.Store (Scope (..)) import qualified Tendermint.SDK.BaseApp.Transaction as T -data Module (name :: Symbol) (h :: *) (q :: *) (s :: EffectRow) (r :: EffectRow) = Module - { moduleTxDeliverer :: T.RouteTx h r 'T.DeliverTx - , moduleTxChecker :: T.RouteTx h r 'T.CheckTx - , moduleQueryServer :: Q.RouteQ q r - , moduleEval :: forall deps. Members BaseAppEffs deps => forall a. Sem (s :& deps) a -> Sem deps a +data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: EffectRow) (r :: EffectRow) = Module + { moduleTxChecker :: T.RouteTx check r + , moduleTxDeliverer :: T.RouteTx deliver r + , moduleQuerier :: Q.RouteQ query r + , moduleEval :: forall deps. Members T.TxEffs deps => forall a. Sem (es :& deps) a -> Sem deps a } -data Modules (ms :: [*]) r where - NilModules :: Modules '[] r - (:+) :: Module name h q s r -> Modules ms r -> Modules (Module name h q s r ': ms) r +data ModuleList ms r where + NilModules :: ModuleList '[] r + (:+) :: Module name check deliver query es r + -> ModuleList ms r + -> ModuleList (Module name check deliver query es r ': ms) r infixr 5 :+ --------------------------------------------------------------------------------- - -appQueryRouter - :: AppQueryRouter ms r - => Q.HasQueryRouter (QApi ms) r - => Modules ms r - -> Q.QueryApplication (Sem r) -appQueryRouter (ms :: Modules ms r) = - Q.serveQueryApplication (Proxy :: Proxy (QApi ms)) (Proxy :: Proxy r) (routeAppQuery ms) - -class AppQueryRouter ms r where - type QApi ms :: * - routeAppQuery :: Modules ms r -> Q.RouteQ (QApi ms) r - -instance AppQueryRouter '[Module name h q s r] r where - type QApi '[Module name h q s r] = name :> q - routeAppQuery (m :+ NilModules) = moduleQueryServer m - -instance AppQueryRouter (m' ': ms) r => AppQueryRouter (Module name h q s r ': m' ': ms) r where - type QApi (Module name h q s r ': m' ': ms) = (name :> q) :<|> QApi (m' ': ms) - routeAppQuery (m :+ rest) = moduleQueryServer m :<|> routeAppQuery rest - --------------------------------------------------------------------------------- - -appTxRouter - :: AppTxRouter ms r 'T.DeliverTx - => AppTxRouter ms r 'T.CheckTx - => T.HasTxRouter (TApi ms) r 'T.DeliverTx - => T.HasTxRouter (TApi ms) r 'T.CheckTx - => Modules ms r - -> T.RouteContext - -> T.TransactionApplication (Sem r) -appTxRouter (ms :: Modules ms r) ctx = - case ctx of - T.CheckTx -> - let checkTxP = Proxy :: Proxy 'T.CheckTx - in T.serveTxApplication (Proxy :: Proxy (TApi ms)) (Proxy :: Proxy r) - checkTxP (routeAppTx checkTxP ms) - T.DeliverTx -> - let deliverTxP = Proxy :: Proxy 'T.DeliverTx - in T.serveTxApplication (Proxy :: Proxy (TApi ms)) (Proxy :: Proxy r) - deliverTxP (routeAppTx deliverTxP ms) - -class AppTxRouter ms r (c :: T.RouteContext) where - type TApi ms :: * - routeAppTx :: Proxy c -> Modules ms r -> T.RouteTx (TApi ms) r c - -instance AppTxRouter '[Module name h q s r] r 'T.CheckTx where - type TApi '[Module name h q s r] = name :> h - routeAppTx _ (m :+ NilModules) = moduleTxChecker m - -instance AppTxRouter (m' ': ms) r 'T.CheckTx => AppTxRouter (Module name h q s r ': m' ': ms) r 'T.CheckTx where - type TApi (Module name h q s r ': m' ': ms) = (name :> h) :<|> TApi (m' ': ms) - routeAppTx pc (m :+ rest) = moduleTxChecker m :<|> routeAppTx pc rest - -instance AppTxRouter '[Module name h q s r] r 'T.DeliverTx where - type TApi '[Module name h q s r] = name :> h - routeAppTx _ (m :+ NilModules) = moduleTxDeliverer m - -instance AppTxRouter (m' ': ms) r 'T.DeliverTx => AppTxRouter (Module name h q s r ': m' ': ms) r 'T.DeliverTx where - type TApi (Module name h q s r ': m' ': ms) = (name :> h) :<|> TApi (m' ': ms) - routeAppTx pc (m :+ rest) = moduleTxDeliverer m :<|> routeAppTx pc rest - --------------------------------------------------------------------------------- - -class Eval ms core where - type Effs ms core :: EffectRow - eval :: Modules ms r - -> forall a. Sem (Effs ms core) a - -> Sem (BaseApp core) a - -instance Eval '[Module name h q s r] core where - type Effs '[Module name h q s r] core = s :& BaseApp core +data Application check deliver query r s = Application + { applicationTxChecker :: T.RouteTx check r + , applicationTxDeliverer :: T.RouteTx deliver r + , applicationQuerier :: Q.RouteQ query s + } + +class ToApplication ms r where + type ApplicationC ms :: * + type ApplicationD ms :: * + type ApplicationQ ms :: * + + toApplication :: ModuleList ms r -> Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) r r + +instance ToApplication '[Module name check deliver query es r] r where + type ApplicationC '[Module name check deliver query es r] = name :> check + type ApplicationD '[Module name check deliver query es r] = name :> deliver + type ApplicationQ '[Module name check deliver query es r] = name :> query + + toApplication (Module{..} :+ NilModules) = + Application + { applicationTxChecker = moduleTxChecker + , applicationTxDeliverer = moduleTxDeliverer + , applicationQuerier = moduleQuerier + } + +instance ToApplication (m' ': ms) r => ToApplication (Module name check deliver query es r ': m' ': ms) r where + type ApplicationC (Module name check deliver query es r ': m' ': ms) = (name :> check) :<|> ApplicationC (m' ': ms) + type ApplicationD (Module name check deliver query es r ': m' ': ms) = (name :> deliver) :<|> ApplicationD (m' ': ms) + type ApplicationQ (Module name check deliver query es r ': m' ': ms) = (name :> query) :<|> ApplicationQ (m' ': ms) + + toApplication (Module{..} :+ rest) = + let app = toApplication rest + in Application + { applicationTxChecker = moduleTxChecker :<|> applicationTxChecker app + , applicationTxDeliverer = moduleTxDeliverer :<|> applicationTxDeliverer app + , applicationQuerier = moduleQuerier :<|> applicationQuerier app + } + +hoistApplication + :: T.HasTxRouter check r 'QueryAndMempool + => T.HasTxRouter deliver r 'Consensus + => Q.HasQueryRouter query s + => (forall a. Sem r a -> Sem r' a) + -> (forall a. Sem s a -> Sem s' a) + -> Application check deliver query r s + -> Application check deliver query r' s' +hoistApplication natT natQ (app :: Application check deliver query r s) = + Application + { applicationTxChecker = T.hoistTxRouter (Proxy @check) (Proxy @r) (Proxy @'QueryAndMempool) natT $ applicationTxChecker app + , applicationTxDeliverer = T.hoistTxRouter (Proxy @deliver) (Proxy @r) (Proxy @'Consensus) natT $ applicationTxDeliverer app + , applicationQuerier = Q.hoistQueryRouter (Proxy @query) (Proxy @s) natQ $ applicationQuerier app + } + +class Eval ms deps where + type Effs ms deps :: EffectRow + eval + :: ModuleList ms r + -> forall a. + Sem (Effs ms deps) a + -> Sem (T.TxEffs :& deps) a + +instance Eval '[Module name check deliver query es r] deps where + type Effs '[Module name check deliver query es r] deps = es :& T.TxEffs :& deps eval (m :+ NilModules) = moduleEval m -instance (Members BaseAppEffs (Effs (m' ': ms) core), Eval (m' ': ms) core) => Eval (Module name h q s r ': m' ': ms) core where - type Effs (Module name h q s r ': m' ': ms) core = s :& (Effs (m': ms)) core +instance ( Members T.TxEffs (Effs (m' ': ms) deps) + , Eval (m' ': ms) deps + ) => Eval (Module name check deliver query es r ': m' ': ms) deps where + type Effs (Module name check deliver query es r ': m' ': ms) deps = es :& (Effs (m': ms)) deps eval (m :+ rest) = eval rest . moduleEval m + +makeApplication + :: Eval ms deps + => ToApplication ms (Effs ms deps) + => T.HasTxRouter (ApplicationC ms) (Effs ms deps) 'QueryAndMempool + => T.HasTxRouter (ApplicationD ms) (Effs ms deps) 'Consensus + => Q.HasQueryRouter (ApplicationQ ms) (Effs ms deps) + => Proxy deps + -> T.AnteHandler (Effs ms deps) + -> ModuleList ms (Effs ms deps) + -> Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) (T.TxEffs :& deps) (Q.QueryEffs :& deps) +makeApplication (Proxy :: Proxy deps) ah (ms :: ModuleList ms (Effs ms deps)) = + let app = applyAnteHandler ah $ toApplication ms :: Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) (Effs ms deps) (Effs ms deps) + -- WEIRD: if you move the eval into a separate let binding then it doesn't typecheck... + in hoistApplication (eval @ms @deps ms) (T.evalReadOnly . eval @ms @deps ms) app + +applyAnteHandler + :: T.HasTxRouter check r 'QueryAndMempool + => T.HasTxRouter deliver r 'Consensus + => T.AnteHandler r + -> Application check deliver query r s + -> Application check deliver query r s +applyAnteHandler ah (app :: Application check deliver query r s) = + app { applicationTxChecker = T.applyAnteHandler (Proxy @check) (Proxy @r) (Proxy @'QueryAndMempool) ah $ + applicationTxChecker app + , applicationTxDeliverer = T.applyAnteHandler (Proxy @deliver) (Proxy @r) (Proxy @'Consensus) ah $ + applicationTxDeliverer app + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 282cf7bb..2414fb81 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -1,23 +1,32 @@ module Tendermint.SDK.BaseApp ( -- * BaseApp - BaseAppEffs - , (:&) + BaseEffs + , defaultCompileToCore + , defaultCompileToPureCore , BaseApp - , ScopedBaseApp - , compileToCoreEffs - , compileScopedEff + , (:&) - -- * CoreEff + -- * Core Effects , CoreEffs , Context(..) , contextLogConfig , contextPrometheusEnv - , contextAuthTree + , contextVersions , makeContext , runCoreEffs + -- * Pure Effects + , PureCoreEffs + , PureContext(..) + , pureContextLogConfig + , pureContextVersions + , pureContextDB + , makePureContext + , runPureCoreEffs + -- * Store - , RawStore + , ReadStore + , WriteStore , RawKey(..) , IsKey(..) , StoreKey(..) @@ -29,15 +38,9 @@ module Tendermint.SDK.BaseApp , Leaf , QA - -- * Scope - , ConnectionScope(..) - , applyScope - -- * Errors , AppError(..) , IsAppError(..) - , SDKError(..) - , throwSDKError -- * Events , Event(..) @@ -48,12 +51,13 @@ module Tendermint.SDK.BaseApp -- * Gas , GasMeter + , withGas -- * Logger , Logger , Tendermint.SDK.BaseApp.Logger.log - , addContext , LogSelect(..) + , addContext , Severity(..) , Select(..) , Verbosity(..) @@ -66,40 +70,38 @@ module Tendermint.SDK.BaseApp , HistogramName(..) -- * Transaction - , TransactionApplication + , AnteHandler(..) , RoutingTx(..) - , RouteContext(..) , RouteTx + , RouteContext(..) , Return , (:~>) , TypedMessage , TxEffs - , EmptyTxServer - , emptyTxServer - , serveTxApplication + , EmptyTxServer(..) , DefaultCheckTx(..) + , VoidReturn -- * Query + , QueryEffs , Queryable(..) , FromQueryData(..) - , QueryApi , RouteQ + , QueryApi , QueryResult(..) , storeQueryHandlers - , serveQueryApplication - , EmptyQueryServer - , emptyQueryServer - + , EmptyQueryServer(..) + , RouterError(ResourceNotFound) ) where -import Tendermint.SDK.BaseApp.BaseApp -import Tendermint.SDK.BaseApp.CoreEff +import Tendermint.SDK.BaseApp.Effects import Tendermint.SDK.BaseApp.Errors import Tendermint.SDK.BaseApp.Events import Tendermint.SDK.BaseApp.Gas import Tendermint.SDK.BaseApp.Logger import Tendermint.SDK.BaseApp.Metrics import Tendermint.SDK.BaseApp.Query +import Tendermint.SDK.BaseApp.Router (RouterError (ResourceNotFound)) import Tendermint.SDK.BaseApp.Store import Tendermint.SDK.BaseApp.Transaction import Tendermint.SDK.Types.Effects ((:&)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs deleted file mode 100644 index 89f719bd..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/BaseApp.hs +++ /dev/null @@ -1,63 +0,0 @@ -module Tendermint.SDK.BaseApp.BaseApp - ( BaseAppEffs - , BaseApp - , ScopedBaseApp - , ScopedEff(..) - , compileScopedEff - , compileToCoreEffs - ) where - -import Control.Exception (throwIO) -import Control.Monad.IO.Class (liftIO) -import Polysemy (Sem) -import Polysemy.Error (Error, runError) -import Polysemy.Resource (Resource, - resourceToIO) -import Tendermint.SDK.BaseApp.CoreEff (CoreEffs) -import Tendermint.SDK.BaseApp.Errors (AppError) -import Tendermint.SDK.BaseApp.Logger (Logger) -import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import Tendermint.SDK.BaseApp.Metrics (Metrics) -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus -import Tendermint.SDK.BaseApp.Store (ApplyScope, ConnectionScope (..), - RawStore, - ResolveScope (..)) -import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT -import Tendermint.SDK.Types.Effects ((:&)) - --- | Concrete row of effects for the BaseApp. Note that because there does --- | not exist an interpreter for an untagged 'RawStore', you must scope --- | these effects before they can be interpreted. -type BaseAppEffs = - [ RawStore - , Metrics - , Logger - , Resource - , Error AppError - ] -type BaseApp r = BaseAppEffs :& r - -type ScopedBaseApp (s :: ConnectionScope) r = ApplyScope s (BaseApp r) - --- | An intermediary interpeter, bringing 'BaseApp' down to 'CoreEff'. -compileToCoreEffs - :: AT.AuthTreeGetter s - => forall a. Sem (ScopedBaseApp s CoreEffs) a -> Sem CoreEffs a -compileToCoreEffs action = do - eRes <- runError . - resourceToIO . - KL.evalKatip . - Prometheus.evalWithMetrics . - resolveScope $ action - either (liftIO . throwIO) return eRes - -data ScopedEff r a where - QueryScoped :: Sem (ScopedBaseApp 'Query r) a -> ScopedEff r a - MempoolScoped :: Sem (ScopedBaseApp 'Mempool r) a -> ScopedEff r a - ConsensusScoped :: Sem (ScopedBaseApp 'Consensus r) a -> ScopedEff r a - -compileScopedEff :: ScopedEff CoreEffs a -> Sem CoreEffs a -compileScopedEff = \case - QueryScoped m -> compileToCoreEffs m - MempoolScoped m -> compileToCoreEffs m - ConsensusScoped m -> compileToCoreEffs m diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs deleted file mode 100644 index f6f7b918..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/CoreEff.hs +++ /dev/null @@ -1,97 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} -{-# LANGUAGE UndecidableInstances #-} -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Tendermint.SDK.BaseApp.CoreEff - ( CoreEffs - , Context(..) - , contextLogConfig - , contextPrometheusEnv - , contextAuthTree - , makeContext - , runCoreEffs - ) where - -import Control.Lens (makeLenses, over, - view) -import Data.Text (Text) -import qualified Katip as K -import Polysemy (Embed, Members, - Sem, runM) -import Polysemy.Reader (Reader, asks, - local, runReader) -import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P -import Tendermint.SDK.BaseApp.Store (MergeScopes, - ResolveScope (..)) -import qualified Tendermint.SDK.BaseApp.Store.AuthTreeStore as AT - --- | CoreEffs is one level below BaseAppEffs, and provides one possible --- | interpretation for its effects to IO. -type CoreEffs = - '[ MergeScopes - , Reader KL.LogConfig - , Reader (Maybe P.PrometheusEnv) - , Reader AT.AuthTreeState - , Embed IO - ] - -instance (Members CoreEffs r) => K.Katip (Sem r) where - getLogEnv = asks $ view KL.logEnv - localLogEnv f m = local (over KL.logEnv f) m - -instance (Members CoreEffs r) => K.KatipContext (Sem r) where - getKatipContext = asks $ view KL.logContext - localKatipContext f m = local (over KL.logContext f) m - getKatipNamespace = asks $ view KL.logNamespace - localKatipNamespace f m = local (over KL.logNamespace f) m - --- | 'Context' is the environment required to run 'CoreEffs' to 'IO' -data Context = Context - { _contextLogConfig :: KL.LogConfig - , _contextPrometheusEnv :: Maybe P.PrometheusEnv - , _contextAuthTree :: AT.AuthTreeState - } - -makeLenses ''Context - -makeContext - :: KL.InitialLogNamespace - -> Maybe P.MetricsScrapingConfig - -> IO Context -makeContext KL.InitialLogNamespace{..} scrapingCfg = do - metCfg <- case scrapingCfg of - Nothing -> pure Nothing - Just scfg -> P.emptyState >>= \es -> - pure . Just $ P.PrometheusEnv es scfg - authTreeState <- AT.initAuthTreeState - logCfg <- mkLogConfig _initialLogEnvironment _initialLogProcessName - pure $ Context - { _contextLogConfig = logCfg - , _contextPrometheusEnv = metCfg - , _contextAuthTree = authTreeState - } - where - mkLogConfig :: Text -> Text -> IO KL.LogConfig - mkLogConfig env pName = do - let mkLogEnv = K.initLogEnv (K.Namespace [pName]) (K.Environment env) - le <- mkLogEnv - return $ KL.LogConfig - { _logNamespace = mempty - , _logContext = mempty - , _logEnv = le - } - -instance (Members CoreEffs r, AT.AuthTreeGetter s) => ResolveScope s r where - resolveScope = AT.evalTagged - --- | The standard interpeter for 'CoreEffs'. -runCoreEffs - :: Context - -> forall a. Sem CoreEffs a -> IO a -runCoreEffs Context{..} = - runM . - runReader _contextAuthTree . - runReader _contextPrometheusEnv . - runReader _contextLogConfig . - AT.evalMergeScopes diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs new file mode 100644 index 00000000..092d16c2 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs @@ -0,0 +1,32 @@ +module Tendermint.SDK.BaseApp.Effects + ( BaseApp + , defaultCompileToCore + , defaultCompileToPureCore + , module Tendermint.SDK.BaseApp.Effects.BaseEffs + , module Tendermint.SDK.BaseApp.Effects.CoreEffs + , module Tendermint.SDK.BaseApp.Effects.PureCoreEffs + ) where + +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Effects.BaseEffs +import Tendermint.SDK.BaseApp.Effects.CoreEffs +import Tendermint.SDK.BaseApp.Effects.PureCoreEffs +import Tendermint.SDK.BaseApp.Store (StoreEffs) +import qualified Tendermint.SDK.BaseApp.Store.IAVLStore as IAVL +import qualified Tendermint.SDK.BaseApp.Store.MemoryStore as Memory +import Tendermint.SDK.Types.Effects ((:&)) + + +type BaseApp core = StoreEffs :& BaseEffs :& core + +defaultCompileToCore + :: forall a. + Sem (BaseApp CoreEffs) a + -> Sem CoreEffs a +defaultCompileToCore = evalBaseEffs . IAVL.evalStoreEffs + +defaultCompileToPureCore + :: forall a. + Sem (BaseApp PureCoreEffs) a + -> Sem PureCoreEffs a +defaultCompileToPureCore = evalBaseEffsPure . Memory.evalStoreEffs diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/BaseEffs.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/BaseEffs.hs new file mode 100644 index 00000000..5bcea832 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/BaseEffs.hs @@ -0,0 +1,56 @@ +module Tendermint.SDK.BaseApp.Effects.BaseEffs + ( BaseEffs + , evalBaseEffs + , evalBaseEffsPure + ) where + +import Control.Exception (throwIO) +import Control.Monad.IO.Class (liftIO) +import Polysemy (Embed, Members, Sem) +import Polysemy.Error (Error, runError) +import Polysemy.Reader (Reader) +import Polysemy.Resource (Resource, + resourceToIO) +import Tendermint.SDK.BaseApp.Errors (AppError) +import Tendermint.SDK.BaseApp.Logger (Logger) +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.BaseApp.Metrics (Metrics) +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as Prometheus +import Tendermint.SDK.Types.Effects ((:&)) + +-- | Concrete row of effects for the BaseApp. Note that because there does +-- | not exist an interpreter for an untagged 'RawStore', you must scope +-- | these effects before they can be interpreted. +type BaseEffs = + [ Metrics + , Logger + , Resource + , Error AppError + ] + +-- | An intermediary interpeter, bringing 'BaseApp' down to 'CoreEff'. +evalBaseEffs + :: Members [Embed IO, Reader KL.LogConfig, Reader (Maybe Prometheus.PrometheusEnv)] core + => forall a. + Sem (BaseEffs :& core) a + -> Sem core a +evalBaseEffs action = do + eRes <- runError . + resourceToIO . + KL.evalKatip . + Prometheus.evalWithMetrics $ + action + either (liftIO . throwIO) return eRes + +evalBaseEffsPure + :: Members [Embed IO, Reader KL.LogConfig] core + => forall a. + Sem (BaseEffs :& core) a + -> Sem core a +evalBaseEffsPure action = do + eRes <- runError . + resourceToIO . + KL.evalKatip . + Prometheus.evalNothing $ + action + either (liftIO . throwIO) return eRes diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/CoreEffs.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/CoreEffs.hs new file mode 100644 index 00000000..c07460e1 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/CoreEffs.hs @@ -0,0 +1,82 @@ +{-# LANGUAGE TemplateHaskell #-} + +module Tendermint.SDK.BaseApp.Effects.CoreEffs + ( CoreEffs + , Context(..) + , contextLogConfig + , contextPrometheusEnv + , contextVersions + , contextGrpcClient + , makeContext + , runCoreEffs + ) where + +import Control.Lens (makeLenses) +import Data.Text (Text) +import qualified Katip as K +import Polysemy (Embed, Sem, runM) +import Polysemy.Reader (Reader, runReader) +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Metrics.Prometheus as P +import qualified Tendermint.SDK.BaseApp.Store.IAVLStore as IAVL + +-- | CoreEffs is one level below BaseAppEffs, and provides one possible +-- | interpretation for its effects to IO. +type CoreEffs = + '[ Reader KL.LogConfig + , Reader (Maybe P.PrometheusEnv) + , Reader IAVL.IAVLVersions + , Reader IAVL.GrpcClient + , Embed IO + ] + +-- | 'Context' is the environment required to run 'CoreEffs' to 'IO' +data Context = Context + { _contextLogConfig :: KL.LogConfig + , _contextPrometheusEnv :: Maybe P.PrometheusEnv + , _contextGrpcClient :: IAVL.GrpcClient + , _contextVersions :: IAVL.IAVLVersions + } + +makeLenses ''Context + +makeContext + :: KL.InitialLogNamespace + -> Maybe P.MetricsScrapingConfig + -> IAVL.IAVLVersions + -> IAVL.GrpcConfig + -> IO Context +makeContext KL.InitialLogNamespace{..} scrapingCfg versions rpcConf = do + metCfg <- case scrapingCfg of + Nothing -> pure Nothing + Just scfg -> P.emptyState >>= \es -> + pure . Just $ P.PrometheusEnv es scfg + logCfg <- mkLogConfig _initialLogEnvironment _initialLogProcessName + grpc <- IAVL.initGrpcClient rpcConf + pure $ Context + { _contextLogConfig = logCfg + , _contextPrometheusEnv = metCfg + , _contextVersions = versions + , _contextGrpcClient = grpc + } + where + mkLogConfig :: Text -> Text -> IO KL.LogConfig + mkLogConfig env pName = do + let mkLogEnv = K.initLogEnv (K.Namespace [pName]) (K.Environment env) + le <- mkLogEnv + return $ KL.LogConfig + { _logNamespace = mempty + , _logContext = mempty + , _logEnv = le + } + +-- | The standard interpeter for 'CoreEffs'. +runCoreEffs + :: Context + -> forall a. Sem CoreEffs a -> IO a +runCoreEffs Context{..} = + runM . + runReader _contextGrpcClient . + runReader _contextVersions . + runReader _contextPrometheusEnv . + runReader _contextLogConfig diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/PureCoreEffs.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/PureCoreEffs.hs new file mode 100644 index 00000000..7d2de79a --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects/PureCoreEffs.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} + +module Tendermint.SDK.BaseApp.Effects.PureCoreEffs + ( PureCoreEffs + , PureContext(..) + , pureContextLogConfig + , pureContextVersions + , pureContextDB + , makePureContext + , runPureCoreEffs + ) where + +import Control.Lens (makeLenses) +import Data.Text (Text) +import qualified Katip as K +import Polysemy (Embed, Sem, runM) +import Polysemy.Error (Error, runError) +import Polysemy.Reader (Reader, runReader) +import Tendermint.SDK.BaseApp.Errors (AppError) +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import qualified Tendermint.SDK.BaseApp.Store.MemoryStore as Memory + +-- | CoreEffs is one level below BaseAppEffs, and provides one possible +-- | interpretation for its effects to IO. +type PureCoreEffs = + '[ Reader KL.LogConfig + , Reader Memory.DBVersions + , Reader Memory.DB + , Error AppError + , Embed IO + ] + +-- | 'Context' is the environment required to run 'CoreEffsPure' to 'IO' +data PureContext = PureContext + { _pureContextLogConfig :: KL.LogConfig + , _pureContextDB :: Memory.DB + , _pureContextVersions :: Memory.DBVersions + } + +makeLenses ''PureContext + +makePureContext + :: KL.InitialLogNamespace + -> IO PureContext +makePureContext KL.InitialLogNamespace{..} = do + logCfg <- mkLogConfig _initialLogEnvironment _initialLogProcessName + versions <- Memory.initDBVersions + db <- Memory.initDB + pure $ PureContext + { _pureContextLogConfig = logCfg + , _pureContextVersions = versions + , _pureContextDB = db + } + where + mkLogConfig :: Text -> Text -> IO KL.LogConfig + mkLogConfig env pName = do + let mkLogEnv = K.initLogEnv (K.Namespace [pName]) (K.Environment env) + le <- mkLogEnv + return $ KL.LogConfig + { _logNamespace = mempty + , _logContext = mempty + , _logEnv = le + } + +-- | The standard interpeter for 'PureCoreEffs'. +runPureCoreEffs + :: PureContext + -> forall a. Sem PureCoreEffs a -> IO (Either AppError a) +runPureCoreEffs PureContext{..} = + runM . + runError . + runReader _pureContextDB . + runReader _pureContextVersions . + runReader _pureContextLogConfig diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index e89659bc..e214ba96 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -81,6 +81,9 @@ data SDKError = | MessageValidation [Text] | SignatureRecoveryError Text | NonceException Word64 Word64 + | RawStoreInvalidOperation Text + | GrpcError Text + deriving (Show) -- | As of right now it's not expected that one can recover from an 'SDKError', -- | so we are throwing them as 'AppError's directly. @@ -120,6 +123,7 @@ instance IsAppError SDKError where , appErrorCodespace = "sdk" , appErrorMessage = "Message failed validation: " <> intercalate "\n" errors } + makeAppError (SignatureRecoveryError msg) = AppError { appErrorCode = 6 , appErrorCodespace = "sdk" @@ -132,3 +136,15 @@ instance IsAppError SDKError where , appErrorMessage = "Incorrect Transaction Nonce: Expected " <> (cs . show $ toInteger expected) <> " but got " <> (cs . show $ toInteger found) <> "." } + + makeAppError (RawStoreInvalidOperation operation) = AppError + { appErrorCode = 8 + , appErrorCodespace = "sdk" + , appErrorMessage = "Unsupported RawStore operation: `" <> operation <> "`" + } + + makeAppError (GrpcError msg) = AppError + { appErrorCode = 9 + , appErrorCodespace = "sdk" + , appErrorMessage = "Grpc error: \n" <> msg + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index 53ea9a08..a1827388 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -1,10 +1,16 @@ module Tendermint.SDK.BaseApp.Events - ( Event(..) - , ToEvent(..) + ( + -- * Class + ToEvent(..) , ContextEvent(..) + , makeEvent + + -- * Effect , emit , logEvent - , makeEvent + + -- * Re-Exports + , Event(..) ) where import qualified Data.Aeson as A diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs index 9244de20..5ed39654 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Gas.hs @@ -1,9 +1,13 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.BaseApp.Gas - ( GasMeter(..) + ( + -- * Effect + GasMeter(..) , GasAmount(..) , withGas + -- * Eval , eval + , doNothing ) where import Data.Int (Int64) @@ -38,3 +42,14 @@ eval = interpretH (\case a <- runT action raise $ eval a ) + +doNothing + :: forall r. + forall a. + Sem (GasMeter ': r) a + -> Sem r a +doNothing = interpretH (\case + WithGas _ action -> do + a <- runT action + raise $ doNothing a + ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs index 4155d415..00fc14fc 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger.hs @@ -1,11 +1,15 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.BaseApp.Logger - ( Logger(..) + ( + -- * Effects + Logger(..) , Tendermint.SDK.BaseApp.Logger.log + , Select(..) , addContext + + -- * Types , LogSelect(..) - , Select(..) , Severity(..) , Verbosity(..) ) where diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs index 64ac0c4c..e8966aa5 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Logger/Katip.hs @@ -1,7 +1,10 @@ -{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE TemplateHaskell #-} +{-# LANGUAGE UndecidableInstances #-} +{-# OPTIONS_GHC -fno-warn-orphans #-} module Tendermint.SDK.BaseApp.Logger.Katip - ( -- setup and config + ( + -- * Setup and Config LogConfig(..) , logNamespace , logContext @@ -9,18 +12,21 @@ module Tendermint.SDK.BaseApp.Logger.Katip , InitialLogNamespace(..) , initialLogEnvironment , initialLogProcessName - -- eval + + -- * Eval , evalKatip ) where +import Control.Lens (over, view) import Control.Lens.TH (makeLenses) import qualified Data.Aeson as A import Data.String (fromString) import Data.String.Conversions (cs) import Data.Text (Text) import qualified Katip as K -import Polysemy (Sem, interpretH, pureT, raise, - runT) +import Polysemy (Embed, Members, Sem, interpretH, + pureT, raise, runT) +import Polysemy.Reader (Reader, asks, local) import Tendermint.SDK.BaseApp.Logger newtype Object a = Object a @@ -60,6 +66,16 @@ data InitialLogNamespace = InitialLogNamespace makeLenses ''InitialLogNamespace +instance (Members [Embed IO, Reader LogConfig] r) => K.Katip (Sem r) where + getLogEnv = asks $ view logEnv + localLogEnv f m = local (over logEnv f) m + +instance (Members [Embed IO, Reader LogConfig] r) => K.KatipContext (Sem r) where + getKatipContext = asks $ view logContext + localKatipContext f m = local (over logContext f) m + getKatipNamespace = asks $ view logNamespace + localKatipNamespace f m = local (over logNamespace f) m + evalKatip :: forall r a. K.KatipContext (Sem r) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs index 0962f7fb..2ff0406f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Metrics/Prometheus.hs @@ -1,7 +1,8 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.BaseApp.Metrics.Prometheus - ( -- config and setup + ( + -- | Config and Setup MetricsScrapingConfig(..) , prometheusPort , MetricsState(..) @@ -13,12 +14,14 @@ module Tendermint.SDK.BaseApp.Metrics.Prometheus , envMetricsScrapingConfig , emptyState , forkMetricsServer - -- utils + + -- * Utils , mkPrometheusMetricId , metricIdStorable , countToIdentifier , histogramToIdentifier - -- eval + + -- * Eval , evalWithMetrics , evalNothing , evalMetrics diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs index b06dfcf7..a56d82b7 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs @@ -1,9 +1,10 @@ module Tendermint.SDK.BaseApp.Query ( serveQueryApplication + -- * Re-Exports , HasQueryRouter(..) , StoreQueryHandlers(..) + , QueryEffs , module Tendermint.SDK.BaseApp.Query.Types - , emptyQueryServer ) where import Control.Lens ((&), (.~)) @@ -13,20 +14,21 @@ import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy (Sem) import Tendermint.SDK.BaseApp.Errors (makeAppError, queryAppError) -import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..), - emptyQueryServer) +import Tendermint.SDK.BaseApp.Query.Effect (QueryEffs) +import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..)) import Tendermint.SDK.BaseApp.Query.Store (StoreQueryHandlers (..)) import Tendermint.SDK.BaseApp.Query.Types import Tendermint.SDK.BaseApp.Router.Delayed (emptyDelayed) import Tendermint.SDK.BaseApp.Router.Router (runRouter) import Tendermint.SDK.BaseApp.Router.Types (Application, RouteResult (..)) +import Tendermint.SDK.Types.Effects ((:&)) serveQueryApplication :: HasQueryRouter layout r => Proxy layout -> Proxy r - -> RouteQ layout r + -> RouteQ layout (QueryEffs :& r) -> QueryApplication (Sem r) serveQueryApplication pl pr server = toQueryApplication (runRouter (routeQ pl pr (emptyDelayed (Route server))) ()) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Effect.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Effect.hs new file mode 100644 index 00000000..d79703f4 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Effect.hs @@ -0,0 +1,47 @@ +module Tendermint.SDK.BaseApp.Query.Effect + ( QueryEffs + , runQuery + ) where + +import Control.Lens ((&), (.~)) +import Data.ByteArray.Base64String (fromBytes) +import Data.Default.Class (def) +import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) +import qualified Network.ABCI.Types.Messages.Response as Response +import Polysemy (Member, Sem) +import Polysemy.Error (Error, runError) +import Polysemy.Tagged (Tagged, tag) +import Tendermint.SDK.BaseApp.Errors (AppError, + queryAppError) +import Tendermint.SDK.BaseApp.Query.Types +import Tendermint.SDK.BaseApp.Store (ReadStore, Scope (..)) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Effects ((:&)) + +type QueryEffs = + '[ ReadStore + , Error AppError + ] + +runQuery + :: HasCodec a + => Member (Tagged 'QueryAndMempool ReadStore) r + => Sem (QueryEffs :& r) (QueryResult a) + -> Sem r Response.Query +runQuery query = do + eRes <- eval query + pure $ case eRes of + Left e -> def & queryAppError .~ e + Right QueryResult{..} -> + def + & Response._queryIndex .~ WrappedVal queryResultIndex + & Response._queryKey .~ queryResultKey + & Response._queryValue .~ fromBytes (encode queryResultData) + & Response._queryProof .~ queryResultProof + & Response._queryHeight .~ WrappedVal queryResultHeight + +eval + :: Member (Tagged 'QueryAndMempool ReadStore) r + => Sem (QueryEffs :& r) (QueryResult a) + -> Sem r (Either AppError (QueryResult a)) +eval = runError . tag diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs index 917f35de..bf8c632e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs @@ -1,38 +1,36 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.SDK.BaseApp.Query.Router ( HasQueryRouter(..) - , emptyQueryServer , methodRouter ) where -import Control.Lens ((&), (.~)) -import Control.Monad (join) -import Data.ByteArray.Base64String (fromBytes) -import Data.Default.Class (def) +import Control.Monad (join) import Data.Proxy -import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.TypeLits (KnownSymbol, symbolVal) -import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) -import Network.ABCI.Types.Messages.Response as Response -import Network.HTTP.Types.URI (QueryText, - parseQueryText) -import Polysemy (Sem) +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.TypeLits (KnownSymbol, symbolVal) +import Network.ABCI.Types.Messages.Response as Response +import Network.HTTP.Types.URI (QueryText, + parseQueryText) +import Polysemy (Member, Sem) +import Polysemy.Tagged (Tagged) import Servant.API -import Servant.API.Modifiers (FoldLenient, - FoldRequired, - RequestArgument, - unfoldRequestArgument) -import Tendermint.SDK.BaseApp.Query.Types (EmptyQueryServer (..), - FromQueryData (..), - Leaf, QA, - QueryArgs (..), - QueryRequest (..), - QueryResult (..)) -import qualified Tendermint.SDK.BaseApp.Router as R -import Tendermint.SDK.Codec (HasCodec (..)) -import Web.HttpApiData (FromHttpApiData (..), - parseUrlPieceMaybe) +import Servant.API.Modifiers (FoldLenient, + FoldRequired, + RequestArgument, + unfoldRequestArgument) +import Tendermint.SDK.BaseApp.Query.Effect (QueryEffs, runQuery) +import Tendermint.SDK.BaseApp.Query.Types (EmptyQueryServer (..), + FromQueryData (..), Leaf, + QA, QueryArgs (..), + QueryRequest (..), + QueryResult (..)) +import qualified Tendermint.SDK.BaseApp.Router as R +import Tendermint.SDK.BaseApp.Store (ReadStore, Scope (..)) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Effects ((:&)) +import Web.HttpApiData (FromHttpApiData (..), + parseUrlPieceMaybe) -------------------------------------------------------------------------------- @@ -44,16 +42,22 @@ class HasQueryRouter layout r where -- | A routeQ handler. type RouteQ layout r :: * -- | Transform a routeQ handler into a 'Router'. - routeQ :: Proxy layout -> Proxy r -> R.Delayed (Sem r) env QueryRequest (RouteQ layout r) - -> R.Router env r QueryRequest Response.Query + routeQ + :: Proxy layout + -> Proxy r + -> R.Delayed (Sem r) env QueryRequest (RouteQ layout (QueryEffs :& r)) + -> R.Router env r QueryRequest Response.Query + + hoistQueryRouter :: Proxy layout -> Proxy r -> (forall a. Sem s a -> Sem s' a) -> RouteQ layout s -> RouteQ layout s' instance (HasQueryRouter a r, HasQueryRouter b r) => HasQueryRouter (a :<|> b) r where type RouteQ (a :<|> b) r = RouteQ a r :<|> RouteQ b r - routeQ _ pr server = R.choice (routeQ pa pr ((\ (a :<|> _) -> a) <$> server)) - (routeQ pb pr ((\ (_ :<|> b) -> b) <$> server)) - where pa = Proxy :: Proxy a - pb = Proxy :: Proxy b + routeQ _ pr server = + R.choice (routeQ (Proxy @a) pr ((\ (a :<|> _) -> a) <$> server)) + (routeQ (Proxy @b) pr ((\ (_ :<|> b) -> b) <$> server)) + hoistQueryRouter _ pr nat (a :<|> b) = + hoistQueryRouter (Proxy @a) pr nat a :<|> hoistQueryRouter (Proxy @b) pr nat b instance (HasQueryRouter sublayout r, KnownSymbol path) => HasQueryRouter (path :> sublayout) r where @@ -63,6 +67,8 @@ instance (HasQueryRouter sublayout r, KnownSymbol path) => HasQueryRouter (path R.pathRouter (cs (symbolVal proxyPath)) (routeQ (Proxy :: Proxy sublayout) pr subserver) where proxyPath = Proxy :: Proxy path + hoistQueryRouter _ pr nat = hoistQueryRouter (Proxy @sublayout) pr nat + instance ( HasQueryRouter sublayout r, KnownSymbol sym, FromHttpApiData a , SBoolI (FoldRequired mods), SBoolI (FoldLenient mods) ) => HasQueryRouter (QueryParam' mods sym a :> sublayout) r where @@ -82,6 +88,8 @@ instance ( HasQueryRouter sublayout r, KnownSymbol sym, FromHttpApiData a delayed = R.addParameter subserver $ R.withRequest parseParam in routeQ (Proxy :: Proxy sublayout) pr delayed + hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @sublayout) pr nat . f + instance (FromHttpApiData a, HasQueryRouter sublayout r) => HasQueryRouter (Capture' mods capture a :> sublayout) r where type RouteQ (Capture' mods capture a :> sublayout) r = a -> RouteQ sublayout r @@ -94,14 +102,9 @@ instance (FromHttpApiData a, HasQueryRouter sublayout r) => HasQueryRouter (Capt Nothing -> R.delayedFail R.PathNotFound Just v -> return v ) + hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @sublayout) pr nat . f -instance HasCodec a => HasQueryRouter (Leaf a) r where - - type RouteQ (Leaf a) r = Sem r (QueryResult a) - routeQ _ _ = methodRouter - -instance (FromQueryData a, HasQueryRouter sublayout r) - => HasQueryRouter (QA a :> sublayout) r where +instance (FromQueryData a, HasQueryRouter sublayout r) => HasQueryRouter (QA a :> sublayout) r where type RouteQ (QA a :> sublayout) r = QueryArgs a -> RouteQ sublayout r @@ -116,24 +119,26 @@ instance (FromQueryData a, HasQueryRouter sublayout r) delayed = R.addBody subserver $ R.withRequest parseQueryArgs in routeQ (Proxy :: Proxy sublayout) pr delayed -emptyQueryServer :: RouteQ EmptyQueryServer r -emptyQueryServer = EmptyQueryServer + hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @sublayout) pr nat . f + +instance (Member (Tagged 'QueryAndMempool ReadStore) r, HasCodec a) => HasQueryRouter (Leaf a) r where + + type RouteQ (Leaf a) r = Sem r (QueryResult a) + routeQ _ _ = methodRouter + hoistQueryRouter _ _ = ($) instance HasQueryRouter EmptyQueryServer r where type RouteQ EmptyQueryServer r = EmptyQueryServer routeQ _ _ _ = R.StaticRouter mempty mempty + hoistQueryRouter _ _ _ = id -------------------------------------------------------------------------------- methodRouter - :: HasCodec b - => R.Delayed (Sem r) env req (Sem r (QueryResult b)) + :: HasCodec a + => Member (Tagged 'QueryAndMempool ReadStore) r + => R.Delayed (Sem r) env req (Sem (QueryEffs :& r) (QueryResult a)) -> R.Router env r req Response.Query -methodRouter action = R.leafRouter route' - where - route' env query = R.runAction action env query $ \QueryResult{..} -> - R.Route $ def & Response._queryIndex .~ WrappedVal queryResultIndex - & Response._queryKey .~ queryResultKey - & Response._queryValue .~ fromBytes (encode queryResultData) - & Response._queryProof .~ queryResultProof - & Response._queryHeight .~ WrappedVal queryResultHeight +methodRouter action = + let route' env q = R.runAction (runQuery <$> action) env q (pure . R.Route) + in R.leafRouter route' diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index 3040649e..8134a31f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -1,6 +1,9 @@ {-# LANGUAGE UndecidableInstances #-} -module Tendermint.SDK.BaseApp.Query.Store where +module Tendermint.SDK.BaseApp.Query.Store + ( StoreLeaf + , StoreQueryHandlers(..) + ) where import Control.Lens (to, (^.)) import Data.ByteArray.Base64String (fromBytes) @@ -8,10 +11,12 @@ import Data.Proxy import Data.String.Conversions (cs) import GHC.TypeLits (KnownSymbol, Symbol, symbolVal) -import Polysemy (Members, Sem) -import Polysemy.Error (Error, throw) +import Polysemy (Member, Members, Sem) +import Polysemy.Error (throw) +import Polysemy.Tagged (Tagged) import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp.Errors (AppError, makeAppError) +import Tendermint.SDK.BaseApp.Errors (makeAppError) +import Tendermint.SDK.BaseApp.Query.Effect (QueryEffs) import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..), methodRouter) import Tendermint.SDK.BaseApp.Query.Types (QA, QueryArgs (..), @@ -20,16 +25,18 @@ import Tendermint.SDK.BaseApp.Query.Types (QA, QueryArgs (..), import Tendermint.SDK.BaseApp.Router (RouterError (..), pathRouter) import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), - RawStore, StoreKey, get) + ReadStore, Scope (..), + StoreKey, get) import Tendermint.SDK.Codec (HasCodec) data StoreLeaf a -instance (Queryable a, KnownSymbol (Name a)) => HasQueryRouter (StoreLeaf a) r where +instance (Queryable a, Member (Tagged 'QueryAndMempool ReadStore) r, KnownSymbol (Name a)) => HasQueryRouter (StoreLeaf a) r where type RouteQ (StoreLeaf a) r = Sem r (QueryResult a) routeQ _ _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter where proxyPath = Proxy :: Proxy (Name a) + hoistQueryRouter _ _ = ($) class StoreQueryHandler a (ns :: Symbol) h where storeQueryHandler :: Proxy a -> StoreKey ns -> h @@ -38,9 +45,9 @@ instance ( IsKey k ns , a ~ Value k ns , HasCodec a - , Members [RawStore, Error AppError] r + , Members QueryEffs r ) - => StoreQueryHandler a ns (QueryArgs k -> (Sem r) (QueryResult a)) where + => StoreQueryHandler a ns (QueryArgs k -> Sem r (QueryResult a)) where storeQueryHandler _ storeKey QueryArgs{..} = do let key = queryArgsData mRes <- get storeKey key @@ -63,7 +70,7 @@ instance ( IsKey k ns , a ~ Value k ns , HasCodec a - , Members [RawStore, Error AppError] r + , Members QueryEffs r ) => StoreQueryHandlers '[(k,a)] ns r where type QueryApi '[(k,a)] = QA k :> StoreLeaf a storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey @@ -73,7 +80,7 @@ instance , a ~ Value k ns , HasCodec a , StoreQueryHandlers ((k', a') ': as) ns r - , Members [RawStore, Error AppError] r + , Members QueryEffs r ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns r where type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> StoreLeaf a) :<|> QueryApi ((k', a') ': as) storeQueryHandlers _ storeKey pr = diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs index 6e2cf6d1..9c80284a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs @@ -1,4 +1,22 @@ -module Tendermint.SDK.BaseApp.Query.Types where +module Tendermint.SDK.BaseApp.Query.Types + ( + -- * Server combinators + Leaf + , QA + , EmptyQueryServer(..) + , FromQueryData(..) + + -- * Query Application + , QueryApplication + , QueryRequest(..) + , parseQueryRequest + , QueryArgs(..) + , defaultQueryArgs + , QueryResult(..) + + -- * Store Queries + , Queryable(..) + ) where import Control.Lens (from, lens, (^.)) import Data.ByteArray.Base64String (Base64String, toBytes) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs index 361d4b3c..9feeb877 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Router/Delayed.hs @@ -64,12 +64,12 @@ runAction :: Delayed (Sem r) env req (Sem r a) -> env -> req - -> (a -> RouteResult b) + -> (a -> Sem r (RouteResult b)) -> Sem r (RouteResult b) runAction action env req k = do res <- runDelayed action env req case res of - Route a -> k <$> a + Route a -> k =<< a Fail e -> pure $ Fail e FailFatal e -> pure $ FailFatal e diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs index fb303f5f..893112e6 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store.hs @@ -1,7 +1,5 @@ module Tendermint.SDK.BaseApp.Store ( module Tendermint.SDK.BaseApp.Store.RawStore - , module Tendermint.SDK.BaseApp.Store.Scope ) where import Tendermint.SDK.BaseApp.Store.RawStore -import Tendermint.SDK.BaseApp.Store.Scope diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs deleted file mode 100644 index 17ccf360..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/AuthTreeStore.hs +++ /dev/null @@ -1,125 +0,0 @@ -module Tendermint.SDK.BaseApp.Store.AuthTreeStore - ( AuthTreeState(..) - , AuthTreeGetter(..) - , initAuthTreeState - , evalMergeScopes - , evalTagged - ) where - -import Control.Concurrent.STM (atomically) -import Control.Concurrent.STM.TVar -import Control.Monad (forM_) -import Control.Monad.IO.Class -import qualified Crypto.Data.Auth.Tree as AT -import qualified Crypto.Data.Auth.Tree.Class as AT -import qualified Crypto.Data.Auth.Tree.Cryptonite as Cryptonite -import qualified Crypto.Hash as Cryptonite -import Data.ByteArray (convert) -import Data.ByteString (ByteString) -import qualified Data.List.NonEmpty as NE -import Data.Proxy -import Polysemy (Embed, Members, Sem, - interpret) -import Polysemy.Reader (Reader, ask, asks) -import Polysemy.Tagged (Tagged (..)) -import Tendermint.SDK.BaseApp.Store.RawStore (RawStore (..), - StoreKey (..)) -import Tendermint.SDK.BaseApp.Store.Scope (ConnectionScope (..), - MergeScopes (..)) - --- At the moment, the 'AuthTreeStore' is our only interpreter for the 'RawStore' effect. --- It is an in memory merklized key value store. You can find the repository here --- https://github.com/oscoin/avl-auth - -newtype AuthTreeHash = AuthTreeHash (Cryptonite.Digest Cryptonite.SHA256) - -instance AT.MerkleHash AuthTreeHash where - emptyHash = AuthTreeHash Cryptonite.emptyHash - hashLeaf k v = AuthTreeHash $ Cryptonite.hashLeaf k v - concatHashes (AuthTreeHash a) (AuthTreeHash b) = AuthTreeHash $ Cryptonite.concatHashes a b - -data AuthTree (c :: ConnectionScope) = AuthTree - { treeVar :: TVar (NE.NonEmpty (AT.Tree ByteString ByteString)) - } - -initAuthTree :: IO (AuthTree c) -initAuthTree = AuthTree <$> newTVarIO (pure AT.empty) - -data AuthTreeState = AuthTreeState - { query :: AuthTree 'Query - , mempool :: AuthTree 'Mempool - , consensus :: AuthTree 'Consensus - } - -initAuthTreeState :: IO AuthTreeState -initAuthTreeState = AuthTreeState <$> initAuthTree <*> initAuthTree <*> initAuthTree - - -class AuthTreeGetter (s :: ConnectionScope) where - getAuthTree :: Proxy s -> AuthTreeState -> AuthTree s - -instance AuthTreeGetter 'Query where - getAuthTree _ = query - -instance AuthTreeGetter 'Mempool where - getAuthTree _ = mempool - -instance AuthTreeGetter 'Consensus where - getAuthTree _ = consensus - -evalTagged - :: forall (s :: ConnectionScope) r. - Members [Reader AuthTreeState, Embed IO] r - => AuthTreeGetter s - => forall a. Sem (Tagged s RawStore ': r) a -> Sem r a -evalTagged m = do - AuthTree{treeVar} <- asks (getAuthTree (Proxy :: Proxy s)) - interpret - (\(Tagged action) -> case action of - RawStorePut (StoreKey sk) k v -> liftIO . atomically $ do - tree NE.:| ts <- readTVar treeVar - writeTVar treeVar $ AT.insert (sk <> k) v tree NE.:| ts - RawStoreGet (StoreKey sk) k -> liftIO . atomically $ do - tree NE.:| _ <- readTVar treeVar - pure $ AT.lookup (sk <> k) tree - RawStoreProve _ _ -> pure Nothing - RawStoreDelete (StoreKey sk) k -> liftIO . atomically $ do - tree NE.:| ts <- readTVar treeVar - writeTVar treeVar $ AT.delete (sk <> k) tree NE.:| ts - RawStoreRoot -> liftIO . atomically $ do - tree NE.:| _ <- readTVar treeVar - let AuthTreeHash hash = AT.merkleHash tree - pure $ convert hash - RawStoreBeginTransaction -> liftIO . atomically $ do - tree NE.:| ts <- readTVar treeVar - writeTVar treeVar $ tree NE.:| tree : ts - RawStoreRollback -> liftIO . atomically $ do - trees <- readTVar treeVar - writeTVar treeVar $ case trees of - t NE.:| [] -> t NE.:| [] - _ NE.:| t' : ts -> t' NE.:| ts - RawStoreCommit -> liftIO . atomically $ do - trees <- readTVar treeVar - writeTVar treeVar $ case trees of - t NE.:| [] -> t NE.:| [] - t NE.:| _ : ts -> t NE.:| ts - ) m - -evalMergeScopes - :: Members [Reader AuthTreeState, Embed IO] r - => Sem (MergeScopes ': r) a - -> Sem r a -evalMergeScopes = - interpret - (\case - MergeScopes -> do - AuthTreeState{query, mempool, consensus} <- ask - liftIO . atomically $ do - let AuthTree queryV = query - AuthTree mempoolV = mempool - AuthTree consensusV = consensus - consensusTrees <- readTVar consensusV - let t = NE.last consensusTrees - forM_ [queryV, mempoolV, consensusV] $ \v -> - writeTVar v $ pure t - ) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs new file mode 100644 index 00000000..c14802aa --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs @@ -0,0 +1,158 @@ +module Tendermint.SDK.BaseApp.Store.IAVLStore + ( + -- * Environment + IAVLVersions(..) + , initIAVLVersions + -- * Eval + , evalStoreEffs + -- * Re-Exports + , GrpcClient + , GrpcConfig(..) + , initGrpcClient + ) where + +import Control.Lens ((&), (.~), (^.)) +import Control.Monad (void) +import Control.Monad.IO.Class +import Data.ByteArray.Base64String (fromBytes) +import Data.IORef (IORef, newIORef, + readIORef, writeIORef) +import Data.ProtoLens.Message (defMessage) +import Data.Text (pack) +import qualified Database.IAVL.RPC as IAVL +import Database.IAVL.RPC.Types (GrpcConfig (..), + initGrpcClient) +import Network.GRPC.Client (RawReply) +import Network.GRPC.Client.Helpers (GrpcClient) +import Network.HTTP2.Client (ClientIO, + TooMuchConcurrency, + runClientIO) +import Polysemy (Embed, Member, Members, + Sem, interpret) +import Polysemy.Error (Error) +import Polysemy.Reader (Reader, ask) +import Polysemy.Tagged (untag) +import qualified Proto.Iavl.Api_Fields as Api +import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..)) +import Tendermint.SDK.BaseApp.Store.RawStore (CommitBlock (..), + CommitResponse (..), + ReadStore (..), + StoreEffs, + Transaction (..), + Version (..), + WriteStore (..), + makeRawKey) +import Tendermint.SDK.Types.Effects ((:&)) + +data IAVLVersions = IAVLVersions + { latest :: IORef Version + , committed :: IORef Version + } + +initIAVLVersions :: IO IAVLVersions +initIAVLVersions = IAVLVersions <$> newIORef Latest <*> newIORef Genesis + +evalWrite + :: Member (Embed IO) r + => GrpcClient + -> forall a. Sem (WriteStore ': r) a -> Sem r a +evalWrite gc m = + interpret + (\case + StorePut k v -> do + let setReq = defMessage & Api.key .~ makeRawKey k + & Api.value .~ v + void . liftIO . runGrpc $ IAVL.set gc setReq + StoreDelete k -> + let remReq = defMessage & Api.key .~ makeRawKey k + in void . liftIO . runGrpc $ IAVL.remove gc remReq + ) m + +evalRead + :: Member (Embed IO) r + => GrpcClient + -> IORef Version + -> forall a. Sem (ReadStore ': r) a -> Sem r a +evalRead gc iavlVersion m = do + interpret + (\case + StoreGet k -> do + version <- liftIO $ readIORef iavlVersion + case version of + Latest -> do + let getReq = defMessage & Api.key .~ makeRawKey k + res <- liftIO . runGrpc $ IAVL.get gc getReq + case res ^. Api.value of + "" -> pure Nothing + val -> pure $ Just val + Version v -> do + let getVerReq = defMessage & Api.key .~ makeRawKey k + & Api.version .~ fromInteger (toInteger v) + res <- liftIO . runGrpc $ IAVL.getVersioned gc getVerReq + case res ^. Api.value of + "" -> pure Nothing + val -> pure $ Just val + Genesis -> pure Nothing + StoreProve _ -> pure Nothing + ) m + +evalTransaction + :: Members [Embed IO, Error AppError] r + => GrpcClient + -> forall a. Sem (Transaction ': r) a -> Sem r a +evalTransaction gc m = do + interpret + (\case + -- NOTICE :: Currently unnecessary with the DB commit/version implementation. + BeginTransaction -> pure () + Rollback -> void . liftIO . runGrpc $ IAVL.rollback gc + Commit -> do + resp <- liftIO . runGrpc $ IAVL.saveVersion gc + pure $ CommitResponse + { rootHash = fromBytes (resp ^. Api.rootHash) + , newVersion = fromInteger . toInteger $ resp ^. Api.version + } + ) m + +evalCommitBlock + :: Members [Embed IO, Error AppError] r + => GrpcClient + -> IAVLVersions + -> forall a. Sem (CommitBlock ': r) a -> Sem r a +evalCommitBlock gc IAVLVersions{committed} = do + interpret + (\case + CommitBlock -> do + versionResp <- liftIO . runGrpc $ IAVL.version gc + let version = Version . fromInteger . toInteger $ versionResp ^. Api.version + liftIO $ writeIORef committed version + hashResp <- liftIO . runGrpc $ IAVL.hash gc + pure . fromBytes $ hashResp ^. Api.rootHash + ) + +evalStoreEffs + :: Members [Embed IO, Reader IAVLVersions, Error AppError, Reader GrpcClient] r + => forall a. + Sem (StoreEffs :& r) a + -> Sem r a +evalStoreEffs action = do + vs@IAVLVersions{..} <- ask + grpc <- ask + evalCommitBlock grpc vs . + evalTransaction grpc . + evalWrite grpc . + untag . + evalRead grpc committed . + untag . + evalRead grpc latest . + untag $ action + +runGrpc + :: ClientIO (Either TooMuchConcurrency (RawReply a)) + -> IO a +runGrpc f = runClientIO f >>= \case + Right (Right (Right (_, _, Right res))) -> pure $ res + Right (Right (Right (_, _, Left err))) -> error . show $ GrpcError (pack $ show err) + Right (Right (Left err)) -> error . show $ GrpcError (pack $ show err) + Right (Left err) -> error . show $ GrpcError (pack $ show err) + Left err -> error . show $ GrpcError (pack $ show err) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs new file mode 100644 index 00000000..fd2da7b7 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs @@ -0,0 +1,197 @@ +module Tendermint.SDK.BaseApp.Store.MemoryStore + ( + -- * Environment + DBVersions(..) + , initDBVersions + , DB + , initDB + -- * Eval + , evalStoreEffs + ) where + + +import Control.Monad.IO.Class (liftIO) +import qualified Crypto.Data.Auth.Tree as AT +import qualified Crypto.Data.Auth.Tree.Class as AT +import qualified Crypto.Data.Auth.Tree.Cryptonite as Cryptonite +import qualified Crypto.Hash as Cryptonite +import Data.ByteArray (convert) +import qualified Data.ByteArray.Base64String as Base64 +import Data.ByteString (ByteString) +import Data.IORef +import Data.List (sortOn) +import Data.Ord (Down (..)) +import Numeric.Natural (Natural) +import Polysemy +import Polysemy.Reader (Reader, ask) +import Polysemy.Tagged (untag) +import Tendermint.SDK.BaseApp.Store.RawStore (CommitBlock (..), + CommitResponse (..), + ReadStore (..), + StoreEffs, + Transaction (..), + Version (..), + WriteStore (..), + makeRawKey) +import Tendermint.SDK.Types.Effects ((:&)) + + + +newtype AuthTreeHash = AuthTreeHash (Cryptonite.Digest Cryptonite.SHA256) + +instance AT.MerkleHash AuthTreeHash where + emptyHash = AuthTreeHash Cryptonite.emptyHash + hashLeaf k v = AuthTreeHash $ Cryptonite.hashLeaf k v + concatHashes (AuthTreeHash a) (AuthTreeHash b) = AuthTreeHash $ Cryptonite.concatHashes a b + +data DB = DB + { dbCommitted :: IORef (AT.Tree Natural (AT.Tree ByteString ByteString)) + , dbLatest :: IORef (AT.Tree ByteString ByteString) + } + +initDB :: IO DB +initDB = + DB <$> newIORef AT.empty + <*> newIORef AT.empty + +evalWrite + :: Member (Embed IO) r + => DB + -> forall a. Sem (WriteStore ': r) a -> Sem r a +evalWrite DB{dbLatest} m = + interpret + (\case + StorePut k v -> do + liftIO . modifyIORef dbLatest $ AT.insert (makeRawKey k) v + StoreDelete k -> + liftIO . modifyIORef dbLatest $ AT.delete (makeRawKey k) + ) m + +evalRead + :: Member (Embed IO) r + => DB + -> IORef Version + -> forall a. Sem (ReadStore ': r) a -> Sem r a +evalRead DB{dbCommitted,dbLatest} iavlVersion m = do + interpret + (\case + StoreGet k -> do + version <- liftIO $ readIORef iavlVersion + case version of + Latest -> do + tree <- liftIO $ readIORef dbLatest + pure $ AT.lookup (makeRawKey k) tree + Version v -> do + tree <- liftIO $ readIORef dbCommitted + pure (AT.lookup v tree >>= AT.lookup (makeRawKey k)) + Genesis -> pure Nothing + StoreProve _ -> pure Nothing + ) m + +evalTransaction + :: Member (Embed IO) r + => DB + -> forall a. Sem (Transaction ': r) a -> Sem r a +evalTransaction db@DB{..} m = do + interpret + (\case + -- NOTICE :: Currently unnecessary with the DB commit/version implementation. + BeginTransaction -> pure () + Rollback -> liftIO $ do + c <- getRecentCommit db + writeIORef dbLatest c + Commit -> liftIO $ do + l <- readIORef dbLatest + v <- makeCommit db l + root <- getRootHash db + pure $ CommitResponse + { rootHash = Base64.fromBytes root + , newVersion = fromInteger . toInteger $ v + } + ) m + +evalCommitBlock + :: Member (Embed IO) r + => DB + -> DBVersions + -> forall a. Sem (CommitBlock ': r) a -> Sem r a +evalCommitBlock db DBVersions{..} = do + interpret + (\case + CommitBlock -> liftIO $ do + mv <- getVersion db + writeIORef committed $ case mv of + Nothing -> Genesis + Just v -> Version v + root <- getRootHash db + pure . Base64.fromBytes $ root + ) + +data DBVersions = DBVersions + { latest :: IORef Version + , committed :: IORef Version + } + +initDBVersions :: IO DBVersions +initDBVersions = DBVersions <$> newIORef Latest <*> newIORef Genesis + +evalStoreEffs + :: Members [Embed IO, Reader DBVersions, Reader DB] r + => forall a. + Sem (StoreEffs :& r) a + -> Sem r a +evalStoreEffs action = do + vs@DBVersions{..} <- ask + db <- ask + evalCommitBlock db vs . + evalTransaction db . + evalWrite db . + untag . + evalRead db committed . + untag . + evalRead db latest . + untag $ action + +getRootHash + :: DB + -> IO ByteString +getRootHash db@DB{dbCommitted} = do + mcv <- getVersion db + case mcv of + Nothing -> pure "" + Just v -> do + cs <- readIORef dbCommitted + case AT.lookup v cs of + Nothing -> pure "" + Just tree -> + let AuthTreeHash hash = AT.merkleHash tree + in pure $ convert hash + +getVersion + :: DB + -> IO (Maybe Natural) +getVersion DB{..}= do + c <- readIORef dbCommitted + pure $ + if c == AT.empty + then Nothing + else Just $ maximum $ map fst $ AT.toList c + +getRecentCommit + :: DB + -> IO (AT.Tree ByteString ByteString) +getRecentCommit DB{..} = do + c <- readIORef dbCommitted + case sortOn (Down . fst) $ AT.toList c of + [] -> pure AT.empty + a : _ -> pure $ snd a + +makeCommit + :: DB + -> AT.Tree ByteString ByteString + -> IO Natural +makeCommit db@DB{dbCommitted} commit = do + mv <- getVersion db + let v = maybe 0 (+1) mv + modifyIORef dbCommitted $ AT.insert v commit + pure v diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs index b3d6fb9e..a632e250 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs @@ -1,47 +1,71 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.BaseApp.Store.RawStore - ( RawStore(..) - , RawKey(..) - , IsKey(..) - , StoreKey(..) + ( + -- * Effects + StoreEffs + , ReadStore(..) , get + , prove + , WriteStore(..) , put + , storePut , delete - , prove - , storeRoot - , withTransaction - , withSandbox - , beginBlock + , storeDelete + , CommitBlock(..) , commitBlock + , Transaction(..) + , beginTransaction + , withSandbox + , withTransaction + , commit + + -- * Types + , Scope(..) + , Version(..) + , RawKey(..) + , IsKey(..) + , RawStoreKey(..) + , makeRawKey + , StoreKey(..) + , CommitResponse(..) ) where import Control.Lens (Iso', iso, (^.)) +import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteString as BS import Data.Proxy import Data.String.Conversions (cs) +import Numeric.Natural (Natural) import Polysemy (Member, Members, Sem, makeSem) import Polysemy.Error (Error, catch, throw) import Polysemy.Resource (Resource, finally, onException) +import Polysemy.Tagged (Tagged) import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (ParseError), throwSDKError) import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address, addressFromBytes, addressToBytes) -newtype StoreKey n = StoreKey BS.ByteString +data RawStoreKey = RawStoreKey + { rsStoreKey :: BS.ByteString + , rsKey :: BS.ByteString + } deriving (Eq, Show, Ord) + +makeRawKey :: RawStoreKey -> BS.ByteString +makeRawKey RawStoreKey{..} = rsStoreKey <> rsKey -data RawStore m a where - RawStorePut :: StoreKey ns -> BS.ByteString -> BS.ByteString -> RawStore m () - RawStoreGet :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) - RawStoreDelete :: StoreKey ns -> BS.ByteString -> RawStore m () - RawStoreProve :: StoreKey ns -> BS.ByteString -> RawStore m (Maybe BS.ByteString) - RawStoreRoot :: RawStore m BS.ByteString - RawStoreBeginTransaction :: RawStore m () - RawStoreRollback :: RawStore m () - RawStoreCommit :: RawStore m () +data ReadStore m a where + StoreGet :: RawStoreKey -> ReadStore m (Maybe BS.ByteString) + StoreProve :: RawStoreKey -> ReadStore m (Maybe BS.ByteString) -makeSem ''RawStore +makeSem ''ReadStore + +data WriteStore m a where + StorePut :: RawStoreKey -> BS.ByteString -> WriteStore m () + StoreDelete :: RawStoreKey -> WriteStore m () + +makeSem ''WriteStore class RawKey k where rawKey :: Iso' k BS.ByteString @@ -49,99 +73,134 @@ class RawKey k where instance RawKey Address where rawKey = iso addressToBytes addressFromBytes - class RawKey k => IsKey k ns where type Value k ns = a | a -> ns k - prefixWith :: Proxy k -> Proxy ns -> BS.ByteString + prefix :: Proxy k -> Proxy ns -> BS.ByteString - default prefixWith :: Proxy k -> Proxy ns -> BS.ByteString - prefixWith _ _ = "" + default prefix :: Proxy k -> Proxy ns -> BS.ByteString + prefix _ _ = "" +newtype StoreKey ns = StoreKey BS.ByteString + +data CommitBlock m a where + CommitBlock :: CommitBlock m Base64String + +makeSem ''CommitBlock put :: forall k r ns. IsKey k ns => HasCodec (Value k ns) - => Member RawStore r + => Member WriteStore r => StoreKey ns -> k -> Value k ns -> Sem r () -put sk k a = - let key = prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey +put (StoreKey sk) k a = + let key = RawStoreKey + { rsStoreKey = sk + , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey + } val = encode a - in rawStorePut sk key val + in storePut key val get :: forall k r ns. IsKey k ns => HasCodec (Value k ns) - => Members [RawStore, Error AppError] r + => Members [ReadStore, Error AppError] r => StoreKey ns -> k -> Sem r (Maybe (Value k ns)) -get sk k = do - let key = prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey - mRes <- rawStoreGet sk key +get (StoreKey sk) k = do + let key = RawStoreKey + { rsStoreKey = sk + , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey + } + mRes <- storeGet key case mRes of Nothing -> pure Nothing Just raw -> case decode raw of - Left e -> throwSDKError (ParseError $ "Impossible codec error " <> cs e) + Left e -> throwSDKError (ParseError $ "Impossible codec error: " <> cs e) Right a -> pure $ Just a delete :: forall k ns r. IsKey k ns - => Member RawStore r + => Member WriteStore r => StoreKey ns -> k -> Sem r () -delete sk k = rawStoreDelete sk $ - prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey +delete (StoreKey sk) k = + let key = RawStoreKey + { rsStoreKey = sk + , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey + } + in storeDelete key prove :: forall k ns r. IsKey k ns - => Member RawStore r + => Member ReadStore r => StoreKey ns -> k -> Sem r (Maybe BS.ByteString) -prove sk k = rawStoreProve sk $ - prefixWith (Proxy @k) (Proxy @ns) <> k ^. rawKey +prove (StoreKey sk) k = + let key = RawStoreKey + { rsStoreKey = sk + , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey + } + in storeProve key + -beginBlock - :: Member RawStore r - => Sem r () -beginBlock = rawStoreBeginTransaction +data CommitResponse = CommitResponse + { rootHash :: Base64String + , newVersion :: Natural + } deriving (Eq, Show) -commitBlock - :: Member RawStore r - => Sem r () -commitBlock = rawStoreCommit +data Transaction m a where + -- transact + BeginTransaction :: Transaction m () + Rollback :: Transaction m () + Commit :: Transaction m CommitResponse -storeRoot - :: Member RawStore r - => Sem r BS.ByteString -storeRoot = rawStoreRoot +makeSem ''Transaction withTransaction :: forall r a. - Members [RawStore, Resource, Error AppError] r + Members [Transaction, Resource, Error AppError] r => Sem r a - -> Sem r a + -> Sem r (a, CommitResponse) withTransaction m = - let tryTx = m `catch` (\e -> rawStoreRollback *> throw e) + let tryTx = m `catch` (\e -> rollback *> throw e) + actionWithCommit = do + res <- tryTx + c <- commit + pure (res, c) in do - rawStoreBeginTransaction - onException (tryTx <* rawStoreCommit) rawStoreRollback + onException actionWithCommit rollback withSandbox :: forall r a. - Members [RawStore, Resource, Error AppError] r + Members [Transaction, Resource, Error AppError] r => Sem r a -> Sem r a withSandbox m = - let tryTx = m `catch` (\e -> rawStoreRollback *> throw e) - in do - rawStoreBeginTransaction - finally (tryTx <* rawStoreRollback) rawStoreRollback + let tryTx = m `catch` (\e -> rollback *> throw e) + in finally (tryTx <* rollback) rollback + +data Scope = Consensus | QueryAndMempool + +data Version = + Genesis + | Version Natural + | Latest + deriving (Eq, Show) + +type StoreEffs = + [ Tagged 'Consensus ReadStore + , Tagged 'QueryAndMempool ReadStore + , Tagged 'Consensus WriteStore + , Transaction + , CommitBlock + ] diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs deleted file mode 100644 index 8adc4271..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Scope.hs +++ /dev/null @@ -1,34 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - -module Tendermint.SDK.BaseApp.Store.Scope - ( ConnectionScope(..) - , ApplyScope - , applyScope - , ResolveScope(..) - , MergeScopes(..) - , mergeScopes - ) where - -import Polysemy (EffectRow, Sem, makeSem, - rewrite) -import Polysemy.Tagged (Tagged (..)) -import Tendermint.SDK.BaseApp.Store.RawStore (RawStore) - -data ConnectionScope = Query | Mempool | Consensus - -type family ApplyScope (s :: ConnectionScope) (es :: EffectRow) :: EffectRow where - ApplyScope s (RawStore ': as) = Tagged s RawStore ': as - ApplyScope s (a ': as) = a ': ApplyScope s as - -applyScope - :: forall s r. - forall a. Sem (RawStore ': r) a -> Sem (Tagged s RawStore ': r) a -applyScope = rewrite (Tagged @s) - -class ResolveScope s r where - resolveScope :: Sem (Tagged s RawStore ': r) a -> Sem r a - -data MergeScopes m a where - MergeScopes :: MergeScopes m () - -makeSem ''MergeScopes diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs index be9a9507..7a130662 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs @@ -1,59 +1,50 @@ module Tendermint.SDK.BaseApp.Transaction ( serveTxApplication - , serveDefaultTxChecker -- * Re-Exports , module Tendermint.SDK.BaseApp.Transaction.Types , HasTxRouter(..) , emptyTxServer , DefaultCheckTx(..) + , VoidReturn , TxEffs + , evalReadOnly + , AnteHandler(..) ) where -import Control.Lens ((&), (.~)) +import Control.Lens ((&), (.~)) +import Data.ByteString (ByteString) +import Data.Default.Class (def) import Data.Proxy -import Polysemy (Sem) -import Tendermint.SDK.BaseApp.Errors (makeAppError, - txResultAppError) -import Tendermint.SDK.BaseApp.Router (Application, - RouteResult (..), - emptyDelayed, - runRouter) -import Tendermint.SDK.BaseApp.Transaction.Checker (DefaultCheckTx (..)) -import Tendermint.SDK.BaseApp.Transaction.Effect (TxEffs) +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Errors (makeAppError, txResultAppError) +import Tendermint.SDK.BaseApp.Router (Application, RouteResult (..), + emptyDelayed, + runRouter) +import Tendermint.SDK.BaseApp.Transaction.AnteHandler +import Tendermint.SDK.BaseApp.Transaction.Cache (Cache) +import Tendermint.SDK.BaseApp.Transaction.Checker +import Tendermint.SDK.BaseApp.Transaction.Effect import Tendermint.SDK.BaseApp.Transaction.Router import Tendermint.SDK.BaseApp.Transaction.Types -import Tendermint.SDK.Types.TxResult (TxResult) - -import Data.ByteString (ByteString) -import Data.Default.Class (def) +import Tendermint.SDK.Types.Effects ((:&)) +import Tendermint.SDK.Types.TxResult (TxResult) serveTxApplication - :: HasTxRouter layout r c + :: HasTxRouter layout r scope => Proxy layout -> Proxy r - -> Proxy (c :: RouteContext) - -> RouteTx layout r c + -> Proxy scope + -> RouteTx layout (TxEffs :& r) -> TransactionApplication (Sem r) -serveTxApplication pl pr pc server = - toTxApplication (runRouter (routeTx pl pr pc (emptyDelayed (Route server))) ()) +serveTxApplication pl pr ps server = + toTxApplication (runRouter (routeTx pl pr ps (emptyDelayed (Route server))) ()) toTxApplication - :: Application (Sem r) (RoutingTx ByteString) TxResult + :: Application (Sem r) (RoutingTx ByteString) (TxResult, Maybe Cache) -> TransactionApplication (Sem r) toTxApplication ra tx = do res <- ra tx case res of - Fail e -> pure $ def & txResultAppError .~ makeAppError e - FailFatal e -> pure $ def & txResultAppError .~ makeAppError e + Fail e -> pure (def & txResultAppError .~ makeAppError e, Nothing) + FailFatal e -> pure (def & txResultAppError .~ makeAppError e, Nothing) Route a -> pure a - - -serveDefaultTxChecker - :: HasTxRouter layout r 'CheckTx - => DefaultCheckTx layout r - => RouteTx layout r 'CheckTx ~ DefaultCheckTxT layout r - => Proxy layout - -> Proxy r - -> TransactionApplication (Sem r) -serveDefaultTxChecker pl pr = - serveTxApplication pl pr (Proxy :: Proxy 'CheckTx) (defaultCheckTx pl pr) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs new file mode 100644 index 00000000..ab17df93 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs @@ -0,0 +1,16 @@ +module Tendermint.SDK.BaseApp.Transaction.AnteHandler + ( AnteHandler(..) + ) where + +import Polysemy (Sem) +import Tendermint.SDK.BaseApp.Transaction.Types (RoutingTx) + +newtype AnteHandler r = AnteHandler + (forall msg a. (RoutingTx msg -> Sem r a) -> (RoutingTx msg -> Sem r a)) + +instance Semigroup (AnteHandler r) where + (<>) (AnteHandler h1) (AnteHandler h2) = + AnteHandler $ h1 . h2 + +instance Monoid (AnteHandler r) where + mempty = AnteHandler id diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs new file mode 100644 index 00000000..f6826609 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs @@ -0,0 +1,66 @@ +module Tendermint.SDK.BaseApp.Transaction.Cache + ( Cache + , emptyCache + , writeCache + , Deleted(..) + , put + , get + , delete + ) where + +import Data.ByteString (ByteString) +import Data.Map.Strict (Map) +import qualified Data.Map.Strict as Map +import Data.Set (Set) +import qualified Data.Set as Set +import Polysemy (Member, Sem) +import Polysemy.Tagged (Tagged, tag) +import Tendermint.SDK.BaseApp.Store.RawStore (RawStoreKey, Scope (..), + WriteStore, storeDelete, + storePut) + +data Cache = Cache + { keysToDelete :: Set RawStoreKey + , stateCache :: Map RawStoreKey ByteString + } deriving (Eq, Show) + +emptyCache :: Cache +emptyCache = Cache Set.empty Map.empty + +put + :: RawStoreKey + -> ByteString + -> Cache + -> Cache +put k v Cache{..} = + let keysToDelete' = Set.delete k keysToDelete + stateCache' = Map.insert k v stateCache + in Cache keysToDelete' stateCache' + +data Deleted = Deleted + +get + :: RawStoreKey + -> Cache + -> Either Deleted (Maybe ByteString) +get k Cache{..} = + if k `Set.member` keysToDelete + then Left Deleted + else Right $ Map.lookup k stateCache + +delete + :: RawStoreKey + -> Cache + -> Cache +delete k Cache{..} = + let keysToDelete' = Set.insert k keysToDelete + stateCache' = Map.delete k stateCache + in Cache keysToDelete' stateCache' + +writeCache + :: Member (Tagged 'Consensus WriteStore) r + => Cache + -> Sem r () +writeCache Cache{..} = do + mapM_ (tag @'Consensus . uncurry storePut) (Map.toList stateCache) + mapM_ (tag @'Consensus . storeDelete) (Set.toList keysToDelete) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs index f1859567..bc801d12 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Checker.hs @@ -1,5 +1,6 @@ module Tendermint.SDK.BaseApp.Transaction.Checker ( DefaultCheckTx(..) + , VoidReturn ) where import Data.Proxy @@ -25,6 +26,11 @@ defaultCheckTxHandler(RoutingTx Tx{txMsg}) = throwSDKError . MessageValidation . map formatMessageSemanticError $ err V.Success _ -> pure () +type family VoidReturn (api :: *) :: * where + VoidReturn (a :<|> b) = VoidReturn a :<|> VoidReturn b + VoidReturn (path :> a) = path :> VoidReturn a + VoidReturn (TypedMessage msg :~> Return a) = TypedMessage msg :~> Return () + class DefaultCheckTx api (r :: EffectRow) where type DefaultCheckTxT api r :: * defaultCheckTx :: Proxy api -> Proxy r -> DefaultCheckTxT api r diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs index 0f068ba7..70017a02 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Effect.hs @@ -1,29 +1,36 @@ module Tendermint.SDK.BaseApp.Transaction.Effect ( TxEffs - , TransactionContext(..) - , newTransactionContext + , runTx , eval + , evalReadOnly ) where import Control.Lens ((&), (.~)) import Control.Monad.IO.Class (liftIO) -import qualified Data.ByteArray.Base64String as Base64 +import Data.ByteArray.Base64String (fromBytes) import Data.Default.Class (def) -import Data.IORef (IORef, newIORef, - readIORef) -import Polysemy (Embed, Member, Sem, - raiseUnder) +import Data.IORef (IORef, readIORef, + writeIORef) +import Data.Proxy +import Polysemy (Embed, Member, + Members, Sem, + interpret, + raiseUnder, rewrite) import Polysemy.Error (Error, runError) -import Polysemy.Output (Output, - runOutputMonoidAssocR) -import Polysemy.State (State, runStateIORef) +import Polysemy.Internal (send) +import Polysemy.Output (Output, ignoreOutput, + runOutputMonoidIORef) +import qualified Polysemy.State as State +import Polysemy.Tagged (Tagged (..)) import Tendermint.SDK.BaseApp.Errors (AppError, txResultAppError) import qualified Tendermint.SDK.BaseApp.Events as E import qualified Tendermint.SDK.BaseApp.Gas as G -import Tendermint.SDK.BaseApp.Transaction.Types (RoutingTx (..), - Tx (..)) -import Tendermint.SDK.Codec (HasCodec (encode)) +import Tendermint.SDK.BaseApp.Store.RawStore (ReadStore (..), + WriteStore (..)) +import qualified Tendermint.SDK.BaseApp.Transaction.Cache as Cache +import Tendermint.SDK.BaseApp.Transaction.Types +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Effects ((:&)) import Tendermint.SDK.Types.TxResult (TxResult, txResultData, @@ -34,45 +41,99 @@ import Tendermint.SDK.Types.TxResult (TxResult, type TxEffs = [ Output E.Event , G.GasMeter + , WriteStore + , ReadStore , Error AppError ] -data TransactionContext = TransactionContext - { gas :: IORef G.GasAmount - } +eval + :: forall r scope a. + Members [Embed IO, Tagged scope ReadStore] r + => Proxy scope + -> TransactionContext + -> Sem (TxEffs :& r) a + -> Sem r (Either AppError a) +eval ps TransactionContext{..} = do + runError . + evalCachedReadStore ps storeCache . + rewrite (Tagged @Cache.Cache) . + evalCachedWriteStore storeCache . + rewrite (Tagged @Cache.Cache) . + State.runStateIORef gasRemaining . + G.eval . + raiseUnder @(State.State G.GasAmount) . + runOutputMonoidIORef events (pure @[]) -newTransactionContext - :: RoutingTx msg - -> IO TransactionContext -newTransactionContext (RoutingTx Tx{txGas}) = do - initialGas <- newIORef $ G.GasAmount txGas - pure TransactionContext - { gas = initialGas - } +evalReadOnly + :: forall r. + forall a. + Sem (TxEffs :& r) a + -> Sem (ReadStore ': Error AppError ': r) a +evalReadOnly = + writeNothing . + G.doNothing . + ignoreOutput + where + writeNothing = interpret (\case + StorePut _ _ -> pure () + StoreDelete _ -> pure () + ) -eval - :: forall r a. - HasCodec a - => Member (Embed IO) r - => TransactionContext +runTx + :: Members [Embed IO, Tagged scope ReadStore] r + => HasCodec a + => Proxy scope + -> TransactionContext -> Sem (TxEffs :& r) a - -> Sem r TxResult -eval TransactionContext{..} action = do - initialGas <- liftIO $ readIORef gas - eRes <- - runError . - runStateIORef gas . - G.eval . - raiseUnder @(State G.GasAmount) $ - runOutputMonoidAssocR (pure @[]) action - gasRemaining <- liftIO $ readIORef gas - let gasUsed = initialGas - gasRemaining + -> Sem r (TxResult, Maybe Cache.Cache) +runTx ps ctx@TransactionContext{..} tx = do + initialGas <- liftIO $ readIORef gasRemaining + eRes <- eval ps ctx tx + finalGas <- liftIO $ readIORef gasRemaining + let gasUsed = initialGas - finalGas baseResponse = def & txResultGasWanted .~ G.unGasAmount initialGas & txResultGasUsed .~ G.unGasAmount gasUsed - return $ case eRes of - Left e -> - baseResponse & txResultAppError .~ e - Right (events, a) -> - baseResponse & txResultEvents .~ events - & txResultData .~ Base64.fromBytes (encode a) + case eRes of + Left e -> return (baseResponse & txResultAppError .~ e, Nothing) + Right a -> do + es <- liftIO $ readIORef events + c <- liftIO $ readIORef storeCache + return ( baseResponse & txResultEvents .~ es + & txResultData .~ fromBytes (encode a) + , Just c + ) + +evalCachedReadStore + :: Members [Embed IO, Tagged scope ReadStore] r + => Proxy scope + -> IORef Cache.Cache + -> Sem (Tagged Cache.Cache ReadStore ': r) a + -> Sem r a +evalCachedReadStore (_ :: Proxy scope) c m = do + interpret + (\(Tagged action) -> case action of + StoreGet k -> do + cache <- liftIO $ readIORef c + case Cache.get k cache of + Left Cache.Deleted -> pure Nothing + Right (Just v) -> pure (Just v) + Right Nothing -> send (Tagged @scope (StoreGet k)) + StoreProve _ -> pure Nothing + ) m + +evalCachedWriteStore + :: Member (Embed IO) r + => IORef Cache.Cache + -> Sem (Tagged Cache.Cache WriteStore ': r) a + -> Sem r a +evalCachedWriteStore c m = do + interpret + (liftIO . \(Tagged action) -> case action of + StorePut k v -> do + cache <- liftIO $ readIORef c + writeIORef c $ Cache.put k v cache + StoreDelete k -> do + cache <- liftIO $ readIORef c + writeIORef c $ Cache.delete k cache + ) m diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs deleted file mode 100644 index 5bf84a80..00000000 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Modifier.hs +++ /dev/null @@ -1,11 +0,0 @@ -module Tendermint.SDK.BaseApp.Transaction.Modifier - ( OnCheck(..) - , OnCheckReturn - ) where - -import Tendermint.SDK.BaseApp.Transaction.Types - -type family OnCheckReturn (ctx :: RouteContext) (oc :: OnCheck) a where - OnCheckReturn 'CheckTx 'OnCheckEval a = a - OnCheckReturn 'CheckTx 'OnCheckUnit a = () - OnCheckReturn 'DeliverTx _ a = a diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs index e91a00a0..41c7305f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs @@ -4,70 +4,104 @@ module Tendermint.SDK.BaseApp.Transaction.Router , emptyTxServer ) where -import Control.Monad.IO.Class (liftIO) -import Data.ByteString (ByteString) +import Control.Monad.IO.Class (liftIO) +import Data.ByteString (ByteString) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, - symbolVal) -import Polysemy (Embed, Member, - Sem) +import Data.String.Conversions (cs) +import GHC.TypeLits (KnownSymbol, + symbolVal) +import Polysemy (EffectRow, + Embed, Members, + Sem) +import Polysemy.Tagged (Tagged) import Servant.API -import qualified Tendermint.SDK.BaseApp.Router as R -import Tendermint.SDK.BaseApp.Transaction.Effect (TxEffs, eval, newTransactionContext) -import Tendermint.SDK.BaseApp.Transaction.Modifier +import qualified Tendermint.SDK.BaseApp.Router as R +import Tendermint.SDK.BaseApp.Store (ReadStore, + Scope) +import Tendermint.SDK.BaseApp.Transaction.AnteHandler (AnteHandler (..)) +import Tendermint.SDK.BaseApp.Transaction.Cache (Cache) +import Tendermint.SDK.BaseApp.Transaction.Effect (TxEffs, runTx) import Tendermint.SDK.BaseApp.Transaction.Types -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Effects ((:&)) -import Tendermint.SDK.Types.Message (HasMessageType (..), - Msg (..)) -import Tendermint.SDK.Types.TxResult (TxResult) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Effects ((:&)) +import Tendermint.SDK.Types.Message (HasMessageType (..), + Msg (..)) +import Tendermint.SDK.Types.TxResult (TxResult) -------------------------------------------------------------------------------- -class HasTxRouter layout r (c :: RouteContext) where - type RouteTx layout r c :: * +class HasTxRouter layout (r :: EffectRow) (scope :: Scope) where + type RouteTx layout (s :: EffectRow) :: * routeTx :: Proxy layout -> Proxy r - -> Proxy c - -> R.Delayed (Sem r) env (RoutingTx ByteString) (RouteTx layout r c) - -> R.Router env r (RoutingTx ByteString) TxResult + -> Proxy scope + -> R.Delayed (Sem r) env (RoutingTx ByteString) (RouteTx layout (TxEffs :& r)) + -> R.Router env r (RoutingTx ByteString) (TxResult, Maybe Cache) -instance (HasTxRouter a r c, HasTxRouter b r c) => HasTxRouter (a :<|> b) r c where - type RouteTx (a :<|> b) r c = RouteTx a r c :<|> RouteTx b r c + applyAnteHandler + :: Proxy layout + -> Proxy r + -> Proxy scope + -> AnteHandler r + -> RouteTx layout r + -> RouteTx layout r - routeTx _ pr pc server = - R.choice (routeTx pa pr pc ((\ (a :<|> _) -> a) <$> server)) - (routeTx pb pr pc ((\ (_ :<|> b) -> b) <$> server)) - where pa = Proxy :: Proxy a - pb = Proxy :: Proxy b + hoistTxRouter + :: Proxy layout + -> Proxy r + -> Proxy scope + -> (forall a. Sem s a -> Sem s' a) + -> RouteTx layout s + -> RouteTx layout s' -instance (HasTxRouter sublayout r c, KnownSymbol path) => HasTxRouter (path :> sublayout) r c where +instance (HasTxRouter a r scope, HasTxRouter b r scope) => HasTxRouter (a :<|> b) r scope where + type RouteTx (a :<|> b) s = RouteTx a s :<|> RouteTx b s - type RouteTx (path :> sublayout) r c = RouteTx sublayout r c + routeTx _ pr ps server = + R.choice (routeTx (Proxy @a) pr ps ((\ (a :<|> _) -> a) <$> server)) + (routeTx (Proxy @b) pr ps ((\ (_ :<|> b) -> b) <$> server)) - routeTx _ pr pc subserver = - R.pathRouter (cs (symbolVal proxyPath)) (routeTx (Proxy :: Proxy sublayout) pr pc subserver) - where proxyPath = Proxy :: Proxy path + applyAnteHandler _ pr ps ah (a :<|> b) = + applyAnteHandler (Proxy @a) pr ps ah a :<|> + applyAnteHandler (Proxy @b) pr ps ah b + + hoistTxRouter _ pr nat ps (a :<|> b) = + hoistTxRouter (Proxy @a) pr nat ps a :<|> hoistTxRouter (Proxy @b) pr nat ps b + +instance (HasTxRouter sublayout r scope, KnownSymbol path) => HasTxRouter (path :> sublayout) r scope where + + type RouteTx (path :> sublayout) s = RouteTx sublayout s + + routeTx _ pr ps subserver = + R.pathRouter (cs (symbolVal proxyPath)) (routeTx (Proxy @sublayout) pr ps subserver) + where proxyPath = Proxy @path + + applyAnteHandler _ pr ps ah = applyAnteHandler (Proxy @sublayout) pr ps ah + + hoistTxRouter _ pr ps nat = hoistTxRouter (Proxy @sublayout) pr ps nat methodRouter :: HasCodec a - => Member (Embed IO) r - => R.Delayed (Sem r) env (RoutingTx msg) (Sem (TxEffs :& r) a) - -> R.Router env r (RoutingTx msg) TxResult -methodRouter action = R.leafRouter route' - where - route' env tx = do - ctx <- liftIO $ newTransactionContext tx - let action' = eval ctx <$> action - R.runAction action' env tx R.Route + => Members [Embed IO, Tagged scope ReadStore] r + => Proxy scope + -> R.Delayed (Sem r) env (RoutingTx msg) (Sem (TxEffs :& r) a) + -> R.Router env r (RoutingTx msg) (TxResult, Maybe Cache) +methodRouter ps action = + let route' env tx = do + ctx <- liftIO $ newTransactionContext tx + let action' = runTx ps ctx <$> action + R.runAction action' env tx (pure . R.Route) + in R.leafRouter route' -instance ( HasMessageType msg, HasCodec msg, HasCodec (OnCheckReturn c oc a), Member (Embed IO) r) => HasTxRouter (TypedMessage msg :~> Return' oc a) r c where +instance ( HasMessageType msg, HasCodec msg + , Members [Tagged scope ReadStore, Embed IO] r + , HasCodec a + ) => HasTxRouter (TypedMessage msg :~> Return a) r scope where - type RouteTx (TypedMessage msg :~> Return' oc a) r c = RoutingTx msg -> Sem (TxEffs :& r) (OnCheckReturn c oc a) + type RouteTx (TypedMessage msg :~> Return a) r = RoutingTx msg -> Sem r a - routeTx _ _ _ subserver = + routeTx _ _ ps subserver = let f (RoutingTx tx@Tx{txMsg}) = if msgType txMsg == mt then case decode $ msgData txMsg of @@ -75,13 +109,20 @@ instance ( HasMessageType msg, HasCodec msg, HasCodec (OnCheckReturn c oc a), Me R.InvalidRequest ("Failed to parse message of type " <> mt <> ": " <> e <> ".") Right a -> pure . RoutingTx $ tx {txMsg = txMsg {msgData = a}} else R.delayedFail R.PathNotFound - in methodRouter $ - R.addBody subserver $ R.withRequest f + in methodRouter ps $ R.addBody subserver $ R.withRequest f where mt = messageType (Proxy :: Proxy msg) -emptyTxServer :: RouteTx EmptyTxServer r c + applyAnteHandler _ _ _ (AnteHandler ah) f = ah f + + hoistTxRouter _ _ _ nat = (.) nat + +emptyTxServer :: RouteTx EmptyTxServer r emptyTxServer = EmptyTxServer -instance HasTxRouter EmptyTxServer r c where - type RouteTx EmptyTxServer r c = EmptyTxServer +instance HasTxRouter EmptyTxServer r scope where + type RouteTx EmptyTxServer r = EmptyTxServer routeTx _ _ _ _ = R.StaticRouter mempty mempty + + applyAnteHandler _ _ _ _ = id + + hoistTxRouter _ _ _ _ = id diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs index 160e2308..4f284458 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Types.hs @@ -1,30 +1,42 @@ +{-# LANGUAGE UndecidableInstances #-} + module Tendermint.SDK.BaseApp.Transaction.Types ( module Tendermint.SDK.BaseApp.Transaction.Types -- * Re-Exports , Tx(..) ) where -import Control.Lens (lens) -import Data.ByteString (ByteString) -import Tendermint.SDK.BaseApp.Router (HasPath (..)) -import Tendermint.SDK.Types.Transaction (Tx (..)) -import Tendermint.SDK.Types.TxResult (TxResult) +import Control.Lens (lens) +import Data.ByteString (ByteString) +import Data.IORef (IORef, newIORef) +import qualified Tendermint.SDK.BaseApp.Events as E +import qualified Tendermint.SDK.BaseApp.Gas as G +import Tendermint.SDK.BaseApp.Router (HasPath (..)) +import qualified Tendermint.SDK.BaseApp.Transaction.Cache as Cache +import Tendermint.SDK.Types.Transaction (Tx (..)) +import Tendermint.SDK.Types.TxResult (TxResult) + +-------------------------------------------------------------------------------- +-- Router Types and Combinators +-------------------------------------------------------------------------------- data msg :~> a data TypedMessage msg -data OnCheck = OnCheckEval | OnCheckUnit +data Return a -data Return' (c :: OnCheck) a +data EmptyTxServer = EmptyTxServer -type Return = Return' 'OnCheckUnit +-------------------------------------------------------------------------------- +-- RouteContext and Singletons +-------------------------------------------------------------------------------- data RouteContext = CheckTx | DeliverTx deriving (Eq, Show) -type TransactionApplication m = RoutingTx ByteString -> m TxResult - -data EmptyTxServer = EmptyTxServer +-------------------------------------------------------------------------------- +-- Transaction Application types +-------------------------------------------------------------------------------- data RoutingTx msg where RoutingTx :: Tx alg msg -> RoutingTx msg @@ -35,3 +47,26 @@ instance Functor RoutingTx where instance HasPath (RoutingTx msg) where path = lens (\(RoutingTx tx) -> txRoute tx) (\(RoutingTx tx) r -> RoutingTx tx {txRoute = r}) + +data TransactionContext = TransactionContext + { gasRemaining :: IORef G.GasAmount + , storeCache :: IORef Cache.Cache + , events :: IORef [E.Event] + } + +newTransactionContext + :: RoutingTx msg + -> IO TransactionContext +newTransactionContext (RoutingTx Tx{txGas}) = do + initialGas <- newIORef $ G.GasAmount txGas + initialCache <- newIORef Cache.emptyCache + es <- newIORef [] + pure TransactionContext + { gasRemaining = initialGas + , storeCache = initialCache + , events = es + } + +type TransactionApplication m = + RoutingTx ByteString -> m (TxResult, Maybe Cache.Cache) + diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 060e901f..a4dd8aa9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -11,27 +11,29 @@ module Tendermint.SDK.Modules.Auth , eval , Api - , server + , querier , module Tendermint.SDK.Modules.Auth.Types ) where -import Polysemy (Members) +import Polysemy (Member, Members) import Tendermint.SDK.Application.Module (Module (..)) -import Tendermint.SDK.BaseApp (BaseAppEffs, EmptyTxServer, - emptyTxServer) +import Tendermint.SDK.BaseApp (BaseEffs, + EmptyTxServer (..), + ReadStore) import Tendermint.SDK.Modules.Auth.Keeper import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types -type AuthM r = Module AuthModule EmptyTxServer Api AuthEffs r +type AuthM r = Module AuthModule EmptyTxServer EmptyTxServer Api AuthEffs r authModule - :: Members BaseAppEffs r + :: Members BaseEffs r + => Member ReadStore r => AuthM r authModule = Module - { moduleTxDeliverer = emptyTxServer - , moduleTxChecker = emptyTxServer - , moduleQueryServer = server + { moduleTxDeliverer = EmptyTxServer + , moduleTxChecker = EmptyTxServer + , moduleQuerier = querier , moduleEval = eval } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs index 2b6f5c3c..f90bccec 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -4,9 +4,9 @@ module Tendermint.SDK.Modules.Auth.Keeper where import Polysemy import Polysemy.Error (Error, mapError, throw) -import Tendermint.SDK.BaseApp (AppError, RawStore, - StoreKey (..), get, - makeAppError, put) +import Tendermint.SDK.BaseApp (AppError, ReadStore, + StoreKey (..), WriteStore, + get, makeAppError, put) import Tendermint.SDK.Modules.Auth.Types import Tendermint.SDK.Types.Address (Address) @@ -38,12 +38,12 @@ createAccount addr = do pure emptyAccount eval - :: Members [RawStore, Error AppError] r + :: Members [ReadStore, WriteStore, Error AppError] r => Sem (Accounts : Error AuthError : r) a -> Sem r a eval = mapError makeAppError . evalAuth where - evalAuth :: Members [RawStore, Error AppError] r + evalAuth :: Members [ReadStore, WriteStore, Error AppError] r => Sem (Accounts : r) a -> Sem r a evalAuth = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs index 8b40e4d9..6a116335 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs @@ -1,12 +1,12 @@ module Tendermint.SDK.Modules.Auth.Query ( Api - , server + , querier ) where import Data.Proxy import Polysemy (Members) -import Polysemy.Error (Error) import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Query (QueryEffs) import Tendermint.SDK.Modules.Auth.Keeper (storeKey) import Tendermint.SDK.Modules.Auth.Types (Account) import Tendermint.SDK.Types.Address (Address) @@ -19,8 +19,8 @@ type AuthContents = '[(Address, Account)] type Api = BaseApp.QueryApi AuthContents -server - :: Members [BaseApp.RawStore, Error BaseApp.AppError] r +querier + :: Members QueryEffs r => BaseApp.RouteQ Api r -server = +querier = BaseApp.storeQueryHandlers (Proxy :: Proxy AuthContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs deleted file mode 100644 index 4634a9ee..00000000 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/AuthTreeStoreSpec.hs +++ /dev/null @@ -1,171 +0,0 @@ -module Tendermint.SDK.Test.AuthTreeStoreSpec (spec) where - -import Control.Lens (iso) -import Control.Monad (void) -import Control.Monad.IO.Class (liftIO) -import Data.Bifunctor (first) -import Data.ByteString (ByteString) -import qualified Data.Serialize as Serialize -import Data.String.Conversions (cs) -import Polysemy (Embed, Sem, runM) -import Polysemy.Error (Error, runError) -import Polysemy.Reader (Reader, runReader) -import Polysemy.Resource (Resource, - resourceToIO) -import Polysemy.Tagged (Tagged) -import Tendermint.SDK.BaseApp.Errors (AppError (..), SDKError (InternalError), - throwSDKError) -import Tendermint.SDK.BaseApp.Store (ConnectionScope (..), - IsKey (..), - MergeScopes, - RawKey (..), - RawStore, - StoreKey (..), - applyScope, - beginBlock, - commitBlock, - delete, get, - mergeScopes, put, - withSandbox, - withTransaction) -import Tendermint.SDK.BaseApp.Store.AuthTreeStore (AuthTreeGetter (..), - AuthTreeState, - evalMergeScopes, - evalTagged, - initAuthTreeState) -import Tendermint.SDK.Codec (HasCodec (..)) -import Test.Hspec - -spec :: Spec -spec = beforeAll beforeAction $ - describe "AuthTreeStore" $ do - - it "can fail to query an empty AuthTreeStore" $ \driver -> do - Right mv <- runAuthTree driver $ applyScope @'Query $ - get storeKey IntStoreKey - mv `shouldBe` Nothing - - it "can set a value and query the value" $ \driver -> do - Right mv <- runAuthTree driver $ applyScope @'Consensus $ do - put storeKey IntStoreKey (IntStore 1) - get storeKey IntStoreKey - mv `shouldBe` Just (IntStore 1) - - it "can make changes and roll back" $ \driver -> do - Right mv'' <- runAuthTree driver $ applyScope @'Mempool $ do - put storeKey IntStoreKey (IntStore 1) - withSandbox $ do - put storeKey IntStoreKey (IntStore 5) - mv <- get storeKey IntStoreKey - liftIO (mv `shouldBe` Just (IntStore 5)) - - delete storeKey IntStoreKey - mv' <- get storeKey IntStoreKey - - liftIO (mv' `shouldBe` Nothing) - get storeKey IntStoreKey - mv'' `shouldBe` Just (IntStore 1) - - it "can roll back if an error occurs during a transaction" $ \driver -> do - Left apperr <- runAuthTree driver $ applyScope @'Consensus $ do - put storeKey IntStoreKey (IntStore 1) - withTransaction $ do - put storeKey IntStoreKey (IntStore 6) - throwSDKError InternalError - appErrorCode apperr `shouldBe` 1 - Right mv <- runAuthTree driver $ applyScope @'Consensus $ - get storeKey IntStoreKey - mv `shouldBe` Just (IntStore 1) - - it "can make changes with a transaction" $ \driver -> do - Right mv <- runAuthTree driver . applyScope @'Consensus . withTransaction $ do - put storeKey IntStoreKey (IntStore 5) - get storeKey IntStoreKey - mv `shouldBe` Just (IntStore 5) - - it "can merge the scopes" $ \driver -> do - -- set all to be initially the same value - void $ runAuthTree driver . applyScope @'Query $ - put storeKey IntStoreKey (IntStore 0) - void $ runAuthTree driver . applyScope @'Mempool $ - put storeKey IntStoreKey (IntStore 0) - void $ runAuthTree driver . applyScope @'Consensus $ - put storeKey IntStoreKey (IntStore 0) - - -- see what happens with a sandboxed checktx - void $ runAuthTree driver . applyScope @'Mempool $ - withSandbox $ put storeKey IntStoreKey (IntStore 1) - - void $ runAuthTree driver . applyScope @'Query $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) - void $ runAuthTree driver . applyScope @'Mempool $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) - void $ runAuthTree driver . applyScope @'Consensus $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) - - - void $ runAuthTree driver . applyScope @'Consensus $ do - beginBlock - withTransaction $ put storeKey IntStoreKey (IntStore 1) - commitBlock - - void $ runAuthTree driver . applyScope @'Query $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) - void $ runAuthTree driver . applyScope @'Mempool $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 0) - void $ runAuthTree driver . applyScope @'Consensus $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) - - void $ runAuthTree driver . applyScope @'Consensus $ mergeScopes - - void $ runAuthTree driver . applyScope @'Query $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) - void $ runAuthTree driver . applyScope @'Mempool $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) - void $ runAuthTree driver . applyScope @'Consensus $ - get storeKey IntStoreKey >>= liftIO . shouldBe (Just 1) - - - - -beforeAction :: IO AuthTreeState -beforeAction = initAuthTreeState - -newtype IntStore = IntStore Int deriving (Eq, Show, Num, Serialize.Serialize) - -data IntStoreKey = IntStoreKey - -instance HasCodec IntStore where - encode = Serialize.encode - decode = first cs . Serialize.decode - -instance RawKey IntStoreKey where - rawKey = iso (\_ -> cs intStoreKey) (const IntStoreKey) - where - intStoreKey :: ByteString - intStoreKey = "IntStore" - -instance IsKey IntStoreKey "int_store" where - type Value IntStoreKey "int_store" = IntStore - -storeKey :: StoreKey "int_store" -storeKey = StoreKey "int_store" - -runAuthTree - :: AuthTreeGetter s - => AuthTreeState - -> Sem [ Tagged s RawStore - , MergeScopes - , Reader AuthTreeState - , Error AppError - , Resource - , Embed IO - ] a - -> IO (Either AppError a) -runAuthTree driver = - runM . - resourceToIO . - runError . - runReader driver . - evalMergeScopes . - evalTagged diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs new file mode 100644 index 00000000..838cabed --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs @@ -0,0 +1,214 @@ +module Tendermint.SDK.Test.IAVLStoreSpec (spec) where + +import Control.Lens (iso) +import Control.Monad (void) +import Control.Monad.IO.Class (liftIO) +import Data.Bifunctor (first) +import Data.ByteString (ByteString) +import Data.Proxy +import qualified Data.Serialize as Serialize +import Data.String.Conversions (cs) +import Network.GRPC.Client.Helpers (GrpcClient) +import Polysemy (Embed, Members, Sem, + runM) +import Polysemy.Error (Error, runError) +import Polysemy.Reader (Reader, runReader) +import Polysemy.Resource (Resource, + resourceToIO) +import Polysemy.Tagged (tag) +import Tendermint.SDK.BaseApp.Errors (AppError (..), SDKError (InternalError), + throwSDKError) +import Tendermint.SDK.BaseApp.Store (IsKey (..), + RawKey (..), + ReadStore, + Scope (..), + StoreEffs, + StoreKey (..), + WriteStore, + WriteStore, commit, + commitBlock, delete, + get, put, + withSandbox, + withTransaction) +import Tendermint.SDK.BaseApp.Store.IAVLStore (GrpcConfig (..), + IAVLVersions, + evalStoreEffs, + initGrpcClient, + initIAVLVersions) +import qualified Tendermint.SDK.BaseApp.Store.MemoryStore as Memory +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Effects ((:&)) +import Test.Hspec + +spec :: Spec +spec = do + beforeAll iavlBeforeAction $ + describe "IAVL tests" $ spec' (Proxy @IAVLEffs) + + beforeAll pureBeforeAction $ + describe "Pure tests" $ spec' (Proxy @PureEffs) + +spec' :: Members [Error AppError, Embed IO, Resource] r => Members StoreEffs r => Proxy r -> SpecWith (Driver r) +spec' (Proxy :: Proxy r) = do + + it "can fail to query an empty AuthTreeStore" $ \(driver :: Driver r) -> do + Right mv <- runDriver driver $ tag @'QueryAndMempool $ + get storeKey IntStoreKey + mv `shouldBe` Nothing + + it "can set a value and query the value" $ \(driver :: Driver r) -> do + Right mv <- runDriver driver $ do + tag @'Consensus @WriteStore $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + tag @'Consensus @ReadStore $ get storeKey IntStoreKey + mv `shouldBe` Just (IntStore 1) + + it "can make changes and roll back" $ \(driver :: Driver r) -> do + Right mv'' <- runDriver driver $ do + void $ withTransaction $ + tag @'Consensus $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + withSandbox $ do + tag @'Consensus @WriteStore $ (put storeKey IntStoreKey (IntStore 5) :: Sem (WriteStore ': r) ()) + mv <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + liftIO (mv `shouldBe` Just (IntStore 5)) + + tag @'Consensus @WriteStore$ (delete storeKey IntStoreKey :: Sem (WriteStore ': r) ()) + mv' <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + + liftIO (mv' `shouldBe` Nothing) + tag @'Consensus @ReadStore $ get storeKey IntStoreKey + mv'' `shouldBe` Just (IntStore 1) + + it "can roll back if an error occurs during a transaction" $ \driver -> do + Left apperr <- runDriver driver $ do + void $ withTransaction $ + tag @'Consensus $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + void $ withTransaction $ do + tag @'Consensus $ (put storeKey IntStoreKey (IntStore 6) :: Sem (WriteStore ': r) ()) + throwSDKError InternalError + appErrorCode apperr `shouldBe` 1 + Right mv <- runDriver driver $ + tag @'Consensus @ReadStore $ get storeKey IntStoreKey + mv `shouldBe` Just (IntStore 1) + + it "can make changes with a transaction" $ \driver -> do + Right (mv, _) <- runDriver driver . withTransaction $ do + tag @'Consensus $ (put storeKey IntStoreKey (IntStore 5) :: Sem (WriteStore ': r) ()) + tag @'Consensus @ReadStore $ get storeKey IntStoreKey + mv `shouldBe` Just (IntStore 5) + + it "can merge the scopes" $ \driver -> do + -- set all to be initially the same value + void $ runDriver driver $ + withTransaction $ + tag @'Consensus $ (put storeKey IntStoreKey (IntStore 0) :: Sem (WriteStore ': r) ()) + + -- mergeScopes so that all are using the latest version + void $ runDriver driver $ commitBlock + + void $ runDriver driver $ do + res <- tag @'QueryAndMempool $ get storeKey IntStoreKey + liftIO (res `shouldBe` Just 0) + void $ runDriver driver $ do + res <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + liftIO (res `shouldBe` Just 0) + + + -- Make another change on Consensus that does not commit + void $ runDriver driver $ do + tag @'Consensus $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + + void $ runDriver driver $ do + res <- tag @'QueryAndMempool @ReadStore $ get storeKey IntStoreKey + liftIO (res `shouldBe` Just 0) + + void $ runDriver driver $ do + res <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + liftIO (res `shouldBe` Just 1) + + -- commit the changes + void $ runDriver driver $ commit + + -- mergeScopes so that all are using the latest version + void $ runDriver driver $ commitBlock + + void $ runDriver driver $ do + res <- tag @'QueryAndMempool @ReadStore $ get storeKey IntStoreKey + liftIO (res `shouldBe` Just 1) + + void $ runDriver driver $ do + res <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + liftIO (res `shouldBe` Just 1) + + +iavlBeforeAction :: IO (Driver IAVLEffs) +iavlBeforeAction = do + vs <- initIAVLVersions + gc <- initGrpcClient $ GrpcConfig "0.0.0.0" 8090 + pure $ runIAVL (vs, gc) + +pureBeforeAction :: IO (Driver PureEffs) +pureBeforeAction = do + vs <- Memory.initDBVersions + db <- Memory.initDB + pure $ runPure (vs, db) + +newtype IntStore = IntStore Int deriving (Eq, Show, Num, Serialize.Serialize) + +data IntStoreKey = IntStoreKey + +instance HasCodec IntStore where + encode = Serialize.encode + decode = first cs . Serialize.decode + +instance RawKey IntStoreKey where + rawKey = iso (\_ -> cs intStoreKey) (const IntStoreKey) + where + intStoreKey :: ByteString + intStoreKey = "IntStore" + +instance IsKey IntStoreKey "int_store" where + type Value IntStoreKey "int_store" = IntStore + +storeKey :: StoreKey "int_store" +storeKey = StoreKey "int_store" + +type IAVLEffs = + StoreEffs :& [Reader IAVLVersions, Reader GrpcClient, Error AppError, Resource, Embed IO] + +runIAVL + :: (IAVLVersions, GrpcClient) + -> Driver IAVLEffs +runIAVL (versions, gc) = Driver $ \action -> + runM . + resourceToIO . + runError . + runReader gc . + runReader versions $ + evalStoreEffs action + + +type PureEffs = + StoreEffs :& [Reader Memory.DBVersions, Reader Memory.DB, Error AppError, Resource, Embed IO] + +runPure + :: (Memory.DBVersions, Memory.DB) + -> Driver PureEffs +runPure (versions, db) = Driver $ \action -> + runM . + resourceToIO . + runError . + runReader db . + runReader versions $ + Memory.evalStoreEffs action + + +newtype Driver core = Driver + (forall a. Sem core a -> IO (Either AppError a)) + +runDriver + :: forall core. + Driver core + -> forall a. + Sem core a + -> IO (Either AppError a) +runDriver (Driver f) a = f a diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs index e9b0dc25..bd2ed167 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs @@ -1,28 +1,39 @@ module Tendermint.SDK.Test.QuerySpec (spec) where -import qualified Data.ByteArray.Base64String as Base64 -import Data.Text (Text) -import qualified Network.ABCI.Types.Messages.Request as Req -import qualified Network.ABCI.Types.Messages.Response as Resp -import qualified Tendermint.SDK.Application as App -import qualified Tendermint.SDK.Application.Module as M -import qualified Tendermint.SDK.BaseApp as BA -import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import Tendermint.SDK.Codec (HasCodec (..)) -import qualified Tendermint.SDK.Test.SimpleStorage as SS -import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (Tx (..)) +import Control.Monad.IO.Class (liftIO) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Maybe (isJust) +import Data.Proxy +import Data.Text (Text) +import qualified Network.ABCI.Types.Messages.Request as Req +import qualified Network.ABCI.Types.Messages.Response as Resp +import qualified Tendermint.SDK.Application as App +import qualified Tendermint.SDK.Application.Module as M +import qualified Tendermint.SDK.BaseApp as BA +import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL +import Tendermint.SDK.BaseApp.Query (serveQueryApplication) +import qualified Tendermint.SDK.BaseApp.Store as Store +import Tendermint.SDK.BaseApp.Transaction (serveTxApplication) +import Tendermint.SDK.BaseApp.Transaction.Cache (writeCache) +import Tendermint.SDK.Codec (HasCodec (..)) +import qualified Tendermint.SDK.Test.SimpleStorage as SS +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) import Test.Hspec -type Effs = SS.SimpleStorage ': BA.BaseApp BA.CoreEffs +type Effs = SS.SimpleStorage ': BA.TxEffs BA.:& BA.BaseApp BA.PureCoreEffs + +type Ms = '[SS.SimpleStorageM Effs] spec :: Spec -spec = beforeAll (BA.makeContext (KL.InitialLogNamespace "test" "spec") Nothing) $ +spec = beforeAll initContext $ describe "Query tests" $ do - let modules :: App.Modules '[SS.SimpleStorageM Effs] Effs + let modules :: App.ModuleList Ms Effs modules = SS.simpleStorageModule App.:+ App.NilModules - ssServer = M.appQueryRouter modules - handler = M.appTxRouter modules BA.DeliverTx + rProxy = Proxy @(BA.BaseApp BA.PureCoreEffs) + app = M.makeApplication rProxy mempty modules + ssServer = serveQueryApplication (Proxy @(M.ApplicationQ Ms)) rProxy $ M.applicationQuerier app + updateCount = serveTxApplication (Proxy @(M.ApplicationD Ms)) rProxy (Proxy @'Store.Consensus) $ M.applicationTxDeliverer app it "Can make a new count and query it with a multiplier" $ \ctx -> do let increaseCountMsg = Msg { msgAuthor = undefined @@ -38,7 +49,13 @@ spec = beforeAll (BA.makeContext (KL.InitialLogNamespace "test" "spec") Nothing) , txSigner = undefined , txNonce = undefined } - _ <- SS.evalToIO ctx $ handler tx + _ <- SS.evalToIO ctx $ do + (_, mCache) <- updateCount tx + liftIO (mCache `shouldSatisfy` isJust) + let (Just cache) = mCache + writeCache cache + _ <- Store.commit + Store.commitBlock let q = Req.Query -- TODO -- this shouldn't require / count { queryPath = "/simple_storage/manipulated/1?factor=4" @@ -50,3 +67,7 @@ spec = beforeAll (BA.makeContext (KL.InitialLogNamespace "test" "spec") Nothing) queryCode `shouldBe` 0 let resultCount = decode (Base64.toBytes queryValue) :: Either Text SS.Count resultCount `shouldBe` Right 3 + +initContext :: IO BA.PureContext +initContext = do + BA.makePureContext (KL.InitialLogNamespace "test" "spec") diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index ae20c0fa..6d8f8af9 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -16,7 +16,6 @@ import Data.ByteArray (convert) import qualified Data.ByteArray.Base64String as Base64 import Data.ByteString (ByteString) import Data.Int (Int32) -import Data.Maybe (fromJust) import Data.Proxy import qualified Data.Serialize as Serialize import Data.Serialize.Text () @@ -24,16 +23,17 @@ import Data.String.Conversions (cs) import Data.Validation (Validation (..)) import GHC.Generics (Generic) import Polysemy -import Polysemy.Error (Error) +import Polysemy.Error (Error, throw) import Servant.API import Tendermint.SDK.Application (Module (..)) -import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Message (HasMessageType (..), Msg (..), ValidateMessage (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) + -------------------------------------------------------------------------------- -- Types -------------------------------------------------------------------------------- @@ -46,18 +46,18 @@ instance HasCodec Count where encode = Serialize.encode decode = first cs . Serialize.decode -instance BaseApp.RawKey CountKey where +instance BA.RawKey CountKey where rawKey = iso (\_ -> cs countKey) (const CountKey) where countKey :: ByteString countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) -instance BaseApp.IsKey CountKey "simple_storage" where +instance BA.IsKey CountKey "simple_storage" where type Value CountKey "simple_storage" = Count -instance BaseApp.FromQueryData CountKey +instance BA.FromQueryData CountKey -instance BaseApp.Queryable Count where +instance BA.Queryable Count where type Name Count = "count" -------------------------------------------------------------------------------- @@ -84,12 +84,12 @@ instance ValidateMessage UpdateCountTx where -- Keeper -------------------------------------------------------------------------------- -storeKey :: BaseApp.StoreKey "simple_storage" -storeKey = BaseApp.StoreKey "simple_storage" +storeKey :: BA.StoreKey "simple_storage" +storeKey = BA.StoreKey "simple_storage" data SimpleStorage m a where PutCount :: Count -> SimpleStorage m () - GetCount :: SimpleStorage m Count + GetCount :: SimpleStorage m (Maybe Count) makeSem ''SimpleStorage @@ -103,11 +103,11 @@ updateCount count = putCount count eval :: forall r. - Members '[BaseApp.RawStore, Error BaseApp.AppError] r + Members BA.TxEffs r => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) eval = interpret (\case - PutCount count -> BaseApp.put storeKey CountKey count - GetCount -> fromJust <$> BaseApp.get storeKey CountKey + PutCount count -> BA.put storeKey CountKey count + GetCount -> BA.get storeKey CountKey ) -------------------------------------------------------------------------------- @@ -115,19 +115,20 @@ eval = interpret (\case -------------------------------------------------------------------------------- type MessageApi = - BaseApp.TypedMessage UpdateCountTx BaseApp.:~> BaseApp.Return () + BA.TypedMessage UpdateCountTx BA.:~> BA.Return () messageHandlers :: Member SimpleStorage r - => BaseApp.RouteTx MessageApi r 'BaseApp.DeliverTx + => Members BA.TxEffs r + => BA.RouteTx MessageApi r messageHandlers = updateCountH updateCountH :: Member SimpleStorage r - => Members BaseApp.TxEffs r - => BaseApp.RoutingTx UpdateCountTx + => Members BA.TxEffs r + => BA.RoutingTx UpdateCountTx -> Sem r () -updateCountH (BaseApp.RoutingTx Tx{txMsg}) = +updateCountH (BA.RoutingTx Tx{txMsg}) = let Msg{msgData} = txMsg UpdateCountTx{updateCountTxCount} = msgData in updateCount (Count updateCountTxCount) @@ -142,33 +143,36 @@ type GetMultipliedCount = "manipulated" :> Capture "subtract" Integer :> QueryParam' '[Required, Strict] "factor" Integer - :> BaseApp.Leaf Count + :> BA.Leaf Count getMultipliedCount - :: Member SimpleStorage r + :: Members [Error BA.AppError, SimpleStorage] r => Integer -> Integer - -> Sem r (BaseApp.QueryResult Count) + -> Sem r (BA.QueryResult Count) getMultipliedCount subtractor multiplier = do let m = fromInteger multiplier s = fromInteger subtractor - c <- getCount - pure $ BaseApp.QueryResult - { queryResultData = m * c - s - , queryResultIndex = 0 - , queryResultKey = Base64.fromBytes $ CountKey ^. BaseApp.rawKey - , queryResultProof = Nothing - , queryResultHeight = 0 - } - -type QueryApi = GetMultipliedCount :<|> BaseApp.QueryApi CountStoreContents - -server + mc <- getCount + case mc of + Nothing -> throw . BA.makeAppError $ BA.ResourceNotFound + Just c -> pure $ BA.QueryResult + { queryResultData = m * c - s + , queryResultIndex = 0 + , queryResultKey = Base64.fromBytes $ CountKey ^. BA.rawKey + , queryResultProof = Nothing + , queryResultHeight = 0 + } + +type QueryApi = GetMultipliedCount :<|> BA.QueryApi CountStoreContents + +querier :: forall r. - Members [SimpleStorage, BaseApp.RawStore, Error BaseApp.AppError] r - => BaseApp.RouteQ QueryApi r -server = - let storeHandlers = BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) + Members BA.QueryEffs r + => Member SimpleStorage r + => BA.RouteQ QueryApi r +querier = + let storeHandlers = BA.storeQueryHandlers (Proxy :: Proxy CountStoreContents) storeKey (Proxy :: Proxy r) in getMultipliedCount :<|> storeHandlers @@ -177,25 +181,24 @@ server = -------------------------------------------------------------------------------- type SimpleStorageM r = - Module "simple_storage" MessageApi QueryApi SimpleStorageEffs r + Module "simple_storage" MessageApi MessageApi QueryApi SimpleStorageEffs r simpleStorageModule :: Member SimpleStorage r - => Members BaseApp.BaseAppEffs r + => Members BA.TxEffs r + => Members BA.BaseEffs r => SimpleStorageM r simpleStorageModule = Module { moduleTxDeliverer = messageHandlers - , moduleTxChecker = BaseApp.defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) - , moduleQueryServer = server + , moduleTxChecker = BA.defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) + , moduleQuerier = querier , moduleEval = eval } evalToIO - :: BaseApp.Context - -> Sem (SimpleStorage ': BaseApp.BaseApp BaseApp.CoreEffs) a + :: BA.PureContext + -> Sem (BA.BaseApp BA.PureCoreEffs) a -> IO a -evalToIO context action = - BaseApp.runCoreEffs context . - BaseApp.compileToCoreEffs . - BaseApp.applyScope @'BaseApp.Consensus $ - eval action +evalToIO context action = do + eRes <- BA.runPureCoreEffs context . BA.defaultCompileToPureCore $ action + either (error . show) pure eRes diff --git a/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs index 55054eb5..2d0d9c70 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Class.hs @@ -8,24 +8,22 @@ module Tendermint.Utils.TxClient.Class , defaultClientTxOpts ) where -import Control.Monad.IO.Class (liftIO) -import Control.Monad.Reader (ReaderT, ask) -import qualified Data.ByteArray.Base64String as Base64 +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Reader (ReaderT, ask) +import qualified Data.ByteArray.Base64String as Base64 import Data.Proxy -import Data.String.Conversions (cs) -import Data.Text (Text) -import Data.Word (Word64) -import GHC.TypeLits (KnownSymbol, - symbolVal) -import qualified Network.Tendermint.Client as RPC -import Servant.API ((:<|>) (..), (:>)) -import qualified Tendermint.SDK.BaseApp.Transaction as T -import qualified Tendermint.SDK.BaseApp.Transaction.Modifier as T -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (HasMessageType (..), - TypedMessage (..)) -import Tendermint.SDK.Types.Transaction (RawTransaction (..)) +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word64) +import GHC.TypeLits (KnownSymbol, symbolVal) +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..), (:>)) +import qualified Tendermint.SDK.BaseApp.Transaction as T +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (HasMessageType (..), + TypedMessage (..)) +import Tendermint.SDK.Types.Transaction (RawTransaction (..)) import Tendermint.Utils.TxClient.Types class Monad m => RunTxClient m where @@ -56,20 +54,21 @@ data ClientTxOpts = ClientTxOpts defaultClientTxOpts :: ClientTxOpts defaultClientTxOpts = ClientTxOpts "" 0 -class HasTxClient m layout where +class HasTxClient m layoutC layoutD where - type ClientT (m :: * -> *) layout :: * - genClientT :: Proxy m -> Proxy layout -> ClientTxOpts -> ClientT m layout + type ClientT (m :: * -> *) layoutC layoutD :: * + genClientT :: Proxy m -> Proxy layoutC -> Proxy layoutD -> ClientTxOpts -> ClientT m layoutC layoutD -instance (HasTxClient m a, HasTxClient m b) => HasTxClient m (a :<|> b) where - type ClientT m (a :<|> b) = ClientT m a :<|> ClientT m b - genClientT pm _ opts = genClientT pm (Proxy @a) opts :<|> genClientT pm (Proxy @b) opts +instance (HasTxClient m a c, HasTxClient m b d) => HasTxClient m (a :<|> b) (c :<|> d) where + type ClientT m (a :<|> b) (c :<|> d) = ClientT m a c :<|> ClientT m b d + genClientT pm _ _ opts = genClientT pm (Proxy @a) (Proxy @c) opts :<|> + genClientT pm (Proxy @b) (Proxy @d) opts -instance (KnownSymbol path, HasTxClient m a) => HasTxClient m (path :> a) where - type ClientT m (path :> a) = ClientT m a - genClientT pm _ clientOpts = +instance (KnownSymbol path, HasTxClient m a b) => HasTxClient m (path :> a) (path :> b) where + type ClientT m (path :> a) (path :> b) = ClientT m a b + genClientT pm _ _ clientOpts = let clientOpts' = clientOpts { clientTxOptsRoute = cs $ symbolVal (Proxy @path) } - in genClientT pm (Proxy @a) clientOpts' + in genClientT pm (Proxy @a) (Proxy @b) clientOpts' makeRawTxForSigning :: forall msg. @@ -89,25 +88,26 @@ makeRawTxForSigning ClientTxOpts{..} TxOpts{..} msg = } instance ( HasMessageType msg, HasCodec msg - , HasCodec a, HasCodec (T.OnCheckReturn 'T.CheckTx oc a) + , HasCodec check, HasCodec deliver , RunTxClient m - ) => HasTxClient m (T.TypedMessage msg T.:~> T.Return' oc a) where - type ClientT m (T.TypedMessage msg T.:~> T.Return' oc a) = TxOpts -> msg -> m (TxClientResponse (T.OnCheckReturn 'T.CheckTx oc a) a) + ) => HasTxClient m (T.TypedMessage msg T.:~> T.Return check) (T.TypedMessage msg T.:~> T.Return deliver) where + type ClientT m (T.TypedMessage msg T.:~> T.Return check) (T.TypedMessage msg T.:~> T.Return deliver) = + TxOpts -> msg -> m (TxClientResponse check deliver) - genClientT _ _ clientOpts opts msg = do + genClientT _ _ _ clientOpts opts msg = do let Signer signerAddress signer = txOptsSigner opts nonce <- getNonce signerAddress let clientOpts' = clientOpts {clientTxOptsNonce = nonce} rawTxForSigning = makeRawTxForSigning clientOpts' opts msg rawTxWithSig = signer rawTxForSigning txRes <- runTx rawTxWithSig - pure $ parseRPCResponse (Proxy @a) (Proxy @oc) txRes + pure $ parseRPCResponse txRes -- | Singleton type representing a client for an empty API. data EmptyTxClient = EmptyTxClient deriving (Eq, Show, Bounded, Enum) -instance HasTxClient m T.EmptyTxServer where - type ClientT m T.EmptyTxServer = EmptyTxClient +instance HasTxClient m T.EmptyTxServer T.EmptyTxServer where + type ClientT m T.EmptyTxServer T.EmptyTxServer = EmptyTxClient - genClientT _ _ _ = EmptyTxClient + genClientT _ _ _ _ = EmptyTxClient diff --git a/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs index 24d67ec6..7cec37b4 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/TxClient/Types.hs @@ -1,28 +1,27 @@ module Tendermint.Utils.TxClient.Types where -import Control.Lens ((^.)) -import Crypto.Hash (Digest) -import Crypto.Hash.Algorithms (SHA256) -import Data.Bifunctor (first) -import qualified Data.ByteArray.Base64String as Base64 -import Data.Int (Int64) +import Control.Lens ((^.)) +import Crypto.Hash (Digest) +import Crypto.Hash.Algorithms (SHA256) +import Data.Bifunctor (first) +import qualified Data.ByteArray.Base64String as Base64 +import Data.Int (Int64) import Data.Proxy -import Data.Text (Text) -import Network.ABCI.Types.Messages.FieldTypes (Event) -import qualified Network.ABCI.Types.Messages.Response as Response -import qualified Network.Tendermint.Client as RPC -import Tendermint.SDK.BaseApp.Errors (AppError, - txResultAppError) -import qualified Tendermint.SDK.BaseApp.Transaction as T -import qualified Tendermint.SDK.BaseApp.Transaction.Modifier as T -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Crypto (RecoverableSignatureSchema (..), - SignatureSchema (..)) -import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Transaction (RawTransaction (..), - signRawTransaction) -import Tendermint.SDK.Types.TxResult (checkTxTxResult, - deliverTxTxResult) +import Data.Text (Text) +import Network.ABCI.Types.Messages.FieldTypes (Event) +import qualified Network.ABCI.Types.Messages.Response as Response +import qualified Network.Tendermint.Client as RPC +import Tendermint.SDK.BaseApp.Errors (AppError, + txResultAppError) +import qualified Tendermint.SDK.BaseApp.Transaction as T +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Crypto (RecoverableSignatureSchema (..), + SignatureSchema (..)) +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Transaction (RawTransaction (..), + signRawTransaction) +import Tendermint.SDK.Types.TxResult (checkTxTxResult, + deliverTxTxResult) data TxOpts = TxOpts { txOptsGas :: Int64 @@ -65,13 +64,12 @@ data TxClientResponse c d = deriving (Eq, Show) parseRPCResponse - :: HasCodec a - => HasCodec (T.OnCheckReturn 'T.CheckTx oc a) - => Proxy a - -> Proxy (oc :: T.OnCheck) - -> RPC.ResultBroadcastTxCommit - -> TxClientResponse (T.OnCheckReturn 'T.CheckTx oc a) a -parseRPCResponse _ _ RPC.ResultBroadcastTxCommit{..} = + :: forall check deliver. + HasCodec check + => HasCodec deliver + => RPC.ResultBroadcastTxCommit + -> TxClientResponse check deliver +parseRPCResponse RPC.ResultBroadcastTxCommit{..} = let makeCheckResp r@Response.CheckTx{..} = case checkTxCode of 0 -> do diff --git a/hs-iavl-client/package.yaml b/hs-iavl-client/package.yaml index bbb2149a..1ff3d9df 100644 --- a/hs-iavl-client/package.yaml +++ b/hs-iavl-client/package.yaml @@ -51,8 +51,10 @@ library: - http2-client-grpc - proto-lens - proto-lens-runtime + - text exposed-modules: - Database.IAVL.RPC + - Database.IAVL.RPC.Types generated-exposed-modules: - Proto.Iavl.Api - Proto.Iavl.Api_Fields diff --git a/hs-iavl-client/src/Database/IAVL/RPC/Types.hs b/hs-iavl-client/src/Database/IAVL/RPC/Types.hs new file mode 100644 index 00000000..99fa083c --- /dev/null +++ b/hs-iavl-client/src/Database/IAVL/RPC/Types.hs @@ -0,0 +1,37 @@ +module Database.IAVL.RPC.Types where + +import Control.Exception (Exception, throwIO) +import Data.Text (Text, pack) +import Network.GRPC.Client (uncompressed) +import Network.GRPC.Client.Helpers (GrpcClient, GrpcClientConfig (..), + grpcClientConfigSimple, + setupGrpcClient) +import Network.HTTP2.Client (runClientIO) + + +-------------------------------------------------------------------------------- +-- | GRPCClientError +-------------------------------------------------------------------------------- +-- | This type represents error with the GRPC Client +data GRPCClientError = ClientSetupError Text + deriving Show + +instance Exception GRPCClientError + +-------------------------------------------------------------------------------- +-- | initGrpcClient +-------------------------------------------------------------------------------- + +data GrpcConfig = GrpcConfig + { grpcHost :: String + , grpcPort :: Integer + } + +-- | Initialize the GRPC Client +initGrpcClient :: GrpcConfig -> IO GrpcClient +initGrpcClient (GrpcConfig host port) = +-- usually 0.0.0.0:8090 + let grpcClient = grpcClientConfigSimple host (fromInteger port) False + in runClientIO (setupGrpcClient (grpcClient{_grpcClientConfigCompression=uncompressed})) >>= \case + Right gc -> pure gc + Left err -> throwIO . ClientSetupError . pack $ show err diff --git a/hs-iavl-client/test/Database/IAVL/RPCSpec.hs b/hs-iavl-client/test/Database/IAVL/RPCSpec.hs index a60ae32e..15990e07 100644 --- a/hs-iavl-client/test/Database/IAVL/RPCSpec.hs +++ b/hs-iavl-client/test/Database/IAVL/RPCSpec.hs @@ -1,23 +1,23 @@ module Database.IAVL.RPCSpec (spec) where -import Control.Lens ((&), (.~), (^.)) -import Control.Monad (void) -import Data.ProtoLens.Message (defMessage) +import Control.Lens ((&), (.~), (^.)) +import Control.Monad (void) +import Data.ProtoLens.Message (defMessage) import Database.IAVL.RPC -import Network.GRPC.Client (RawReply, uncompressed) -import Network.GRPC.Client.Helpers (GrpcClient, GrpcClientConfig (..), - grpcClientConfigSimple, - setupGrpcClient) -import Network.HTTP2.Client (ClientIO, TooMuchConcurrency, - runClientIO) -import qualified Proto.Iavl.Api_Fields as Api +import Database.IAVL.RPC.Types +import Network.GRPC.Client (RawReply) +import Network.HTTP2.Client (ClientIO, TooMuchConcurrency, + runClientIO) +import qualified Proto.Iavl.Api_Fields as Api import Test.Hspec spec :: Spec -spec = beforeAll initGrpcClient $ do +spec = beforeAll (initGrpcClient $ GrpcConfig "0.0.0.0" 8090) $ do let testKey = "test-key" testValue = "test-value" - rootWithTestKey = "\209)\148\US[p\231 do @@ -34,6 +34,23 @@ spec = beforeAll initGrpcClient $ do let getReq = defMessage & Api.key .~ testKey res <- runGrpc $ get gc getReq res ^. Api.value `shouldBe` testValue + res ^. Api.index `shouldBe` 0 + + it "should call `get` RPC method on a newly set key and get the expected value and index" $ \gc -> do + let setReq = defMessage & Api.key .~ testKey2 + & Api.value .~ testValue2 + sres <- runGrpc $ set gc setReq + sres ^. Api.result `shouldBe` False + + let getReq = defMessage & Api.key .~ testKey2 + gres <- runGrpc $ get gc getReq + gres ^. Api.value `shouldBe` testValue2 + gres ^. Api.index `shouldBe` 1 + + it "should call `get` RPC method and fail to get the expected value" $ \gc -> do + let getReq = defMessage & Api.key .~ "non-existing-key" + res <- runGrpc $ get gc getReq + res ^. Api.value `shouldBe` "" it "should call `saveVersion` RPC method and get the latest hash" $ \gc -> do _ <- runGrpc $ saveVersion gc @@ -123,6 +140,10 @@ spec = beforeAll initGrpcClient $ do verExistsRes <- runGrpc $ versionExists gc verExistsReq verExistsRes ^. Api.result `shouldBe` True + it "should call `version` RPC method and get latest version number" $ \gc -> do + verRes <- runGrpc $ version gc + verRes ^. Api.version `shouldBe` 4 + it "should call `versionExists` RPC method on non-existing version" $ \gc -> do let verExistsReq = defMessage & Api.version .~ 25 verExistsRes <- runGrpc $ versionExists gc verExistsReq @@ -148,6 +169,17 @@ spec = beforeAll initGrpcClient $ do delVerRes ^. Api.rootHash `shouldBe` rootWithTestKey verExistsRes ^. Api.result `shouldBe` False + it "should call `rollback` RPC method without changing the version" $ \gc -> do + verRes <- runGrpc $ version gc + let oldVersion = verRes ^. Api.version + + _ <- runGrpc $ rollback gc + + verRes' <- runGrpc $ version gc + let newVersion = verRes' ^. Api.version + + oldVersion `shouldBe` newVersion + it "should call `rollback` RPC method" $ \gc -> do let key = "key-with-rollback" value = "value-with-rollback" @@ -159,18 +191,28 @@ spec = beforeAll initGrpcClient $ do let getReq = defMessage & Api.key .~ key getRes <- runGrpc $ get gc getReq getRes ^. Api.value `shouldBe` value + getRes ^. Api.index `shouldBe` 1 _ <- runGrpc $ rollback gc getRes' <- runGrpc $ get gc getReq getRes' ^. Api.value `shouldBe` "" -initGrpcClient :: IO GrpcClient -initGrpcClient = - let grpcClient = grpcClientConfigSimple "localhost" 8090 False - in runClientIO (setupGrpcClient (grpcClient{_grpcClientConfigCompression=uncompressed})) >>= \case - Right gc -> pure gc - _ -> error "Error creating GrpcClient" + it "should call `has` RPC method on current working tree" $ \gc -> do + let key = "key-has" + value = "value-has" + setReq = defMessage & Api.key .~ key + & Api.value .~ value + res <- runGrpc $ set gc setReq + res ^. Api.result `shouldBe` False + verRes <- runGrpc $ version gc + let currentVersion = verRes ^. Api.version + hasReq = defMessage & Api.key .~ key + & Api.version .~ currentVersion + print $ "The current version is " <> show currentVersion + hasRes <- runGrpc $ has gc hasReq + hasRes ^. Api.result `shouldBe` False + runGrpc :: ClientIO (Either TooMuchConcurrency (RawReply a)) -> IO a runGrpc f = runClientIO f >>= \case From 25e752c6311238fb5914f5ae40c4c6e2e37f3ed7 Mon Sep 17 00:00:00 2001 From: Carl Factora Date: Sun, 16 Feb 2020 21:19:47 -0500 Subject: [PATCH 60/70] Impl bank module (#201) * Move token to sdk * rename token to bank * temp remove bs and validation deps from nameservice * prelim interface b/t auth and bank * wip: removing amount + making bank module * wip: use auth in bank keeper * wip: custom bank query * compiles - bring back Amount - add CoinId newtype - use updated auth and bank in examples apps * comment out faucet * move some faucet stuff * remove faucet comments and add exports to nameservice * fix nameservice test imports + stylish * impl bank custom query * compiles - codecs for address http api data are wrong * define HasCodec for address + use for httpApiData codecs + remove bank storekey * queries working, but addresses aren't persisting inside of bank * remove bank eff and use auth summands in bank keeper * fix windings in query * simplify type for getBalance * add bank error for no account found * add bankeff constraint for errors * some code clean up * wip: debugging * typo * fix antehandler * remove test self-buy test for some reason, the bank module has removed the ability to profit from buying your own names. * rework nonce antehandler * wip: antehandler on check and deliver * remove antehandler non-existent acc error * change logic for checking nonces tx nonce should always be +1 of account nonce * remove trace statements * add createAccountAnteHandler * stylish * changes from merge * apps + libs build; onto tests * tests compile * fix nonce issues in apps * address review comments * use Endo for antehandlers * remove proto3-suite in auth; onto bank * Add unknown account error in nonce AH * use protolens in bank * add message types for newtype containing records; compiles * fix tutorial, protogen and weeder * put generated bank proto modules in weeder's non-compiled field * shorten title * use nameserviceCoinId Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- .weeder.yaml | 4 + hs-abci-docs/nameservice/interact/Interact.hs | 21 ++- hs-abci-docs/nameservice/package.yaml | 6 - hs-abci-docs/nameservice/protogen/Protogen.hs | 9 +- .../src/Nameservice/Application.hs | 8 +- .../src/Nameservice/Modules/Nameservice.hs | 7 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 45 ++++-- .../Modules/Nameservice/Messages.hs | 80 ++++++++++- .../Nameservice/Modules/Nameservice/Router.hs | 26 +++- .../Nameservice/Modules/Nameservice/Types.hs | 51 ++++++- .../src/Nameservice/Modules/Token.hs | 65 --------- .../src/Nameservice/Modules/Token/Keeper.hs | 113 --------------- .../src/Nameservice/Modules/Token/Messages.hs | 79 ----------- .../src/Nameservice/Modules/Token/Query.hs | 22 --- .../src/Nameservice/Modules/Token/Router.hs | 56 -------- .../src/Nameservice/Modules/Token/Types.hs | 116 ---------------- .../test/Nameservice/Test/E2ESpec.hs | 50 +++---- .../test/Nameservice/Test/EventOrphans.hs | 6 +- .../tutorial/Foundations/02-BaseApp.md | 2 +- .../tutorial/Tutorial/Nameservice/02-Types.md | 40 ++++-- .../Tutorial/Nameservice/03-Message.md | 39 ++++-- .../Tutorial/Nameservice/04-Keeper.md | 15 +- .../Tutorial/Nameservice/06-Module.md | 14 +- .../Tutorial/Nameservice/07-Application.md | 8 +- .../tutorial/Tutorial/Nameservice/Router.md | 18 +-- .../test/SimpleStorage/Test/E2ESpec.hs | 5 +- hs-abci-sdk/package.yaml | 9 ++ hs-abci-sdk/protos/modules/auth.proto | 16 ++- hs-abci-sdk/protos/modules/bank.proto | 15 ++ .../Tendermint/SDK/Application/AnteHandler.hs | 54 +++++--- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 2 +- .../src/Tendermint/SDK/BaseApp/Errors.hs | 8 ++ .../src/Tendermint/SDK/BaseApp/Transaction.hs | 2 +- .../SDK/BaseApp/Transaction/AnteHandler.hs | 13 +- .../SDK/BaseApp/Transaction/Router.hs | 5 +- .../src/Tendermint/SDK/Modules/Auth.hs | 15 +- .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 34 ++--- .../src/Tendermint/SDK/Modules/Auth/Types.hs | 131 ++++++++++++++---- .../src/Tendermint/SDK/Modules/Bank.hs | 41 ++++++ .../src/Tendermint/SDK/Modules/Bank/Keeper.hs | 106 ++++++++++++++ .../Tendermint/SDK/Modules/Bank/Messages.hs | 86 ++++++++++++ .../src/Tendermint/SDK/Modules/Bank/Query.hs | 44 ++++++ .../src/Tendermint/SDK/Modules/Bank/Router.hs | 49 +++++++ .../src/Tendermint/SDK/Modules/Bank/Types.hs | 50 +++++++ .../src/Tendermint/SDK/Types/Address.hs | 27 ++++ 45 files changed, 940 insertions(+), 672 deletions(-) delete mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs delete mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs delete mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Messages.hs delete mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs delete mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs delete mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Types.hs create mode 100644 hs-abci-sdk/protos/modules/bank.proto create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs diff --git a/.weeder.yaml b/.weeder.yaml index 67136eea..9a8491a9 100644 --- a/.weeder.yaml +++ b/.weeder.yaml @@ -78,6 +78,8 @@ - module: - Proto.Modules.Auth - Proto.Modules.Auth_Fields + - Proto.Modules.Bank + - Proto.Modules.Bank_Fields - Proto.Types.Transaction - Proto.Types.Transaction_Fields - message: @@ -85,6 +87,8 @@ - module: - Proto.Modules.Auth - Proto.Modules.Auth_Fields + - Proto.Modules.Bank + - Proto.Modules.Bank_Fields - Proto.Types.Transaction - Proto.Types.Transaction_Fields - message: diff --git a/hs-abci-docs/nameservice/interact/Interact.hs b/hs-abci-docs/nameservice/interact/Interact.hs index a3f43347..049d0b52 100644 --- a/hs-abci-docs/nameservice/interact/Interact.hs +++ b/hs-abci-docs/nameservice/interact/Interact.hs @@ -16,7 +16,6 @@ import qualified Faker.Name as Name import qualified Faker.Utils as Utils import Nameservice.Application import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..)) import Tendermint.SDK.Application.Module (ApplicationC, ApplicationD, @@ -45,14 +44,14 @@ import Test.RandomStrings (onlyWith, randomASCII, -- Actions -------------------------------------------------------------------------------- -faucetAccount :: Signer -> T.Amount -> IO () +faucetAccount :: Signer -> Auth.Amount -> IO () faucetAccount s@(Signer addr _) amount = - runAction_ s faucet $ T.FaucetAccount addr amount + runAction_ s faucet $ N.FaucetAccount addr N.nameserviceCoinId amount createName :: Signer -> N.Name -> Text -> IO () createName s name val = buyName s name val 0 -buyName :: Signer -> N.Name -> Text -> T.Amount -> IO () +buyName :: Signer -> N.Name -> Text -> Auth.Amount -> IO () buyName s@(Signer addr _) name newVal amount = runAction_ s buy $ N.BuyName amount name newVal addr @@ -123,13 +122,14 @@ txClientConfig = , queryArgsProve = False , queryArgsData = addr } + -- @NOTE: TxNonce should be +1 of accountNonce case resp of QueryError e -> if appErrorCode e == 2 - then pure 0 + then pure 1 else error $ "Unknown nonce error: " <> show (appErrorMessage e) QueryResponse QueryResult {queryResultData} -> - pure $ Auth.accountNonce queryResultData + pure $ 1 + Auth.accountNonce queryResultData in ClientConfig { clientGetNonce = getNonce @@ -157,14 +157,13 @@ delete -> N.DeleteName -> TxClientM (TxClientResponse () ()) --- Token Client faucet :: TxOpts - -> T.FaucetAccount + -> N.FaucetAccount -> TxClientM (TxClientResponse () ()) -(buy :<|> set :<|> delete) :<|> - (_ :<|> _ :<|> faucet) :<|> +(buy :<|> set :<|> delete :<|> faucet) :<|> + (_ :<|> _) :<|> EmptyTxClient = genClientT (Proxy @TxClientM) txApiCP txApiDP defaultClientTxOpts where @@ -189,7 +188,7 @@ genName = do name <- Name.name return . fromString $ name -genAmount :: IO T.Amount +genAmount :: IO Auth.Amount genAmount = do genAmt <- Utils.randomNum (1, 1000) return . fromInteger . toInteger $ genAmt diff --git a/hs-abci-docs/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml index f0e8750d..acccc429 100644 --- a/hs-abci-docs/nameservice/package.yaml +++ b/hs-abci-docs/nameservice/package.yaml @@ -75,12 +75,6 @@ library: - Nameservice.Config - Nameservice.Aeson - Nameservice.Server - - Nameservice.Modules.Token - - Nameservice.Modules.Token.Messages - - Nameservice.Modules.Token.Types - - Nameservice.Modules.Token.Keeper - - Nameservice.Modules.Token.Query - - Nameservice.Modules.Token.Router - Nameservice.Modules.Nameservice - Nameservice.Modules.Nameservice.Messages - Nameservice.Modules.Nameservice.Types diff --git a/hs-abci-docs/nameservice/protogen/Protogen.hs b/hs-abci-docs/nameservice/protogen/Protogen.hs index 589b0381..74eaa1be 100644 --- a/hs-abci-docs/nameservice/protogen/Protogen.hs +++ b/hs-abci-docs/nameservice/protogen/Protogen.hs @@ -8,12 +8,11 @@ module Protogen (messagesProtoFile, whoisProtoFile) where import Data.Aeson.Casing (snakeCase) import qualified Data.ByteString.Lazy as BL import GHC.Exts (Proxy#, proxy#) -import Nameservice.Modules.Nameservice.Messages (BuyName (..), +import Nameservice.Modules.Nameservice.Messages (BuyNameMessage (..), DeleteName (..), SetName (..)) import Nameservice.Modules.Nameservice.Types (Name (..), - Whois (..)) -import Nameservice.Modules.Token (Amount (..)) + WhoisMessage (..)) import Proto3.Suite (DotProtoDefinition, Message, fromByteString, @@ -46,10 +45,10 @@ msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } messagesProtoFile :: String messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" ([ message (proxy# :: Proxy# SetName) - , message (proxy# :: Proxy# BuyName) + , message (proxy# :: Proxy# BuyNameMessage) , message (proxy# :: Proxy# DeleteName) ] :: [DotProtoDefinition]) whoisProtoFile :: String whoisProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" - ([ message (proxy# :: Proxy# Whois) ] :: [DotProtoDefinition]) + ([ message (proxy# :: Proxy# WhoisMessage) ] :: [DotProtoDefinition]) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Application.hs b/hs-abci-docs/nameservice/src/Nameservice/Application.hs index 84b88b30..14ddc7c3 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Application.hs @@ -6,24 +6,24 @@ module Nameservice.Application import Data.Proxy import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T import Tendermint.SDK.Application (HandlersContext (..), ModuleList (..), baseAppAnteHandler) import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A +import qualified Tendermint.SDK.Modules.Bank as B type EffR = N.NameserviceEffs BA.:& - T.TokenEffs BA.:& + B.BankEffs BA.:& A.AuthEffs BA.:& BA.TxEffs BA.:& BA.BaseApp BA.CoreEffs type NameserviceModules = '[ N.NameserviceM EffR - , T.TokenM EffR + , B.BankM EffR , A.AuthM EffR ] @@ -38,6 +38,6 @@ handlersContext = HandlersContext nameserviceModules :: ModuleList NameserviceModules EffR nameserviceModules = N.nameserviceModule - :+ T.tokenModule + :+ B.bankModule :+ A.authModule :+ NilModules diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs index 191aa0dd..1b37817d 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -10,6 +10,7 @@ module Nameservice.Modules.Nameservice , module Nameservice.Modules.Nameservice.Router , module Nameservice.Modules.Nameservice.Types + ) where import Data.Proxy @@ -18,20 +19,22 @@ import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types -import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) import Tendermint.SDK.Application (Module (..)) import Tendermint.SDK.BaseApp (BaseEffs, DefaultCheckTx (..), TxEffs) +import Tendermint.SDK.Modules.Auth (AuthEffs) +import Tendermint.SDK.Modules.Bank (BankEffs) type NameserviceM r = Module "nameservice" MessageApi MessageApi QueryApi NameserviceEffs r nameserviceModule :: Members BaseEffs r + => Members AuthEffs r => Members TxEffs r - => Members TokenEffs r + => Members BankEffs r => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 04f95c05..4ad789bb 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -3,10 +3,12 @@ module Nameservice.Modules.Nameservice.Keeper ( NameserviceKeeper , NameserviceEffs + , nameserviceCoinId , setName , deleteName , buyName , storeKey + , faucetAccount , eval ) where @@ -15,14 +17,16 @@ import Data.String.Conversions (cs) import GHC.TypeLits (symbolVal) import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Types -import Nameservice.Modules.Token (Token, TokenEffs, - burn, mint, transfer) import Polysemy (Members, Sem, interpret, makeSem) import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Modules.Auth (AuthEffs, Coin (..), + CoinId) +import Tendermint.SDK.Modules.Bank (BankEffs, burn, mint, + transfer) data NameserviceKeeper m a where PutWhois :: Name -> Whois -> NameserviceKeeper m () @@ -36,6 +40,9 @@ type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] storeKey :: BaseApp.StoreKey NameserviceModuleName storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @NameserviceModuleName +nameserviceCoinId :: CoinId +nameserviceCoinId = "nameservice" + eval :: Members BaseApp.TxEffs r => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a @@ -57,6 +64,22 @@ eval = mapError BaseApp.makeAppError . evalNameservice -------------------------------------------------------------------------------- +faucetAccount + :: Members [BaseApp.Logger, Output BaseApp.Event] r + => Members AuthEffs r + => FaucetAccount + -> Sem r () +faucetAccount FaucetAccount{..} = do + let coin = Coin faucetAccountCoinId faucetAccountAmount + mint faucetAccountTo coin + let event = Faucetted + { faucettedAccount = faucetAccountTo + , faucettedCoinId = faucetAccountCoinId + , faucettedAmount = faucetAccountAmount + } + BaseApp.emit event + BaseApp.logEvent event + setName :: Members [BaseApp.Logger, Output BaseApp.Event] r => Members NameserviceEffs r @@ -80,7 +103,8 @@ setName SetName{..} = do BaseApp.logEvent event deleteName - :: Members [BaseApp.Logger, Token, Output BaseApp.Event] r + :: Members [BaseApp.Logger, Output BaseApp.Event] r + => Members AuthEffs r => Members NameserviceEffs r => DeleteName -> Sem r () @@ -92,7 +116,7 @@ deleteName DeleteName{..} = do if whoisOwner /= deleteNameOwner then throw $ InvalidDelete "Deleter must be the owner." else do - mint deleteNameOwner whoisPrice + mint deleteNameOwner (Coin nameserviceCoinId whoisPrice) deleteWhois deleteNameName let event = NameDeleted { nameDeletedName = deleteNameName @@ -102,7 +126,8 @@ deleteName DeleteName{..} = do buyName :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members TokenEffs r + => Members AuthEffs r + => Members BankEffs r => Members NameserviceEffs r => BuyName -> Sem r () @@ -120,12 +145,13 @@ buyName msg = do where buyUnclaimedName :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members TokenEffs r + => Members AuthEffs r + => Members BankEffs r => Members NameserviceEffs r => BuyName -> Sem r () buyUnclaimedName BuyName{..} = do - burn buyNameBuyer buyNameBid + burn buyNameBuyer (Coin nameserviceCoinId buyNameBid) let whois = Whois { whoisOwner = buyNameBuyer , whoisValue = buyNameValue @@ -143,7 +169,8 @@ buyName msg = do buyClaimedName :: Members NameserviceEffs r - => Members TokenEffs r + => Members AuthEffs r + => Members BankEffs r => Members [BaseApp.Logger, Output BaseApp.Event] r => BuyName -> Whois @@ -152,7 +179,7 @@ buyName msg = do let Whois{ whoisPrice = forsalePrice, whoisOwner = previousOwner } = currentWhois in if buyNameBid > forsalePrice then do - transfer buyNameBuyer buyNameBid previousOwner + transfer buyNameBuyer (Coin nameserviceCoinId buyNameBid) previousOwner -- update new owner, price and value based on BuyName putWhois buyNameName currentWhois { whoisOwner = buyNameBuyer , whoisPrice = buyNameBid diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index a08d88ff..2e7a0ff1 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -1,20 +1,26 @@ module Nameservice.Modules.Nameservice.Messages ( SetName(..) , BuyName(..) + , BuyNameMessage(..) , DeleteName(..) + , FaucetAccount(..) ) where -import Data.Bifunctor (first) +import Data.Bifunctor (bimap, first) import Data.Foldable (sequenceA_) import Data.String.Conversions (cs) import Data.Text (Text) +import Data.Validation (Validation (..)) +import Data.Word (Word64) import GHC.Generics (Generic) import Nameservice.Modules.Nameservice.Types (Name (..)) -import Nameservice.Modules.Token (Amount (..)) import Proto3.Suite (Message, Named, fromByteString, toLazyByteString) import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Modules.Auth (Amount (..), + CoinId (..)) +import Tendermint.SDK.Modules.Bank () import Tendermint.SDK.Types.Address (Address (..)) import Tendermint.SDK.Types.Message (HasMessageType (..), Msg (..), @@ -26,6 +32,45 @@ import Tendermint.SDK.Types.Message (HasMessageType (..), -- @NOTE: .proto genration will use these type names as is -- only field names stripped of prefixes during generation +data FaucetAccount = FaucetAccount + { faucetAccountTo :: Address + , faucetAccountCoinId :: CoinId + , faucetAccountAmount :: Amount + } deriving (Eq, Show) + +data FaucetAccountMessage = FaucetAccountMessage + { faucetAccountMessageTo :: Address + , faucetAccountMessageCoinId :: Text + , faucetAccountMessageAmount :: Word64 + } deriving (Eq, Show, Generic) +instance Message FaucetAccountMessage +instance Named FaucetAccountMessage + +instance HasMessageType FaucetAccount where + messageType _ = "FaucetAccount" + +instance HasCodec FaucetAccount where + encode FaucetAccount {..} = + let faucetAccountMessaage = FaucetAccountMessage + { faucetAccountMessageTo = faucetAccountTo + , faucetAccountMessageCoinId = unCoinId faucetAccountCoinId + , faucetAccountMessageAmount = unAmount faucetAccountAmount + } + in cs . toLazyByteString $ faucetAccountMessaage + decode = + let toFaucetAccount FaucetAccountMessage {..} = FaucetAccount + { faucetAccountTo = faucetAccountMessageTo + , faucetAccountCoinId = CoinId faucetAccountMessageCoinId + , faucetAccountAmount = Amount faucetAccountMessageAmount + } + in bimap (formatMessageParseError . coerceProto3Error) toFaucetAccount + . fromByteString @FaucetAccountMessage + +instance ValidateMessage FaucetAccount where + validateMessage _ = Success () + +-------------------------------------------------------------------------------- + data SetName = SetName { setNameName :: Name , setNameOwner :: Address @@ -86,17 +131,38 @@ data BuyName = BuyName , buyNameName :: Name , buyNameValue :: Text , buyNameBuyer :: Address - } deriving (Eq, Show, Generic) + } deriving (Eq, Show) -instance Message BuyName -instance Named BuyName +data BuyNameMessage = BuyNameMessage + { buyNameMessageBid :: Word64 + , buyNameMessageName :: Name + , buyNameMessageValue :: Text + , buyNameMessageBuyer :: Address + } deriving (Eq, Show, Generic) +instance Message BuyNameMessage +instance Named BuyNameMessage instance HasMessageType BuyName where messageType _ = "BuyName" instance HasCodec BuyName where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + encode BuyName {..} = + let buyNameMessage = BuyNameMessage + { buyNameMessageBid = unAmount buyNameBid + , buyNameMessageName = buyNameName + , buyNameMessageValue = buyNameValue + , buyNameMessageBuyer = buyNameBuyer + } + in cs . toLazyByteString $ buyNameMessage + decode = + let toBuyName BuyNameMessage {..} = BuyName + { buyNameBid = Amount buyNameMessageBid + , buyNameName = buyNameMessageName + , buyNameValue = buyNameMessageValue + , buyNameBuyer = buyNameMessageBuyer + } + in bimap (formatMessageParseError . coerceProto3Error) toBuyName + . fromByteString @BuyNameMessage instance ValidateMessage BuyName where validateMessage msg@Msg{..} = diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 8f7632f9..472225ac 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -5,10 +5,11 @@ module Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, buyName, deleteName, + faucetAccount, setName) import Nameservice.Modules.Nameservice.Messages (BuyName, DeleteName, + FaucetAccount, SetName) -import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members, Sem) import Servant.API ((:<|>) (..)) import Tendermint.SDK.BaseApp ((:~>), BaseEffs, @@ -16,28 +17,32 @@ import Tendermint.SDK.BaseApp ((:~>), BaseEffs, RoutingTx (..), TxEffs, TypedMessage, incCount, withTimer) +import Tendermint.SDK.Modules.Auth (AuthEffs) +import Tendermint.SDK.Modules.Bank (BankEffs) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) - type MessageApi = TypedMessage BuyName :~> Return () :<|> TypedMessage SetName :~> Return () :<|> TypedMessage DeleteName :~> Return () + :<|> TypedMessage FaucetAccount :~> Return () messageHandlers :: Members BaseEffs r - => Members TokenEffs r + => Members BankEffs r + => Members AuthEffs r => Members TxEffs r => Members NameserviceEffs r => RouteTx MessageApi r -messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH +messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH :<|> faucetH buyNameH :: Members BaseEffs r => Members TxEffs r - => Members TokenEffs r + => Members AuthEffs r + => Members BankEffs r => Members NameserviceEffs r => RoutingTx BuyName -> Sem r () @@ -58,10 +63,19 @@ setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do deleteNameH :: Members BaseEffs r => Members TxEffs r - => Members TokenEffs r + => Members AuthEffs r => Members NameserviceEffs r => RoutingTx DeleteName -> Sem r () deleteNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do incCount "delete_total" withTimer "delete_duration_seconds" $ deleteName msgData + +faucetH + :: Members AuthEffs r + => Members TxEffs r + => Members BaseEffs r + => RoutingTx FaucetAccount + -> Sem r () +faucetH (RoutingTx Tx{txMsg=Msg{msgData}}) = + faucetAccount msgData diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 5c510eed..6dbc3f27 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -1,15 +1,17 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + module Nameservice.Modules.Nameservice.Types where import Control.Lens (iso) import Data.Aeson as A -import Data.Bifunctor (first) +import Data.Bifunctor (bimap) import Data.String (IsString (..)) import Data.String.Conversions (cs) import Data.Text (Text) import qualified Data.Text.Lazy as TL +import Data.Word (Word64) import GHC.Generics (Generic) import Nameservice.Aeson (defaultNameserviceOptions) -import Nameservice.Modules.Token (Amount (..)) import Proto3.Suite (HasDefault, Message, MessageField, Named, Primitive (..), fromByteString, @@ -19,6 +21,8 @@ import qualified Proto3.Wire.Decode as Decode import qualified Proto3.Wire.Encode as Encode import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Modules.Auth (Amount (..), CoinId (..)) +import Tendermint.SDK.Modules.Bank () import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- @@ -43,13 +47,31 @@ data Whois = Whois { whoisValue :: Text , whoisOwner :: Address , whoisPrice :: Amount + } deriving (Eq, Show) + +data WhoisMessage = WhoisMessage + { whoisMessageValue :: Text + , whoisMessageOwner :: Address + , whoisMessagePrice :: Word64 } deriving (Eq, Show, Generic) -instance Message Whois -instance Named Whois +instance Message WhoisMessage +instance Named WhoisMessage instance HasCodec Whois where - encode = cs . toLazyByteString - decode = first (cs . show) . fromByteString + encode Whois {..} = + let whoisMessage = WhoisMessage + { whoisMessageValue = whoisValue + , whoisMessageOwner = whoisOwner + , whoisMessagePrice = unAmount whoisPrice + } + in cs . toLazyByteString $ whoisMessage + decode = + let toWhois WhoisMessage {..} = Whois + { whoisValue = whoisMessageValue + , whoisOwner = whoisMessageOwner + , whoisPrice = Amount whoisMessagePrice + } + in bimap (cs . show) toWhois . fromByteString @WhoisMessage instance BaseApp.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) @@ -93,6 +115,23 @@ instance BaseApp.IsAppError NameserviceError where -- Events -------------------------------------------------------------------------------- +data Faucetted = Faucetted + { faucettedAccount :: Address + , faucettedCoinId :: CoinId + , faucettedAmount :: Amount + } deriving (Eq, Show, Generic) + +faucettedAesonOptions :: A.Options +faucettedAesonOptions = defaultNameserviceOptions "faucetted" + +instance ToJSON Faucetted where + toJSON = A.genericToJSON faucettedAesonOptions +instance FromJSON Faucetted where + parseJSON = A.genericParseJSON faucettedAesonOptions +instance BaseApp.ToEvent Faucetted where + makeEventType _ = "Faucetted" +instance BaseApp.Select Faucetted + data NameClaimed = NameClaimed { nameClaimedOwner :: Address , nameClaimedName :: Name diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs deleted file mode 100644 index 9b1bc699..00000000 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token.hs +++ /dev/null @@ -1,65 +0,0 @@ -module Nameservice.Modules.Token - ( - - -- * Module - TokenM - , tokenModule - -- * types - , Address - , Amount(..) - , TokenError(..) - - , Transfer(..) - , Burn(..) - , FaucetAccount(..) - - -- * effects - , Token - , TokenEffs - , Faucetted(..) - , TransferEvent(..) - , getBalance - , faucetAccount - , transfer - , mint - , burn - - -- * interpreter - , eval - - -- * Transaction - , MessageApi - , messageHandlers - - -- * Query Api - , QueryApi - , querier - - ) where - -import Data.Proxy -import Nameservice.Modules.Token.Keeper -import Nameservice.Modules.Token.Messages -import Nameservice.Modules.Token.Query -import Nameservice.Modules.Token.Router -import Nameservice.Modules.Token.Types -import Polysemy (Members) -import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseEffs, - DefaultCheckTx (..), - TxEffs) -import Tendermint.SDK.Types.Address (Address) - -type TokenM r = Module "token" MessageApi MessageApi QueryApi TokenEffs r - -tokenModule - :: Members BaseEffs r - => Members TxEffs r - => Members TokenEffs r - => TokenM r -tokenModule = Module - { moduleTxDeliverer = messageHandlers - , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) - , moduleQuerier = querier - , moduleEval = eval - } diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs deleted file mode 100644 index b8e9747d..00000000 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Keeper.hs +++ /dev/null @@ -1,113 +0,0 @@ -{-# LANGUAGE TemplateHaskell #-} - -module Nameservice.Modules.Token.Keeper where - -import Data.Maybe (fromMaybe) -import Nameservice.Modules.Token.Messages (FaucetAccount (..)) -import Nameservice.Modules.Token.Types (Amount (..), - Faucetted (..), - TokenError (..), - TransferEvent (..)) -import Polysemy -import Polysemy.Error (Error, mapError, throw) -import Polysemy.Output (Output) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.Types.Address (Address) - -data Token m a where - PutBalance :: Address -> Amount -> Token m () - GetBalance' :: Address -> Token m (Maybe Amount) - -makeSem ''Token - -type TokenEffs = '[Token, Error TokenError] - -storeKey :: BaseApp.StoreKey "token" -storeKey = BaseApp.StoreKey "token" - -eval - :: Members BaseApp.TxEffs r - => forall a. Sem (Token ': Error TokenError ': r) a -> Sem r a -eval = mapError BaseApp.makeAppError . evalToken - where - evalToken - :: Members BaseApp.TxEffs r - => forall a. Sem (Token ': r) a -> Sem r a - evalToken = - interpret - (\case - GetBalance' address -> - BaseApp.get storeKey address - PutBalance address balance -> - BaseApp.put storeKey address balance - ) - --------------------------------------------------------------------------------- - -faucetAccount - :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members TokenEffs r - => FaucetAccount - -> Sem r () -faucetAccount FaucetAccount{..} = do - mint faucetAccountTo faucetAccountAmount - let event = Faucetted - { faucettedAccount = faucetAccountTo - , faucettedAmount = faucetAccountAmount - } - BaseApp.emit event - BaseApp.logEvent event - -getBalance - :: Member Token r - => Address - -> Sem r Amount -getBalance address = - fromMaybe (Amount 0) <$> getBalance' address - -transfer - :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members TokenEffs r - => Address - -> Amount - -> Address - -> Sem r () -transfer addr1 amount addr2 = do - -- check if addr1 has amt - addr1Bal <- getBalance addr1 - if addr1Bal >= amount - then do - addr2Bal <- getBalance addr2 - let newBalance1 = addr1Bal - amount - newBalance2 = addr2Bal + amount - -- update both balances - putBalance addr1 newBalance1 - putBalance addr2 newBalance2 - let event = TransferEvent - { transferEventAmount = amount - , transferEventTo = addr2 - , transferEventFrom = addr1 - } - BaseApp.emit event - BaseApp.logEvent event - else throw (InsufficientFunds "Insufficient funds for transfer.") - -burn - :: Members TokenEffs r - => Address - -> Amount - -> Sem r () -burn addr amount = do - bal <- getBalance addr - if bal < amount - then throw $ InsufficientFunds "Insuffient funds for burn." - else putBalance addr (bal - amount) - -mint - :: Member Token r - => Address - -> Amount - -> Sem r () -mint addr amount = do - bal <- getBalance addr - putBalance addr (bal + amount) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Messages.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Messages.hs deleted file mode 100644 index 592cd38c..00000000 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Messages.hs +++ /dev/null @@ -1,79 +0,0 @@ -module Nameservice.Modules.Token.Messages - ( Transfer(..) - , Burn(..) - , FaucetAccount(..) - ) where - -import Data.Bifunctor (first) -import Data.String.Conversions (cs) -import Data.Validation (Validation (..)) -import GHC.Generics (Generic) -import Nameservice.Modules.Token.Types (Amount) -import Proto3.Suite (Message, Named, - fromByteString, - toLazyByteString) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (HasMessageType (..), - ValidateMessage (..), - coerceProto3Error, - formatMessageParseError) - -data FaucetAccount = FaucetAccount - { faucetAccountTo :: Address - , faucetAccountAmount :: Amount - } deriving (Eq, Show, Generic) - -instance Message FaucetAccount -instance Named FaucetAccount - -instance HasMessageType FaucetAccount where - messageType _ = "FaucetAccount" - -instance HasCodec FaucetAccount where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString - -instance ValidateMessage FaucetAccount where - validateMessage _ = Success () - --------------------------------------------------------------------------------- - -data Transfer = Transfer - { transferTo :: Address - , transferFrom :: Address - , transferAmount :: Amount - } deriving (Eq, Show, Generic) - -instance Message Transfer -instance Named Transfer - -instance HasMessageType Transfer where - messageType _ = "Transfer" - -instance HasCodec Transfer where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString - -instance ValidateMessage Transfer where - validateMessage _ = Success () - --------------------------------------------------------------------------------- - -data Burn = Burn - { burnAddress :: Address - , burnAmount :: Amount - } deriving (Eq, Show, Generic) - -instance Message Burn -instance Named Burn - -instance HasMessageType Burn where - messageType _ = "Burn" - -instance HasCodec Burn where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString - -instance ValidateMessage Burn where - validateMessage _ = Success () diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs deleted file mode 100644 index 1585d6b1..00000000 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Query.hs +++ /dev/null @@ -1,22 +0,0 @@ -module Nameservice.Modules.Token.Query where - -import Data.Proxy -import Nameservice.Modules.Token.Keeper (storeKey) -import Nameservice.Modules.Token.Types (Amount) -import Polysemy -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.Types.Address (Address) - --------------------------------------------------------------------------------- --- | Query Api --------------------------------------------------------------------------------- - -type TokenContents = '[(Address, Amount)] - -type QueryApi = BaseApp.QueryApi TokenContents - -querier - :: Members BaseApp.QueryEffs r - => BaseApp.RouteQ QueryApi r -querier = - BaseApp.storeQueryHandlers (Proxy :: Proxy TokenContents) storeKey (Proxy :: Proxy r) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs deleted file mode 100644 index 240bd4b6..00000000 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Router.hs +++ /dev/null @@ -1,56 +0,0 @@ -module Nameservice.Modules.Token.Router - ( MessageApi - , messageHandlers - - ) where - -import Nameservice.Modules.Token.Keeper (TokenEffs, burn, - faucetAccount, transfer) -import Nameservice.Modules.Token.Messages (Burn (..), FaucetAccount, - Transfer (..)) -import Polysemy (Members, Sem) -import Servant.API ((:<|>) (..)) -import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, - RouteTx, RoutingTx (..), - TxEffs, TypedMessage) -import Tendermint.SDK.Types.Message (Msg (..)) -import Tendermint.SDK.Types.Transaction (Tx (..)) - -type MessageApi = - TypedMessage Burn :~> Return () - :<|> TypedMessage Transfer :~> Return () - :<|> TypedMessage FaucetAccount :~> Return () - -messageHandlers - :: Members TokenEffs r - => Members BaseEffs r - => Members TxEffs r - => RouteTx MessageApi r -messageHandlers = burnH :<|> transferH :<|> faucetH - -transferH - :: Members TokenEffs r - => Members TxEffs r - => Members BaseEffs r - => RoutingTx Transfer - -> Sem r () -transferH (RoutingTx Tx{txMsg=Msg{msgData}}) = - let Transfer{..} = msgData - in transfer transferFrom transferAmount transferTo - -burnH - :: Members TokenEffs r - => RoutingTx Burn - -> Sem r () -burnH (RoutingTx Tx{txMsg=Msg{msgData}}) = - let Burn{..} = msgData - in burn burnAddress burnAmount - -faucetH - :: Members TokenEffs r - => Members TxEffs r - => Members BaseEffs r - => RoutingTx FaucetAccount - -> Sem r () -faucetH (RoutingTx Tx{txMsg=Msg{msgData}}) = - faucetAccount msgData diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Types.hs deleted file mode 100644 index 13a6d796..00000000 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Token/Types.hs +++ /dev/null @@ -1,116 +0,0 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} - -module Nameservice.Modules.Token.Types where - -import Data.Aeson as A -import Data.Bifunctor (bimap) -import qualified Data.ByteArray.HexString as Hex -import Data.String.Conversions (cs) -import Data.Text (Text) -import Data.Word (Word64) -import GHC.Generics (Generic) -import Nameservice.Aeson (defaultNameserviceOptions) -import Proto3.Suite (HasDefault (..), MessageField, - Primitive (..)) -import qualified Proto3.Suite.DotProto as DotProto -import qualified Proto3.Wire.Decode as Decode -import qualified Proto3.Wire.Encode as Encode -import Proto3.Wire.Types (fieldNumber) -import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Types.Address (Address, addressFromBytes, - addressToBytes) - --------------------------------------------------------------------------------- - -type TokenModule = "token" - --------------------------------------------------------------------------------- - -newtype Amount = Amount Word64 deriving (Eq, Show, Num, Generic, Ord, A.ToJSON, A.FromJSON) - -instance Primitive Amount where - encodePrimitive n (Amount amt) = Encode.uint64 n amt - decodePrimitive = Amount <$> Decode.uint64 - primType _ = DotProto.UInt64 -instance HasDefault Amount -instance MessageField Amount - -instance BaseApp.Queryable Amount where - type Name Amount = "balance" - --- @NOTE: hacks -instance HasCodec Amount where - encode (Amount b) = - -- proto3-wire only exports encoders for message types - let dummyMsgEncoder = Encode.uint64 (fieldNumber 1) - in cs . Encode.toLazyByteString . dummyMsgEncoder $ b - decode = bimap (cs . show) Amount . Decode.parse dummyMsgParser - where - -- field is always present; 0 is an arbitrary value - fieldParser = Decode.one Decode.uint64 0 - dummyMsgParser = Decode.at fieldParser (fieldNumber 1) - --- orphans -instance Primitive Address where - encodePrimitive n a = Encode.byteString n $ addressToBytes a - decodePrimitive = addressFromBytes <$> Decode.byteString - primType _ = DotProto.Bytes -instance HasDefault Hex.HexString -instance HasDefault Address -instance MessageField Address - -instance BaseApp.IsKey Address "token" where - type Value Address "token" = Amount - --------------------------------------------------------------------------------- --- Exceptions --------------------------------------------------------------------------------- - -data TokenError = - InsufficientFunds Text - -instance BaseApp.IsAppError TokenError where - makeAppError (InsufficientFunds msg) = - BaseApp.AppError - { appErrorCode = 1 - , appErrorCodespace = "token" - , appErrorMessage = msg - } - --------------------------------------------------------------------------------- --- Events --------------------------------------------------------------------------------- - -data Faucetted = Faucetted - { faucettedAccount :: Address - , faucettedAmount :: Amount - } deriving (Eq, Show, Generic) - -faucettedAesonOptions :: A.Options -faucettedAesonOptions = defaultNameserviceOptions "faucetted" - -instance ToJSON Faucetted where - toJSON = A.genericToJSON faucettedAesonOptions -instance FromJSON Faucetted where - parseJSON = A.genericParseJSON faucettedAesonOptions -instance BaseApp.ToEvent Faucetted where - makeEventType _ = "Faucetted" -instance BaseApp.Select Faucetted - -data TransferEvent = TransferEvent - { transferEventAmount :: Amount - , transferEventTo :: Address - , transferEventFrom :: Address - } deriving (Eq, Show, Generic) - -transferEventAesonOptions :: A.Options -transferEventAesonOptions = defaultNameserviceOptions "transferEvent" - -instance A.ToJSON TransferEvent where - toJSON = A.genericToJSON transferEventAesonOptions -instance A.FromJSON TransferEvent where - parseJSON = A.genericParseJSON transferEventAesonOptions -instance BaseApp.ToEvent TransferEvent where - makeEventType _ = "TransferEvent" -instance BaseApp.Select TransferEvent diff --git a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs index 7c2d67bc..e076bb4a 100644 --- a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -6,7 +6,6 @@ import Data.Default.Class (def) import Data.Proxy import Nameservice.Application import qualified Nameservice.Modules.Nameservice as N -import qualified Nameservice.Modules.Token as T import Nameservice.Test.EventOrphans () import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..)) @@ -16,6 +15,7 @@ import Tendermint.SDK.BaseApp.Query (QueryArgs (..), QueryResult (..), defaultQueryArgs) import qualified Tendermint.SDK.Modules.Auth as Auth +import qualified Tendermint.SDK.Modules.Bank as B import Tendermint.SDK.Types.Address (Address) import Tendermint.Utils.Client (ClientConfig (..), EmptyTxClient (..), @@ -49,7 +49,8 @@ spec = do it "Can query account balances" $ do void . assertQuery . RPC.runTendermintM rpcConfig $ - getBalance defaultQueryArgs { queryArgsData = signerAddress user1 } + let queryArgs = defaultQueryArgs { queryArgsData = signerAddress user1 } + in getBalance queryArgs N.nameserviceCoinId it "Can create a name" $ do let val = "hello world" @@ -261,9 +262,10 @@ spec = do it "Can fail a transfer" $ do addr2Balance <- getUserBalance user2 let tooMuchToTransfer = addr2Balance + 1 - msg = T.Transfer + msg = B.Transfer { transferFrom = signerAddress user2 , transferTo = signerAddress user1 + , transferCoinId = N.nameserviceCoinId , transferAmount = tooMuchToTransfer } opts = TxOpts @@ -279,13 +281,15 @@ spec = do balance2 <- getUserBalance user2 let transferAmount = 1 msg = - T.Transfer + B.Transfer { transferFrom = signerAddress user1 , transferTo = signerAddress user2 + , transferCoinId = N.nameserviceCoinId , transferAmount = transferAmount } - transferLog = T.TransferEvent + transferLog = B.TransferEvent { transferEventAmount = transferAmount + , transferEventCoinId = N.nameserviceCoinId , transferEventTo = signerAddress user2 , transferEventFrom = signerAddress user1 } @@ -296,7 +300,7 @@ spec = do resp <- assertTx . runTxClientM $ transfer opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @T.TransferEvent) resp + (errs, es) <- deliverTxEvents (Proxy @B.TransferEvent) resp errs `shouldBe` [] filter (transferLog ==) es `shouldBe` [transferLog] @@ -307,12 +311,12 @@ spec = do balance2' `shouldBe` balance2 + transferAmount faucetUser - :: T.Amount + :: Auth.Amount -> Signer -> IO () faucetUser amount s@(Signer addr _) = void . assertTx .runTxClientM $ - let msg = T.FaucetAccount addr amount + let msg = N.FaucetAccount addr N.nameserviceCoinId amount opts = TxOpts { txOptsGas = 0 , txOptsSigner = s @@ -321,9 +325,10 @@ faucetUser amount s@(Signer addr _) = getUserBalance :: Signer - -> IO T.Amount -getUserBalance usr = fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ - getBalance defaultQueryArgs { queryArgsData = signerAddress usr } + -> IO Auth.Amount +getUserBalance usr = fmap (Auth.coinAmount . queryResultData) . assertQuery . RPC.runTendermintM rpcConfig $ + let queryArgs = defaultQueryArgs { queryArgsData = signerAddress usr } + in getBalance queryArgs N.nameserviceCoinId -------------------------------------------------------------------------------- @@ -349,7 +354,8 @@ getWhois getBalance :: QueryArgs Address - -> RPC.TendermintM (QueryClientResponse T.Amount) + -> Auth.CoinId + -> RPC.TendermintM (QueryClientResponse Auth.Coin) getWhois :<|> getBalance :<|> getAccount = genClientQ (Proxy :: Proxy m) queryApiP def @@ -371,13 +377,14 @@ txClientConfig = , queryArgsProve = False , queryArgsData = addr } + -- @NOTE: TxNonce should be +1 of accountNonce case resp of QueryError e -> if appErrorCode e == 2 - then pure 0 + then pure 1 else error $ "Unknown nonce error: " <> show (appErrorMessage e) QueryResponse QueryResult {queryResultData} -> - pure $ Auth.accountNonce queryResultData + pure $ 1 + Auth.accountNonce queryResultData in ClientConfig { clientGetNonce = getNonce @@ -406,24 +413,19 @@ deleteName -> N.DeleteName -> TxClientM (TxClientResponse () ()) --- Token Client ---burn --- :: TxOpts --- -> T.Burn --- -> TxClientM (TxClientResponse () ()) - +-- Bank Client transfer :: TxOpts - -> T.Transfer + -> B.Transfer -> TxClientM (TxClientResponse () ()) faucet :: TxOpts - -> T.FaucetAccount + -> N.FaucetAccount -> TxClientM (TxClientResponse () ()) -(buyName :<|> setName :<|> deleteName) :<|> - (_ :<|> transfer :<|> faucet) :<|> +(buyName :<|> setName :<|> deleteName :<|> faucet) :<|> + (_ :<|> transfer) :<|> EmptyTxClient = genClientT (Proxy @TxClientM) txApiCP txApiDP defaultClientTxOpts where diff --git a/hs-abci-docs/nameservice/test/Nameservice/Test/EventOrphans.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/EventOrphans.hs index 85602217..a410fc6a 100644 --- a/hs-abci-docs/nameservice/test/Nameservice/Test/EventOrphans.hs +++ b/hs-abci-docs/nameservice/test/Nameservice/Test/EventOrphans.hs @@ -2,9 +2,9 @@ module Nameservice.Test.EventOrphans where -import Nameservice.Modules.Nameservice (NameClaimed, NameDeleted, - NameRemapped) -import Nameservice.Modules.Token (Faucetted, TransferEvent) +import Nameservice.Modules.Nameservice (Faucetted, NameClaimed, + NameDeleted, NameRemapped) +import Tendermint.SDK.Modules.Bank (TransferEvent) import qualified Tendermint.Utils.Events as Event -- Orphan instances for retrieving event logs for unit testing diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md index 319f5d6b..0e17886c 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md @@ -1,5 +1,5 @@ --- -title: Foundations - BaseEffs, TxEffs, and QueryEffs +title: Foundations - SDK Effects --- # BaseEffs diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md index 48da5dc3..22ddc6d8 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md @@ -17,15 +17,15 @@ although the definition of `RawStore` is different than the above. The interface we give is actually a typed key value store. This means that within the scope of a module `m`, for any key type `k`, there is only one possible value type `v` associated with `k`. -For example, a user's balance in the `Token` module, might be modeled by a mapping +For example, a user's balance in the `Bank` module, might be modeled by a mapping ~~~ haskell ignore balance :: Tendermint.SDK.Types.Address -> Integer ~~~ -(We'll properly introduce the module `Token` later in the walkthrough.) +(We'll properly introduce the module `Bank` later in the walkthrough.) -This means that in the scope of the `Token` module, the database utlity `get` function applied to a value of type `Address` will result in a value of type `Integer`. If the `Token` module would like to store another mapping whose keys have type `Tendermint.SDK.Types.Address`, you must use a newtype instead. Otherwise you will get a compiler error. +This means that in the scope of the `Bank` module, the database utlity `get` function applied to a value of type `Address` will result in a value of type `Integer`. If the `Bank` module would like to store another mapping whose keys have type `Tendermint.SDK.Types.Address`, you must use a newtype instead. Otherwise you will get a compiler error. At the same time, you are free to define another mapping from `k -> v'` in the scope of a different module. For example, you can have both the `balance` mapping described above, as well a mapping @@ -43,19 +43,20 @@ module Tutorial.Nameservice.Types where import Control.Lens (iso) import qualified Data.Aeson as A -import Data.Bifunctor (first) +import Data.Bifunctor (bimap) import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text) +import Data.Word (Word64) import GHC.Generics (Generic) import GHC.TypeLits (symbolVal) import Nameservice.Aeson (defaultNameserviceOptions) -import Nameservice.Modules.Token (Amount) import Proto3.Suite (Message, fromByteString, toLazyByteString) import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Codec (HasCodec(..)) import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (coerceProto3Error, formatMessageParseError) +import Tendermint.SDK.Modules.Auth (Amount (..)) +import Tendermint.SDK.Modules.Bank () ~~~ ### Storage types @@ -69,7 +70,7 @@ data Whois = Whois { whoisValue :: Text , whoisOwner :: Address , whoisPrice :: Amount - } deriving (Eq, Show, Generic) + } deriving (Eq, Show) ~~~ The way that we register `Name` as a key in the store is by using the `RawKey` typeclass @@ -97,15 +98,32 @@ class HasCodec a where This class is used everywhere in the SDK as the binary codec class for things like storage items, messages, transaction formats etc. It's agnostic to the actual serialization format, you can use `JSON`, `CBOR`, `Protobuf`, etc. Throughout the SDK we typically use `protobuf` as it is powerful in addition to the fact that there's decent support for this in Haskell either through the `proto3-suite` package or the `proto-lens` package. -So we can implement a `HasCodec` instance for `Whois` +So we can implement a `HasCodec` instance for `Whois` via the `WhoisMessage` type: ~~~ haskell -- Message is a class from proto3-suite that defines protobuf codecs generically. -instance Message Whois +data WhoisMessage = WhoisMessage + { whoisMessageValue :: Text + , whoisMessageOwner :: Address + , whoisMessagePrice :: Word64 + } deriving (Eq, Show, Generic) +instance Message WhoisMessage instance HasCodec Whois where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + encode Whois {..} = + let whoisMessage = WhoisMessage + { whoisMessageValue = whoisValue + , whoisMessageOwner = whoisOwner + , whoisMessagePrice = unAmount whoisPrice + } + in cs . toLazyByteString $ whoisMessage + decode = + let toWhois WhoisMessage {..} = Whois + { whoisValue = whoisMessageValue + , whoisOwner = whoisMessageOwner + , whoisPrice = Amount whoisMessagePrice + } + in bimap (cs . show) toWhois . fromByteString @WhoisMessage ~~~ Finally we can register `(Name, Whois)` with the module's store with the `IsKey` class, which tells how to associate a key type with a value type within the scope of given module, where the scope is represented by the modules name as a type level string. There is an optional prefixing function for the key in this context in order to avoid collisions in the database. This would be useful for example if you were using multiple newtyped `Address` types as keys in the same module. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md index d48a7029..c98aa5d1 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md @@ -23,24 +23,26 @@ All in all, neither is really difficult to work with, and depending on what stag ~~~ haskell module Tutorial.Nameservice.Message where -import Data.Bifunctor (first) +import Data.Bifunctor (bimap, first) import Data.Foldable (sequenceA_) import Data.String.Conversions (cs) import Data.Text (Text) +import Data.Word (Word64) import GHC.Generics (Generic) import Nameservice.Modules.Nameservice.Types (Name(..)) -import Nameservice.Modules.Token (Amount) import Proto3.Suite (Named, Message, fromByteString, toLazyByteString) import Tendermint.SDK.Types.Address (Address) import Tendermint.SDK.Types.Message (Msg(..), ValidateMessage(..), HasMessageType(..), isAuthorCheck, nonEmptyCheck, coerceProto3Error, formatMessageParseError) +import Tendermint.SDK.Modules.Auth (Amount (..)) +import Tendermint.SDK.Modules.Bank () import Tendermint.SDK.Codec (HasCodec(..)) ~~~ ### Message Definitions -For the puroposes of the tutorial, we will use the `proto3-suite` for the message codecs: +For the puroposes of the tutorial, we will use the `proto3-suite` for the message codecs. For `BuyName`, an intermediary datatype, `BuyNameMessage` is used to support encoding for `Amount`: ~~~ haskell @@ -74,14 +76,35 @@ data BuyName = BuyName , buyNameName :: Name , buyNameValue :: Text , buyNameBuyer :: Address - } deriving (Eq, Show, Generic) + } deriving (Eq, Show) -instance Message BuyName -instance Named BuyName +data BuyNameMessage = BuyNameMessage + { buyNameMessageBid :: Word64 + , buyNameMessageName :: Name + , buyNameMessageValue :: Text + , buyNameMessageBuyer :: Address + } deriving (Eq, Show, Generic) +instance Message BuyNameMessage +instance Named BuyNameMessage instance HasCodec BuyName where - encode = cs . toLazyByteString - decode = first (formatMessageParseError . coerceProto3Error) . fromByteString + encode BuyName {..} = + let buyNameMessage = BuyNameMessage + { buyNameMessageBid = unAmount buyNameBid + , buyNameMessageName = buyNameName + , buyNameMessageValue = buyNameValue + , buyNameMessageBuyer = buyNameBuyer + } + in cs . toLazyByteString $ buyNameMessage + decode = + let toBuyName BuyNameMessage {..} = BuyName + { buyNameBid = Amount buyNameMessageBid + , buyNameName = buyNameMessageName + , buyNameValue = buyNameMessageValue + , buyNameBuyer = buyNameMessageBuyer + } + in bimap (formatMessageParseError . coerceProto3Error) toBuyName + . fromByteString @BuyNameMessage ~~~ As `protobuf` is a schemaless format, parsing is sometimes ambiguous if two types are the same up to field names, or one is a subset of the other. For this reason we use the type class `HasTypedMessage` diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md index c602eb7e..cc09f37a 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md @@ -17,13 +17,14 @@ module Tutorial.Nameservice.Keeper where import Data.Proxy import Data.String.Conversions (cs) import GHC.TypeLits (symbolVal) -import Polysemy (Sem, Members, makeSem, interpret) +import Polysemy (Sem, Member, Members, makeSem, interpret) import Polysemy.Error (Error, throw, mapError) import Polysemy.Output (Output) import Nameservice.Modules.Nameservice.Messages (DeleteName(..)) import Nameservice.Modules.Nameservice.Types (Whois(..), Name, NameDeleted(..), NameserviceModuleName, NameserviceError(..)) -import Nameservice.Modules.Token (Token, mint) import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.Modules.Auth (AuthEffs, Coin(..)) +import Tendermint.SDK.Modules.Bank (BankEffs, mint) ~~~ Generally a keeper is defined by a set of effects that the module introduces and depends on. In the case of Nameservice, we introduce the custom `Nameservice` effect: @@ -52,7 +53,9 @@ We can then write the top level function for example for deleting a name: ~~~ haskell deleteName - :: Members [Token, Output BA.Event] r + :: Member (Output BA.Event) r + => Members AuthEffs r + => Members BankEffs r => Members [NameserviceKeeper, Error NameserviceError] r => DeleteName -> Sem r () @@ -64,7 +67,7 @@ deleteName DeleteName{..} = do if whoisOwner /= deleteNameOwner then throw $ InvalidDelete "Deleter must be the owner." else do - mint deleteNameOwner whoisPrice + mint deleteNameOwner (Coin "nameservice" whoisPrice) deleteWhois deleteNameName BA.emit NameDeleted { nameDeletedName = deleteNameName @@ -81,12 +84,12 @@ The control flow should be pretty clear: Taking a look at the class constraints, we see ~~~ haskell ignore -(Members NameserviceEffs, Members [Token, Output Event] r) +(Members NameserviceEffs, Member (Output Event) r) ~~~ - The `NameserviceKeeper` effect is required because the function may manipulate the modules database with `deleteName`. - The `Error NameserviceError` effect is required because the function may throw an error. -- The `Token` effect is required because the function will mint coins. +- The `Auth` effect is required because the function will mint coins. - The `Output Event` effect is required because the function may emit a `NameDeleted` event. ### Evaluating Module Effects diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md index e65a1e17..f925b83e 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md @@ -15,11 +15,12 @@ import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, eval) import Nameservice.Modules.Nameservice.Query (QueryApi, querier) import Nameservice.Modules.Nameservice.Router (MessageApi, messageHandlers) import Nameservice.Modules.Nameservice.Types (NameserviceModuleName) -import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members) -import Data.Proxy import Tendermint.SDK.Application (Module (..)) import Tendermint.SDK.BaseApp (BaseEffs, TxEffs, DefaultCheckTx (..)) +import Tendermint.SDK.Modules.Bank (BankEffs) +import Data.Proxy +import Tendermint.SDK.Modules.Auth (AuthEffs) -- a convenient type alias type NameserviceM r = @@ -27,8 +28,9 @@ type NameserviceM r = nameserviceModule :: Members BaseEffs r + => Members BankEffs r => Members TxEffs r - => Members TokenEffs r + => Members AuthEffs r => Members NameserviceEffs r => NameserviceM r nameserviceModule = Module @@ -65,12 +67,12 @@ Note the constraints on `r` in the Module's type: ~~~ haskell ignore ... - :: Members BaseAppEffs r - => Members TokenEffs r + :: Members BaseEffs r + => Members BankEffs r => Members NameserviceEffs r ... ~~~ -This is saying that we can run this module in any context for which `r` has the effects from `BaseApp`, `Token`, and `Nameservice`. This is how we explicitly declare module dependencies, by using the constraint system. +This is saying that we can run this module in any context for which `r` has the effects from `BaseApp`, `Bank`, and `Nameservice`. This is how we explicitly declare module dependencies, by using the constraint system. Other than that, there is nothing really to note. We are just collecting the pieces we have already defined in one place. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md index d1c9d1f9..a2143911 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md @@ -55,13 +55,13 @@ module Tutorial.Nameservice.Application where import Data.Proxy import Nameservice.Modules.Nameservice (nameserviceModule, NameserviceM, NameserviceEffs) -import Nameservice.Modules.Token (tokenModule, TokenM, TokenEffs) import Network.ABCI.Server.App (App) import Polysemy (Sem) import Tendermint.SDK.Modules.Auth (authModule, AuthEffs, AuthM) import Tendermint.SDK.Application (ModuleList(..), HandlersContext(..), baseAppAnteHandler, makeApp, createIOApp) import Tendermint.SDK.BaseApp (BaseApp, CoreEffs, Context, TxEffs, (:&), defaultCompileToCore, runCoreEffs) import Tendermint.SDK.Crypto (Secp256k1) +import Tendermint.SDK.Modules.Bank (bankModule, BankM, BankEffs) ~~~ This is the part of the application where the effects list must be given a monomorphic type. The only requirement is that you list the effects in the same order that the corresponding modules appear in the `NameserviceModules` list: @@ -70,14 +70,14 @@ This is the part of the application where the effects list must be given a monom ~~~ haskell type EffR = NameserviceEffs :& - TokenEffs :& + BankEffs :& AuthEffs :& TxEffs :& BaseApp CoreEffs type NameserviceModules = '[ NameserviceM EffR - , TokenM EffR + , BankM EffR , AuthM EffR ] ~~~ @@ -98,7 +98,7 @@ handlersContext = HandlersContext nameserviceModules :: ModuleList NameserviceModules EffR nameserviceModules = nameserviceModule - :+ tokenModule + :+ bankModule :+ authModule :+ NilModules ~~~ diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md index dda61f7b..4bf85f40 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md @@ -13,10 +13,10 @@ import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, setName) import Nameservice.Modules.Nameservice.Messages (BuyName, DeleteName, SetName) -import Nameservice.Modules.Token (TokenEffs) import Polysemy (Members, Sem) +import Tendermint.SDK.Modules.Bank (BankEffs) import Servant.API ((:<|>) (..)) -import Tendermint.SDK.BaseApp ((:~>), BaseAppEffs, +import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, RouteContext (..), RouteTx, @@ -76,16 +76,16 @@ Rather than cover all possible cases, we just note that in the case of the Names ~~~ haskell messageHandlers - :: Members BaseAppEffs r - => Members TokenEffs r + :: Members BaseEffs r + => Members BankEffs r => Members NameserviceEffs r => RouteTx MessageApi r 'DeliverTx messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH buyNameH - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TxEffs r - => Members TokenEffs r + => Members BankEffs r => Members NameserviceEffs r => RoutingTx BuyName -> Sem r () @@ -94,7 +94,7 @@ buyNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do withTimer "buy_duration_seconds" $ buyName msgData setNameH - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TxEffs r => Members NameserviceEffs r => RoutingTx SetName @@ -104,9 +104,9 @@ setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do withTimer "set_duration_seconds" $ setName msgData deleteNameH - :: Members BaseAppEffs r + :: Members BaseEffs r => Members TxEffs r - => Members TokenEffs r + => Members BankEffs r => Members NameserviceEffs r => RoutingTx DeleteName -> Sem r () diff --git a/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index 2b66d28b..a292941e 100644 --- a/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -87,13 +87,14 @@ txClientConfig = let getNonce addr = do resp <- RPC.runTendermintM rpcConfig $ getAccount $ defaultQueryArgs { queryArgsData = addr } + -- @NOTE: TxNonce should be +1 of accountNonce case resp of QueryError e -> if appErrorCode e == 2 - then pure 0 + then pure 1 else error $ "Unknown nonce error: " <> show (appErrorMessage e) QueryResponse QueryResult{queryResultData} -> - pure $ Auth.accountNonce queryResultData + pure $ 1 + Auth.accountNonce queryResultData in ClientConfig { clientGetNonce = getNonce diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index cd92bea3..d7ea8f21 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -74,6 +74,7 @@ library: - prometheus - proto-lens - proto-lens-runtime + - proto3-suite - proto3-wire - secp256k1-haskell - servant @@ -128,6 +129,12 @@ library: - Tendermint.SDK.Codec - Tendermint.SDK.Crypto - Tendermint.SDK.Modules.Auth + - Tendermint.SDK.Modules.Bank + - Tendermint.SDK.Modules.Bank.Messages + - Tendermint.SDK.Modules.Bank.Types + - Tendermint.SDK.Modules.Bank.Keeper + - Tendermint.SDK.Modules.Bank.Query + - Tendermint.SDK.Modules.Bank.Router - Tendermint.SDK.Types.Address - Tendermint.SDK.Types.Effects - Tendermint.SDK.Types.Message @@ -137,6 +144,8 @@ library: generated-exposed-modules: - Proto.Modules.Auth - Proto.Modules.Auth_Fields + - Proto.Modules.Bank + - Proto.Modules.Bank_Fields - Proto.Types.Transaction - Proto.Types.Transaction_Fields diff --git a/hs-abci-sdk/protos/modules/auth.proto b/hs-abci-sdk/protos/modules/auth.proto index be8369f9..929af1b0 100644 --- a/hs-abci-sdk/protos/modules/auth.proto +++ b/hs-abci-sdk/protos/modules/auth.proto @@ -1,12 +1,20 @@ syntax = "proto3"; package Auth; +message CoinId { + string id = 1; +} + +message Amount { + uint64 amount = 1; +} + message Coin { - string denomination = 1; - uint64 amount = 2; + CoinId id = 1; + Amount amount = 2; } message Account { - repeated Coin coins = 1; - uint64 nonce = 2; + repeated Coin coins = 1; + uint64 nonce = 2; } \ No newline at end of file diff --git a/hs-abci-sdk/protos/modules/bank.proto b/hs-abci-sdk/protos/modules/bank.proto new file mode 100644 index 00000000..fc6c1d7e --- /dev/null +++ b/hs-abci-sdk/protos/modules/bank.proto @@ -0,0 +1,15 @@ +syntax = "proto3"; +package Bank; + +message Transfer { + bytes to = 1; + bytes from = 2; + string cid = 3; + uint64 amount = 4; +} + +message Burn { + bytes address = 1; + string cid = 2; + uint64 amount = 3; +} \ No newline at end of file diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs index 8b73474f..c21b99d2 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs @@ -1,47 +1,65 @@ module Tendermint.SDK.Application.AnteHandler ( module Tendermint.SDK.Application.AnteHandler -- Re-Exports - , AnteHandler(..) + , AnteHandler ) where -import Control.Monad (unless) +import Control.Monad (unless, void) +import Data.Foldable (fold) +import Data.Monoid (Endo (..)) import Polysemy import Polysemy.Error (Error) import Tendermint.SDK.BaseApp.Errors (AppError, SDKError (..), throwSDKError) -import Tendermint.SDK.BaseApp.Transaction (AnteHandler (..), +import Tendermint.SDK.BaseApp.Transaction (AnteHandler, RoutingTx (..)) import qualified Tendermint.SDK.Modules.Auth as A import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) +createAccountAnteHandler + :: Members A.AuthEffs r + => AnteHandler r +createAccountAnteHandler = Endo $ + \txApplication tx@(RoutingTx Tx{..}) -> do + let Msg{msgAuthor} = txMsg + mAcnt <- A.getAccount msgAuthor + case mAcnt of + Nothing -> void $ A.createAccount msgAuthor + _ -> pure () + txApplication tx >>= pure + nonceAnteHandler :: Members A.AuthEffs r => Member (Error AppError) r => AnteHandler r -nonceAnteHandler = AnteHandler $ +nonceAnteHandler = Endo $ \txApplication tx@(RoutingTx Tx{..}) -> do let Msg{msgAuthor} = txMsg - mAcnt <- A.getAccount msgAuthor - account <- case mAcnt of - Just a@A.Account{accountNonce} -> do - unless (accountNonce <= txNonce) $ - throwSDKError (NonceException accountNonce txNonce) - pure a - Nothing -> do - unless (txNonce == 0) $ - throwSDKError (NonceException 0 txNonce) - A.createAccount msgAuthor + preMAcnt <- A.getAccount msgAuthor + case preMAcnt of + Just A.Account{accountNonce} -> do + let expectedNonce = accountNonce + 1 + unless (txNonce == expectedNonce) $ + throwSDKError (NonceException expectedNonce txNonce) + Nothing -> throwSDKError (UnknownAccountError msgAuthor) result <- txApplication tx - A.putAccount msgAuthor $ - account { A.accountNonce = A.accountNonce account + 1} + postMAcnt <- A.getAccount msgAuthor + case postMAcnt of + Just acnt@A.Account{accountNonce} -> do + A.putAccount msgAuthor $ + acnt { A.accountNonce = accountNonce + 1} + -- @NOTE: no-op when no nonce is availble to update + Nothing -> pure () pure result baseAppAnteHandler :: Members A.AuthEffs r => Member (Error AppError) r => AnteHandler r -baseAppAnteHandler = mconcat $ - [ nonceAnteHandler +baseAppAnteHandler = fold $ + -- @NOTE: antehandlers in this list are applied top to bottom + [ createAccountAnteHandler + , nonceAnteHandler ] diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 2414fb81..015ab9ea 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -70,7 +70,7 @@ module Tendermint.SDK.BaseApp , HistogramName(..) -- * Transaction - , AnteHandler(..) + , AnteHandler , RoutingTx(..) , RouteTx , RouteContext(..) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index e214ba96..9394e35f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -15,6 +15,7 @@ import Data.Word (Word32, Word64) import qualified Network.ABCI.Types.Messages.Response as Response import Polysemy import Polysemy.Error (Error, throw) +import Tendermint.SDK.Types.Address (Address) import Tendermint.SDK.Types.TxResult (TxResult (..)) -- | This type represents a common error response for the query, checkTx, @@ -83,6 +84,7 @@ data SDKError = | NonceException Word64 Word64 | RawStoreInvalidOperation Text | GrpcError Text + | UnknownAccountError Address deriving (Show) -- | As of right now it's not expected that one can recover from an 'SDKError', @@ -148,3 +150,9 @@ instance IsAppError SDKError where , appErrorCodespace = "sdk" , appErrorMessage = "Grpc error: \n" <> msg } + + makeAppError (UnknownAccountError addr) = AppError + { appErrorCode = 10 + , appErrorCodespace = "sdk" + , appErrorMessage = "Unknown account at " <> (cs . show $ addr) + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs index 7a130662..5637dec9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction.hs @@ -8,7 +8,7 @@ module Tendermint.SDK.BaseApp.Transaction , VoidReturn , TxEffs , evalReadOnly - , AnteHandler(..) + , AnteHandler ) where import Control.Lens ((&), (.~)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs index ab17df93..b8ff6b08 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/AnteHandler.hs @@ -1,16 +1,9 @@ module Tendermint.SDK.BaseApp.Transaction.AnteHandler - ( AnteHandler(..) + ( AnteHandler ) where +import Data.Monoid (Endo) import Polysemy (Sem) import Tendermint.SDK.BaseApp.Transaction.Types (RoutingTx) -newtype AnteHandler r = AnteHandler - (forall msg a. (RoutingTx msg -> Sem r a) -> (RoutingTx msg -> Sem r a)) - -instance Semigroup (AnteHandler r) where - (<>) (AnteHandler h1) (AnteHandler h2) = - AnteHandler $ h1 . h2 - -instance Monoid (AnteHandler r) where - mempty = AnteHandler id +type AnteHandler r = forall msg a. (Endo (RoutingTx msg -> Sem r a)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs index 41c7305f..fafa25e8 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Router.hs @@ -6,6 +6,7 @@ module Tendermint.SDK.BaseApp.Transaction.Router import Control.Monad.IO.Class (liftIO) import Data.ByteString (ByteString) +import Data.Monoid import Data.Proxy import Data.String.Conversions (cs) import GHC.TypeLits (KnownSymbol, @@ -18,7 +19,7 @@ import Servant.API import qualified Tendermint.SDK.BaseApp.Router as R import Tendermint.SDK.BaseApp.Store (ReadStore, Scope) -import Tendermint.SDK.BaseApp.Transaction.AnteHandler (AnteHandler (..)) +import Tendermint.SDK.BaseApp.Transaction.AnteHandler (AnteHandler) import Tendermint.SDK.BaseApp.Transaction.Cache (Cache) import Tendermint.SDK.BaseApp.Transaction.Effect (TxEffs, runTx) import Tendermint.SDK.BaseApp.Transaction.Types @@ -112,7 +113,7 @@ instance ( HasMessageType msg, HasCodec msg in methodRouter ps $ R.addBody subserver $ R.withRequest f where mt = messageType (Proxy :: Proxy msg) - applyAnteHandler _ _ _ (AnteHandler ah) f = ah f + applyAnteHandler _ _ _ ah f = appEndo ah f hoistTxRouter _ _ _ nat = (.) nat diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index a4dd8aa9..b1211d30 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -2,17 +2,8 @@ module Tendermint.SDK.Modules.Auth ( AuthM , authModule - , AuthEffs - , Account(..) - , Accounts(..) - , getAccount - , putAccount - , createAccount - , eval - - , Api - , querier - + , module Tendermint.SDK.Modules.Auth.Keeper + , module Tendermint.SDK.Modules.Auth.Query , module Tendermint.SDK.Modules.Auth.Types ) where @@ -21,7 +12,7 @@ import Tendermint.SDK.Application.Module (Module (..)) import Tendermint.SDK.BaseApp (BaseEffs, EmptyTxServer (..), ReadStore) -import Tendermint.SDK.Modules.Auth.Keeper +import Tendermint.SDK.Modules.Auth.Keeper hiding (storeKey) import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs index f90bccec..e7185a46 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -21,22 +21,6 @@ type AuthEffs = '[Accounts, Error AuthError] storeKey :: StoreKey AuthModule storeKey = StoreKey "auth" -createAccount - :: Members [Accounts, Error AuthError] r - => Address - -> Sem r Account -createAccount addr = do - mAcct <- getAccount addr - case mAcct of - Just _ -> throw $ AccountAlreadExists addr - Nothing -> do - let emptyAccount = Account - { accountCoins = [] - , accountNonce = 0 - } - putAccount addr emptyAccount - pure emptyAccount - eval :: Members [ReadStore, WriteStore, Error AppError] r => Sem (Accounts : Error AuthError : r) a @@ -53,3 +37,21 @@ eval = mapError makeAppError . evalAuth PutAccount addr acnt -> put storeKey addr acnt ) + +-------------------------------------------------------------------------------- + +createAccount + :: Members [Accounts, Error AuthError] r + => Address + -> Sem r Account +createAccount addr = do + mAcct <- getAccount addr + case mAcct of + Just _ -> throw $ AccountAlreadyExists addr + Nothing -> do + let emptyAccount = Account + { accountCoins = [] + , accountNonce = 0 + } + putAccount addr emptyAccount + pure emptyAccount diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs index df8b1d38..07150e29 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -5,11 +5,13 @@ module Tendermint.SDK.Modules.Auth.Types where import Control.Lens (Wrapped (..), from, iso, view, (&), (.~), (^.), (^..), _Unwrapped') +import Data.Aeson as JSON import Data.Bifunctor (bimap) import qualified Data.ProtoLens as P import Data.Proxy (Proxy (..)) +import Data.String (IsString (..)) import Data.String.Conversions (cs) -import Data.Text (Text) +import Data.Text (Text, pack) import Data.Word import GHC.Generics (Generic) import GHC.TypeLits (symbolVal) @@ -17,15 +19,95 @@ import qualified Proto.Modules.Auth as A import qualified Proto.Modules.Auth_Fields as A import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..), IsKey (..), Queryable (..)) -import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Codec (HasCodec (..), + defaultSDKAesonOptions) import Tendermint.SDK.Types.Address (Address) +import Web.HttpApiData (FromHttpApiData (..), + ToHttpApiData (..)) + +-------------------------------------------------------------------------------- type AuthModule = "auth" +instance IsKey Address AuthModule where + type Value Address AuthModule = Account + +instance Queryable Account where + type Name Account = "account" + +-------------------------------------------------------------------------------- +-- Exceptions +-------------------------------------------------------------------------------- + +data AuthError = + AccountAlreadyExists Address + +instance IsAppError AuthError where + makeAppError (AccountAlreadyExists addr) = + AppError + { appErrorCode = 1 + , appErrorCodespace = cs (symbolVal $ Proxy @AuthModule) + , appErrorMessage = "Account Already Exists " <> (cs . show $ addr) <> "." + } + +-------------------------------------------------------------------------------- + +newtype CoinId = CoinId { unCoinId :: Text } deriving (Eq, Show, Generic) + +instance Wrapped CoinId where + type Unwrapped CoinId = A.CoinId + + _Wrapped' = iso t f + where + t CoinId {..} = + P.defMessage + & A.id .~ unCoinId + f message = CoinId + { unCoinId = message ^. A.id + } + +instance HasCodec CoinId where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + +instance IsString CoinId where + fromString = CoinId . pack +instance JSON.ToJSON CoinId where + toJSON = JSON.genericToJSON JSON.defaultOptions +instance JSON.FromJSON CoinId where + parseJSON = JSON.genericParseJSON JSON.defaultOptions +instance ToHttpApiData CoinId where + toQueryParam = unCoinId +instance FromHttpApiData CoinId where + parseQueryParam = fmap CoinId . parseQueryParam + +-------------------------------------------------------------------------------- + +newtype Amount = Amount { unAmount :: Word64 } + deriving (Eq, Show, Num, Generic, Ord, JSON.ToJSON, JSON.FromJSON) + +instance Wrapped Amount where + type Unwrapped Amount = A.Amount + + _Wrapped' = iso t f + where + t Amount {..} = + P.defMessage + & A.amount .~ unAmount + f message = Amount + { unAmount = message ^. A.amount + } + +instance HasCodec Amount where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + +-------------------------------------------------------------------------------- + data Coin = Coin - { coinDenomination :: Text - , coinAmount :: Word64 - } deriving Generic + { coinId :: CoinId + , coinAmount :: Amount + } deriving (Eq, Show, Generic) instance Wrapped Coin where type Unwrapped Coin = A.Coin @@ -34,17 +116,29 @@ instance Wrapped Coin where where t Coin {..} = P.defMessage - & A.denomination .~ coinDenomination - & A.amount .~ coinAmount + & A.id .~ coinId ^. _Wrapped' + & A.amount .~ coinAmount ^. _Wrapped' f message = Coin - { coinDenomination = message ^. A.denomination - , coinAmount = message ^. A.amount + { coinId = message ^. A.id . _Unwrapped' + , coinAmount = message ^. A.amount . _Unwrapped' } +instance HasCodec Coin where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + +coinAesonOptions :: JSON.Options +coinAesonOptions = defaultSDKAesonOptions "coin" + +instance Queryable Coin where + type Name Coin = "balance" + +-------------------------------------------------------------------------------- + data Account = Account { accountCoins :: [Coin] , accountNonce :: Word64 - } deriving Generic + } deriving (Show, Generic) instance Wrapped Account where type Unwrapped Account = A.Account @@ -63,20 +157,3 @@ instance Wrapped Account where instance HasCodec Account where encode = P.encodeMessage . view _Wrapped' decode = bimap cs (view $ from _Wrapped') . P.decodeMessage - -instance IsKey Address AuthModule where - type Value Address AuthModule = Account - -instance Queryable Account where - type Name Account = "account" - -data AuthError = - AccountAlreadExists Address - -instance IsAppError AuthError where - makeAppError (AccountAlreadExists addr) = - AppError - { appErrorCode = 1 - , appErrorCodespace = cs (symbolVal $ Proxy @AuthModule) - , appErrorMessage = "Account Already Exists " <> (cs . show $ addr) <> "." - } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs new file mode 100644 index 00000000..41a3796d --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs @@ -0,0 +1,41 @@ +module Tendermint.SDK.Modules.Bank + ( + -- * Module + BankM + , bankModule + + , module Tendermint.SDK.Modules.Bank.Keeper + , module Tendermint.SDK.Modules.Bank.Messages + , module Tendermint.SDK.Modules.Bank.Query + , module Tendermint.SDK.Modules.Bank.Router + , module Tendermint.SDK.Modules.Bank.Types + + ) where + +import Data.Proxy +import Polysemy (Members) +import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.BaseApp (BaseEffs, + DefaultCheckTx (..), + TxEffs) +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Modules.Bank.Keeper +import Tendermint.SDK.Modules.Bank.Messages +import Tendermint.SDK.Modules.Bank.Query +import Tendermint.SDK.Modules.Bank.Router +import Tendermint.SDK.Modules.Bank.Types + +type BankM r = Module "bank" MessageApi MessageApi QueryApi BankEffs r + +bankModule + :: Members BaseEffs r + => Members Auth.AuthEffs r + => Members BankEffs r + => Members TxEffs r + => BankM r +bankModule = Module + { moduleTxDeliverer = messageHandlers + , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) + , moduleQuerier = querier + , moduleEval = eval + } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs new file mode 100644 index 00000000..71306cf3 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs @@ -0,0 +1,106 @@ +module Tendermint.SDK.Modules.Bank.Keeper where + +import Data.List (find) +import Data.Maybe (fromMaybe) +import Polysemy +import Polysemy.Error (Error, mapError, throw) +import Polysemy.Output (Output) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Modules.Bank.Types (BankError (..), + TransferEvent (..)) +import Tendermint.SDK.Types.Address (Address) + +type BankEffs = '[Error BankError] + +eval + :: Member (Error BaseApp.AppError) r + => forall a. Sem (Error BankError ': r) a -> Sem r a +eval = mapError BaseApp.makeAppError + +-------------------------------------------------------------------------------- + +getCoinBalance + :: Members Auth.AuthEffs r + => Address + -> Auth.CoinId + -> Sem r Auth.Coin +getCoinBalance address cid = do + mAcnt <- Auth.getAccount address + let zeroBalance = Auth.Coin cid 0 + case mAcnt of + Nothing -> pure zeroBalance + Just (Auth.Account coins _) -> + let mCoin = find (\(Auth.Coin cid1 _) -> cid == cid1) coins + in pure $ fromMaybe zeroBalance mCoin + +replaceCoinValue :: Auth.Coin -> [Auth.Coin] -> [Auth.Coin] +replaceCoinValue c [] = [c] +replaceCoinValue c@(Auth.Coin cid _) (c1@(Auth.Coin cid' _):rest) = + if cid' == cid + then c : rest + else c1 : replaceCoinValue c rest + +putCoinBalance + :: Members Auth.AuthEffs r + => Address + -> Auth.Coin + -> Sem r () +putCoinBalance address coin = do + mAcnt <- Auth.getAccount address + acnt <- case mAcnt of + Nothing -> Auth.createAccount address >>= pure + Just a -> pure a + let updatedCoins = replaceCoinValue coin (Auth.accountCoins acnt) + updatedAcnt = acnt { Auth.accountCoins = updatedCoins } + Auth.putAccount address updatedAcnt + +transfer + :: Members [BaseApp.Logger, Output BaseApp.Event] r + => Members Auth.AuthEffs r + => Members BankEffs r + => Address + -> Auth.Coin + -> Address + -> Sem r () +transfer addr1 (Auth.Coin cid amount) addr2 = do + -- check if addr1 has amt + (Auth.Coin _ addr1Bal) <- getCoinBalance addr1 cid + if addr1Bal >= amount + then do + (Auth.Coin _ addr2Bal) <- getCoinBalance addr2 cid + let newCoinBalance1 = Auth.Coin cid (addr1Bal - amount) + newCoinBalance2 = Auth.Coin cid (addr2Bal + amount) + -- update both balances + putCoinBalance addr1 newCoinBalance1 + putCoinBalance addr2 newCoinBalance2 + let event = TransferEvent + { transferEventAmount = amount + , transferEventCoinId = cid + , transferEventTo = addr2 + , transferEventFrom = addr1 + } + BaseApp.emit event + BaseApp.logEvent event + else throw (InsufficientFunds "Insufficient funds for transfer.") + +burn + :: Members Auth.AuthEffs r + => Members BankEffs r + => Address + -> Auth.Coin + -> Sem r () +burn addr (Auth.Coin cid amount) = do + (Auth.Coin _ bal) <- getCoinBalance addr cid + if bal < amount + then throw $ InsufficientFunds "Insufficient funds for burn." + else putCoinBalance addr (Auth.Coin cid (bal - amount)) + +mint + :: Members Auth.AuthEffs r + => Address + -> Auth.Coin + -> Sem r () +mint addr (Auth.Coin cid amount) = do + (Auth.Coin _ bal) <- getCoinBalance addr cid + putCoinBalance addr (Auth.Coin cid (bal + amount)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs new file mode 100644 index 00000000..aa768849 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs @@ -0,0 +1,86 @@ +module Tendermint.SDK.Modules.Bank.Messages where + +import Control.Lens (Wrapped (..), from, iso, view, + (&), (.~), (^.)) +import Data.Bifunctor (bimap) +import qualified Data.ProtoLens as P +import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) +import GHC.Generics (Generic) +import qualified Proto.Modules.Bank as B +import qualified Proto.Modules.Bank_Fields as B +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Modules.Auth (Amount (..), CoinId (..)) +import Tendermint.SDK.Types.Address (Address, addressFromBytes, + addressToBytes) +import Tendermint.SDK.Types.Message (HasMessageType (..), + ValidateMessage (..)) + +data Transfer = Transfer + { transferTo :: Address + , transferFrom :: Address + , transferCoinId :: CoinId + , transferAmount :: Amount + } deriving (Eq, Show, Generic) + +instance Wrapped Transfer where + type Unwrapped Transfer = B.Transfer + + _Wrapped' = iso t f + where + t Transfer {..} = + P.defMessage + & B.to .~ addressToBytes transferTo + & B.from .~ addressToBytes transferFrom + & B.cid .~ unCoinId transferCoinId + & B.amount .~ unAmount transferAmount + f message = Transfer + { transferTo = addressFromBytes $ message ^. B.to + , transferFrom = addressFromBytes $ message ^. B.from + , transferCoinId = CoinId $ message ^. B.cid + , transferAmount = Amount $ message ^. B.amount + } + +instance HasMessageType Transfer where + messageType _ = "Transfer" + +instance HasCodec Transfer where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + +instance ValidateMessage Transfer where + validateMessage _ = Success () + +-------------------------------------------------------------------------------- + +data Burn = Burn + { burnAddress :: Address + , burnCoinId :: CoinId + , burnAmount :: Amount + } deriving (Eq, Show, Generic) + +instance Wrapped Burn where + type Unwrapped Burn = B.Burn + + _Wrapped' = iso t f + where + t Burn {..} = + P.defMessage + & B.address .~ addressToBytes burnAddress + & B.cid .~ unCoinId burnCoinId + & B.amount .~ unAmount burnAmount + f message = Burn + { burnAddress = addressFromBytes $ message ^. B.address + , burnCoinId = CoinId $ message ^. B.cid + , burnAmount = Amount $ message ^. B.amount + } + +instance HasMessageType Burn where + messageType _ = "Burn" + +instance HasCodec Burn where + encode = P.encodeMessage . view _Wrapped' + decode = bimap cs (view $ from _Wrapped') . P.decodeMessage + +instance ValidateMessage Burn where + validateMessage _ = Success () diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs new file mode 100644 index 00000000..8771a1b2 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs @@ -0,0 +1,44 @@ +module Tendermint.SDK.Modules.Bank.Query where + +import Control.Lens ((^.)) +import qualified Data.ByteArray.Base64String as Base64 +import Polysemy +import Servant.API +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.BaseApp.Query (QueryArgs (..)) +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Modules.Bank.Keeper (getCoinBalance) +import Tendermint.SDK.Types.Address (Address) + +-------------------------------------------------------------------------------- +-- | Query Api +-------------------------------------------------------------------------------- + +type GetAddressCoinBalance = + "balance" + :> BaseApp.QA Address + :> QueryParam' '[Required, Strict] "coin_id" Auth.CoinId + :> BaseApp.Leaf Auth.Coin + +getAddressCoinBalance + :: Members Auth.AuthEffs r + => QueryArgs Address + -> Auth.CoinId + -> Sem r (BaseApp.QueryResult Auth.Coin) +getAddressCoinBalance (QueryArgs _ address _) cid = do + coin <- getCoinBalance address cid + pure $ BaseApp.QueryResult + { queryResultData = coin + , queryResultIndex = 0 + , queryResultKey = Base64.fromBytes $ address ^. BaseApp.rawKey + , queryResultProof = Nothing + , queryResultHeight = 0 + } + +type QueryApi = GetAddressCoinBalance + +querier + :: forall r. + Members Auth.AuthEffs r + => BaseApp.RouteQ QueryApi r +querier = getAddressCoinBalance diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs new file mode 100644 index 00000000..439ad508 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs @@ -0,0 +1,49 @@ +module Tendermint.SDK.Modules.Bank.Router + ( MessageApi + , messageHandlers + ) where + +import Polysemy (Members, Sem) +import Servant.API ((:<|>) (..)) +import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, + RouteTx, RoutingTx (..), + TxEffs, TypedMessage) +import Tendermint.SDK.Modules.Auth (AuthEffs, Coin (..)) +import Tendermint.SDK.Modules.Bank.Keeper (BankEffs, burn, transfer) +import Tendermint.SDK.Modules.Bank.Messages (Burn (..), Transfer (..)) +import Tendermint.SDK.Types.Message (Msg (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) + +type MessageApi = + TypedMessage Burn :~> Return () + :<|> TypedMessage Transfer :~> Return () + +messageHandlers + :: Members AuthEffs r + => Members BankEffs r + => Members TxEffs r + => Members BaseEffs r + => RouteTx MessageApi r +messageHandlers = burnH :<|> transferH + +transferH + :: Members AuthEffs r + => Members BankEffs r + => Members TxEffs r + => Members BaseEffs r + => RoutingTx Transfer + -> Sem r () +transferH (RoutingTx Tx{txMsg=Msg{msgData}}) = + let Transfer{..} = msgData + coin = Coin transferCoinId transferAmount + in transfer transferFrom coin transferTo + +burnH + :: Members AuthEffs r + => Members BankEffs r + => RoutingTx Burn + -> Sem r () +burnH (RoutingTx Tx{txMsg=Msg{msgData}}) = + let Burn{..} = msgData + coin = Coin burnCoinId burnAmount + in burn burnAddress coin diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs new file mode 100644 index 00000000..e72c4d9d --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs @@ -0,0 +1,50 @@ +module Tendermint.SDK.Modules.Bank.Types where + +import Data.Aeson as A +import Data.Text (Text) +import GHC.Generics (Generic) +import qualified Tendermint.SDK.BaseApp as BaseApp +import Tendermint.SDK.Codec (defaultSDKAesonOptions) +import qualified Tendermint.SDK.Modules.Auth as Auth +import Tendermint.SDK.Types.Address (Address (..)) + +-------------------------------------------------------------------------------- + +type BankModule = "bank" + +-------------------------------------------------------------------------------- +-- Exceptions +-------------------------------------------------------------------------------- + +data BankError = + InsufficientFunds Text + +instance BaseApp.IsAppError BankError where + makeAppError (InsufficientFunds msg) = + BaseApp.AppError + { appErrorCode = 1 + , appErrorCodespace = "bank" + , appErrorMessage = msg + } + +-------------------------------------------------------------------------------- +-- Events +-------------------------------------------------------------------------------- + +data TransferEvent = TransferEvent + { transferEventCoinId :: Auth.CoinId + , transferEventAmount :: Auth.Amount + , transferEventTo :: Address + , transferEventFrom :: Address + } deriving (Eq, Show, Generic) + +transferEventAesonOptions :: A.Options +transferEventAesonOptions = defaultSDKAesonOptions "transferEvent" + +instance A.ToJSON TransferEvent where + toJSON = A.genericToJSON transferEventAesonOptions +instance A.FromJSON TransferEvent where + parseJSON = A.genericParseJSON transferEventAesonOptions +instance BaseApp.ToEvent TransferEvent where + makeEventType _ = "TransferEvent" +instance BaseApp.Select TransferEvent diff --git a/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs b/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs index b1823e0c..712ae92b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Types/Address.hs @@ -1,16 +1,43 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} + module Tendermint.SDK.Types.Address where import qualified Crypto.Secp256k1 as Crypto import qualified Data.Aeson as A import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString) +import Data.String (fromString) +import Data.Text (unpack) import GHC.Generics (Generic) +import Proto3.Suite (HasDefault (..), MessageField, + Primitive (..)) +import qualified Proto3.Suite.DotProto as DotProto +import qualified Proto3.Wire.Decode as Decode +import qualified Proto3.Wire.Encode as Encode +import Tendermint.SDK.Codec (HasCodec (..)) +import Web.HttpApiData (FromHttpApiData (..), + ToHttpApiData (..)) -- | Used as a unique identifier for an account. newtype Address = Address Hex.HexString deriving (Eq, Show, Generic, Ord, A.ToJSON, A.FromJSON) +instance Primitive Address where + encodePrimitive n a = Encode.byteString n $ addressToBytes a + decodePrimitive = addressFromBytes <$> Decode.byteString + primType _ = DotProto.Bytes +instance HasDefault Hex.HexString +instance HasDefault Address +instance MessageField Address +instance HasCodec Address where + decode = Right . addressFromBytes + encode = addressToBytes +instance ToHttpApiData Address where + toQueryParam (Address aHex) = Hex.format aHex +instance FromHttpApiData Address where + parseQueryParam = Right . Address . fromString . unpack + addressToBytes :: Address -> ByteString addressToBytes (Address addrHex) = Hex.toBytes addrHex From 2c0a92393306b6a3e5ed71bf368c3c5b6ae148a1 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 17 Feb 2020 16:24:06 -0800 Subject: [PATCH 61/70] allow modules to depend explicitly on other modules. (#212) * using deps type variable in module type to list dependencies * Added AppEffs * better syntax by eliminating type variable in HandlersContext * tutorial updated * udpdate docs symlinks * updated tutorial sym link files * typos * add frontmatter for router --- hs-abci-docs/doc/0320-BaseApp.md | 1 - hs-abci-docs/doc/0320-Effects.md | 1 + hs-abci-docs/doc/0460-Module.md | 1 - hs-abci-docs/doc/0460-Router.md | 1 + hs-abci-docs/doc/0470-Application.md | 1 - hs-abci-docs/doc/0470-Module.md | 1 + hs-abci-docs/doc/0480-Application.md | 1 + hs-abci-docs/doc/0490-Testing.md | 1 + hs-abci-docs/nameservice/package.yaml | 6 + .../src/Nameservice/Application.hs | 18 +- .../src/Nameservice/Modules/Nameservice.hs | 25 ++- .../Nameservice/Modules/Nameservice/Keeper.hs | 10 +- .../Nameservice/Modules/Nameservice/Router.hs | 9 +- .../tutorial/Foundations/01-Overview.md | 7 +- .../{02-BaseApp.md => 02-Effects.md} | 41 +++-- .../tutorial/Foundations/03-Modules.md | 27 ++- hs-abci-docs/nameservice/tutorial/README.md | 2 + .../Nameservice/{Router.md => 06-Router.md} | 3 + .../{06-Module.md => 07-Module.md} | 39 +++-- .../{07-Application.md => 08-Application.md} | 37 ++-- .../Tutorial/Nameservice/09-Testing.md | 158 ++++++++++++++++++ .../Tutorial/Nameservice/Application.lhs | 2 +- .../tutorial/Tutorial/Nameservice/Module.lhs | 2 +- .../tutorial/Tutorial/Nameservice/Router.lhs | 1 + .../tutorial/Tutorial/Nameservice/Testing.lhs | 1 + .../src/SimpleStorage/Application.hs | 18 +- .../SimpleStorage/Modules/SimpleStorage.hs | 25 ++- .../Modules/SimpleStorage/Keeper.hs | 22 ++- .../Modules/SimpleStorage/Router.hs | 6 +- .../Modules/SimpleStorage/Types.hs | 2 + hs-abci-sdk/src/Tendermint/SDK/Application.hs | 5 + .../Tendermint/SDK/Application/Handlers.hs | 75 +++++---- .../src/Tendermint/SDK/Application/Module.hs | 100 ++++++----- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 2 +- .../src/Tendermint/SDK/BaseApp/Effects.hs | 10 +- .../src/Tendermint/SDK/Modules/Auth.hs | 18 +- .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 2 +- .../src/Tendermint/SDK/Modules/Auth/Types.hs | 8 +- .../src/Tendermint/SDK/Modules/Bank.hs | 18 +- .../src/Tendermint/SDK/Modules/Bank/Keeper.hs | 117 +++++++------ .../src/Tendermint/SDK/Modules/Bank/Query.hs | 8 +- .../src/Tendermint/SDK/Modules/Bank/Router.hs | 11 +- .../src/Tendermint/SDK/Modules/Bank/Types.hs | 2 +- .../test/Tendermint/SDK/Test/QuerySpec.hs | 11 +- .../test/Tendermint/SDK/Test/SimpleStorage.hs | 40 +++-- 45 files changed, 551 insertions(+), 345 deletions(-) delete mode 120000 hs-abci-docs/doc/0320-BaseApp.md create mode 120000 hs-abci-docs/doc/0320-Effects.md delete mode 120000 hs-abci-docs/doc/0460-Module.md create mode 120000 hs-abci-docs/doc/0460-Router.md delete mode 120000 hs-abci-docs/doc/0470-Application.md create mode 120000 hs-abci-docs/doc/0470-Module.md create mode 120000 hs-abci-docs/doc/0480-Application.md create mode 120000 hs-abci-docs/doc/0490-Testing.md rename hs-abci-docs/nameservice/tutorial/Foundations/{02-BaseApp.md => 02-Effects.md} (68%) rename hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/{Router.md => 06-Router.md} (99%) rename hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/{06-Module.md => 07-Module.md} (69%) rename hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/{07-Application.md => 08-Application.md} (70%) create mode 100644 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.lhs create mode 120000 hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Testing.lhs diff --git a/hs-abci-docs/doc/0320-BaseApp.md b/hs-abci-docs/doc/0320-BaseApp.md deleted file mode 120000 index a496f4c5..00000000 --- a/hs-abci-docs/doc/0320-BaseApp.md +++ /dev/null @@ -1 +0,0 @@ -../nameservice/tutorial/Foundations/02-BaseApp.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0320-Effects.md b/hs-abci-docs/doc/0320-Effects.md new file mode 120000 index 00000000..28168f98 --- /dev/null +++ b/hs-abci-docs/doc/0320-Effects.md @@ -0,0 +1 @@ +../nameservice/tutorial/Foundations/02-Effects.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0460-Module.md b/hs-abci-docs/doc/0460-Module.md deleted file mode 120000 index 6168482b..00000000 --- a/hs-abci-docs/doc/0460-Module.md +++ /dev/null @@ -1 +0,0 @@ -../nameservice/tutorial/Tutorial/Nameservice/06-Module.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0460-Router.md b/hs-abci-docs/doc/0460-Router.md new file mode 120000 index 00000000..a5269423 --- /dev/null +++ b/hs-abci-docs/doc/0460-Router.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/06-Router.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0470-Application.md b/hs-abci-docs/doc/0470-Application.md deleted file mode 120000 index 33990a65..00000000 --- a/hs-abci-docs/doc/0470-Application.md +++ /dev/null @@ -1 +0,0 @@ -../nameservice/tutorial/Tutorial/Nameservice/07-Application.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0470-Module.md b/hs-abci-docs/doc/0470-Module.md new file mode 120000 index 00000000..8c145b0f --- /dev/null +++ b/hs-abci-docs/doc/0470-Module.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/07-Module.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0480-Application.md b/hs-abci-docs/doc/0480-Application.md new file mode 120000 index 00000000..1598c70c --- /dev/null +++ b/hs-abci-docs/doc/0480-Application.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/08-Application.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0490-Testing.md b/hs-abci-docs/doc/0490-Testing.md new file mode 120000 index 00000000..ecdd844f --- /dev/null +++ b/hs-abci-docs/doc/0490-Testing.md @@ -0,0 +1 @@ +../nameservice/tutorial/Tutorial/Nameservice/09-Testing.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml index acccc429..fb10f96a 100644 --- a/hs-abci-docs/nameservice/package.yaml +++ b/hs-abci-docs/nameservice/package.yaml @@ -149,18 +149,24 @@ tests: - Tutorial.Nameservice.Module - Tutorial.Nameservice.Query - Tutorial.Nameservice.Types + - Tutorial.Nameservice.Testing ghc-options: -Wall -pgmL markdown-unlit dependencies: - aeson - base + - data-default-class - hs-abci-sdk - hs-abci-server + - hs-abci-test-utils + - hs-tendermint-client - lens - markdown-unlit + - mtl - nameservice - polysemy - polysemy-plugin - proto3-suite + - servant - string-conversions - text nameservice-test: diff --git a/hs-abci-docs/nameservice/src/Nameservice/Application.hs b/hs-abci-docs/nameservice/src/Nameservice/Application.hs index 14ddc7c3..36568480 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Application.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Application.hs @@ -1,6 +1,5 @@ module Nameservice.Application - ( EffR - , NameserviceModules + ( NameserviceModules , handlersContext ) where @@ -14,20 +13,14 @@ import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A import qualified Tendermint.SDK.Modules.Bank as B -type EffR = - N.NameserviceEffs BA.:& - B.BankEffs BA.:& - A.AuthEffs BA.:& - BA.TxEffs BA.:& - BA.BaseApp BA.CoreEffs type NameserviceModules = - '[ N.NameserviceM EffR - , B.BankM EffR - , A.AuthM EffR + '[ N.Nameservice + , B.Bank + , A.Auth ] -handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR BA.CoreEffs +handlersContext :: HandlersContext Secp256k1 NameserviceModules BA.CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules @@ -35,7 +28,6 @@ handlersContext = HandlersContext , anteHandler = baseAppAnteHandler } where - nameserviceModules :: ModuleList NameserviceModules EffR nameserviceModules = N.nameserviceModule :+ B.bankModule diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs index 1b37817d..056cf5b7 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -2,7 +2,7 @@ module Nameservice.Modules.Nameservice ( -- * Module - NameserviceM + Nameservice , nameserviceModule , module Nameservice.Modules.Nameservice.Keeper , module Nameservice.Modules.Nameservice.Messages @@ -20,23 +20,18 @@ import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router import Nameservice.Modules.Nameservice.Types import Polysemy (Members) -import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseEffs, - DefaultCheckTx (..), - TxEffs) -import Tendermint.SDK.Modules.Auth (AuthEffs) -import Tendermint.SDK.Modules.Bank (BankEffs) +import Tendermint.SDK.Application (Module (..), + ModuleEffs) +import Tendermint.SDK.BaseApp (DefaultCheckTx (..)) +import Tendermint.SDK.Modules.Bank (Bank) -type NameserviceM r = - Module "nameservice" MessageApi MessageApi QueryApi NameserviceEffs r + +type Nameservice = + Module "nameservice" MessageApi MessageApi QueryApi NameserviceEffs '[Bank] nameserviceModule - :: Members BaseEffs r - => Members AuthEffs r - => Members TxEffs r - => Members BankEffs r - => Members NameserviceEffs r - => NameserviceM r + :: Members (ModuleEffs Nameservice) r + => Nameservice r nameserviceModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 4ad789bb..ae09afe4 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -23,8 +23,7 @@ import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.Modules.Auth (AuthEffs, Coin (..), - CoinId) +import Tendermint.SDK.Modules.Auth (Coin (..), CoinId) import Tendermint.SDK.Modules.Bank (BankEffs, burn, mint, transfer) @@ -66,7 +65,7 @@ eval = mapError BaseApp.makeAppError . evalNameservice faucetAccount :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members AuthEffs r + => Members BankEffs r => FaucetAccount -> Sem r () faucetAccount FaucetAccount{..} = do @@ -104,7 +103,7 @@ setName SetName{..} = do deleteName :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members AuthEffs r + => Members BankEffs r => Members NameserviceEffs r => DeleteName -> Sem r () @@ -126,7 +125,6 @@ deleteName DeleteName{..} = do buyName :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members AuthEffs r => Members BankEffs r => Members NameserviceEffs r => BuyName @@ -145,7 +143,6 @@ buyName msg = do where buyUnclaimedName :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members AuthEffs r => Members BankEffs r => Members NameserviceEffs r => BuyName @@ -169,7 +166,6 @@ buyName msg = do buyClaimedName :: Members NameserviceEffs r - => Members AuthEffs r => Members BankEffs r => Members [BaseApp.Logger, Output BaseApp.Event] r => BuyName diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 472225ac..3cbbc1b7 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -17,7 +17,6 @@ import Tendermint.SDK.BaseApp ((:~>), BaseEffs, RoutingTx (..), TxEffs, TypedMessage, incCount, withTimer) -import Tendermint.SDK.Modules.Auth (AuthEffs) import Tendermint.SDK.Modules.Bank (BankEffs) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) @@ -32,7 +31,6 @@ type MessageApi = messageHandlers :: Members BaseEffs r => Members BankEffs r - => Members AuthEffs r => Members TxEffs r => Members NameserviceEffs r => RouteTx MessageApi r @@ -41,7 +39,6 @@ messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH :<|> faucetH buyNameH :: Members BaseEffs r => Members TxEffs r - => Members AuthEffs r => Members BankEffs r => Members NameserviceEffs r => RoutingTx BuyName @@ -63,7 +60,7 @@ setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do deleteNameH :: Members BaseEffs r => Members TxEffs r - => Members AuthEffs r + => Members BankEffs r => Members NameserviceEffs r => RoutingTx DeleteName -> Sem r () @@ -72,8 +69,8 @@ deleteNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do withTimer "delete_duration_seconds" $ deleteName msgData faucetH - :: Members AuthEffs r - => Members TxEffs r + :: Members TxEffs r + => Members BankEffs r => Members BaseEffs r => RoutingTx FaucetAccount -> Sem r () diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md b/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md index 1955824f..a009653c 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md @@ -10,15 +10,16 @@ The SDK relies heavily on two abstractions to facilitate application development The effects system is backed by a library called `polysemy` which we mentioned in the introduction. An application basically has three layers of effects -1. **Application level effects**: These are introduced by the application developer in order to customize application behavior. +1. **Application level effects**: These are introduced by the application developer in order to customize application behavior. Examples include things like `AuthEffs` from `Tendermint.SDK.Modules.Auth` that allow for manipulating accounts and throwing custom `Auth` errors. 2. **Transaction effects**: These are the effects that allow you to interpret transactions, emit events, meter gas, and handle storage requests. 3. **Base effects**: These include things like logging, metrics, exception handling, and some error handling. -4. **Core effects**: These are largely internal and used to interpet the other effects to `IO`. There are a two different core options available in the SDK (distinguished by a an in-memory versus production database), but the more advanced developer might wish to write their own. +4. **Store effects**: These are the effects that describe the possible interactions with an abstract merkelized key-value database. +5. **Core effects**: These are largely internal and used to interpet the other effects to `IO`. There are a two different core options available in the SDK (distinguished by a an in-memory versus production database), but the more advanced developer might wish to write their own. The tutorial explains the multiple points at which you can hook your application specific effects and types into the SDK. ## Modules -The core building block of an application is a `Module`. There are some modules that ship with the SDK and make up a kind of standard library. These modules are of general utility, like dealing with things like authentication or tokens, and are considered to be safe. +The core building block of an application is a `Module`. There are some modules that ship with the SDK and make up a kind of standard library. These modules are of general utility, like dealing with things like authentication or coins, and are considered to be safe. The most useful part of the SDK is that you are free to define your own modules, or depend on other third party modules outside the SDK. Since they all have the same type, they all easily compose into larger applications as standalone components or dependencies. diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md b/hs-abci-docs/nameservice/tutorial/Foundations/02-Effects.md similarity index 68% rename from hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md rename to hs-abci-docs/nameservice/tutorial/Foundations/02-Effects.md index 0e17886c..c7cc47ef 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/02-BaseApp.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/02-Effects.md @@ -1,8 +1,13 @@ --- -title: Foundations - SDK Effects +title: Foundations - Effects --- -# BaseEffs +# Effects Lists + +There are several distinguished effects lists you should be familiar with that come up at different parts of the sdk or application development. They are often combined or stacked using the `:&` operator, which is simply concatenation of type level lists. + + +## BaseEffs `BaseEffs` is a set of effects that the SDK operates with and are freely available for an application developer to make use of in any part of their application code. They @@ -26,7 +31,7 @@ These effects are: The SDK does not make any assumptions about how `BaseEffs` will be interpreted at runtime, it only assumes that the developer might want use one of the provided core effects systems to interpret them. For example, the standard `CoreEffs` uses a prometheus metrics server to interpret the `Metrics` effect while `PureCoreEffs` just ignores the effect entirely. -# TxEffs +## TxEffs `TxEffs` are the effects used to interpret transactions and are defined as @@ -50,7 +55,7 @@ where `TxEffs` effects are available any time during transactions and are interpreted at the time of transaction routing. It's worth noting that the interpreters take care of finalizing writes to the database when it's appropriate (i.e. during a `deliverTx` message) and not otherwise (e.g. during a `checkTx` message). -# QueryEffs +## QueryEffs `QueryEffs` are used to interpret queries and are defined as @@ -68,15 +73,9 @@ where `QueryEffs` are available any time you are writing handlers for a module's query api. The SDK manages a separate connection for reading from committed (i.e. via blocks) state when `QueryEffs` are present. -# BaseApp - -There is a type alias in the SDK called `BaseApp` with the definition - -~~~ haskell ignore -type BaseApp core = BaseEffs :& StoreEffs :& core -~~~ +## StoreEffs -where `StoreEffs` is given by +`StoreEffs` describe the possible interactions with an abstract merkelized key-value database: ~~~ haskell ignore type StoreEffs = @@ -88,5 +87,23 @@ type StoreEffs = ] ~~~ + They are used to interpret `TxEffects` depending on what context you're in, e.g. while executing a `delierTx` versus `checkTx` message, or a `query`. Some effects are tagged with a promoted value of type `Scope`, i.e. `'Consensus` and `'QueryAndMempool`. This is because your application will keep multiple connections to the database that are used in different situations. For example, since writing to the state at a previous blocktimes is impossible, we disallow writing to the database in such instances. + + +# Effects Type Synonyms + +There are a few effects lists that appear so frequently at different points in the SDK that they deserve synonyms: + +## Effs + +`Effs` is technically a type family coming from the class `Tendermint.SDK.Application.Module.Eval`. It is used to enumerate all the effects that would be needed in order to run an application defined by a `ModuleList`. + +## BaseAppEffs + +There is a type alias in the SDK called `BaseAppEffs` with the definition + +~~~ haskell ignore +type BaseApp core = BaseEffs :& StoreEffs :& core +~~~ It sits at the bottom of any applications effects list and ultimately everything is interpreted through these effects before being run, e.g. `TxEffs` and `QueryEffs`. This effects list is pretty much the only place where the application developer needs to decide how the interpretation should be done. There are two options in the SDK, using `PureCoreEffs` or `CoreEffs` depending on whether you want to rely on an external or in-memory database. \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md index e89b4be4..a64065ab 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md @@ -2,28 +2,43 @@ title: Foundations - Module --- -# Modules +# Modules and Components + +First a note. There is a small technical distinction betweek a `Module` and a `Component`, but we often use these words interchangable. A `Component` is simply a type synonym for a partially applied `Module`, which leaves the `r` parameter free. It's basically the type level description of a `Module` that hasn't yet been put in the context of a larger application, which is where the `r` comes in. ## Definition A `Module` has a very specific meaning in the context of this SDK. A `Module` is something between a library and a small state machine. Here is the type: ~~~ haskell ignore -data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: EffectRow) (r :: EffectRow) = Module +data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: EffectRow) (deps :: [Component]) (r :: EffectRow) = Module { moduleTxChecker :: T.RouteTx check r , moduleTxDeliverer :: T.RouteTx deliver r , moduleQuerier :: Q.RouteQ query r - , moduleEval :: forall deps. Members T.TxEffs deps => forall a. Sem (es :& deps) a -> Sem deps a + , moduleEval :: forall s. (Members T.TxEffs s, Members (DependencyEffs deps) s) => forall a. Sem (es :& s) a -> Sem s a } + +~~~ + +where `DependencyEffs` is a type level function that gathers effect dependencies from a list of `Component`s that the module depends on: + +~~~ haskell ignore + +type family DependencyEffs (ms :: [Component]) :: EffectRow where + DependencyEffs '[] = '[] + DependencyEffs (Module _ _ _ _ es deps ': rest) = es :& DependencyEffs rest + DependencyEffs _ = TypeError ('Text "DependencyEffs is a partial function defined only on partially applied Modules") + ~~~ -where the type parameters +Let's take a look at the type parameters - `name` is the name of the module, e.g. `"bank"`. - `check` is the transaction router api type for `checkTx` messages. - `deliver` is the transaction router api type for `checkTx` messages. - `query` is the query router api type for `query` messages - `es` is the set of effects introduced by this module. +- `deps` is the list of `Components` (i.e. Modules) that this module depends on, in the sense that the `eval` function for this module will interpret into those effects. (For example, the `BankEffs` for the `Bank` module are interpreted into `AuthEffs`) - `r` is the global set of effects that this module will run in when part of a larger application (more on this later). Below that line we see the fields for the `Module` data type, where @@ -42,11 +57,11 @@ Note that in the event that a `Module` is _abstract_, meaning it doesn't have an `Module`s are meant to be composed to create larger applications. We will see examples of this with the `Nameservice` application. The way to do this is easy, as the `ModuleList` data type allows you to simply combine them in a heterogeneous list: ~~~ haskell ignore -data ModuleList (ms :: [*]) r where +data ModuleList (ms :: [Component]) r where NilModules :: Modules '[] r (:+) :: Module name check deliver query es r -> Modules ms r - -> Modules (Module name check deliver query es r ': ms) r + -> Modules (Module name check deliver query es ': ms) r ~~~ When you are ready to create your application, you simply specify a value of type `ModuleList` and some other configuration data, and the SDK will create an `App` for you. diff --git a/hs-abci-docs/nameservice/tutorial/README.md b/hs-abci-docs/nameservice/tutorial/README.md index dcf218b7..db450596 100644 --- a/hs-abci-docs/nameservice/tutorial/README.md +++ b/hs-abci-docs/nameservice/tutorial/README.md @@ -29,6 +29,8 @@ The application consists of three modules: ## How to Read this Tutorial +First a warning. The `hs-abci-sdk` package is a sophisticated *framework* for building blockchain applications backed by tendermint consensus. As it is a framework, there are certain points when syntax is simplified at the expense of introducing indirection, type synonyms, and a few type families. There was a serious amount of effort to expose as little of this as possible, but alas sometimes things will be confusing and it's best to blindly follow the examples. + This tutorial is largely written as a literate haskell file to simulate developing the Nameservice app from scratch. The file structure is similar to the actual app. We will partially develop a haskell module corresponding to what you find in the app, but possibly not the whole thing. Thus whenever we depend on a haskell module in the tutorial, rather than importing from the tutorial itself we will import from the app. The benefit of this is that we don't have to develop the entire application in this tutorial. Any breaking changes in the app will (hopefully) break the tutorial and so if you can read this, the tutorial is correct. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Router.md similarity index 99% rename from hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Router.md index 4bf85f40..4e886bb0 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Router.md @@ -1,3 +1,6 @@ +--- +title: Nameservice - Router +--- # Router diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md similarity index 69% rename from hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md index f925b83e..49cd3211 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/06-Module.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md @@ -15,24 +15,19 @@ import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, eval) import Nameservice.Modules.Nameservice.Query (QueryApi, querier) import Nameservice.Modules.Nameservice.Router (MessageApi, messageHandlers) import Nameservice.Modules.Nameservice.Types (NameserviceModuleName) -import Polysemy (Members) -import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseEffs, TxEffs, DefaultCheckTx (..)) -import Tendermint.SDK.Modules.Bank (BankEffs) +import Tendermint.SDK.Application (Module (..), ModuleEffs) +import Tendermint.SDK.BaseApp (DefaultCheckTx (..)) +import Tendermint.SDK.Modules.Bank (Bank) import Data.Proxy -import Tendermint.SDK.Modules.Auth (AuthEffs) +import Polysemy (Members) -- a convenient type alias -type NameserviceM r = - Module NameserviceModuleName MessageApi MessageApi QueryApi NameserviceEffs r +type Nameservice = + Module NameserviceModuleName MessageApi MessageApi QueryApi NameserviceEffs '[Bank] nameserviceModule - :: Members BaseEffs r - => Members BankEffs r - => Members TxEffs r - => Members AuthEffs r - => Members NameserviceEffs r - => NameserviceM r + :: Members (ModuleEffs Nameservice) r + => Nameservice r nameserviceModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) @@ -63,16 +58,24 @@ Note that this checker can be used to implement any transaction for which To generate a server for which every transaction has these properties, we used the `defaultCheckTx` type class method on the `MessageApi` type. This will generate a server of type `VoidReturn MessageApi`, which has the exact same shape as `MessageApi` just will all the return values changed to `Return ()`. In this paricular case all handlers for `MessageApi` already return `()`, so we have `MessageApi ~ VoidReturn MessageApi` and there's no need to use the `VoidReturn` family in the module type. -Note the constraints on `r` in the Module's type: +Note the constraint on `r` in the Module's type using the constraint-valued type family `ModuleEffs`. In this case it evaluates to the following equivalent set of constraints: ~~~ haskell ignore ... - :: Members BaseEffs r - => Members BankEffs r - => Members NameserviceEffs r + ModuleEffs Nameservice r + ~ ( Members NameserviceEffs r + , Members (DependencyEffs '[Bank] r) + , Members TxEffs r + , Members BaseEffs r + ) + ~ ( Members NameserviceEffs r + , Members BankEffs r + , Members TxEffs r + , Members BaseEffs r + ) ... ~~~ -This is saying that we can run this module in any context for which `r` has the effects from `BaseApp`, `Bank`, and `Nameservice`. This is how we explicitly declare module dependencies, by using the constraint system. +This is saying that we can run this module in any context for which `r` has the effects from `NameserviceEffs`, `BankEffs`, `TxEffs`, and `BaseEffs`. This is how we explicitly declare global effect dependencies for a module, by using the constraint system. Other than that, there is nothing really to note. We are just collecting the pieces we have already defined in one place. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/08-Application.md similarity index 70% rename from hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md rename to hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/08-Application.md index a2143911..d810d13b 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Application.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/08-Application.md @@ -16,11 +16,11 @@ newtype App m = App and ultimately our configuration of modules must be converted to this format. This is probably the most important part of the SDK, to provide the bridge between the list of modules - a heterogeneous list of type `ModuleList` - and the actual application. The type that provides the input for this bridge is `HandlersContext`: ~~~ haskell ignore -data HandlersContext alg ms r core = HandlersContext +data HandlersContext alg ms core = HandlersContext { signatureAlgP :: Proxy alg - , modules :: M.ModuleList ms r - , anteHandler :: BA.AnteHandler r - , compileToCore :: forall a. Sem (BA.BaseApp core) a -> Sem core a + , modules :: M.ModuleList ms (Effs ms core) + , anteHandler :: BA.AnteHandler (Effs ms core) + , compileToCore :: forall a. Sem (BA.BaseAppEffs core) a -> Sem core a } ~~~ @@ -29,6 +29,7 @@ where - `ms` is the type level list of modules - `r` is the global effects list for the application - `core` is the set of core effects that are used to interpet `BaseApp` to `IO`. +- `Effs` is a type family that gathers the effect dependencies for `ms` in the appropriate order. We should say a few words on this `compileToCore` field. The application developer has to, at the end of the day, specify how the entire effects system for the application will be interpreted to `IO`. Luckily most of these decisions are abstracted away, but the one that remains is dealing with `BaseApp core`. The sdk provides two default methods for two different types of `core`: @@ -54,40 +55,31 @@ The difference is that `defaultCompileToCore` uses the IAVL store external datab module Tutorial.Nameservice.Application where import Data.Proxy -import Nameservice.Modules.Nameservice (nameserviceModule, NameserviceM, NameserviceEffs) +import Nameservice.Modules.Nameservice (Nameservice, nameserviceModule) import Network.ABCI.Server.App (App) import Polysemy (Sem) -import Tendermint.SDK.Modules.Auth (authModule, AuthEffs, AuthM) +import Tendermint.SDK.Modules.Auth (Auth, authModule) import Tendermint.SDK.Application (ModuleList(..), HandlersContext(..), baseAppAnteHandler, makeApp, createIOApp) -import Tendermint.SDK.BaseApp (BaseApp, CoreEffs, Context, TxEffs, (:&), defaultCompileToCore, runCoreEffs) +import Tendermint.SDK.BaseApp (CoreEffs, Context, defaultCompileToCore, runCoreEffs) import Tendermint.SDK.Crypto (Secp256k1) -import Tendermint.SDK.Modules.Bank (bankModule, BankM, BankEffs) +import Tendermint.SDK.Modules.Bank (Bank, bankModule) ~~~ -This is the part of the application where the effects list must be given a monomorphic type. The only requirement is that you list the effects in the same order that the corresponding modules appear in the `NameserviceModules` list: +At this point we need to simply list the modules we want to use in our application. We only require that if a module is declared as a dependency by another (via the `deps` type variable in the `Module` type), then that dependency should be inserted below that module. For example, since `Nameservice` depends on `Bank`, we must list `Bank` after `Nameservice`. Similarly since `Bank` depends on `Auth`, we must list `Auth` after `Bank`: ~~~ haskell -type EffR = - NameserviceEffs :& - BankEffs :& - AuthEffs :& - TxEffs :& - BaseApp CoreEffs - type NameserviceModules = - '[ NameserviceM EffR - , BankM EffR - , AuthM EffR + '[ Nameservice + , Bank + , Auth ] ~~~ -Also notice that `TxEffs :& BaseApp CoreEffs` appears at the end of the effects list, but doesn't strictly corrospond to a module. This needs to be here as ultimately all transactions and queries are intereted to `TxEffs :& BaseApp CoreEffs` before being run. - We're now ready to define the `HandlersContext` for our application: ~~~ haskell -handlersContext :: HandlersContext Secp256k1 NameserviceModules EffR CoreEffs +handlersContext :: HandlersContext Secp256k1 NameserviceModules CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = nameserviceModules @@ -95,7 +87,6 @@ handlersContext = HandlersContext , anteHandler = baseAppAnteHandler } where - nameserviceModules :: ModuleList NameserviceModules EffR nameserviceModules = nameserviceModule :+ bankModule diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md new file mode 100644 index 00000000..cfcdeb42 --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md @@ -0,0 +1,158 @@ +--- +title: Nameservice - Testing +--- + +# Testing and Client Generation + +It's time to see the real benefits of including as much information as possible in the types, which goes beyond a simple guarantee that certain things won't fail at runtime. Since the `api`s for querying state and delivering transactions was specified in the type of each module, hence in the type of the application via the `ModulesList`, we are able to generate client libraries for these actions for free. This is especially useful in testing to eliminate as much boilerplate as possible, and to get compile time failures whenever an api change would break your tests. + + +Let's take a look at how this works in the `E2E` test suite: + + +~~~ haskell + +module Tutorial.Nameservice.Testing where + +import Control.Monad.Reader (ReaderT, runReaderT) +import Data.Default.Class (def) +import Data.Proxy +import Nameservice.Application +import qualified Nameservice.Modules.Nameservice as N +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..)) +import qualified Tendermint.SDK.Application.Module as M +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + QueryResult (..)) +import qualified Tendermint.SDK.Modules.Auth as Auth +import qualified Tendermint.SDK.Modules.Bank as B +import Tendermint.SDK.Types.Address (Address) +import Tendermint.Utils.Client (ClientConfig (..), + EmptyTxClient (..), + QueryClientResponse (..), + TxClientResponse (..), + TxOpts (..), HasTxClient(..), + HasQueryClient(..), + defaultClientTxOpts) +import Tendermint.Utils.ClientUtils (rpcConfig) +~~~ + +First let's look at how to generate a client for querying state. If you've ever used servant client, this should look familiar since the design was heavily influenced (i.e. shamelessly stolen) from there: + + +~~~ haskell + +getAccount + :: QueryArgs Address + -> RPC.TendermintM (QueryClientResponse Auth.Account) + +getWhois + :: QueryArgs N.Name + -> RPC.TendermintM (QueryClientResponse N.Whois) + +getBalance + :: QueryArgs Address + -> Auth.CoinId + -> RPC.TendermintM (QueryClientResponse Auth.Coin) + +getWhois :<|> getBalance :<|> getAccount = + genClientQ (Proxy :: Proxy m) queryApiP def + where + queryApiP :: Proxy (M.ApplicationQ NameserviceModules) + queryApiP = Proxy +~~~ + +We can then use these generated functions by simply providing an `RPCConfig` object as defined in the Tendermint client library: + +~~~ haskell +getWhois' :: QueryArgs N.Name -> IO (QueryClientResponse N.Whois) +getWhois' = RPC.runTendermintM rpcConfig . getWhois +~~~ + +Similarly we can generate a client for sending transactions as well. This is slightly tricker because of the `nonce` problem, exlpained in the following chain of reasoning: + +1. In order to submit a valid transaction, we need to provide the correct nonce value for the transaction author, which is an ever increasing sequence of natural numbers. +2. In order to get the current nonce value for a transaction author, we need to query the accounts module for their current nonce value. +3. Therefore in order to generate a client for submitting transactions, we should make use of our query client for the auth module, using the returned nonce value to template the transaction. + +Therefore the `ClientConfig` object for the transaction client includes the method for querying nonces: + + +~~~ haskell +type TxClientM = ReaderT ClientConfig IO + +runTxClientM :: TxClientM a -> IO a +runTxClientM m = runReaderT m txClientConfig + +txClientConfig :: ClientConfig +txClientConfig = + let getNonce addr = do + resp <- RPC.runTendermintM rpcConfig $ getAccount $ + QueryArgs + { queryArgsHeight = -1 + , queryArgsProve = False + , queryArgsData = addr + } + -- @NOTE: TxNonce should be +1 of accountNonce + case resp of + QueryError e -> + if appErrorCode e == 2 + then pure 1 + else error $ "Unknown nonce error: " <> show (appErrorMessage e) + QueryResponse QueryResult {queryResultData} -> + pure $ 1 + Auth.accountNonce queryResultData + + in ClientConfig + { clientGetNonce = getNonce + , clientRPC = rpcConfig + } +~~~ + + +Once we have defined our monad capable of querying nonces, we can then generate the transaction client using this monad as our context. + +~~~ haskell + +-- Nameservice Client +buyName + :: TxOpts + -> N.BuyName + -> TxClientM (TxClientResponse () ()) + +setName + :: TxOpts + -> N.SetName + -> TxClientM (TxClientResponse () ()) + +deleteName + :: TxOpts + -> N.DeleteName + -> TxClientM (TxClientResponse () ()) + +-- Bank Client +transfer + :: TxOpts + -> B.Transfer + -> TxClientM (TxClientResponse () ()) + +faucet + :: TxOpts + -> N.FaucetAccount + -> TxClientM (TxClientResponse () ()) + +(buyName :<|> setName :<|> deleteName :<|> faucet) :<|> + (_ :<|> transfer) :<|> + EmptyTxClient = + genClientT (Proxy @TxClientM) txApiCP txApiDP defaultClientTxOpts + where + txApiCP :: Proxy (M.ApplicationC NameserviceModules) + txApiCP = Proxy + txApiDP :: Proxy (M.ApplicationD NameserviceModules) + txApiDP = Proxy +~~~ + +Here you'll see that the `TxClientResponse` has two type variables, which in this case are always both `()`. This is because it is possible to return separate values depending on whether we are in the `checkTx` versus `deliverTx` context. + + +To see how these clients are used together with other test combinators for the `hs-abci-test-utils` package, you can view the `E2E` test files in the nameservice test suite. \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs index a1b50380..4bc43a41 120000 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Application.lhs @@ -1 +1 @@ -07-Application.md \ No newline at end of file +08-Application.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs index e6e3abfd..5f1386c0 120000 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Module.lhs @@ -1 +1 @@ -06-Module.md \ No newline at end of file +07-Module.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.lhs new file mode 120000 index 00000000..aeb17b8a --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Router.lhs @@ -0,0 +1 @@ +06-Router.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Testing.lhs b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Testing.lhs new file mode 120000 index 00000000..2784e992 --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/Testing.lhs @@ -0,0 +1 @@ +09-Testing.md \ No newline at end of file diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs index c3052ffe..9a952053 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Application.hs @@ -1,6 +1,5 @@ module SimpleStorage.Application - ( EffR - , SimpleStorageModules + ( SimpleStorageModules , handlersContext ) where @@ -9,26 +8,18 @@ import SimpleStorage.Modules.SimpleStorage as SimpleStorage import Tendermint.SDK.Application (HandlersContext (..), ModuleList (..), baseAppAnteHandler) -import Tendermint.SDK.BaseApp ((:&)) import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Crypto (Secp256k1) import qualified Tendermint.SDK.Modules.Auth as A -------------------------------------------------------------------------------- -type EffR = - SimpleStorage.SimpleStorageEffs :& - A.AuthEffs :& - BA.TxEffs :& - BA.BaseApp BA.CoreEffs - - type SimpleStorageModules = - '[ SimpleStorage.SimpleStorageM EffR - , A.AuthM EffR + '[ SimpleStorage.SimpleStorage + , A.Auth ] -handlersContext :: HandlersContext Secp256k1 SimpleStorageModules EffR BA.CoreEffs +handlersContext :: HandlersContext Secp256k1 SimpleStorageModules BA.CoreEffs handlersContext = HandlersContext { signatureAlgP = Proxy @Secp256k1 , modules = simpleStorageModules @@ -36,7 +27,6 @@ handlersContext = HandlersContext , anteHandler = baseAppAnteHandler } where - simpleStorageModules :: ModuleList SimpleStorageModules EffR simpleStorageModules = SimpleStorage.simpleStorageModule :+ A.authModule diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index c884d348..dd5efe20 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -1,32 +1,29 @@ module SimpleStorage.Modules.SimpleStorage - ( SimpleStorageM - , QueryApi - , MessageApi + ( SimpleStorage , simpleStorageModule + , module SimpleStorage.Modules.SimpleStorage.Keeper , module SimpleStorage.Modules.SimpleStorage.Message , module SimpleStorage.Modules.SimpleStorage.Types ) where import Data.Proxy -import Polysemy (Member, Members) +import Polysemy (Members) import SimpleStorage.Modules.SimpleStorage.Keeper hiding (storeKey) import SimpleStorage.Modules.SimpleStorage.Message -import SimpleStorage.Modules.SimpleStorage.Query (QueryApi, querier) -import SimpleStorage.Modules.SimpleStorage.Router (MessageApi, - messageHandlers) +import SimpleStorage.Modules.SimpleStorage.Query +import SimpleStorage.Modules.SimpleStorage.Router import SimpleStorage.Modules.SimpleStorage.Types -import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.Application (Module (..), + ModuleEffs) import qualified Tendermint.SDK.BaseApp as BaseApp -type SimpleStorageM r = - Module "simple_storage" MessageApi MessageApi QueryApi SimpleStorageEffs r +type SimpleStorage = + Module SimpleStorageName MessageApi MessageApi QueryApi SimpleStorageEffs '[] simpleStorageModule - :: Member SimpleStorage r - => Members BaseApp.TxEffs r - => Members BaseApp.BaseEffs r - => SimpleStorageM r + :: Members (ModuleEffs SimpleStorage) r + => SimpleStorage r simpleStorageModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = BaseApp.defaultCheckTx (Proxy @MessageApi) (Proxy :: Proxy r) diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs index 65689b68..b24003e4 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -1,6 +1,6 @@ {-# LANGUAGE TemplateHaskell #-} module SimpleStorage.Modules.SimpleStorage.Keeper - ( SimpleStorage + ( SimpleStorageKeeper , SimpleStorageEffs , updateCount , getCount @@ -11,24 +11,22 @@ module SimpleStorage.Modules.SimpleStorage.Keeper import Polysemy (Members, Sem, interpret, makeSem) import Polysemy.Output (Output) -import SimpleStorage.Modules.SimpleStorage.Types (Count, - CountKey (..), - CountSet (..)) +import SimpleStorage.Modules.SimpleStorage.Types import qualified Tendermint.SDK.BaseApp as BaseApp -storeKey :: BaseApp.StoreKey "simple_storage" +storeKey :: BaseApp.StoreKey SimpleStorageName storeKey = BaseApp.StoreKey "simple_storage" -data SimpleStorage m a where - PutCount :: Count -> SimpleStorage m () - GetCount :: SimpleStorage m (Maybe Count) +data SimpleStorageKeeper m a where + PutCount :: Count -> SimpleStorageKeeper m () + GetCount :: SimpleStorageKeeper m (Maybe Count) -makeSem ''SimpleStorage +makeSem ''SimpleStorageKeeper -type SimpleStorageEffs = '[SimpleStorage] +type SimpleStorageEffs = '[SimpleStorageKeeper] updateCount - :: Members '[SimpleStorage, Output BaseApp.Event, BaseApp.Logger] r + :: Members '[SimpleStorageKeeper, Output BaseApp.Event, BaseApp.Logger] r => Count -> Sem r () updateCount count = do @@ -40,7 +38,7 @@ updateCount count = do eval :: forall r. Members BaseApp.TxEffs r - => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) + => forall a. (Sem (SimpleStorageKeeper ': r) a -> Sem r a) eval = interpret (\case PutCount count -> do BaseApp.put storeKey CountKey count diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs index 86e12d82..f361ad7e 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -5,7 +5,7 @@ module SimpleStorage.Modules.SimpleStorage.Router import Polysemy (Member, Members, Sem) -import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorage, +import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorageKeeper, updateCount) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) @@ -22,14 +22,14 @@ type MessageApi = TypedMessage UpdateCountTx :~> Return () messageHandlers - :: Member SimpleStorage r + :: Member SimpleStorageKeeper r => Members TxEffs r => Members BaseEffs r => RouteTx MessageApi r messageHandlers = updateCountH updateCountH - :: Member SimpleStorage r + :: Member SimpleStorageKeeper r => Members TxEffs r => Members BaseEffs r => RoutingTx UpdateCountTx diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs index 8c7d53c0..90daeca6 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -55,3 +55,5 @@ instance BaseApp.ToEvent CountSet where makeEventType _ = "count_set" instance BaseApp.Select CountSet + +type SimpleStorageName = "simple_storage" diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application.hs b/hs-abci-sdk/src/Tendermint/SDK/Application.hs index 6389e284..47da4d10 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application.hs @@ -1,6 +1,9 @@ module Tendermint.SDK.Application ( ModuleList(..) , Module(..) + , Component + , Eval(..) + , ModuleEffs , HandlersContext(..) , baseAppAnteHandler , createIOApp @@ -11,3 +14,5 @@ import Tendermint.SDK.Application.AnteHandler import Tendermint.SDK.Application.App import Tendermint.SDK.Application.Handlers import Tendermint.SDK.Application.Module + + diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs index 3173f9e9..625659ff 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Handlers.hs @@ -71,52 +71,55 @@ defaultHandlers = Handlers defaultHandler = const $ pure def -data HandlersContext alg ms r core = HandlersContext +data HandlersContext alg ms core = HandlersContext { signatureAlgP :: Proxy alg - , modules :: M.ModuleList ms r - , anteHandler :: BA.AnteHandler r - , compileToCore :: forall a. Sem (BA.BaseApp core) a -> Sem core a + , modules :: M.ModuleList ms (M.Effs ms core) + , anteHandler :: BA.AnteHandler (M.Effs ms core) + , compileToCore :: forall a. Sem (BA.BaseAppEffs core) a -> Sem core a } -- Common function between checkTx and deliverTx makeHandlers - :: forall alg ms r core. + :: forall alg ms core. RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 - => M.ToApplication ms (M.Effs ms (BA.BaseApp core)) - => T.HasTxRouter (M.ApplicationC ms) (M.Effs ms (BA.BaseApp core)) 'Store.QueryAndMempool - => T.HasTxRouter (M.ApplicationC ms) (BA.BaseApp core) 'Store.QueryAndMempool - => T.HasTxRouter (M.ApplicationD ms) (M.Effs ms (BA.BaseApp core)) 'Store.Consensus - => T.HasTxRouter (M.ApplicationD ms) (BA.BaseApp core) 'Store.Consensus - => Q.HasQueryRouter (M.ApplicationQ ms) (M.Effs ms (BA.BaseApp core)) - => Q.HasQueryRouter (M.ApplicationQ ms) (BA.BaseApp core) - => M.Eval ms (BA.BaseApp core) - => M.Effs ms (BA.BaseApp core) ~ r - => HandlersContext alg ms r core - -> Handlers (BA.BaseApp core) -makeHandlers (HandlersContext{..} :: HandlersContext alg ms r core) = + => M.ToApplication ms (M.Effs ms core) + => T.HasTxRouter (M.ApplicationC ms) (M.Effs ms core) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationC ms) (BA.BaseAppEffs core) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationD ms) (M.Effs ms core) 'Store.Consensus + => T.HasTxRouter (M.ApplicationD ms) (BA.BaseAppEffs core) 'Store.Consensus + => Q.HasQueryRouter (M.ApplicationQ ms) (M.Effs ms core) + => Q.HasQueryRouter (M.ApplicationQ ms) (BA.BaseAppEffs core) + => M.Eval ms core + -- => M.Effs ms core ~ (BA.AppEffs (M.ModulesEffs ms) core) + => HandlersContext alg ms core + -> Handlers (BA.BaseAppEffs core) +makeHandlers (HandlersContext{..} :: HandlersContext alg ms core) = let - rProxy :: Proxy (BA.BaseApp core) + cProxy :: Proxy core + cProxy = Proxy + + rProxy :: Proxy (BA.BaseAppEffs core) rProxy = Proxy app :: M.Application (M.ApplicationC ms) (M.ApplicationD ms) (M.ApplicationQ ms) - (T.TxEffs BA.:& BA.BaseApp core) (Q.QueryEffs BA.:& BA.BaseApp core) - app = M.makeApplication rProxy anteHandler modules + (T.TxEffs BA.:& BA.BaseAppEffs core) (Q.QueryEffs BA.:& BA.BaseAppEffs core) + app = M.makeApplication cProxy anteHandler modules txParser bs = case parseTx signatureAlgP bs of Left err -> throwSDKError $ ParseError err Right tx -> pure $ T.RoutingTx tx - checkServer :: T.TransactionApplication (Sem (BA.BaseApp core)) + checkServer :: T.TransactionApplication (Sem (BA.BaseAppEffs core)) checkServer = T.serveTxApplication (Proxy @(M.ApplicationC ms)) rProxy (Proxy @'Store.QueryAndMempool) $ M.applicationTxChecker app - deliverServer :: T.TransactionApplication (Sem (BA.BaseApp core)) + deliverServer :: T.TransactionApplication (Sem (BA.BaseAppEffs core)) deliverServer = T.serveTxApplication (Proxy @(M.ApplicationD ms)) rProxy (Proxy @'Store.Consensus) $ M.applicationTxDeliverer app - queryServer :: Q.QueryApplication (Sem (BA.BaseApp core)) + queryServer :: Q.QueryApplication (Sem (BA.BaseAppEffs core)) queryServer = Q.serveQueryApplication (Proxy @(M.ApplicationQ ms)) rProxy $ M.applicationQuerier app query (RequestQuery q) = @@ -155,7 +158,7 @@ makeHandlers (HandlersContext{..} :: HandlersContext alg ms r core) = ) return . ResponseDeliverTx $ res ^. from deliverTxTxResult - commit :: Handler 'MTCommit (BA.BaseApp core) + commit :: Handler 'MTCommit (BA.BaseAppEffs core) commit _ = do _ <- Store.commit rootHash <- Store.commitBlock @@ -170,23 +173,23 @@ makeHandlers (HandlersContext{..} :: HandlersContext alg ms r core) = } makeApp - :: forall alg ms r core. + :: forall alg ms core. RecoverableSignatureSchema alg => Message alg ~ Digest SHA256 - => M.ToApplication ms (M.Effs ms (BA.BaseApp core)) - => T.HasTxRouter (M.ApplicationC ms) (M.Effs ms (BA.BaseApp core)) 'Store.QueryAndMempool - => T.HasTxRouter (M.ApplicationC ms) (BA.BaseApp core) 'Store.QueryAndMempool - => T.HasTxRouter (M.ApplicationD ms) (M.Effs ms (BA.BaseApp core)) 'Store.Consensus - => T.HasTxRouter (M.ApplicationD ms) (BA.BaseApp core) 'Store.Consensus - => Q.HasQueryRouter (M.ApplicationQ ms) (M.Effs ms (BA.BaseApp core)) - => Q.HasQueryRouter (M.ApplicationQ ms) (BA.BaseApp core) - => M.Eval ms (BA.BaseApp core) - => M.Effs ms (BA.BaseApp core) ~ r - => HandlersContext alg ms r core + => M.ToApplication ms (M.Effs ms core) + => T.HasTxRouter (M.ApplicationC ms) (M.Effs ms core) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationC ms) (BA.BaseAppEffs core) 'Store.QueryAndMempool + => T.HasTxRouter (M.ApplicationD ms) (M.Effs ms core) 'Store.Consensus + => T.HasTxRouter (M.ApplicationD ms) (BA.BaseAppEffs core) 'Store.Consensus + => Q.HasQueryRouter (M.ApplicationQ ms) (M.Effs ms core) + => Q.HasQueryRouter (M.ApplicationQ ms) (BA.BaseAppEffs core) + => M.Eval ms core + -- => M.Effs ms (BA.BaseAppEffs core) ~ (BA.AppEffs (M.ModulesEffs ms) core) + => HandlersContext alg ms core -> App (Sem core) makeApp handlersContext@HandlersContext{compileToCore} = - let Handlers{..} = makeHandlers handlersContext :: Handlers (BA.BaseApp core) + let Handlers{..} = makeHandlers handlersContext :: Handlers (BA.BaseAppEffs core) in transformApp compileToCore $ App $ \case RequestEcho echo -> pure . ResponseEcho $ def diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index 646867d0..c2818c0b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -2,6 +2,8 @@ module Tendermint.SDK.Application.Module ( Module(..) + , Component + , ModuleEffs , ModuleList(..) , Application(..) , ToApplication(..) @@ -12,27 +14,43 @@ module Tendermint.SDK.Application.Module ) where +import Data.Kind (Type) import Data.Proxy -import GHC.TypeLits (Symbol) +import GHC.TypeLits (ErrorMessage (..), Symbol, + TypeError) import Polysemy (EffectRow, Members, Sem) import Servant.API ((:<|>) (..), (:>)) -import Tendermint.SDK.BaseApp ((:&)) +import Tendermint.SDK.BaseApp ((:&), BaseAppEffs, + BaseEffs) import qualified Tendermint.SDK.BaseApp.Query as Q import Tendermint.SDK.BaseApp.Store (Scope (..)) import qualified Tendermint.SDK.BaseApp.Transaction as T -data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: EffectRow) (r :: EffectRow) = Module +type Component = EffectRow -> Type + +-- NOTE: This does not pull in transitive dependencies on purpose to avoid +-- unintended enlarged scope +type family DependencyEffs (ms :: [Component]) :: EffectRow where + DependencyEffs '[] = '[] + DependencyEffs (Module _ _ _ _ es deps ': rest) = es :& DependencyEffs rest + DependencyEffs _ = TypeError ('Text "DependencyEffs is a partial function defined only on partially applied Modules") + +data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: EffectRow) (deps :: [Component]) (r :: EffectRow) = Module { moduleTxChecker :: T.RouteTx check r , moduleTxDeliverer :: T.RouteTx deliver r , moduleQuerier :: Q.RouteQ query r - , moduleEval :: forall deps. Members T.TxEffs deps => forall a. Sem (es :& deps) a -> Sem deps a + , moduleEval :: forall s. (Members T.TxEffs s, Members (DependencyEffs deps) s) => forall a. Sem (es :& s) a -> Sem s a } -data ModuleList ms r where +type family ModuleEffs (m :: Component) :: EffectRow where + ModuleEffs (Module _ _ _ _ es deps) = es :& DependencyEffs deps :& T.TxEffs :& BaseEffs + ModuleEffs _ = TypeError ('Text "ModuleEffs is a partial function defined only on Component") + +data ModuleList (ms :: [Component]) r where NilModules :: ModuleList '[] r - (:+) :: Module name check deliver query es r + (:+) :: Module name check deliver query es deps r -> ModuleList ms r - -> ModuleList (Module name check deliver query es r ': ms) r + -> ModuleList (Module name check deliver query es deps ': ms) r infixr 5 :+ @@ -49,10 +67,10 @@ class ToApplication ms r where toApplication :: ModuleList ms r -> Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) r r -instance ToApplication '[Module name check deliver query es r] r where - type ApplicationC '[Module name check deliver query es r] = name :> check - type ApplicationD '[Module name check deliver query es r] = name :> deliver - type ApplicationQ '[Module name check deliver query es r] = name :> query +instance ToApplication '[Module name check deliver query es deps] r where + type ApplicationC '[Module name check deliver query es deps] = name :> check + type ApplicationD '[Module name check deliver query es deps] = name :> deliver + type ApplicationQ '[Module name check deliver query es deps] = name :> query toApplication (Module{..} :+ NilModules) = Application @@ -61,10 +79,10 @@ instance ToApplication '[Module name check deliver query es r] r where , applicationQuerier = moduleQuerier } -instance ToApplication (m' ': ms) r => ToApplication (Module name check deliver query es r ': m' ': ms) r where - type ApplicationC (Module name check deliver query es r ': m' ': ms) = (name :> check) :<|> ApplicationC (m' ': ms) - type ApplicationD (Module name check deliver query es r ': m' ': ms) = (name :> deliver) :<|> ApplicationD (m' ': ms) - type ApplicationQ (Module name check deliver query es r ': m' ': ms) = (name :> query) :<|> ApplicationQ (m' ': ms) +instance ToApplication (m' ': ms) r => ToApplication (Module name check deliver query es deps ': m' ': ms) r where + type ApplicationC (Module name check deliver query es deps ': m' ': ms) = (name :> check) :<|> ApplicationC (m' ': ms) + type ApplicationD (Module name check deliver query es deps ': m' ': ms) = (name :> deliver) :<|> ApplicationD (m' ': ms) + type ApplicationQ (Module name check deliver query es deps ': m' ': ms) = (name :> query) :<|> ApplicationQ (m' ': ms) toApplication (Module{..} :+ rest) = let app = toApplication rest @@ -89,38 +107,40 @@ hoistApplication natT natQ (app :: Application check deliver query r s) = , applicationQuerier = Q.hoistQueryRouter (Proxy @query) (Proxy @s) natQ $ applicationQuerier app } -class Eval ms deps where - type Effs ms deps :: EffectRow +class Eval ms (core :: EffectRow) where + type Effs ms core :: EffectRow eval - :: ModuleList ms r + :: proxy core + -> ModuleList ms r -> forall a. - Sem (Effs ms deps) a - -> Sem (T.TxEffs :& deps) a + Sem (Effs ms core) a + -> Sem (T.TxEffs :& BaseAppEffs core) a -instance Eval '[Module name check deliver query es r] deps where - type Effs '[Module name check deliver query es r] deps = es :& T.TxEffs :& deps - eval (m :+ NilModules) = moduleEval m +instance (DependencyEffs deps ~ '[]) => Eval '[Module name check deliver query es deps] core where + type Effs '[Module name check deliver query es deps] core = es :& T.TxEffs :& BaseAppEffs core + eval _ (m :+ NilModules) = moduleEval m -instance ( Members T.TxEffs (Effs (m' ': ms) deps) - , Eval (m' ': ms) deps - ) => Eval (Module name check deliver query es r ': m' ': ms) deps where - type Effs (Module name check deliver query es r ': m' ': ms) deps = es :& (Effs (m': ms)) deps - eval (m :+ rest) = eval rest . moduleEval m +instance ( Members (DependencyEffs deps) (Effs (m' ': ms) s) + , Members T.TxEffs (Effs (m' ': ms) s) + , Eval (m' ': ms) s + ) => Eval (Module name check deliver query es deps ': m' ': ms) s where + type Effs (Module name check deliver query es deps ': m' ': ms) s = es :& (Effs (m': ms)) s + eval pcore (m :+ rest) = eval pcore rest . moduleEval m makeApplication - :: Eval ms deps - => ToApplication ms (Effs ms deps) - => T.HasTxRouter (ApplicationC ms) (Effs ms deps) 'QueryAndMempool - => T.HasTxRouter (ApplicationD ms) (Effs ms deps) 'Consensus - => Q.HasQueryRouter (ApplicationQ ms) (Effs ms deps) - => Proxy deps - -> T.AnteHandler (Effs ms deps) - -> ModuleList ms (Effs ms deps) - -> Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) (T.TxEffs :& deps) (Q.QueryEffs :& deps) -makeApplication (Proxy :: Proxy deps) ah (ms :: ModuleList ms (Effs ms deps)) = - let app = applyAnteHandler ah $ toApplication ms :: Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) (Effs ms deps) (Effs ms deps) + :: Eval ms core + => ToApplication ms (Effs ms core) + => T.HasTxRouter (ApplicationC ms) (Effs ms core) 'QueryAndMempool + => T.HasTxRouter (ApplicationD ms) (Effs ms core) 'Consensus + => Q.HasQueryRouter (ApplicationQ ms) (Effs ms core) + => Proxy core + -> T.AnteHandler (Effs ms core) + -> ModuleList ms (Effs ms core) + -> Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) (T.TxEffs :& BaseAppEffs core) (Q.QueryEffs :& BaseAppEffs core) +makeApplication p@(Proxy :: Proxy core) ah (ms :: ModuleList ms (Effs ms core)) = + let app = applyAnteHandler ah $ toApplication ms :: Application (ApplicationC ms) (ApplicationD ms) (ApplicationQ ms) (Effs ms core) (Effs ms core) -- WEIRD: if you move the eval into a separate let binding then it doesn't typecheck... - in hoistApplication (eval @ms @deps ms) (T.evalReadOnly . eval @ms @deps ms) app + in hoistApplication (eval @ms @core p ms) (T.evalReadOnly . eval @ms @core p ms) app applyAnteHandler :: T.HasTxRouter check r 'QueryAndMempool diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index 015ab9ea..b802d72f 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -3,7 +3,7 @@ module Tendermint.SDK.BaseApp BaseEffs , defaultCompileToCore , defaultCompileToPureCore - , BaseApp + , BaseAppEffs , (:&) -- * Core Effects diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs index 092d16c2..56300bfd 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Effects.hs @@ -1,5 +1,5 @@ module Tendermint.SDK.BaseApp.Effects - ( BaseApp + ( BaseAppEffs , defaultCompileToCore , defaultCompileToPureCore , module Tendermint.SDK.BaseApp.Effects.BaseEffs @@ -17,16 +17,18 @@ import qualified Tendermint.SDK.BaseApp.Store.MemoryStore as Memory import Tendermint.SDK.Types.Effects ((:&)) -type BaseApp core = StoreEffs :& BaseEffs :& core + + +type BaseAppEffs core = StoreEffs :& BaseEffs :& core defaultCompileToCore :: forall a. - Sem (BaseApp CoreEffs) a + Sem (BaseAppEffs CoreEffs) a -> Sem CoreEffs a defaultCompileToCore = evalBaseEffs . IAVL.evalStoreEffs defaultCompileToPureCore :: forall a. - Sem (BaseApp PureCoreEffs) a + Sem (BaseAppEffs PureCoreEffs) a -> Sem PureCoreEffs a defaultCompileToPureCore = evalBaseEffsPure . Memory.evalStoreEffs diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index b1211d30..339efdaf 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -1,5 +1,5 @@ module Tendermint.SDK.Modules.Auth - ( AuthM + ( Auth , authModule , module Tendermint.SDK.Modules.Auth.Keeper @@ -7,21 +7,19 @@ module Tendermint.SDK.Modules.Auth , module Tendermint.SDK.Modules.Auth.Types ) where -import Polysemy (Member, Members) -import Tendermint.SDK.Application.Module (Module (..)) -import Tendermint.SDK.BaseApp (BaseEffs, - EmptyTxServer (..), - ReadStore) +import Polysemy (Members) +import Tendermint.SDK.Application.Module (Module (..), ModuleEffs) +import Tendermint.SDK.BaseApp (EmptyTxServer (..)) import Tendermint.SDK.Modules.Auth.Keeper hiding (storeKey) import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types -type AuthM r = Module AuthModule EmptyTxServer EmptyTxServer Api AuthEffs r +type Auth = + Module AuthName EmptyTxServer EmptyTxServer Api AuthEffs '[] authModule - :: Members BaseEffs r - => Member ReadStore r - => AuthM r + :: Members (ModuleEffs Auth) r + => Auth r authModule = Module { moduleTxDeliverer = EmptyTxServer , moduleTxChecker = EmptyTxServer diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs index e7185a46..d05cc6b9 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -18,7 +18,7 @@ makeSem ''Accounts type AuthEffs = '[Accounts, Error AuthError] -storeKey :: StoreKey AuthModule +storeKey :: StoreKey AuthName storeKey = StoreKey "auth" eval diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs index 07150e29..272f6292 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -27,10 +27,10 @@ import Web.HttpApiData (FromHttpApiData (..), -------------------------------------------------------------------------------- -type AuthModule = "auth" +type AuthName = "auth" -instance IsKey Address AuthModule where - type Value Address AuthModule = Account +instance IsKey Address AuthName where + type Value Address AuthName = Account instance Queryable Account where type Name Account = "account" @@ -46,7 +46,7 @@ instance IsAppError AuthError where makeAppError (AccountAlreadyExists addr) = AppError { appErrorCode = 1 - , appErrorCodespace = cs (symbolVal $ Proxy @AuthModule) + , appErrorCodespace = cs (symbolVal $ Proxy @AuthName) , appErrorMessage = "Account Already Exists " <> (cs . show $ addr) <> "." } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs index 41a3796d..da0ee394 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank.hs @@ -1,7 +1,7 @@ module Tendermint.SDK.Modules.Bank ( -- * Module - BankM + Bank , bankModule , module Tendermint.SDK.Modules.Bank.Keeper @@ -14,10 +14,8 @@ module Tendermint.SDK.Modules.Bank import Data.Proxy import Polysemy (Members) -import Tendermint.SDK.Application (Module (..)) -import Tendermint.SDK.BaseApp (BaseEffs, - DefaultCheckTx (..), - TxEffs) +import Tendermint.SDK.Application (Module (..), ModuleEffs) +import Tendermint.SDK.BaseApp (DefaultCheckTx (..)) import qualified Tendermint.SDK.Modules.Auth as Auth import Tendermint.SDK.Modules.Bank.Keeper import Tendermint.SDK.Modules.Bank.Messages @@ -25,14 +23,12 @@ import Tendermint.SDK.Modules.Bank.Query import Tendermint.SDK.Modules.Bank.Router import Tendermint.SDK.Modules.Bank.Types -type BankM r = Module "bank" MessageApi MessageApi QueryApi BankEffs r +type Bank = + Module BankName MessageApi MessageApi QueryApi BankEffs '[Auth.Auth] bankModule - :: Members BaseEffs r - => Members Auth.AuthEffs r - => Members BankEffs r - => Members TxEffs r - => BankM r + :: Members (ModuleEffs Bank) r + => Bank r bankModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs index 71306cf3..cf216cae 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs @@ -1,3 +1,5 @@ +{-# LANGUAGE TemplateHaskell #-} + module Tendermint.SDK.Modules.Bank.Keeper where import Data.List (find) @@ -11,53 +13,73 @@ import Tendermint.SDK.Modules.Bank.Types (BankError (..), TransferEvent (..)) import Tendermint.SDK.Types.Address (Address) -type BankEffs = '[Error BankError] +type BankEffs = '[BankKeeper, Error BankError] + +data BankKeeper m a where + GetBalance :: Address -> Auth.CoinId -> BankKeeper m Auth.Coin + PutBalance :: Address -> Auth.Coin -> BankKeeper m () + +makeSem ''BankKeeper eval :: Member (Error BaseApp.AppError) r - => forall a. Sem (Error BankError ': r) a -> Sem r a -eval = mapError BaseApp.makeAppError + => Members Auth.AuthEffs r + => forall a. Sem (BankKeeper ': Error BankError ': r) a -> Sem r a +eval = mapError BaseApp.makeAppError . evalBankKeeper + where + evalBankKeeper + :: forall r. + Members Auth.AuthEffs r + => forall a. + Sem (BankKeeper ': r) a + -> Sem r a + evalBankKeeper = interpret (\case + GetBalance addr coinId -> getCoinBalance addr coinId + PutBalance addr coins -> putCoinBalance addr coins + ) --------------------------------------------------------------------------------- + getCoinBalance + :: Members Auth.AuthEffs r + => Address + -> Auth.CoinId + -> Sem r Auth.Coin + getCoinBalance address cid = do + mAcnt <- Auth.getAccount address + let zeroBalance = Auth.Coin cid 0 + case mAcnt of + Nothing -> pure zeroBalance + Just (Auth.Account coins _) -> + let mCoin = find (\(Auth.Coin cid1 _) -> cid == cid1) coins + in pure $ fromMaybe zeroBalance mCoin -getCoinBalance - :: Members Auth.AuthEffs r - => Address - -> Auth.CoinId - -> Sem r Auth.Coin -getCoinBalance address cid = do - mAcnt <- Auth.getAccount address - let zeroBalance = Auth.Coin cid 0 - case mAcnt of - Nothing -> pure zeroBalance - Just (Auth.Account coins _) -> - let mCoin = find (\(Auth.Coin cid1 _) -> cid == cid1) coins - in pure $ fromMaybe zeroBalance mCoin + replaceCoinValue + :: Auth.Coin + -> [Auth.Coin] + -> [Auth.Coin] + replaceCoinValue c [] = [c] + replaceCoinValue c@(Auth.Coin cid _) (c1@(Auth.Coin cid' _):rest) = + if cid' == cid + then c : rest + else c1 : replaceCoinValue c rest -replaceCoinValue :: Auth.Coin -> [Auth.Coin] -> [Auth.Coin] -replaceCoinValue c [] = [c] -replaceCoinValue c@(Auth.Coin cid _) (c1@(Auth.Coin cid' _):rest) = - if cid' == cid - then c : rest - else c1 : replaceCoinValue c rest + putCoinBalance + :: Members Auth.AuthEffs r + => Address + -> Auth.Coin + -> Sem r () + putCoinBalance address coin = do + mAcnt <- Auth.getAccount address + acnt <- case mAcnt of + Nothing -> Auth.createAccount address >>= pure + Just a -> pure a + let updatedCoins = replaceCoinValue coin (Auth.accountCoins acnt) + updatedAcnt = acnt { Auth.accountCoins = updatedCoins } + Auth.putAccount address updatedAcnt -putCoinBalance - :: Members Auth.AuthEffs r - => Address - -> Auth.Coin - -> Sem r () -putCoinBalance address coin = do - mAcnt <- Auth.getAccount address - acnt <- case mAcnt of - Nothing -> Auth.createAccount address >>= pure - Just a -> pure a - let updatedCoins = replaceCoinValue coin (Auth.accountCoins acnt) - updatedAcnt = acnt { Auth.accountCoins = updatedCoins } - Auth.putAccount address updatedAcnt +-------------------------------------------------------------------------------- transfer :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members Auth.AuthEffs r => Members BankEffs r => Address -> Auth.Coin @@ -65,15 +87,15 @@ transfer -> Sem r () transfer addr1 (Auth.Coin cid amount) addr2 = do -- check if addr1 has amt - (Auth.Coin _ addr1Bal) <- getCoinBalance addr1 cid + (Auth.Coin _ addr1Bal) <- getBalance addr1 cid if addr1Bal >= amount then do - (Auth.Coin _ addr2Bal) <- getCoinBalance addr2 cid + (Auth.Coin _ addr2Bal) <- getBalance addr2 cid let newCoinBalance1 = Auth.Coin cid (addr1Bal - amount) newCoinBalance2 = Auth.Coin cid (addr2Bal + amount) -- update both balances - putCoinBalance addr1 newCoinBalance1 - putCoinBalance addr2 newCoinBalance2 + putBalance addr1 newCoinBalance1 + putBalance addr2 newCoinBalance2 let event = TransferEvent { transferEventAmount = amount , transferEventCoinId = cid @@ -85,22 +107,21 @@ transfer addr1 (Auth.Coin cid amount) addr2 = do else throw (InsufficientFunds "Insufficient funds for transfer.") burn - :: Members Auth.AuthEffs r - => Members BankEffs r + :: Members BankEffs r => Address -> Auth.Coin -> Sem r () burn addr (Auth.Coin cid amount) = do - (Auth.Coin _ bal) <- getCoinBalance addr cid + (Auth.Coin _ bal) <- getBalance addr cid if bal < amount then throw $ InsufficientFunds "Insufficient funds for burn." - else putCoinBalance addr (Auth.Coin cid (bal - amount)) + else putBalance addr (Auth.Coin cid (bal - amount)) mint - :: Members Auth.AuthEffs r + :: Member BankKeeper r => Address -> Auth.Coin -> Sem r () mint addr (Auth.Coin cid amount) = do - (Auth.Coin _ bal) <- getCoinBalance addr cid - putCoinBalance addr (Auth.Coin cid (bal + amount)) + (Auth.Coin _ bal) <- getBalance addr cid + putBalance addr (Auth.Coin cid (bal + amount)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs index 8771a1b2..6ef9b59b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Query.hs @@ -7,7 +7,7 @@ import Servant.API import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.BaseApp.Query (QueryArgs (..)) import qualified Tendermint.SDK.Modules.Auth as Auth -import Tendermint.SDK.Modules.Bank.Keeper (getCoinBalance) +import Tendermint.SDK.Modules.Bank.Keeper (BankKeeper, getBalance) import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- @@ -21,12 +21,12 @@ type GetAddressCoinBalance = :> BaseApp.Leaf Auth.Coin getAddressCoinBalance - :: Members Auth.AuthEffs r + :: Member BankKeeper r => QueryArgs Address -> Auth.CoinId -> Sem r (BaseApp.QueryResult Auth.Coin) getAddressCoinBalance (QueryArgs _ address _) cid = do - coin <- getCoinBalance address cid + coin <- getBalance address cid pure $ BaseApp.QueryResult { queryResultData = coin , queryResultIndex = 0 @@ -39,6 +39,6 @@ type QueryApi = GetAddressCoinBalance querier :: forall r. - Members Auth.AuthEffs r + Member BankKeeper r => BaseApp.RouteQ QueryApi r querier = getAddressCoinBalance diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs index 439ad508..1ccd6555 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs @@ -8,7 +8,7 @@ import Servant.API ((:<|>) (..)) import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, RouteTx, RoutingTx (..), TxEffs, TypedMessage) -import Tendermint.SDK.Modules.Auth (AuthEffs, Coin (..)) +import Tendermint.SDK.Modules.Auth (Coin (..)) import Tendermint.SDK.Modules.Bank.Keeper (BankEffs, burn, transfer) import Tendermint.SDK.Modules.Bank.Messages (Burn (..), Transfer (..)) import Tendermint.SDK.Types.Message (Msg (..)) @@ -19,16 +19,14 @@ type MessageApi = :<|> TypedMessage Transfer :~> Return () messageHandlers - :: Members AuthEffs r - => Members BankEffs r + ::Members BankEffs r => Members TxEffs r => Members BaseEffs r => RouteTx MessageApi r messageHandlers = burnH :<|> transferH transferH - :: Members AuthEffs r - => Members BankEffs r + :: Members BankEffs r => Members TxEffs r => Members BaseEffs r => RoutingTx Transfer @@ -39,8 +37,7 @@ transferH (RoutingTx Tx{txMsg=Msg{msgData}}) = in transfer transferFrom coin transferTo burnH - :: Members AuthEffs r - => Members BankEffs r + :: Members BankEffs r => RoutingTx Burn -> Sem r () burnH (RoutingTx Tx{txMsg=Msg{msgData}}) = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs index e72c4d9d..fe7a28d2 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs @@ -10,7 +10,7 @@ import Tendermint.SDK.Types.Address (Address (..)) -------------------------------------------------------------------------------- -type BankModule = "bank" +type BankName = "bank" -------------------------------------------------------------------------------- -- Exceptions diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs index bd2ed167..d6f4c3de 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs @@ -21,17 +21,16 @@ import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) import Test.Hspec -type Effs = SS.SimpleStorage ': BA.TxEffs BA.:& BA.BaseApp BA.PureCoreEffs - -type Ms = '[SS.SimpleStorageM Effs] +type Ms = '[SS.SimpleStorage] spec :: Spec spec = beforeAll initContext $ describe "Query tests" $ do - let modules :: App.ModuleList Ms Effs + let modules :: App.ModuleList Ms (App.Effs Ms BA.PureCoreEffs) modules = SS.simpleStorageModule App.:+ App.NilModules - rProxy = Proxy @(BA.BaseApp BA.PureCoreEffs) - app = M.makeApplication rProxy mempty modules + cProxy = Proxy @BA.PureCoreEffs + rProxy = Proxy @(BA.BaseAppEffs BA.PureCoreEffs) + app = M.makeApplication cProxy mempty modules ssServer = serveQueryApplication (Proxy @(M.ApplicationQ Ms)) rProxy $ M.applicationQuerier app updateCount = serveTxApplication (Proxy @(M.ApplicationD Ms)) rProxy (Proxy @'Store.Consensus) $ M.applicationTxDeliverer app it "Can make a new count and query it with a multiplier" $ \ctx -> do diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index 6d8f8af9..24a165fe 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -1,8 +1,7 @@ {-# LANGUAGE TemplateHaskell #-} module Tendermint.SDK.Test.SimpleStorage - ( SimpleStorageM - , SimpleStorage + ( SimpleStorage , UpdateCountTx(..) , simpleStorageModule , evalToIO @@ -25,7 +24,7 @@ import GHC.Generics (Generic) import Polysemy import Polysemy.Error (Error, throw) import Servant.API -import Tendermint.SDK.Application (Module (..)) +import Tendermint.SDK.Application (Module (..), ModuleEffs) import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Message (HasMessageType (..), @@ -87,24 +86,25 @@ instance ValidateMessage UpdateCountTx where storeKey :: BA.StoreKey "simple_storage" storeKey = BA.StoreKey "simple_storage" -data SimpleStorage m a where - PutCount :: Count -> SimpleStorage m () - GetCount :: SimpleStorage m (Maybe Count) +data SimpleStorageKeeper m a where + PutCount :: Count -> SimpleStorageKeeper m () + GetCount :: SimpleStorageKeeper m (Maybe Count) -makeSem ''SimpleStorage +makeSem ''SimpleStorageKeeper -type SimpleStorageEffs = '[SimpleStorage] updateCount - :: Member SimpleStorage r + :: Member SimpleStorageKeeper r => Count -> Sem r () updateCount count = putCount count +type SimpleStorageEffs = '[SimpleStorageKeeper] + eval :: forall r. Members BA.TxEffs r - => forall a. (Sem (SimpleStorage ': r) a -> Sem r a) + => forall a. (Sem (SimpleStorageKeeper ': r) a -> Sem r a) eval = interpret (\case PutCount count -> BA.put storeKey CountKey count GetCount -> BA.get storeKey CountKey @@ -118,13 +118,13 @@ type MessageApi = BA.TypedMessage UpdateCountTx BA.:~> BA.Return () messageHandlers - :: Member SimpleStorage r + :: Member SimpleStorageKeeper r => Members BA.TxEffs r => BA.RouteTx MessageApi r messageHandlers = updateCountH updateCountH - :: Member SimpleStorage r + :: Member SimpleStorageKeeper r => Members BA.TxEffs r => BA.RoutingTx UpdateCountTx -> Sem r () @@ -146,7 +146,7 @@ type GetMultipliedCount = :> BA.Leaf Count getMultipliedCount - :: Members [Error BA.AppError, SimpleStorage] r + :: Members [Error BA.AppError, SimpleStorageKeeper] r => Integer -> Integer -> Sem r (BA.QueryResult Count) @@ -169,7 +169,7 @@ type QueryApi = GetMultipliedCount :<|> BA.QueryApi CountStoreContents querier :: forall r. Members BA.QueryEffs r - => Member SimpleStorage r + => Member SimpleStorageKeeper r => BA.RouteQ QueryApi r querier = let storeHandlers = BA.storeQueryHandlers (Proxy :: Proxy CountStoreContents) @@ -180,14 +180,12 @@ querier = -- Module Definition -------------------------------------------------------------------------------- -type SimpleStorageM r = - Module "simple_storage" MessageApi MessageApi QueryApi SimpleStorageEffs r +type SimpleStorage = + Module "simple_storage" MessageApi MessageApi QueryApi SimpleStorageEffs '[] simpleStorageModule - :: Member SimpleStorage r - => Members BA.TxEffs r - => Members BA.BaseEffs r - => SimpleStorageM r + :: Members (ModuleEffs SimpleStorage) r + => SimpleStorage r simpleStorageModule = Module { moduleTxDeliverer = messageHandlers , moduleTxChecker = BA.defaultCheckTx (Proxy :: Proxy MessageApi) (Proxy :: Proxy r) @@ -197,7 +195,7 @@ simpleStorageModule = Module evalToIO :: BA.PureContext - -> Sem (BA.BaseApp BA.PureCoreEffs) a + -> Sem (BA.BaseAppEffs BA.PureCoreEffs) a -> IO a evalToIO context action = do eRes <- BA.runPureCoreEffs context . BA.defaultCompileToPureCore $ action From c0509791b959bf76d43a6c5194c46ac86f3298f4 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Wed, 19 Feb 2020 23:18:33 -0800 Subject: [PATCH 62/70] Test error throwing catching (#214) * bigger application in queryspec * added more robust test of errors * weeder --- .../src/Tendermint/SDK/Modules/Auth/Types.hs | 7 +- .../src/Tendermint/SDK/Modules/Bank/Keeper.hs | 2 +- .../src/Tendermint/SDK/Modules/Bank/Types.hs | 7 +- .../test/Tendermint/SDK/Test/QuerySpec.hs | 107 +++++++++++++++--- .../test/Tendermint/SDK/Test/SimpleStorage.hs | 81 +++++++++++-- 5 files changed, 177 insertions(+), 27 deletions(-) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs index 272f6292..d9388153 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -1,6 +1,9 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} -module Tendermint.SDK.Modules.Auth.Types where +module Tendermint.SDK.Modules.Auth.Types + ( module Tendermint.SDK.Modules.Auth.Types + , Address(..) + ) where import Control.Lens (Wrapped (..), from, iso, view, (&), (.~), (^.), (^..), @@ -21,7 +24,7 @@ import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..), IsKey (..), Queryable (..)) import Tendermint.SDK.Codec (HasCodec (..), defaultSDKAesonOptions) -import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Address (Address (..)) import Web.HttpApiData (FromHttpApiData (..), ToHttpApiData (..)) diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs index cf216cae..aa7e56ed 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs @@ -70,7 +70,7 @@ eval = mapError BaseApp.makeAppError . evalBankKeeper putCoinBalance address coin = do mAcnt <- Auth.getAccount address acnt <- case mAcnt of - Nothing -> Auth.createAccount address >>= pure + Nothing -> Auth.createAccount address Just a -> pure a let updatedCoins = replaceCoinValue coin (Auth.accountCoins acnt) updatedAcnt = acnt { Auth.accountCoins = updatedCoins } diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs index fe7a28d2..2c396461 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs @@ -1,4 +1,9 @@ -module Tendermint.SDK.Modules.Bank.Types where +module Tendermint.SDK.Modules.Bank.Types + ( module Tendermint.SDK.Modules.Bank.Types + , Auth.Amount(..) + , Auth.Coin(..) + , Auth.CoinId(..) + ) where import Data.Aeson as A import Data.Text (Text) diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs index d6f4c3de..37d5c290 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/QuerySpec.hs @@ -1,38 +1,62 @@ module Tendermint.SDK.Test.QuerySpec (spec) where +import Control.Lens ((^.)) import Control.Monad.IO.Class (liftIO) import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) import Data.Maybe (isJust) import Data.Proxy import Data.Text (Text) import qualified Network.ABCI.Types.Messages.Request as Req import qualified Network.ABCI.Types.Messages.Response as Resp +import Polysemy (Sem) import qualified Tendermint.SDK.Application as App import qualified Tendermint.SDK.Application.Module as M import qualified Tendermint.SDK.BaseApp as BA import qualified Tendermint.SDK.BaseApp.Logger.Katip as KL -import Tendermint.SDK.BaseApp.Query (serveQueryApplication) +import Tendermint.SDK.BaseApp.Query (QueryApplication, + serveQueryApplication) import qualified Tendermint.SDK.BaseApp.Store as Store -import Tendermint.SDK.BaseApp.Transaction (serveTxApplication) +import Tendermint.SDK.BaseApp.Transaction (TransactionApplication, + serveTxApplication) import Tendermint.SDK.BaseApp.Transaction.Cache (writeCache) import Tendermint.SDK.Codec (HasCodec (..)) +import qualified Tendermint.SDK.Modules.Auth as A +import qualified Tendermint.SDK.Modules.Bank as B import qualified Tendermint.SDK.Test.SimpleStorage as SS import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) import Test.Hspec -type Ms = '[SS.SimpleStorage] +type Ms = '[SS.SimpleStorage, B.Bank, A.Auth] + +modules :: App.ModuleList Ms (App.Effs Ms BA.PureCoreEffs) +modules = + SS.simpleStorageModule App.:+ + B.bankModule App.:+ + A.authModule App.:+ + App.NilModules + +cProxy :: Proxy BA.PureCoreEffs +cProxy = Proxy + +rProxy :: Proxy (BA.BaseAppEffs BA.PureCoreEffs) +rProxy = Proxy + +app :: M.Application (M.ApplicationC Ms) (M.ApplicationD Ms) (M.ApplicationQ Ms) + (BA.TxEffs BA.:& BA.BaseAppEffs BA.PureCoreEffs) (BA.QueryEffs BA.:& BA.BaseAppEffs BA.PureCoreEffs) +app = M.makeApplication cProxy mempty modules + +doQuery :: QueryApplication (Sem (BA.BaseAppEffs BA.PureCoreEffs)) +doQuery = serveQueryApplication (Proxy @(M.ApplicationQ Ms)) rProxy $ M.applicationQuerier app + +doTx :: TransactionApplication (Sem (BA.BaseAppEffs BA.PureCoreEffs)) +doTx = serveTxApplication (Proxy @(M.ApplicationD Ms)) rProxy (Proxy @'Store.Consensus) $ M.applicationTxDeliverer app spec :: Spec -spec = beforeAll initContext $ +spec = beforeAll initContext $ do describe "Query tests" $ do - let modules :: App.ModuleList Ms (App.Effs Ms BA.PureCoreEffs) - modules = SS.simpleStorageModule App.:+ App.NilModules - cProxy = Proxy @BA.PureCoreEffs - rProxy = Proxy @(BA.BaseAppEffs BA.PureCoreEffs) - app = M.makeApplication cProxy mempty modules - ssServer = serveQueryApplication (Proxy @(M.ApplicationQ Ms)) rProxy $ M.applicationQuerier app - updateCount = serveTxApplication (Proxy @(M.ApplicationD Ms)) rProxy (Proxy @'Store.Consensus) $ M.applicationTxDeliverer app it "Can make a new count and query it with a multiplier" $ \ctx -> do let increaseCountMsg = Msg { msgAuthor = undefined @@ -49,7 +73,7 @@ spec = beforeAll initContext $ , txNonce = undefined } _ <- SS.evalToIO ctx $ do - (_, mCache) <- updateCount tx + (_, mCache) <- doTx tx liftIO (mCache `shouldSatisfy` isJust) let (Just cache) = mCache writeCache cache @@ -62,11 +86,68 @@ spec = beforeAll initContext $ , queryProve = False , queryHeight = 0 } - Resp.Query{..} <- SS.evalToIO ctx $ ssServer q + Resp.Query{..} <- SS.evalToIO ctx $ doQuery q queryCode `shouldBe` 0 let resultCount = decode (Base64.toBytes queryValue) :: Either Text SS.Count resultCount `shouldBe` Right 3 + describe "throw/catch tests" $ do + it "Can throw and catch a bank error" $ \ctx -> do + let addr = A.Address . Hex.fromBytes $ ("1234" :: ByteString) + + queryBalance = do + let path = "/bank/balance?coin_id=" <> B.unCoinId SS.simpleStorageCoinId + q1 = Req.Query + { queryPath = path + , queryData = Base64.fromBytes (addr ^. BA.rawKey) + , queryProve = False + , queryHeight = 0 + } + Resp.Query{..} <- SS.evalToIO ctx $ doQuery q1 + queryCode `shouldBe` 0 + let Right resultBalance = decode (Base64.toBytes queryValue) :: Either Text B.Coin + pure resultBalance + + initialBalance <- queryBalance + + let increaseCountMsg = Msg + { msgAuthor = addr + , msgType = "update_paid_count" + , msgData = encode $ SS.UpdatePaidCountTx 33 10 + } + + tx = BA.RoutingTx $ Tx + { txMsg = increaseCountMsg + , txRoute = "simple_storage" + , txGas = 0 + , txSignature = undefined + , txSignBytes = undefined + , txSigner = undefined + , txNonce = undefined + } + _ <- SS.evalToIO ctx $ do + (_, mCache) <- doTx tx + liftIO (mCache `shouldSatisfy` isJust) + let (Just cache) = mCache + writeCache cache + _ <- Store.commit + Store.commitBlock + let q = Req.Query + -- TODO -- this shouldn't require / count + { queryPath = "/simple_storage/count" + , queryData = undefined + , queryProve = False + , queryHeight = 0 + } + Resp.Query{..} <- SS.evalToIO ctx $ doQuery q + queryCode `shouldBe` 0 + let resultCount = decode (Base64.toBytes queryValue) :: Either Text SS.Count + resultCount `shouldBe` Right 33 + + finalBalance <- queryBalance + finalBalance `shouldBe` initialBalance {B.coinAmount = B.coinAmount initialBalance + 1} + + initContext :: IO BA.PureContext initContext = do BA.makePureContext (KL.InitialLogNamespace "test" "spec") diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index 24a165fe..c91df931 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -3,7 +3,9 @@ module Tendermint.SDK.Test.SimpleStorage ( SimpleStorage , UpdateCountTx(..) + , UpdatePaidCountTx(..) , simpleStorageModule + , simpleStorageCoinId , evalToIO , Count(..) ) where @@ -20,13 +22,17 @@ import qualified Data.Serialize as Serialize import Data.Serialize.Text () import Data.String.Conversions (cs) import Data.Validation (Validation (..)) +import Data.Word (Word64) import GHC.Generics (Generic) +import GHC.TypeLits (symbolVal) import Polysemy -import Polysemy.Error (Error, throw) +import Polysemy.Error (Error, catch, throw) import Servant.API import Tendermint.SDK.Application (Module (..), ModuleEffs) import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Codec (HasCodec (..)) +import qualified Tendermint.SDK.Modules.Bank as B +import Tendermint.SDK.Types.Address (Address) import Tendermint.SDK.Types.Message (HasMessageType (..), Msg (..), ValidateMessage (..)) @@ -36,6 +42,10 @@ import Tendermint.SDK.Types.Transaction (Tx (..)) -------------------------------------------------------------------------------- -- Types -------------------------------------------------------------------------------- +type SimpleStorageName = "simple_storage" + +simpleStorageCoinId :: B.CoinId +simpleStorageCoinId = B.CoinId . cs . symbolVal $ Proxy @SimpleStorageName newtype Count = Count Int32 deriving (Eq, Show, Ord, Num, Serialize.Serialize) @@ -51,8 +61,8 @@ instance BA.RawKey CountKey where countKey :: ByteString countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) -instance BA.IsKey CountKey "simple_storage" where - type Value CountKey "simple_storage" = Count +instance BA.IsKey CountKey SimpleStorageName where + type Value CountKey SimpleStorageName = Count instance BA.FromQueryData CountKey @@ -79,12 +89,29 @@ instance HasCodec UpdateCountTx where instance ValidateMessage UpdateCountTx where validateMessage _ = Success () +data UpdatePaidCountTx = UpdatePaidCountTx + { updatePaidCountTxCount :: Int32 + , updatePaidCountTxAmount :: Word64 + } deriving (Show, Eq, Generic) + +instance Serialize.Serialize UpdatePaidCountTx + +instance HasMessageType UpdatePaidCountTx where + messageType _ = "update_paid_count" + +instance HasCodec UpdatePaidCountTx where + encode = Serialize.encode + decode = first cs . Serialize.decode + +instance ValidateMessage UpdatePaidCountTx where + validateMessage _ = Success () + -------------------------------------------------------------------------------- -- Keeper -------------------------------------------------------------------------------- -storeKey :: BA.StoreKey "simple_storage" -storeKey = BA.StoreKey "simple_storage" +storeKey :: BA.StoreKey SimpleStorageName +storeKey = BA.StoreKey (cs . symbolVal $ Proxy @SimpleStorageName) data SimpleStorageKeeper m a where PutCount :: Count -> SimpleStorageKeeper m () @@ -99,6 +126,27 @@ updateCount -> Sem r () updateCount count = putCount count +updatePaidCount + :: forall r . + Member SimpleStorageKeeper r + => Members B.BankEffs r + => Members BA.BaseEffs r + => Address + -> Count + -> B.Amount + -> Sem r () +updatePaidCount from count amount = + catch @_ @r + ( do + B.burn from (B.Coin simpleStorageCoinId amount) + updateCount count + ) + (\(B.InsufficientFunds _) -> do + let mintAmount = B.Coin simpleStorageCoinId (amount + 1) + B.mint from mintAmount + updatePaidCount from count amount + ) + type SimpleStorageEffs = '[SimpleStorageKeeper] eval @@ -115,17 +163,18 @@ eval = interpret (\case -------------------------------------------------------------------------------- type MessageApi = - BA.TypedMessage UpdateCountTx BA.:~> BA.Return () + BA.TypedMessage UpdateCountTx BA.:~> BA.Return () :<|> + BA.TypedMessage UpdatePaidCountTx BA.:~> BA.Return () messageHandlers :: Member SimpleStorageKeeper r - => Members BA.TxEffs r + => Members B.BankEffs r + => Members BA.BaseEffs r => BA.RouteTx MessageApi r -messageHandlers = updateCountH +messageHandlers = updateCountH :<|> updatePaidCountH updateCountH :: Member SimpleStorageKeeper r - => Members BA.TxEffs r => BA.RoutingTx UpdateCountTx -> Sem r () updateCountH (BA.RoutingTx Tx{txMsg}) = @@ -133,6 +182,18 @@ updateCountH (BA.RoutingTx Tx{txMsg}) = UpdateCountTx{updateCountTxCount} = msgData in updateCount (Count updateCountTxCount) +updatePaidCountH + :: Member SimpleStorageKeeper r + => Members B.BankEffs r + => Members BA.BaseEffs r + => BA.RoutingTx UpdatePaidCountTx + -> Sem r () +updatePaidCountH (BA.RoutingTx Tx{txMsg}) = + let Msg{msgData, msgAuthor} = txMsg + UpdatePaidCountTx{..} = msgData + in updatePaidCount msgAuthor (Count updatePaidCountTxCount) + (B.Amount updatePaidCountTxAmount) + -------------------------------------------------------------------------------- -- Server -------------------------------------------------------------------------------- @@ -181,7 +242,7 @@ querier = -------------------------------------------------------------------------------- type SimpleStorage = - Module "simple_storage" MessageApi MessageApi QueryApi SimpleStorageEffs '[] + Module SimpleStorageName MessageApi MessageApi QueryApi SimpleStorageEffs '[B.Bank] simpleStorageModule :: Members (ModuleEffs SimpleStorage) r From 551c4515bb4d56213a2a7d513a1f2b7f2f64d7ff Mon Sep 17 00:00:00 2001 From: Kristoffer Josefsson Date: Fri, 21 Feb 2020 16:10:59 -0800 Subject: [PATCH 63/70] WIP: Stackgarbage (#216) * stackarbage * works --- INSTALL.md | 68 +-------------------------- hs-abci-docs/doc/0020-Installation.md | 68 ++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 68 deletions(-) mode change 100644 => 120000 INSTALL.md mode change 120000 => 100644 hs-abci-docs/doc/0020-Installation.md diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index b818511f..00000000 --- a/INSTALL.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: Installation ---- - -## Build - -### Prerequisites - -#### stack -At the moment the project's build is managed by `stack`. You can find everything you need regarding how to install stack on your machine [here](https://docs.haskellstack.org/en/stable/README/). - -#### protoc -We use a custom setup to generate Haskell bindings to the protobuf files, using the proto-lens library from Google. In order for this to work you need to have the protobuf compiler `protoc` on your machine. You can get installation instructions [here](https://google.github.io/proto-lens/installing-protoc.html) - -#### libsecp256k1 -In order to build with stack you will need this. On MacOS you can use brew: - -``` -> brew tap cuber/homebrew-libsecp256k1 -> brew install libsecp256k1 -``` - -On linux: - -``` -> sudo add-apt-repository ppa:tah83/secp256k1 -> sudo apt-get update -> sudo apt-get install libsecp256k1 -``` - -#### style -You will also need to install `hlint` and `stylish-haskell` for code hygiene during development. In the project root simply run - -```bash -> stack install hlint stylish-haskell -``` - -### Commands -There is a `Makefile` for this project where you can find all of the options for building, testing etc. The `Makefile` -is documented and there is a help menu which you can access via the commands `make` or `make help`. - -## Protobuf Files and Generated Modules -The protobuf files are all in the `/protos` directory, and we use a custom setup in order -to generate the corresponding `Proto.*` Haskell modules. If you want to view all of these -generated modules, you can run - -```bash -> find hs-abci-types/.stack-work -path '*autogen/Proto' -``` - -to find the root directory. - -## Style Guide -There is a `.stylish-haskell.yaml` file that controls some of the style guide, particularly -around import statements and some indentation rules. There is also a small guide for things that -fall outside of this in the [style wiki](https://github.com/f-o-a-m/kepler/wiki/code-style-guide). -If it's not covered by either of these but you think it's really important, mention it in an issue. - -## Building documentation -You can build the haddocks for the library code only using - -```bash -make build-docs-local -``` - -This does not build and link documentation for dependencies, useful mostly for testing -documentation formatting. diff --git a/INSTALL.md b/INSTALL.md new file mode 120000 index 00000000..075652d7 --- /dev/null +++ b/INSTALL.md @@ -0,0 +1 @@ +hs-abci-docs/doc/0020-Installation.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0020-Installation.md b/hs-abci-docs/doc/0020-Installation.md deleted file mode 120000 index 5f52b2be..00000000 --- a/hs-abci-docs/doc/0020-Installation.md +++ /dev/null @@ -1 +0,0 @@ -../../INSTALL.md \ No newline at end of file diff --git a/hs-abci-docs/doc/0020-Installation.md b/hs-abci-docs/doc/0020-Installation.md new file mode 100644 index 00000000..b818511f --- /dev/null +++ b/hs-abci-docs/doc/0020-Installation.md @@ -0,0 +1,67 @@ +--- +title: Installation +--- + +## Build + +### Prerequisites + +#### stack +At the moment the project's build is managed by `stack`. You can find everything you need regarding how to install stack on your machine [here](https://docs.haskellstack.org/en/stable/README/). + +#### protoc +We use a custom setup to generate Haskell bindings to the protobuf files, using the proto-lens library from Google. In order for this to work you need to have the protobuf compiler `protoc` on your machine. You can get installation instructions [here](https://google.github.io/proto-lens/installing-protoc.html) + +#### libsecp256k1 +In order to build with stack you will need this. On MacOS you can use brew: + +``` +> brew tap cuber/homebrew-libsecp256k1 +> brew install libsecp256k1 +``` + +On linux: + +``` +> sudo add-apt-repository ppa:tah83/secp256k1 +> sudo apt-get update +> sudo apt-get install libsecp256k1 +``` + +#### style +You will also need to install `hlint` and `stylish-haskell` for code hygiene during development. In the project root simply run + +```bash +> stack install hlint stylish-haskell +``` + +### Commands +There is a `Makefile` for this project where you can find all of the options for building, testing etc. The `Makefile` +is documented and there is a help menu which you can access via the commands `make` or `make help`. + +## Protobuf Files and Generated Modules +The protobuf files are all in the `/protos` directory, and we use a custom setup in order +to generate the corresponding `Proto.*` Haskell modules. If you want to view all of these +generated modules, you can run + +```bash +> find hs-abci-types/.stack-work -path '*autogen/Proto' +``` + +to find the root directory. + +## Style Guide +There is a `.stylish-haskell.yaml` file that controls some of the style guide, particularly +around import statements and some indentation rules. There is also a small guide for things that +fall outside of this in the [style wiki](https://github.com/f-o-a-m/kepler/wiki/code-style-guide). +If it's not covered by either of these but you think it's really important, mention it in an issue. + +## Building documentation +You can build the haddocks for the library code only using + +```bash +make build-docs-local +``` + +This does not build and link documentation for dependencies, useful mostly for testing +documentation formatting. From 30b0b8d3ab0d75c500354937e06b4396aa9c3ccb Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Mon, 24 Feb 2020 08:22:18 -0800 Subject: [PATCH 64/70] allow ns type param in store key to be any kind (#219) * fix * Delete layout.gv --- .../src/Nameservice/Modules/Nameservice.hs | 2 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 4 ++-- .../src/Nameservice/Modules/Nameservice/Types.hs | 8 +++++--- .../tutorial/Tutorial/Nameservice/02-Types.md | 16 +++++++--------- .../tutorial/Tutorial/Nameservice/04-Keeper.md | 9 +++------ .../tutorial/Tutorial/Nameservice/07-Module.md | 4 ++-- .../Modules/SimpleStorage/Keeper.hs | 2 +- .../SimpleStorage/Modules/SimpleStorage/Types.hs | 6 ++++-- .../src/Tendermint/SDK/BaseApp/Query/Store.hs | 7 +++---- .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 2 +- .../src/Tendermint/SDK/Modules/Auth/Types.hs | 8 ++++---- .../test/Tendermint/SDK/Test/SimpleStorage.hs | 8 +++++--- 12 files changed, 38 insertions(+), 38 deletions(-) diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs index 056cf5b7..ff58bfa2 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -27,7 +27,7 @@ import Tendermint.SDK.Modules.Bank (Bank) type Nameservice = - Module "nameservice" MessageApi MessageApi QueryApi NameserviceEffs '[Bank] + Module NameserviceName MessageApi MessageApi QueryApi NameserviceEffs '[Bank] nameserviceModule :: Members (ModuleEffs Nameservice) r diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index ae09afe4..2c5fd99a 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -36,8 +36,8 @@ makeSem ''NameserviceKeeper type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] -storeKey :: BaseApp.StoreKey NameserviceModuleName -storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @NameserviceModuleName +storeKey :: BaseApp.StoreKey NameserviceNamespace +storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @NameserviceName nameserviceCoinId :: CoinId nameserviceCoinId = "nameservice" diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 6dbc3f27..163e26e2 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -27,7 +27,9 @@ import Tendermint.SDK.Types.Address (Address) -------------------------------------------------------------------------------- -type NameserviceModuleName = "nameservice" +type NameserviceName = "nameservice" + +data NameserviceNamespace -------------------------------------------------------------------------------- @@ -76,8 +78,8 @@ instance HasCodec Whois where instance BaseApp.RawKey Name where rawKey = iso (\(Name n) -> cs n) (Name . cs) -instance BaseApp.IsKey Name NameserviceModuleName where - type Value Name NameserviceModuleName = Whois +instance BaseApp.IsKey Name NameserviceNamespace where + type Value Name NameserviceNamespace = Whois instance BaseApp.Queryable Whois where type Name Whois = "whois" diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md index 22ddc6d8..bc88f58f 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md @@ -44,12 +44,10 @@ module Tutorial.Nameservice.Types where import Control.Lens (iso) import qualified Data.Aeson as A import Data.Bifunctor (bimap) -import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word (Word64) import GHC.Generics (Generic) -import GHC.TypeLits (symbolVal) import Nameservice.Aeson (defaultNameserviceOptions) import Proto3.Suite (Message, fromByteString, toLazyByteString) import qualified Tendermint.SDK.BaseApp as BA @@ -140,10 +138,10 @@ class RawKey k => IsKey k ns where For the case of the `Name -> Whois` mapping, the `IsKey` instance looked like looks like this: ~~~ haskell -type NameserviceModuleName = "nameservice" +data NameserviceNamespace -instance BA.IsKey Name NameserviceModuleName where - type Value Name NameserviceModuleName = Whois +instance BA.IsKey Name NameserviceNamespace where + type Value Name NameserviceNamespace = Whois ~~~ At is point, you can use the database operations exported by `Tendermint.SDK.BaseApp.Store` such as `put`/`set`/`delete` for key value pairs of type `(Name, Whois)`. @@ -194,23 +192,23 @@ data NameserviceError = | InvalidDelete Text instance BA.IsAppError NameserviceError where - -- remember 'symbolVal (Proxy @NameserviceModuleName)' resolves to "nameservice" + -- remember 'symbolVal (Proxy @NameserviceName)' resolves to "nameservice" makeAppError (InsufficientBid msg) = BA.AppError { appErrorCode = 1 - , appErrorCodespace = cs $ symbolVal (Proxy @NameserviceModuleName) + , appErrorCodespace = "nameservice" , appErrorMessage = msg } makeAppError (UnauthorizedSet msg) = BA.AppError { appErrorCode = 2 - , appErrorCodespace = cs $ symbolVal (Proxy @NameserviceModuleName) + , appErrorCodespace = "nameservice" , appErrorMessage = msg } makeAppError (InvalidDelete msg) = BA.AppError { appErrorCode = 3 - , appErrorCodespace = cs $ symbolVal (Proxy @NameserviceModuleName) + , appErrorCodespace = "nameservice" , appErrorMessage = msg } ~~~ diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md index cc09f37a..da5a0c00 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md @@ -14,14 +14,11 @@ title: Nameservice - Keeper {-# LANGUAGE TemplateHaskell #-} module Tutorial.Nameservice.Keeper where -import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (symbolVal) import Polysemy (Sem, Member, Members, makeSem, interpret) import Polysemy.Error (Error, throw, mapError) import Polysemy.Output (Output) import Nameservice.Modules.Nameservice.Messages (DeleteName(..)) -import Nameservice.Modules.Nameservice.Types (Whois(..), Name, NameDeleted(..), NameserviceModuleName, NameserviceError(..)) +import Nameservice.Modules.Nameservice.Types (Whois(..), Name, NameDeleted(..), NameserviceNamespace, NameserviceError(..)) import qualified Tendermint.SDK.BaseApp as BA import Tendermint.SDK.Modules.Auth (AuthEffs, Coin(..)) import Tendermint.SDK.Modules.Bank (BankEffs, mint) @@ -100,8 +97,8 @@ Like we said before, all transactions must ultimately compile to the set of effe A `StoreKey` is effectively a namespacing inside the database, and is unique for a given module. In theory it could be any `ByteString`, but the natural definition in the case of Nameservice is would be something like ~~~ haskell -storeKey :: BA.StoreKey NameserviceModuleName -storeKey = BA.StoreKey . cs . symbolVal $ (Proxy @NameserviceModuleName) +storeKey :: BA.StoreKey NameserviceNamespace +storeKey = BA.StoreKey "nameservice" ~~~ With this `storeKey` it is possible to write the `eval` function to resolve the effects defined in Nameservice, namely the `NameserviceKeeper` effect and `Error NameserviceError`: diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md index 49cd3211..40789369 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/07-Module.md @@ -14,7 +14,7 @@ module Tutorial.Nameservice.Module where import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, eval) import Nameservice.Modules.Nameservice.Query (QueryApi, querier) import Nameservice.Modules.Nameservice.Router (MessageApi, messageHandlers) -import Nameservice.Modules.Nameservice.Types (NameserviceModuleName) +import Nameservice.Modules.Nameservice.Types (NameserviceName) import Tendermint.SDK.Application (Module (..), ModuleEffs) import Tendermint.SDK.BaseApp (DefaultCheckTx (..)) import Tendermint.SDK.Modules.Bank (Bank) @@ -23,7 +23,7 @@ import Polysemy (Members) -- a convenient type alias type Nameservice = - Module NameserviceModuleName MessageApi MessageApi QueryApi NameserviceEffs '[Bank] + Module NameserviceName MessageApi MessageApi QueryApi NameserviceEffs '[Bank] nameserviceModule :: Members (ModuleEffs Nameservice) r diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs index b24003e4..c985717e 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -14,7 +14,7 @@ import Polysemy.Output (Output) import SimpleStorage.Modules.SimpleStorage.Types import qualified Tendermint.SDK.BaseApp as BaseApp -storeKey :: BaseApp.StoreKey SimpleStorageName +storeKey :: BaseApp.StoreKey SimpleStorageNamespace storeKey = BaseApp.StoreKey "simple_storage" data SimpleStorageKeeper m a where diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs index 90daeca6..467a7c13 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -14,6 +14,8 @@ import GHC.Generics (Generic) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) +data SimpleStorageNamespace + newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON, Serialize.Serialize) data CountKey = CountKey @@ -28,8 +30,8 @@ instance BaseApp.RawKey CountKey where countKey :: ByteString countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) -instance BaseApp.IsKey CountKey "simple_storage" where - type Value CountKey "simple_storage" = Count +instance BaseApp.IsKey CountKey SimpleStorageNamespace where + type Value CountKey SimpleStorageNamespace = Count instance BaseApp.FromQueryData CountKey diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index 8134a31f..c6cb965d 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -9,8 +9,7 @@ import Control.Lens (to, (^.)) import Data.ByteArray.Base64String (fromBytes) import Data.Proxy import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, Symbol, - symbolVal) +import GHC.TypeLits (KnownSymbol, symbolVal) import Polysemy (Member, Members, Sem) import Polysemy.Error (throw) import Polysemy.Tagged (Tagged) @@ -38,7 +37,7 @@ instance (Queryable a, Member (Tagged 'QueryAndMempool ReadStore) r, KnownSymbo where proxyPath = Proxy :: Proxy (Name a) hoistQueryRouter _ _ = ($) -class StoreQueryHandler a (ns :: Symbol) h where +class StoreQueryHandler a ns h where storeQueryHandler :: Proxy a -> StoreKey ns -> h instance @@ -62,7 +61,7 @@ instance , queryResultHeight = 0 } -class StoreQueryHandlers (kvs :: [*]) (ns :: Symbol) r where +class StoreQueryHandlers (kvs :: [*]) ns r where type QueryApi kvs :: * storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy r -> RouteQ (QueryApi kvs) r diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs index d05cc6b9..ce053568 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -18,7 +18,7 @@ makeSem ''Accounts type AuthEffs = '[Accounts, Error AuthError] -storeKey :: StoreKey AuthName +storeKey :: StoreKey AuthNamespace storeKey = StoreKey "auth" eval diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs index d9388153..ca34bd9e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -1,5 +1,3 @@ -{-# OPTIONS_GHC -fno-warn-orphans #-} - module Tendermint.SDK.Modules.Auth.Types ( module Tendermint.SDK.Modules.Auth.Types , Address(..) @@ -32,8 +30,10 @@ import Web.HttpApiData (FromHttpApiData (..), type AuthName = "auth" -instance IsKey Address AuthName where - type Value Address AuthName = Account +data AuthNamespace + +instance IsKey Address AuthNamespace where + type Value Address AuthNamespace = Account instance Queryable Account where type Name Account = "account" diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index c91df931..0e9056b1 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -44,6 +44,8 @@ import Tendermint.SDK.Types.Transaction (Tx (..)) -------------------------------------------------------------------------------- type SimpleStorageName = "simple_storage" +data SimpleStorageNamespace + simpleStorageCoinId :: B.CoinId simpleStorageCoinId = B.CoinId . cs . symbolVal $ Proxy @SimpleStorageName @@ -61,8 +63,8 @@ instance BA.RawKey CountKey where countKey :: ByteString countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) -instance BA.IsKey CountKey SimpleStorageName where - type Value CountKey SimpleStorageName = Count +instance BA.IsKey CountKey SimpleStorageNamespace where + type Value CountKey SimpleStorageNamespace = Count instance BA.FromQueryData CountKey @@ -110,7 +112,7 @@ instance ValidateMessage UpdatePaidCountTx where -- Keeper -------------------------------------------------------------------------------- -storeKey :: BA.StoreKey SimpleStorageName +storeKey :: BA.StoreKey SimpleStorageNamespace storeKey = BA.StoreKey (cs . symbolVal $ Proxy @SimpleStorageName) data SimpleStorageKeeper m a where From 01a7b576764d9123a5a5b8907ff4bcfcff3244b5 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Wed, 26 Feb 2020 09:35:02 -0800 Subject: [PATCH 65/70] remove inj restriction, update tests (#220) --- .../Tendermint/SDK/BaseApp/Store/RawStore.hs | 2 +- .../test/Tendermint/SDK/Test/SimpleStorage.hs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs index a632e250..551d4ffd 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs @@ -74,7 +74,7 @@ instance RawKey Address where rawKey = iso addressToBytes addressFromBytes class RawKey k => IsKey k ns where - type Value k ns = a | a -> ns k + type Value k ns :: * prefix :: Proxy k -> Proxy ns -> BS.ByteString default prefix :: Proxy k -> Proxy ns -> BS.ByteString diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index 0e9056b1..f78350d5 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -71,6 +71,15 @@ instance BA.FromQueryData CountKey instance BA.Queryable Count where type Name Count = "count" +newtype AmountPaid = AmountPaid B.Amount deriving (Eq, Show, Num, Ord, HasCodec) + +-- for reporting how much paid to change count +instance BA.IsKey Address SimpleStorageNamespace where + type Value Address SimpleStorageNamespace = AmountPaid + +instance BA.Queryable AmountPaid where + type Name AmountPaid = "paid" + -------------------------------------------------------------------------------- -- Message Types -------------------------------------------------------------------------------- @@ -118,6 +127,7 @@ storeKey = BA.StoreKey (cs . symbolVal $ Proxy @SimpleStorageName) data SimpleStorageKeeper m a where PutCount :: Count -> SimpleStorageKeeper m () GetCount :: SimpleStorageKeeper m (Maybe Count) + StoreAmountPaid :: Address -> AmountPaid -> SimpleStorageKeeper m () makeSem ''SimpleStorageKeeper @@ -142,6 +152,7 @@ updatePaidCount from count amount = ( do B.burn from (B.Coin simpleStorageCoinId amount) updateCount count + storeAmountPaid from (AmountPaid amount) ) (\(B.InsufficientFunds _) -> do let mintAmount = B.Coin simpleStorageCoinId (amount + 1) @@ -158,6 +169,7 @@ eval eval = interpret (\case PutCount count -> BA.put storeKey CountKey count GetCount -> BA.get storeKey CountKey + StoreAmountPaid from amt -> BA.put storeKey from amt ) -------------------------------------------------------------------------------- @@ -200,7 +212,10 @@ updatePaidCountH (BA.RoutingTx Tx{txMsg}) = -- Server -------------------------------------------------------------------------------- -type CountStoreContents = '[(CountKey, Count)] +type CountStoreContents = + '[ (CountKey, Count) + , (Address, AmountPaid) + ] type GetMultipliedCount = "manipulated" From f9426c32b7ffea44bd365c02326cd60505cbea69 Mon Sep 17 00:00:00 2001 From: Charles Crain Date: Thu, 27 Feb 2020 13:24:05 -0800 Subject: [PATCH 66/70] Generic to event rebased (#215) * WIP | base event classes * comiles with gInstances * sdk compileS * fromEvent class * WIP | compiling, fails test due to heterogeneous list when parsing events * moved event parsing to individual tests * switch to new ToEvent/FromEvent functions for tests * rebase with master * tests updated with latest event changes * passing event monitoring test * event monitoring tests in kvstore test * reflect ToEvent in FromEvent with uppercase * more appropriate ordering Co-authored-by: Martin Allen <31280145+blinky3713@users.noreply.github.com> --- hs-abci-docs/nameservice/package.yaml | 6 + .../Nameservice/Modules/Nameservice/Types.hs | 15 +- .../test/Nameservice/Test/E2ESpec.hs | 158 ++++++++++---- .../tutorial/Tutorial/Nameservice/02-Types.md | 17 +- .../Modules/SimpleStorage/Types.hs | 3 +- .../src/Tendermint/SDK/BaseApp/Events.hs | 66 +++--- hs-abci-sdk/src/Tendermint/SDK/Codec.hs | 21 +- .../src/Tendermint/SDK/Modules/Bank/Types.hs | 3 +- hs-abci-test-utils/package.yaml | 4 +- .../src/Tendermint/Utils/ClientUtils.hs | 15 +- .../src/Tendermint/Utils/Events.hs | 70 ++++--- .../test/Tendermint/Utils/Test/EventSpec.hs | 24 ++- .../kv-test/KVStore/Test/KVSpec.hs | 197 ++++++++++++------ hs-tendermint-client/package.yaml | 4 + 14 files changed, 409 insertions(+), 194 deletions(-) diff --git a/hs-abci-docs/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml index fb10f96a..9f05ecef 100644 --- a/hs-abci-docs/nameservice/package.yaml +++ b/hs-abci-docs/nameservice/package.yaml @@ -188,6 +188,12 @@ tests: - hs-abci-test-utils - hs-tendermint-client - hspec + - aeson - mtl - nameservice - servant + - text + - unordered-containers + - lens-aeson + - lens + - string-conversions diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 163e26e2..131d9ada 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -33,7 +33,8 @@ data NameserviceNamespace -------------------------------------------------------------------------------- -newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON) +newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON, HasCodec) + instance Primitive Name where encodePrimitive n (Name txt) = Encode.text n . TL.fromStrict $ txt decodePrimitive = Name . TL.toStrict <$> Decode.text @@ -130,8 +131,7 @@ instance ToJSON Faucetted where toJSON = A.genericToJSON faucettedAesonOptions instance FromJSON Faucetted where parseJSON = A.genericParseJSON faucettedAesonOptions -instance BaseApp.ToEvent Faucetted where - makeEventType _ = "Faucetted" +instance BaseApp.ToEvent Faucetted instance BaseApp.Select Faucetted data NameClaimed = NameClaimed @@ -148,8 +148,7 @@ instance ToJSON NameClaimed where toJSON = A.genericToJSON nameClaimedAesonOptions instance FromJSON NameClaimed where parseJSON = A.genericParseJSON nameClaimedAesonOptions -instance BaseApp.ToEvent NameClaimed where - makeEventType _ = "NameClaimed" +instance BaseApp.ToEvent NameClaimed instance BaseApp.Select NameClaimed data NameRemapped = NameRemapped @@ -165,8 +164,7 @@ instance ToJSON NameRemapped where toJSON = A.genericToJSON nameRemappedAesonOptions instance FromJSON NameRemapped where parseJSON = A.genericParseJSON nameRemappedAesonOptions -instance BaseApp.ToEvent NameRemapped where - makeEventType _ = "NameRemapped" +instance BaseApp.ToEvent NameRemapped instance BaseApp.Select NameRemapped data NameDeleted = NameDeleted @@ -180,6 +178,5 @@ instance ToJSON NameDeleted where toJSON = A.genericToJSON nameDeletedAesonOptions instance FromJSON NameDeleted where parseJSON = A.genericParseJSON nameDeletedAesonOptions -instance BaseApp.ToEvent NameDeleted where - makeEventType _ = "NameDeleted" +instance BaseApp.ToEvent NameDeleted instance BaseApp.Select NameDeleted diff --git a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs index e076bb4a..e594f340 100644 --- a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,9 +1,18 @@ module Nameservice.Test.E2ESpec (spec) where +import Control.Concurrent (forkIO) +import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, + readMVar) +import Control.Lens ((^?)) import Control.Monad (forM_, void) import Control.Monad.Reader (ReaderT, runReaderT) +import qualified Data.Aeson as A +import Data.Aeson.Lens (key) import Data.Default.Class (def) +import Data.HashSet (fromList) import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) import Nameservice.Application import qualified Nameservice.Modules.Nameservice as N import Nameservice.Test.EventOrphans () @@ -11,6 +20,7 @@ import qualified Network.Tendermint.Client as RPC import Servant.API ((:<|>) (..)) import qualified Tendermint.SDK.Application.Module as M import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Events (Event (..), ToEvent (..)) import Tendermint.SDK.BaseApp.Query (QueryArgs (..), QueryResult (..), defaultQueryArgs) @@ -31,28 +41,32 @@ import Tendermint.Utils.ClientUtils (assertQuery, assertTx, ensureQueryResponseCode, ensureResponseCodes, rpcConfig) +import Tendermint.Utils.Events (FromEvent (..)) import Tendermint.Utils.User (makeSignerFromUser, makeUser) + import Test.Hspec + + spec :: Spec spec = do let satoshi = N.Name "satoshi" faucetAmount = 1000 - beforeAll (forM_ [user1, user2] $ faucetUser faucetAmount) $ do + beforeAll (testInit faucetAmount) $ do describe "Nameservice Spec" $ do - it "Can query /health to make sure the node is alive" $ do + it "Can query /health to make sure the node is alive" $ const $ do resp <- RPC.runTendermintM rpcConfig $ RPC.health resp `shouldBe` RPC.ResultHealth - it "Can query account balances" $ do + it "Can query account balances" $ const $ do void . assertQuery . RPC.runTendermintM rpcConfig $ let queryArgs = defaultQueryArgs { queryArgsData = signerAddress user1 } in getBalance queryArgs N.nameserviceCoinId - it "Can create a name" $ do + it "Can create a name" $ \tenv -> do let val = "hello world" msg = N.BuyName { buyNameBid = 0 @@ -70,13 +84,16 @@ spec = do { txOptsSigner = user1 , txOptsGas = 0 } + + -- Add event to be monitored and later checked for + addEventToCheck tenv claimedLog + resp <- assertTx . runTxClientM $ buyName opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @N.NameClaimed) resp - errs `shouldBe` [] - filter (claimedLog ==) es `shouldBe` [claimedLog] + [evclaimedLog] <- deliverTxEvents (Proxy @N.NameClaimed) resp + fromEvent evclaimedLog `shouldBe` Right claimedLog - it "Can query for a name" $ do + it "Can query for a name" $ const $ do let expected = N.Whois { whoisValue = "hello world" , whoisOwner = signerAddress user1 @@ -86,13 +103,13 @@ spec = do getWhois defaultQueryArgs { queryArgsData = satoshi } foundWhois `shouldBe` expected - it "Can query for a name that doesn't exist" $ do + it "Can query for a name that doesn't exist" $ const $ do let nope = N.Name "nope" resp <- RPC.runTendermintM rpcConfig $ getWhois defaultQueryArgs { queryArgsData = nope } ensureQueryResponseCode 2 resp - it "Can set a name value" $ do + it "Can set a name value" $ \tenv -> do let oldVal = "hello world" newVal = "goodbye to a world" msg = N.SetName @@ -109,11 +126,14 @@ spec = do { txOptsSigner = user1 , txOptsGas = 0 } + + -- Add event to be monitored and later checked for + addEventToCheck tenv remappedLog + resp <- assertTx . runTxClientM $ setName opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @N.NameRemapped) resp - errs `shouldBe` [] - filter (remappedLog ==) es `shouldBe` [remappedLog] + [evremappedLog] <- deliverTxEvents (Proxy @N.NameRemapped) resp + fromEvent evremappedLog `shouldBe` Right remappedLog let expected = N.Whois { whoisValue = "goodbye to a world" @@ -124,7 +144,7 @@ spec = do getWhois defaultQueryArgs { queryArgsData = satoshi } foundWhois `shouldBe` expected - it "Can fail to set a name" $ do + it "Can fail to set a name" $ const $ do -- try to set a name without being the owner let msg = N.SetName { setNameName = satoshi @@ -138,7 +158,7 @@ spec = do resp <- assertTx . runTxClientM $ setName opts msg ensureResponseCodes (0,2) resp - it "Can buy an existing name" $ do + it "Can buy an existing name" $ \tenv -> do balance1 <- getUserBalance user1 balance2 <- getUserBalance user2 N.Whois{whoisPrice} <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ @@ -157,16 +177,26 @@ spec = do , nameClaimedValue = newVal , nameClaimedBid = purchaseAmount } + transferLog = B.TransferEvent + { transferEventAmount = purchaseAmount + , transferEventCoinId = N.nameserviceCoinId + , transferEventTo = signerAddress user1 + , transferEventFrom = signerAddress user2 + } opts = TxOpts { txOptsSigner = user2 , txOptsGas = 0 } + -- Add event to be monitored and later checked for + addEventToCheck tenv transferLog + addEventToCheck tenv claimedLog + resp <- assertTx . runTxClientM $ buyName opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @N.NameClaimed) resp - errs `shouldBe` [] - filter (claimedLog ==) es `shouldBe` [claimedLog] + [evtransferLog, evclaimedLog] <- deliverTxEvents (Proxy @N.NameClaimed) resp + fromEvent evtransferLog `shouldBe` Right transferLog + fromEvent evclaimedLog `shouldBe` Right claimedLog -- check for updated balances - seller: addr1, buyer: addr2 sellerFoundAmount <- getUserBalance user1 @@ -185,13 +215,14 @@ spec = do -- @NOTE: this is possibly a problem with the go application too -- https://cosmos.network/docs/tutorial/buy-name.html#msg - it "Can buy self-owned names and make a profit " $ do + it "Can buy self-owned names and make a profit " $ \tenv -> do -- check balance before beforeBuyAmount <- getUserBalance user2 -- buy - let val = "hello (again) world" + let bid = 500 + val = "hello (again) world" msg = N.BuyName - { buyNameBid = 500 + { buyNameBid = bid , buyNameName = satoshi , buyNameValue = val , buyNameBuyer = signerAddress user2 @@ -200,25 +231,35 @@ spec = do { nameClaimedOwner = signerAddress user2 , nameClaimedName = satoshi , nameClaimedValue = val - , nameClaimedBid = 500 + , nameClaimedBid = bid + } + transferLog = B.TransferEvent + { transferEventAmount = bid + , transferEventCoinId = N.nameserviceCoinId + , transferEventTo = signerAddress user2 + , transferEventFrom = signerAddress user2 } opts = TxOpts { txOptsSigner = user2 , txOptsGas = 0 } + -- Add event to be monitored and later checked for + addEventToCheck tenv transferLog + addEventToCheck tenv claimedLog + resp <- assertTx . runTxClientM $ buyName opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @N.NameClaimed) resp - errs `shouldBe` [] - filter (claimedLog ==) es `shouldBe` [claimedLog] + [evtransferLog, evclaimedLog] <- deliverTxEvents (Proxy @N.NameClaimed) resp + fromEvent evtransferLog `shouldBe` Right transferLog + fromEvent evclaimedLog `shouldBe` Right claimedLog -- check balance after afterBuyAmount <- getUserBalance user2 -- owner/buyer still profits afterBuyAmount `shouldSatisfy` (> beforeBuyAmount) - it "Can fail to buy a name" $ do + it "Can fail to buy a name" $ const $ do -- try to buy at a lower price let msg = N.BuyName { buyNameBid = 100 @@ -234,7 +275,7 @@ spec = do resp <- assertTx . runTxClientM $ buyName opts msg ensureResponseCodes (0,1) resp - it "Can delete names" $ do + it "Can delete names" $ \tenv -> do let msg = N.DeleteName { deleteNameOwner = signerAddress user2 , deleteNameName = satoshi @@ -248,18 +289,20 @@ spec = do , txOptsGas = 0 } + -- Add event to be monitored and later checked for + addEventToCheck tenv deletedLog + resp <- assertTx . runTxClientM $ deleteName opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @N.NameDeleted) resp - errs `shouldBe` [] - filter (deletedLog ==) es `shouldBe` [deletedLog] + [evdeletedLog] <- deliverTxEvents (Proxy @N.NameDeleted) resp + fromEvent evdeletedLog `shouldBe` Right deletedLog respQ <- RPC.runTendermintM rpcConfig $ getWhois defaultQueryArgs { queryArgsData = satoshi } ensureQueryResponseCode 2 respQ - it "Can fail a transfer" $ do + it "Can fail a transfer" $ const $ do addr2Balance <- getUserBalance user2 let tooMuchToTransfer = addr2Balance + 1 msg = B.Transfer @@ -276,7 +319,7 @@ spec = do resp <- assertTx . runTxClientM $ transfer opts msg ensureResponseCodes (0,1) resp - it "Can transfer" $ do + it "Can transfer" $ \tenv -> do balance1 <- getUserBalance user1 balance2 <- getUserBalance user2 let transferAmount = 1 @@ -298,11 +341,13 @@ spec = do , txOptsGas = 0 } + -- Add event to be monitored and later checked for + addEventToCheck tenv transferLog + resp <- assertTx . runTxClientM $ transfer opts msg ensureResponseCodes (0,0) resp - (errs, es) <- deliverTxEvents (Proxy @B.TransferEvent) resp - errs `shouldBe` [] - filter (transferLog ==) es `shouldBe` [transferLog] + [evtransferLog] <- deliverTxEvents (Proxy @B.TransferEvent) resp + fromEvent evtransferLog `shouldBe` Right transferLog -- check balances balance1' <- getUserBalance user1 @@ -310,6 +355,12 @@ spec = do balance2' <- getUserBalance user2 balance2' `shouldBe` balance2 + transferAmount + it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do + expected <- readMVar mvex + res <- readMVar mvres + fromList expected `shouldBe` fromList res + + faucetUser :: Auth.Amount -> Signer @@ -433,3 +484,40 @@ faucet txApiCP = Proxy txApiDP :: Proxy (M.ApplicationD NameserviceModules) txApiDP = Proxy + +-- Test Init +data TestEnv = TestEnv (MVar [A.Value]) (MVar [A.Value]) [Text] + +testInit :: Auth.Amount -> IO TestEnv +testInit faucetAmount = do + forM_ [user1, user2] $ faucetUser faucetAmount + expectedEventsMVar <- newMVar [] + resultEventsMVar <- newMVar [] + pure $ TestEnv expectedEventsMVar resultEventsMVar [] + +addEventToCheck :: ToEvent a => TestEnv -> a -> IO () +addEventToCheck (TestEnv mvexpected mvres ses) ev = do + modifyMVar_ mvexpected $ \es -> pure $ es <> [A.toJSON . toEvent $ ev] + let evType = eventType (toEvent ev) + if evType`elem` ses + then pure () + else startNewListener evType + where + startNewListener evType = + let subReq = RPC.RequestSubscribe ("tm.event = 'Tx' AND " <> evType <> " EXISTS") + forkTendermintM = void . forkIO . void . RPC.runTendermintM rpcConfig + in forkTendermintM $ RPC.subscribe subReq (handler evType) + handler evType res = case res ^? txEvents of + Nothing -> pure () + Just v -> case A.fromJSON v of + A.Error _ -> error ("Failed to parse\n" <> cs (A.encode v) ) + A.Success evs -> + let filterFn v' = evType == eventType v' + filteredEvs = filter filterFn evs + in modifyMVar_ mvres $ \es -> pure $ es <> map A.toJSON filteredEvs + txEvents = key "result" + . key "data" + . key "value" + . key "TxResult" + . key "result" + . key "events" diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md index bc88f58f..ad964e18 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md @@ -4,7 +4,7 @@ title: Nameservice - Types # Types -The `Types` module is used to define the basic types that the module will make use of. This includes things like custom error types, event types, database types, etc. +The `Types` module is used to define the basic types that the module will make use of. This includes things like custom error types, event types, database types, etc. ## Using A Typed Key Value Store It is important to note that the database modeled by the `RawStore` effect (in the `BaseApp` type) is just a key value store for raw `ByteString`s. This means you can _think_ of `RawStore` as @@ -15,9 +15,9 @@ type RawStore = Map ByteString ByteString although the definition of `RawStore` is different than the above. -The interface we give is actually a typed key value store. This means that within the scope of a module `m`, for any key type `k`, there is only one possible value type `v` associated with `k`. +The interface we give is actually a typed key value store. This means that within the scope of a module `m`, for any key type `k`, there is only one possible value type `v` associated with `k`. -For example, a user's balance in the `Bank` module, might be modeled by a mapping +For example, a user's balance in the `Bank` module, might be modeled by a mapping ~~~ haskell ignore balance :: Tendermint.SDK.Types.Address -> Integer @@ -27,11 +27,11 @@ balance :: Tendermint.SDK.Types.Address -> Integer This means that in the scope of the `Bank` module, the database utlity `get` function applied to a value of type `Address` will result in a value of type `Integer`. If the `Bank` module would like to store another mapping whose keys have type `Tendermint.SDK.Types.Address`, you must use a newtype instead. Otherwise you will get a compiler error. -At the same time, you are free to define another mapping from `k -> v'` in the scope of a different module. For example, you can have both the `balance` mapping described above, as well a mapping +At the same time, you are free to define another mapping from `k -> v'` in the scope of a different module. For example, you can have both the `balance` mapping described above, as well a mapping ~~~ haskell ignore owner :: Tendermint.SDK.Types.Address -> Account -~~~ +~~~ in the `Auth` module. ## Tutorial.Nameservice.Types @@ -62,7 +62,7 @@ import Tendermint.SDK.Modules.Bank () Remember the `Nameservice` module is responsible for maintaining a marketplace around a mapping `Name -> Whois`. Let us define the types for the marketplace mapping as ~~~ haskell -newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON) +newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON, HasCodec) data Whois = Whois { whoisValue :: Text @@ -168,7 +168,7 @@ since `Whois` already implements the `HasCodec` class. ### Error Types -You might want to define a module specific error type that has a `throw`/`catch` interface. This error type should be accessible by any other dependent modules, and any uncaught error should eventually be converted into some kind of generic application error understandable by Tendermint. +You might want to define a module specific error type that has a `throw`/`catch` interface. This error type should be accessible by any other dependent modules, and any uncaught error should eventually be converted into some kind of generic application error understandable by Tendermint. There is a simple way to do this using the `IsAppError` typeclass @@ -267,6 +267,5 @@ instance A.ToJSON NameClaimed where instance A.FromJSON NameClaimed where parseJSON = A.genericParseJSON nameClaimedAesonOptions -instance BA.ToEvent NameClaimed where - makeEventType _ = "NameClaimed" +instance BA.ToEvent NameClaimed ~~~ diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs index 467a7c13..e62bbf07 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -53,8 +53,7 @@ instance A.ToJSON CountSet where instance A.FromJSON CountSet where parseJSON = A.genericParseJSON countSetOptions -instance BaseApp.ToEvent CountSet where - makeEventType _ = "count_set" +instance BaseApp.ToEvent CountSet instance BaseApp.Select CountSet diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs index a1827388..8c5b0421 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Events.hs @@ -3,7 +3,6 @@ module Tendermint.SDK.BaseApp.Events -- * Class ToEvent(..) , ContextEvent(..) - , makeEvent -- * Effect , emit @@ -14,57 +13,75 @@ module Tendermint.SDK.BaseApp.Events ) where import qualified Data.Aeson as A -import Data.Bifunctor (bimap) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteString as BS -import Data.Proxy +import Data.Char (toLower) import Data.String.Conversions (cs) -import GHC.Exts (toList) +import GHC.Generics import Network.ABCI.Types.Messages.FieldTypes (Event (..), KVPair (..)) import Polysemy (Member, Sem) import Polysemy.Output (Output, output) import qualified Tendermint.SDK.BaseApp.Logger as Log +import Tendermint.SDK.Codec (HasCodec (..)) {- TODO : These JSON instances are fragile but convenient. We should come up with a custom solution. -} +class GToNamedEventPrimatives f where + gtoNamedEventPrimatives :: f a -> [(BS.ByteString, BS.ByteString)] + +instance (GToNamedEventPrimatives f) => GToNamedEventPrimatives (C1 c f) where + gtoNamedEventPrimatives = gtoNamedEventPrimatives . unM1 + +instance (Selector s, HasCodec a) => GToNamedEventPrimatives (S1 s (K1 i a)) where + gtoNamedEventPrimatives m1@(M1 x) = + let name = cs $ selName m1 + val = encode $ unK1 x + in [(name, val)] + +instance (GToNamedEventPrimatives a, GToNamedEventPrimatives b) => GToNamedEventPrimatives (a :*: b) where + gtoNamedEventPrimatives (a :*: b) = gtoNamedEventPrimatives a <> gtoNamedEventPrimatives b + +class GToEvent f where + gmakeEvent :: f p -> Event + +instance (GToNamedEventPrimatives f, Datatype d) => GToEvent (D1 d f) where + gmakeEvent m1@(M1 x) = Event + { eventType = cs . lowerFirst $ datatypeName m1 + , eventAttributes = (\(k, v) -> KVPair (Base64.fromBytes k) (Base64.fromBytes v)) <$> gtoNamedEventPrimatives x + } + where + lowerFirst [] = [] + lowerFirst (y : ys) = toLower y : ys + -- | A class representing a type that can be emitted as an event in the -- | event logs for the deliverTx response. class ToEvent e where - makeEventType :: Proxy e -> String - makeEventData :: e -> [(BS.ByteString, BS.ByteString)] + toEvent :: e -> Event - default makeEventData :: A.ToJSON e => e -> [(BS.ByteString, BS.ByteString)] - makeEventData e = case A.toJSON e of - A.Object obj -> bimap cs (cs . A.encode) <$> toList obj - _ -> mempty - -makeEvent - :: ToEvent e - => e - -> Event -makeEvent (e :: e) = Event - { eventType = cs $ makeEventType (Proxy :: Proxy e) - , eventAttributes = (\(k, v) -> KVPair (Base64.fromBytes k) (Base64.fromBytes v)) <$> makeEventData e - } + default toEvent :: (Generic e, GToEvent (Rep e)) => e -> Event + toEvent = gmakeEvent . from emit :: ToEvent e => Member (Output Event) r => e -> Sem r () -emit e = output $ makeEvent e +emit e = output $ toEvent e + + -- | Special event wrapper to add contextual event_type info newtype ContextEvent t = ContextEvent t instance (A.ToJSON a, ToEvent a) => A.ToJSON (ContextEvent a) where toJSON (ContextEvent a) = - A.object [ "event_type" A..= makeEventType (Proxy :: Proxy a) - , "event" A..= A.toJSON a - ] + let Event{eventType} = toEvent a + in A.object [ "event_type" A..= eventType + , "event" A..= A.toJSON a + ] instance Log.Select a => Log.Select (ContextEvent a) where select v (ContextEvent a) = Log.select v a @@ -75,4 +92,5 @@ logEvent => e -> Sem r () logEvent event = Log.addContext (ContextEvent event) $ - Log.log Log.Info (cs $ makeEventType (Proxy :: Proxy e)) + let Event{eventType} = toEvent event + in Log.log Log.Info eventType diff --git a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs index 9e779474..4dd91d58 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs @@ -3,10 +3,11 @@ module Tendermint.SDK.Codec , defaultSDKAesonOptions ) where -import Data.Aeson (Options) -import Data.Aeson.Casing (aesonDrop, snakeCase) -import qualified Data.ByteString as BS -import Data.Text (Text) +import Data.Aeson (Options) +import Data.Aeson.Casing (aesonDrop, snakeCase) +import qualified Data.ByteString as BS +import Data.String.Conversions (cs) +import Data.Text (Text) -- | This class is used as a codec for all items stored in -- | the database as well as incoming transaction messages. @@ -18,5 +19,17 @@ instance HasCodec () where encode = const "" decode = const $ pure () +instance HasCodec String where + encode = cs + decode = Right . cs + +instance HasCodec Text where + encode = cs + decode = Right . cs + +instance HasCodec BS.ByteString where + encode = id + decode = Right + defaultSDKAesonOptions :: String -> Options defaultSDKAesonOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs index 2c396461..d62e130d 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Types.hs @@ -50,6 +50,5 @@ instance A.ToJSON TransferEvent where toJSON = A.genericToJSON transferEventAesonOptions instance A.FromJSON TransferEvent where parseJSON = A.genericParseJSON transferEventAesonOptions -instance BaseApp.ToEvent TransferEvent where - makeEventType _ = "TransferEvent" +instance BaseApp.ToEvent TransferEvent instance BaseApp.Select TransferEvent diff --git a/hs-abci-test-utils/package.yaml b/hs-abci-test-utils/package.yaml index aaeb5f0f..01ce29e8 100644 --- a/hs-abci-test-utils/package.yaml +++ b/hs-abci-test-utils/package.yaml @@ -25,6 +25,7 @@ default-extensions: - ScopedTypeVariables - TypeOperators - FlexibleContexts + - GeneralizedNewtypeDeriving library: source-dirs: src @@ -41,7 +42,6 @@ library: - base >= 4.7 && < 5 - bytestring - cryptonite - - errors - http-api-data - lens - mtl @@ -70,3 +70,5 @@ tests: - hs-abci-test-utils - hspec - hspec-core + - text + - string-conversions diff --git a/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs b/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs index 05a586ec..c0213edf 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/ClientUtils.hs @@ -3,21 +3,17 @@ module Tendermint.Utils.ClientUtils where import Control.Monad (unless) import Data.Aeson (ToJSON) import Data.Aeson.Encode.Pretty (encodePretty) -import Data.Either (partitionEithers) import Data.Proxy import Data.String.Conversions (cs) -import Data.Text (Text) import Data.Word (Word32) import Network.ABCI.Types.Messages.FieldTypes (Event (..)) import qualified Network.Tendermint.Client as RPC import Tendermint.SDK.BaseApp.Errors (AppError (..)) -import Tendermint.SDK.BaseApp.Events (ToEvent (..)) import Tendermint.SDK.BaseApp.Query (QueryResult (..)) import Tendermint.Utils.Client (QueryClientResponse (..), SynchronousResponse (..), TxClientResponse (..), TxResponse (..)) -import Tendermint.Utils.Events (FromEvent (..)) -------------------------------------------------------------------------------- -- | Tx helpers @@ -38,16 +34,13 @@ assertTx m = do -- get the logged events from a deliver response, deliverTxEvents :: Monad m - => FromEvent e => Proxy e -> SynchronousResponse a b - -> m ([Text],[e]) -deliverTxEvents pE SynchronousResponse{deliverTxResponse} = + -> m [Event] +deliverTxEvents _ SynchronousResponse{deliverTxResponse} = case deliverTxResponse of - TxResponse {txResponseEvents} -> - let eventName = cs $ makeEventType pE - es = filter ((== eventName) . eventType) txResponseEvents - in return . partitionEithers . map fromEvent $ es + TxResponse {txResponseEvents} -> do + pure txResponseEvents TxError appError -> fail (show appError) -- check for a specific check response code diff --git a/hs-abci-test-utils/src/Tendermint/Utils/Events.hs b/hs-abci-test-utils/src/Tendermint/Utils/Events.hs index 4f4e9cd3..cac27b36 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/Events.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/Events.hs @@ -1,33 +1,53 @@ +{-# OPTIONS_GHC -fno-warn-orphans #-} module Tendermint.Utils.Events where -import Control.Error (fmapL) -import qualified Data.Aeson as A import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteString as BS -import Data.Proxy +import Data.Char (toUpper) +import qualified Data.List as L import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.Exts (fromList) -import Network.ABCI.Types.Messages.FieldTypes (Event (..), - KVPair (..)) -import Tendermint.SDK.BaseApp.Events (ToEvent, makeEventType) - --- | A class that can parse event log items in the deliverTx response. Primarily --- | useful for client applications and testing. +import Data.Text (Text, pack, unpack) +import GHC.Generics +import Network.ABCI.Types.Messages.FieldTypes (KVPair (..)) +import Tendermint.SDK.BaseApp.Events (Event (..), ToEvent) +import Tendermint.SDK.Codec (HasCodec (..)) + + +class GFromNamedEventPrimatives f where + gfromNamedEventPrimatives :: [(BS.ByteString, BS.ByteString)] -> Either Text (f a) + +instance (Selector s, HasCodec a) => GFromNamedEventPrimatives (S1 s (K1 i a)) where + gfromNamedEventPrimatives kvs = + let name = selName (undefined :: S1 s (K1 i a) p) + in case L.lookup (cs name) kvs of + Nothing -> Left $ "Could not find key " <> cs name <> " in Event key-values." + Just val -> M1 . K1 <$> decode val + +instance (GFromNamedEventPrimatives f) => GFromNamedEventPrimatives (C1 c f) where + gfromNamedEventPrimatives = fmap M1 . gfromNamedEventPrimatives + + +instance (GFromNamedEventPrimatives a, GFromNamedEventPrimatives b) => GFromNamedEventPrimatives (a :*: b) where + gfromNamedEventPrimatives kvs = + (:*:) <$> gfromNamedEventPrimatives kvs <*> gfromNamedEventPrimatives kvs + +class GFromEvent f where + gfromEventData :: Event -> Either Text (f p) + +instance (GFromNamedEventPrimatives f, Datatype d) => GFromEvent (D1 d f) where + gfromEventData Event{eventType, eventAttributes} = + let upperFirstChar [] = [] + upperFirstChar (x : xs) = toUpper x : xs + eventType' = pack . upperFirstChar . unpack $ eventType + dt = cs $ datatypeName (undefined :: D1 d f p) + in if dt == eventType' + then fmap M1 . gfromNamedEventPrimatives $ + map (\(KVPair k v) -> (Base64.toBytes k, Base64.toBytes v)) eventAttributes + else Left $ "Expected Event type " <> dt <> " does not match found Event type " <> cs eventType' <> "." + class ToEvent e => FromEvent e where fromEvent :: Event -> Either Text e - default fromEvent :: A.FromJSON e => Event -> Either Text e - fromEvent Event{eventType, eventAttributes} = - let expectedType = makeEventType (Proxy @e) - in if cs eventType /= expectedType - then fail ("Couldn't match expected event type " <> expectedType <> - " with found type " <> cs eventType) - else - let fromKVPair :: KVPair -> Either String (Text, A.Value) - fromKVPair (KVPair k v) = do - value <- A.eitherDecode . cs @BS.ByteString . Base64.toBytes $ v - return (cs @BS.ByteString . Base64.toBytes $ k, value) - in fmapL cs $ do - kvPairs <- traverse fromKVPair eventAttributes - A.eitherDecode . A.encode . A.Object . fromList $ kvPairs + default fromEvent :: (Generic e, GFromEvent (Rep e)) => Event -> Either Text e + fromEvent = fmap to . gfromEventData + diff --git a/hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs b/hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs index bc2064a9..2a9169cd 100644 --- a/hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs +++ b/hs-abci-test-utils/test/Tendermint/Utils/Test/EventSpec.hs @@ -1,8 +1,12 @@ module Tendermint.Utils.Test.EventSpec (spec) where import qualified Data.Aeson as A +import Data.Bifunctor (first) +import Data.String.Conversions (cs) +import Data.Text (pack) import GHC.Generics (Generic) -import Tendermint.SDK.BaseApp.Events (ToEvent (..), makeEvent) +import Tendermint.SDK.BaseApp.Events (ToEvent (..)) +import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.Utils.Events (FromEvent (..)) import Test.Hspec @@ -14,18 +18,24 @@ spec = describe "Event Tests" $ do , from = "you" , amount = 1 } - fromEvent (makeEvent transferEv) `shouldBe` Right transferEv + fromEvent (toEvent transferEv) `shouldBe` Right transferEv + + +newtype WrappedInt = WrappedInt {unwrapInt :: Int} + deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON, Num) + +instance HasCodec WrappedInt where + encode (WrappedInt i) = cs $ A.encode i + decode = first pack . A.eitherDecodeStrict data Transfer = Transfer { to :: String , from :: String - , amount :: Int + , amount :: WrappedInt } deriving (Eq, Show, Generic) -instance A.ToJSON Transfer - -instance ToEvent Transfer where - makeEventType _ = "transfer" +instance A.ToJSON Transfer instance A.FromJSON Transfer +instance ToEvent Transfer instance FromEvent Transfer diff --git a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs index 06c97dc8..709c4107 100644 --- a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs +++ b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs @@ -1,86 +1,105 @@ module KVStore.Test.KVSpec (spec) where +import Control.Concurrent (forkIO) +import Control.Concurrent.MVar (MVar, modifyMVar_, + newMVar, readMVar) import Control.Lens (to, (^.)) +import Control.Lens.Fold ((^?)) +import Control.Monad (void) import Control.Monad.Catch (try) -import Data.Aeson (ToJSON) +import qualified Data.Aeson as A import Data.Aeson.Encode.Pretty (encodePretty) +import qualified Data.Aeson.Lens as A import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString) import Data.Default.Class (def) import Data.Either (isRight) +import Data.HashSet (fromList) import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.Generics (Generic) import qualified Network.ABCI.Types.Messages.Response as Response import qualified Network.Tendermint.Client as RPC +import Tendermint.SDK.BaseApp.Events (Event (..), ToEvent (..)) import Test.Hspec spec :: Spec spec = do - describe "Tendermint KV Store - via hs-tendermint-client" $ do - - it "Can query /health to make sure the node is alive" $ do - resp <- runRPC RPC.health - resp `shouldBe` RPC.ResultHealth - - it "Can query /abci_info and parse the result" $ do - result :: Either RPC.JsonRpcException RPC.ResultABCIInfo <- try $ runRPC RPC.abciInfo - result `shouldSatisfy` isRight - - it "Can query /block and parse the result" $ do - -- @NOTE: this defaults to latest block - result :: Either RPC.JsonRpcException RPC.ResultBlock <- try $ runRPC (RPC.block def) - result `shouldSatisfy` isRight - - it "Can submit a async tx and the response code is 0 (success)" $ do - let asyncTxReq = RPC.RequestBroadcastTxAsync { RPC.requestBroadcastTxAsyncTx = encodeTx "abcd" } - -- async returns nothing - resp <- runRPC $ RPC.broadcastTxAsync asyncTxReq - RPC.resultBroadcastTxCode resp `shouldBe` 0 - - it "Can submit a sync tx and the response code is 0 (success)" $ do - let txReq = RPC.RequestBroadcastTxSync { RPC.requestBroadcastTxSyncTx = encodeTx "efgh" } - -- sync only returns a CheckTx - resp <- runRPC $ RPC.broadcastTxSync txReq - RPC.resultBroadcastTxCode resp `shouldBe` 0 - - it "Can submit a commit tx, make sure the response code is 0 (success), and get the result(s)" $ do - -- /broadcast_tx_commit - -- set name key - let broadcastTxReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeTx "name=satoshi" } - broadcastResp <- runRPC $ RPC.broadcastTxCommit broadcastTxReq - let deliverResp = RPC.resultBroadcastTxCommitDeliverTx broadcastResp - deliverRespCode = deliverResp ^. Response._deliverTxCode - deliverRespCode `shouldBe` 0 - -- /abci_query (w+w/o proof) - -- get name key value - let dName = Hex.fromBytes $ cs @String @ByteString "name" - queryReq = def { RPC.requestABCIQueryData = dName } - queryReqWProof = def { RPC.requestABCIQueryData = dName - , RPC.requestABCIQueryProve = True - } - queryResp <- fmap RPC.resultABCIQueryResponse . runRPC $ - RPC.abciQuery queryReq - queryRespWProof <- fmap RPC.resultABCIQueryResponse . runRPC $ - RPC.abciQuery queryReqWProof - let foundName = queryResp ^. Response._queryValue . to decodeName - foundNameWProof = queryRespWProof ^. Response._queryValue . to decodeName - foundName `shouldBe` "satoshi" - foundNameWProof `shouldBe` "satoshi" - -- check with /tx endpoint (w+w/o proof) - let hash = RPC.resultBroadcastTxCommitHash $ broadcastResp - -- convert hex to base64 - baseHash = Base64.fromBytes . Hex.toBytes @ByteString $ hash - txReq = def { RPC.requestTxHash = Just baseHash } - txReqWP = RPC.RequestTx { RPC.requestTxHash = Just baseHash - , RPC.requestTxProve = True - } - -- check the hashes are the same - txResultHash <- fmap RPC.resultTxHash . runRPC $ RPC.tx txReq - txResultWPHash <- fmap RPC.resultTxHash . runRPC $ RPC.tx txReqWP - txResultHash `shouldBe` hash - txResultWPHash `shouldBe` hash + beforeAll testInit $ do + describe "Tendermint KV Store - via hs-tendermint-client" $ do + + it "Can query /health to make sure the node is alive" $ const $ do + resp <- runRPC RPC.health + resp `shouldBe` RPC.ResultHealth + + it "Can query /abci_info and parse the result" $ const $ do + result :: Either RPC.JsonRpcException RPC.ResultABCIInfo <- try $ runRPC RPC.abciInfo + result `shouldSatisfy` isRight + + it "Can query /block and parse the result" $ const $ do + -- @NOTE: this defaults to latest block + result :: Either RPC.JsonRpcException RPC.ResultBlock <- try $ runRPC (RPC.block def) + result `shouldSatisfy` isRight + + it "Can submit a async tx and the response code is 0 (success)" $ \tenv -> do + let asyncTxReq = RPC.RequestBroadcastTxAsync { RPC.requestBroadcastTxAsyncTx = encodeTx "abcd" } + addEventToCheck tenv $ mkAppEvent "abcd" + -- async returns nothing + resp <- runRPC $ RPC.broadcastTxAsync asyncTxReq + RPC.resultBroadcastTxCode resp `shouldBe` 0 + + it "Can submit a sync tx and the response code is 0 (success)" $ \tenv -> do + let txReq = RPC.RequestBroadcastTxSync { RPC.requestBroadcastTxSyncTx = encodeTx "efgh" } + addEventToCheck tenv $ mkAppEvent "efgh" + -- sync only returns a CheckTx + resp <- runRPC $ RPC.broadcastTxSync txReq + RPC.resultBroadcastTxCode resp `shouldBe` 0 + + it "Can submit a commit tx, make sure the response code is 0 (success), and get the result(s)" $ \tenv -> do + -- /broadcast_tx_commit + -- set name key + let broadcastTxReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeTx "name=satoshi" } + addEventToCheck tenv $ mkAppEvent "name" + broadcastResp <- runRPC $ RPC.broadcastTxCommit broadcastTxReq + let deliverResp = RPC.resultBroadcastTxCommitDeliverTx broadcastResp + deliverRespCode = deliverResp ^. Response._deliverTxCode + deliverRespCode `shouldBe` 0 + -- /abci_query (w+w/o proof) + -- get name key value + let dName = Hex.fromBytes $ cs @String @ByteString "name" + queryReq = def { RPC.requestABCIQueryData = dName } + queryReqWProof = def { RPC.requestABCIQueryData = dName + , RPC.requestABCIQueryProve = True + } + queryResp <- fmap RPC.resultABCIQueryResponse . runRPC $ + RPC.abciQuery queryReq + queryRespWProof <- fmap RPC.resultABCIQueryResponse . runRPC $ + RPC.abciQuery queryReqWProof + let foundName = queryResp ^. Response._queryValue . to decodeName + foundNameWProof = queryRespWProof ^. Response._queryValue . to decodeName + foundName `shouldBe` "satoshi" + foundNameWProof `shouldBe` "satoshi" + -- check with /tx endpoint (w+w/o proof) + let hash = RPC.resultBroadcastTxCommitHash $ broadcastResp + -- convert hex to base64 + baseHash = Base64.fromBytes . Hex.toBytes @ByteString $ hash + txReq = def { RPC.requestTxHash = Just baseHash } + txReqWP = RPC.RequestTx { RPC.requestTxHash = Just baseHash + , RPC.requestTxProve = True + } + -- check the hashes are the same + txResultHash <- fmap RPC.resultTxHash . runRPC $ RPC.tx txReq + txResultWPHash <- fmap RPC.resultTxHash . runRPC $ RPC.tx txReqWP + txResultHash `shouldBe` hash + txResultWPHash `shouldBe` hash + + it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do + expected <- readMVar mvex + res <- readMVar mvres + fromList res `shouldBe` fromList expected encodeTx :: String -> Base64String encodeTx = Base64.fromBytes . cs @String @ByteString @@ -94,6 +113,54 @@ runRPC = RPC.runTendermintM rpcConfig rpcConfig :: RPC.Config rpcConfig = let RPC.Config baseReq _ _ host port tls = RPC.defaultConfig "localhost" 26657 False - prettyPrint :: forall b. ToJSON b => String -> b -> IO () + prettyPrint :: forall b. A.ToJSON b => String -> b -> IO () prettyPrint prefix a = putStrLn $ prefix <> "\n" <> (cs . encodePretty $ a) in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") host port tls + +-- See https://github.com/tendermint/tendermint/blob/master/abci/example/kvstore/kvstore.go#L101 +mkAppEvent :: Text -> App +mkAppEvent k = App "Cosmoshi Netowoko" k + +data App = App + { creator :: Text + , key :: Text + } deriving (Show, Eq, Generic) + +instance ToEvent App + + +-- Test Init +data TestEnv = TestEnv (MVar [A.Value]) (MVar [A.Value]) [Text] + +testInit :: IO TestEnv +testInit = do + expectedEventsMVar <- newMVar [] + resultEventsMVar <- newMVar [] + pure $ TestEnv expectedEventsMVar resultEventsMVar [] + +addEventToCheck :: ToEvent a => TestEnv -> a -> IO () +addEventToCheck (TestEnv mvexpected mvres ses) ev = do + modifyMVar_ mvexpected $ \es -> pure $ es <> [A.toJSON . toEvent $ ev] + let evType = eventType (toEvent ev) + if evType`elem` ses + then pure () + else startNewListener evType + where + startNewListener evType = + let subReq = RPC.RequestSubscribe ("tm.event = 'Tx' AND " <> evType <> " EXISTS") + forkTendermintM = void . forkIO . void . runRPC + in forkTendermintM $ RPC.subscribe subReq (handler evType) + handler evType res = case res ^? txEvents of + Nothing -> pure () + Just v -> case A.fromJSON v of + A.Error _ -> error ("Failed to parse\n" <> cs (A.encode v) ) + A.Success evs -> + let filterFn v' = evType == eventType v' + filteredEvs = filter filterFn evs + in modifyMVar_ mvres $ \es -> pure $ es <> map A.toJSON filteredEvs + txEvents = A.key "result" + . A.key "data" + . A.key "value" + . A.key "TxResult" + . A.key "result" + . A.key "events" diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index 3e62b1bc..686a021a 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -72,5 +72,9 @@ tests: - hs-abci-types - hs-tendermint-client - hspec + - lens-aeson - lens + - text + - unordered-containers - string-conversions + - hs-abci-sdk From 175c1e7b6e0c61a1389fa9a8965dd04b9260ef11 Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Thu, 5 Mar 2020 10:12:14 -0800 Subject: [PATCH 67/70] Storage lists (#222) * added basic list functionality * added list to package yaml * change delete * fix iavl spec * added foldl * wip * remove inj restriction, update tests * sdk tests compile * rename StoreKeyRoot * name shuffle * sdk and tests compile * update nameservice * fix tutorial * updated test * stylish * added list spec boilerplate * start with latest * weeder * foldl * length * added elemIndex * rename files * update tests * lists seem to work * fix deleteWHen * test get at index * MapSpec boilerplate * map spec * more lenient test * weeder * Fix query server (#223) * put in var module * added var testsw * StoreQueryHandler for all storage types * query modules compile * bank module compiles * sdk tests compile * simple storage tests compile * nameservice e2e compiles * tutorial compiles but leaves a lot to be desired * added Storage section to tutorial (#224) --- hs-abci-docs/doc/0340-Storage.md | 1 + hs-abci-docs/nameservice/interact/Interact.hs | 30 +- hs-abci-docs/nameservice/package.yaml | 2 +- hs-abci-docs/nameservice/protogen/Protogen.hs | 11 +- .../src/Nameservice/Modules/Nameservice.hs | 4 +- .../Nameservice/Modules/Nameservice/Keeper.hs | 118 +++--- .../Modules/Nameservice/Messages.hs | 120 +++--- .../Nameservice/Modules/Nameservice/Query.hs | 17 +- .../Nameservice/Modules/Nameservice/Router.hs | 50 +-- .../Nameservice/Modules/Nameservice/Store.hs | 38 ++ .../Nameservice/Modules/Nameservice/Types.hs | 39 +- .../test/Nameservice/Test/E2ESpec.hs | 46 +-- .../tutorial/Foundations/04-Storage.md | 77 ++++ .../tutorial/Tutorial/Nameservice/02-Types.md | 16 - .../Tutorial/Nameservice/03-Message.md | 61 ++- .../Tutorial/Nameservice/04-Keeper.md | 111 +++--- .../tutorial/Tutorial/Nameservice/05-Query.md | 25 +- .../Tutorial/Nameservice/09-Testing.md | 10 +- hs-abci-docs/simple-storage/package.yaml | 1 + .../SimpleStorage/Modules/SimpleStorage.hs | 2 +- .../Modules/SimpleStorage/Keeper.hs | 70 +++- .../Modules/SimpleStorage/Query.hs | 18 +- .../Modules/SimpleStorage/Router.hs | 12 +- .../Modules/SimpleStorage/Types.hs | 23 +- .../test/SimpleStorage/Test/E2ESpec.hs | 4 +- hs-abci-sdk/package.yaml | 8 + .../Tendermint/SDK/Application/AnteHandler.hs | 6 +- .../src/Tendermint/SDK/Application/Module.hs | 3 +- hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs | 12 +- .../src/Tendermint/SDK/BaseApp/Errors.hs | 12 +- .../src/Tendermint/SDK/BaseApp/Query.hs | 5 +- .../Tendermint/SDK/BaseApp/Query/Router.hs | 6 +- .../src/Tendermint/SDK/BaseApp/Query/Store.hs | 181 ++++++--- .../src/Tendermint/SDK/BaseApp/Query/Types.hs | 29 +- .../src/Tendermint/SDK/BaseApp/Store/Array.hs | 256 ++++++++++++ .../Tendermint/SDK/BaseApp/Store/IAVLStore.hs | 10 +- .../src/Tendermint/SDK/BaseApp/Store/List.hs | 363 ++++++++++++++++++ .../src/Tendermint/SDK/BaseApp/Store/Map.hs | 89 +++++ .../SDK/BaseApp/Store/MemoryStore.hs | 18 +- .../Tendermint/SDK/BaseApp/Store/RawStore.hs | 160 +++++--- .../src/Tendermint/SDK/BaseApp/Store/Var.hs | 72 ++++ .../SDK/BaseApp/Transaction/Cache.hs | 12 +- hs-abci-sdk/src/Tendermint/SDK/Codec.hs | 34 +- .../src/Tendermint/SDK/Modules/Auth.hs | 2 +- .../src/Tendermint/SDK/Modules/Auth/Keeper.hs | 79 +++- .../src/Tendermint/SDK/Modules/Auth/Query.hs | 13 +- .../src/Tendermint/SDK/Modules/Auth/Types.hs | 26 +- .../src/Tendermint/SDK/Modules/Bank/Keeper.hs | 140 ++++--- .../Tendermint/SDK/Modules/Bank/Messages.hs | 36 +- .../src/Tendermint/SDK/Modules/Bank/Router.hs | 24 +- .../test/Tendermint/SDK/Test/ArraySpec.hs | 128 ++++++ .../test/Tendermint/SDK/Test/IAVLStoreSpec.hs | 56 +-- .../test/Tendermint/SDK/Test/ListSpec.hs | 187 +++++++++ .../test/Tendermint/SDK/Test/MapSpec.hs | 107 ++++++ .../test/Tendermint/SDK/Test/SimpleStorage.hs | 211 +++++----- .../test/Tendermint/SDK/Test/VarSpec.hs | 95 +++++ .../src/Tendermint/Utils/QueryClient/Class.hs | 32 +- .../kv-test/KVStore/Test/KVSpec.hs | 14 +- hs-tendermint-client/package.yaml | 1 - 59 files changed, 2481 insertions(+), 852 deletions(-) create mode 120000 hs-abci-docs/doc/0340-Storage.md create mode 100644 hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Store.hs create mode 100644 hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Array.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/List.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Map.hs create mode 100644 hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Var.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/ArraySpec.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/ListSpec.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/MapSpec.hs create mode 100644 hs-abci-sdk/test/Tendermint/SDK/Test/VarSpec.hs diff --git a/hs-abci-docs/doc/0340-Storage.md b/hs-abci-docs/doc/0340-Storage.md new file mode 120000 index 00000000..da8a1d4e --- /dev/null +++ b/hs-abci-docs/doc/0340-Storage.md @@ -0,0 +1 @@ +../nameservice/tutorial/Foundations/04-Storage.md \ No newline at end of file diff --git a/hs-abci-docs/nameservice/interact/Interact.hs b/hs-abci-docs/nameservice/interact/Interact.hs index 049d0b52..8a19b59e 100644 --- a/hs-abci-docs/nameservice/interact/Interact.hs +++ b/hs-abci-docs/nameservice/interact/Interact.hs @@ -46,22 +46,22 @@ import Test.RandomStrings (onlyWith, randomASCII, faucetAccount :: Signer -> Auth.Amount -> IO () faucetAccount s@(Signer addr _) amount = - runAction_ s faucet $ N.FaucetAccount addr N.nameserviceCoinId amount + runAction_ s faucet $ N.FaucetAccountMsg addr N.nameserviceCoinId amount -createName :: Signer -> N.Name -> Text -> IO () +createName :: Signer -> Text -> Text -> IO () createName s name val = buyName s name val 0 -buyName :: Signer -> N.Name -> Text -> Auth.Amount -> IO () +buyName :: Signer -> Text -> Text -> Auth.Amount -> IO () buyName s@(Signer addr _) name newVal amount = - runAction_ s buy $ N.BuyName amount name newVal addr + runAction_ s buy $ N.BuyNameMsg amount name newVal addr -deleteName :: Signer -> N.Name -> IO () +deleteName :: Signer -> Text -> IO () deleteName s@(Signer addr _) name = - runAction_ s delete $ N.DeleteName addr name + runAction_ s delete $ N.DeleteNameMsg addr name -setName :: Signer -> N.Name -> Text -> IO () +setName :: Signer -> Text -> Text -> IO () setName s@(Signer addr _) name val = - runAction_ s set $ N.SetName name addr val + runAction_ s set $ N.SetNameMsg name addr val runAction_ :: Signer @@ -144,22 +144,22 @@ runTxClientM m = runReaderT m txClientConfig -- Nameservice Client buy :: TxOpts - -> N.BuyName + -> N.BuyNameMsg -> TxClientM (TxClientResponse () ()) set :: TxOpts - -> N.SetName + -> N.SetNameMsg -> TxClientM (TxClientResponse () ()) delete :: TxOpts - -> N.DeleteName + -> N.DeleteNameMsg -> TxClientM (TxClientResponse () ()) faucet :: TxOpts - -> N.FaucetAccount + -> N.FaucetAccountMsg -> TxClientM (TxClientResponse () ()) (buy :<|> set :<|> delete :<|> faucet) :<|> @@ -183,10 +183,8 @@ genWords = do ws <- replicateM numWords Lorem.word return . cs . unwords $ ws -genName :: IO N.Name -genName = do - name <- Name.name - return . fromString $ name +genName :: IO Text +genName = fromString <$> Name.name genAmount :: IO Auth.Amount genAmount = do diff --git a/hs-abci-docs/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml index 9f05ecef..6652d8de 100644 --- a/hs-abci-docs/nameservice/package.yaml +++ b/hs-abci-docs/nameservice/package.yaml @@ -56,7 +56,6 @@ library: - polysemy - polysemy-plugin - proto3-suite - - proto3-wire - servant - string-conversions - text @@ -78,6 +77,7 @@ library: - Nameservice.Modules.Nameservice - Nameservice.Modules.Nameservice.Messages - Nameservice.Modules.Nameservice.Types + - Nameservice.Modules.Nameservice.Store - Nameservice.Modules.Nameservice.Keeper - Nameservice.Modules.Nameservice.Query - Nameservice.Modules.Nameservice.Router diff --git a/hs-abci-docs/nameservice/protogen/Protogen.hs b/hs-abci-docs/nameservice/protogen/Protogen.hs index 74eaa1be..18f831d6 100644 --- a/hs-abci-docs/nameservice/protogen/Protogen.hs +++ b/hs-abci-docs/nameservice/protogen/Protogen.hs @@ -9,10 +9,9 @@ import Data.Aeson.Casing (snakeCase) import qualified Data.ByteString.Lazy as BL import GHC.Exts (Proxy#, proxy#) import Nameservice.Modules.Nameservice.Messages (BuyNameMessage (..), - DeleteName (..), - SetName (..)) -import Nameservice.Modules.Nameservice.Types (Name (..), - WhoisMessage (..)) + DeleteNameMsg (..), + SetNameMsg (..)) +import Nameservice.Modules.Nameservice.Types (WhoisMessage (..)) import Proto3.Suite (DotProtoDefinition, Message, fromByteString, @@ -44,9 +43,9 @@ msgStripPrefixOptions = defRenderingOptions { roSelectorName = stripPrefixName } messagesProtoFile :: String messagesProtoFile = toProtoFile msgStripPrefixOptions $ packageFromDefs "nameservice" - ([ message (proxy# :: Proxy# SetName) + ([ message (proxy# :: Proxy# SetNameMsg) , message (proxy# :: Proxy# BuyNameMessage) - , message (proxy# :: Proxy# DeleteName) + , message (proxy# :: Proxy# DeleteNameMsg) ] :: [DotProtoDefinition]) whoisProtoFile :: String diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs index ff58bfa2..a5db500f 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice.hs @@ -6,6 +6,7 @@ module Nameservice.Modules.Nameservice , nameserviceModule , module Nameservice.Modules.Nameservice.Keeper , module Nameservice.Modules.Nameservice.Messages + , module Nameservice.Modules.Nameservice.Store , module Nameservice.Modules.Nameservice.Query , module Nameservice.Modules.Nameservice.Router , module Nameservice.Modules.Nameservice.Types @@ -14,10 +15,11 @@ module Nameservice.Modules.Nameservice ) where import Data.Proxy -import Nameservice.Modules.Nameservice.Keeper hiding (storeKey) +import Nameservice.Modules.Nameservice.Keeper import Nameservice.Modules.Nameservice.Messages import Nameservice.Modules.Nameservice.Query import Nameservice.Modules.Nameservice.Router +import Nameservice.Modules.Nameservice.Store (Name (..)) import Nameservice.Modules.Nameservice.Types import Polysemy (Members) import Tendermint.SDK.Application (Module (..), diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs index 2c5fd99a..d45ca683 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Keeper.hs @@ -1,74 +1,78 @@ {-# LANGUAGE TemplateHaskell #-} module Nameservice.Modules.Nameservice.Keeper - ( NameserviceKeeper - , NameserviceEffs + ( NameserviceEffs + , NameserviceKeeper(..) , nameserviceCoinId , setName , deleteName , buyName - , storeKey , faucetAccount + , getWhois , eval ) where -import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (symbolVal) import Nameservice.Modules.Nameservice.Messages +import Nameservice.Modules.Nameservice.Store import Nameservice.Modules.Nameservice.Types -import Polysemy (Members, Sem, +import Polysemy (Member, Members, Sem, interpret, makeSem) import Polysemy.Error (Error, mapError, throw) import Polysemy.Output (Output) import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Store.Map as M import Tendermint.SDK.Modules.Auth (Coin (..), CoinId) import Tendermint.SDK.Modules.Bank (BankEffs, burn, mint, transfer) data NameserviceKeeper m a where - PutWhois :: Name -> Whois -> NameserviceKeeper m () + FaucetAccount :: FaucetAccountMsg -> NameserviceKeeper m () + BuyName :: BuyNameMsg -> NameserviceKeeper m () + DeleteName :: DeleteNameMsg -> NameserviceKeeper m () + SetName :: SetNameMsg -> NameserviceKeeper m () GetWhois :: Name -> NameserviceKeeper m (Maybe Whois) - DeleteWhois :: Name -> NameserviceKeeper m () makeSem ''NameserviceKeeper type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] -storeKey :: BaseApp.StoreKey NameserviceNamespace -storeKey = BaseApp.StoreKey . cs . symbolVal $ Proxy @NameserviceName - nameserviceCoinId :: CoinId nameserviceCoinId = "nameservice" eval :: Members BaseApp.TxEffs r + => Members BankEffs r + => Members BaseApp.BaseEffs r => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a -> Sem r a eval = mapError BaseApp.makeAppError . evalNameservice where evalNameservice :: Members BaseApp.TxEffs r + => Members BaseApp.BaseEffs r + => Members BankEffs r + => Member (Error NameserviceError) r => Sem (NameserviceKeeper ': r) a -> Sem r a evalNameservice = interpret (\case - GetWhois name -> - BaseApp.get storeKey name - PutWhois name whois -> - BaseApp.put storeKey name whois - DeleteWhois name -> - BaseApp.delete storeKey name + FaucetAccount msg -> faucetAccountF msg + BuyName msg -> buyNameF msg + DeleteName msg -> deleteNameF msg + SetName msg -> setNameF msg + GetWhois name -> M.lookup name whoisMap ) -------------------------------------------------------------------------------- -faucetAccount +-------------------------------------------------------------------------------- + +faucetAccountF :: Members [BaseApp.Logger, Output BaseApp.Event] r => Members BankEffs r - => FaucetAccount + => FaucetAccountMsg -> Sem r () -faucetAccount FaucetAccount{..} = do +faucetAccountF FaucetAccountMsg{..} = do let coin = Coin faucetAccountCoinId faucetAccountAmount mint faucetAccountTo coin let event = Faucetted @@ -79,20 +83,21 @@ faucetAccount FaucetAccount{..} = do BaseApp.emit event BaseApp.logEvent event -setName - :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members NameserviceEffs r - => SetName +setNameF + :: Members BaseApp.TxEffs r + => Members BaseApp.BaseEffs r + => Member (Error NameserviceError) r + => SetNameMsg -> Sem r () -setName SetName{..} = do - mwhois <- getWhois setNameName +setNameF SetNameMsg{..} = do + mwhois <- M.lookup (Name setNameName) whoisMap case mwhois of Nothing -> throw $ UnauthorizedSet "Cannot claim name with SetMessage tx." Just currentWhois@Whois{..} -> if whoisOwner /= setNameOwner then throw $ UnauthorizedSet "Setter must be the owner of the Name." else do - putWhois setNameName currentWhois {whoisValue = setNameValue} + M.insert (Name setNameName) (currentWhois {whoisValue = setNameValue}) whoisMap let event = NameRemapped { nameRemappedName = setNameName , nameRemappedNewValue = setNameValue @@ -101,14 +106,15 @@ setName SetName{..} = do BaseApp.emit event BaseApp.logEvent event -deleteName - :: Members [BaseApp.Logger, Output BaseApp.Event] r +deleteNameF + :: Members BaseApp.TxEffs r + => Members BaseApp.BaseEffs r => Members BankEffs r - => Members NameserviceEffs r - => DeleteName + => Member (Error NameserviceError) r + => DeleteNameMsg -> Sem r () -deleteName DeleteName{..} = do - mWhois <- getWhois deleteNameName +deleteNameF DeleteNameMsg{..} = do + mWhois <- M.lookup (Name deleteNameName) whoisMap case mWhois of Nothing -> throw $ InvalidDelete "Can't remove unassigned name." Just Whois{..} -> @@ -116,23 +122,24 @@ deleteName DeleteName{..} = do then throw $ InvalidDelete "Deleter must be the owner." else do mint deleteNameOwner (Coin nameserviceCoinId whoisPrice) - deleteWhois deleteNameName + M.delete (Name deleteNameName) whoisMap let event = NameDeleted { nameDeletedName = deleteNameName } BaseApp.emit event BaseApp.logEvent event -buyName - :: Members [BaseApp.Logger, Output BaseApp.Event] r +buyNameF + :: Members BaseApp.TxEffs r => Members BankEffs r - => Members NameserviceEffs r - => BuyName + => Members BaseApp.BaseEffs r + => Member (Error NameserviceError) r + => BuyNameMsg -> Sem r () -- ^ did it succeed -buyName msg = do +buyNameF msg = do let name = buyNameName msg - mWhois <- getWhois name + mWhois <- M.lookup (Name name) whoisMap case mWhois of -- The name is unclaimed, go ahead and debit the account -- and create it. @@ -142,19 +149,19 @@ buyName msg = do Just whois -> buyClaimedName msg whois where buyUnclaimedName - :: Members [BaseApp.Logger, Output BaseApp.Event] r + :: Members BaseApp.TxEffs r + => Members BaseApp.BaseEffs r => Members BankEffs r - => Members NameserviceEffs r - => BuyName + => BuyNameMsg -> Sem r () - buyUnclaimedName BuyName{..} = do + buyUnclaimedName BuyNameMsg{..} = do burn buyNameBuyer (Coin nameserviceCoinId buyNameBid) let whois = Whois { whoisOwner = buyNameBuyer , whoisValue = buyNameValue , whoisPrice = buyNameBid } - putWhois buyNameName whois + M.insert (Name buyNameName) whois whoisMap let event = NameClaimed { nameClaimedOwner = buyNameBuyer , nameClaimedName = buyNameName @@ -165,22 +172,25 @@ buyName msg = do BaseApp.logEvent event buyClaimedName - :: Members NameserviceEffs r + :: Members BaseApp.TxEffs r + => Member (Error NameserviceError) r + => Members BaseApp.BaseEffs r => Members BankEffs r - => Members [BaseApp.Logger, Output BaseApp.Event] r - => BuyName + => BuyNameMsg -> Whois -> Sem r () - buyClaimedName BuyName{..} currentWhois = + buyClaimedName BuyNameMsg{..} currentWhois = let Whois{ whoisPrice = forsalePrice, whoisOwner = previousOwner } = currentWhois in if buyNameBid > forsalePrice then do transfer buyNameBuyer (Coin nameserviceCoinId buyNameBid) previousOwner -- update new owner, price and value based on BuyName - putWhois buyNameName currentWhois { whoisOwner = buyNameBuyer - , whoisPrice = buyNameBid - , whoisValue = buyNameValue - } + let whois' = currentWhois + { whoisOwner = buyNameBuyer + , whoisPrice = buyNameBid + , whoisValue = buyNameValue + } + M.insert (Name buyNameName) whois' whoisMap let event = NameClaimed { nameClaimedOwner = buyNameBuyer , nameClaimedName = buyNameName diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs index 2e7a0ff1..04dbfb55 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Messages.hs @@ -1,38 +1,33 @@ module Nameservice.Modules.Nameservice.Messages - ( SetName(..) - , BuyName(..) + ( SetNameMsg(..) + , BuyNameMsg(..) + , DeleteNameMsg(..) + , FaucetAccountMsg(..) , BuyNameMessage(..) - , DeleteName(..) - , FaucetAccount(..) ) where -import Data.Bifunctor (bimap, first) -import Data.Foldable (sequenceA_) -import Data.String.Conversions (cs) -import Data.Text (Text) -import Data.Validation (Validation (..)) -import Data.Word (Word64) -import GHC.Generics (Generic) -import Nameservice.Modules.Nameservice.Types (Name (..)) -import Proto3.Suite (Message, Named, - fromByteString, - toLazyByteString) -import Tendermint.SDK.Codec (HasCodec (..)) -import Tendermint.SDK.Modules.Auth (Amount (..), - CoinId (..)) -import Tendermint.SDK.Modules.Bank () -import Tendermint.SDK.Types.Address (Address (..)) -import Tendermint.SDK.Types.Message (HasMessageType (..), - Msg (..), - ValidateMessage (..), - coerceProto3Error, - formatMessageParseError, - isAuthorCheck, - nonEmptyCheck) +import Data.Bifunctor (bimap, first) +import Data.Foldable (sequenceA_) +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Validation (Validation (..)) +import Data.Word (Word64) +import GHC.Generics (Generic) +import Proto3.Suite (Message, Named, fromByteString, + toLazyByteString) +import Tendermint.SDK.Codec (HasCodec (..)) +import Tendermint.SDK.Modules.Auth (Amount (..), CoinId (..)) +import Tendermint.SDK.Modules.Bank () +import Tendermint.SDK.Types.Address (Address (..)) +import Tendermint.SDK.Types.Message (HasMessageType (..), Msg (..), + ValidateMessage (..), + coerceProto3Error, + formatMessageParseError, + isAuthorCheck, nonEmptyCheck) -- @NOTE: .proto genration will use these type names as is -- only field names stripped of prefixes during generation -data FaucetAccount = FaucetAccount +data FaucetAccountMsg = FaucetAccountMsg { faucetAccountTo :: Address , faucetAccountCoinId :: CoinId , faucetAccountAmount :: Amount @@ -46,11 +41,11 @@ data FaucetAccountMessage = FaucetAccountMessage instance Message FaucetAccountMessage instance Named FaucetAccountMessage -instance HasMessageType FaucetAccount where +instance HasMessageType FaucetAccountMsg where messageType _ = "FaucetAccount" -instance HasCodec FaucetAccount where - encode FaucetAccount {..} = +instance HasCodec FaucetAccountMsg where + encode FaucetAccountMsg {..} = let faucetAccountMessaage = FaucetAccountMessage { faucetAccountMessageTo = faucetAccountTo , faucetAccountMessageCoinId = unCoinId faucetAccountCoinId @@ -58,7 +53,7 @@ instance HasCodec FaucetAccount where } in cs . toLazyByteString $ faucetAccountMessaage decode = - let toFaucetAccount FaucetAccountMessage {..} = FaucetAccount + let toFaucetAccount FaucetAccountMessage {..} = FaucetAccountMsg { faucetAccountTo = faucetAccountMessageTo , faucetAccountCoinId = CoinId faucetAccountMessageCoinId , faucetAccountAmount = Amount faucetAccountMessageAmount @@ -66,87 +61,85 @@ instance HasCodec FaucetAccount where in bimap (formatMessageParseError . coerceProto3Error) toFaucetAccount . fromByteString @FaucetAccountMessage -instance ValidateMessage FaucetAccount where +instance ValidateMessage FaucetAccountMsg where validateMessage _ = Success () -------------------------------------------------------------------------------- -data SetName = SetName - { setNameName :: Name +data SetNameMsg = SetNameMsg + { setNameName :: Text , setNameOwner :: Address , setNameValue :: Text } deriving (Eq, Show, Generic) -instance Message SetName -instance Named SetName +instance Message SetNameMsg +instance Named SetNameMsg -instance HasMessageType SetName where +instance HasMessageType SetNameMsg where messageType _ = "SetName" -instance HasCodec SetName where +instance HasCodec SetNameMsg where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -- TL;DR. ValidateBasic: https://cosmos.network/docs/tutorial/set-name.html#msg -instance ValidateMessage SetName where +instance ValidateMessage SetNameMsg where validateMessage msg@Msg{..} = - let SetName{setNameName, setNameValue} = msgData - Name name = setNameName + let SetNameMsg{setNameName, setNameValue} = msgData in sequenceA_ - [ nonEmptyCheck "Name" name + [ nonEmptyCheck "Name" setNameName , nonEmptyCheck "Value" setNameValue , isAuthorCheck "Owner" msg setNameOwner ] -------------------------------------------------------------------------------- -data DeleteName = DeleteName +data DeleteNameMsg = DeleteNameMsg { deleteNameOwner :: Address - , deleteNameName :: Name + , deleteNameName :: Text } deriving (Eq, Show, Generic) -instance Message DeleteName -instance Named DeleteName +instance Message DeleteNameMsg +instance Named DeleteNameMsg -instance HasMessageType DeleteName where +instance HasMessageType DeleteNameMsg where messageType _ = "DeleteName" -instance HasCodec DeleteName where +instance HasCodec DeleteNameMsg where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -instance ValidateMessage DeleteName where +instance ValidateMessage DeleteNameMsg where validateMessage msg@Msg{..} = - let DeleteName{deleteNameName} = msgData - Name name = deleteNameName + let DeleteNameMsg{deleteNameName} = msgData in sequenceA_ - [ nonEmptyCheck "Name" name + [ nonEmptyCheck "Name" deleteNameName , isAuthorCheck "Owner" msg deleteNameOwner ] -------------------------------------------------------------------------------- -data BuyName = BuyName +data BuyNameMsg = BuyNameMsg { buyNameBid :: Amount - , buyNameName :: Name + , buyNameName :: Text , buyNameValue :: Text , buyNameBuyer :: Address } deriving (Eq, Show) data BuyNameMessage = BuyNameMessage { buyNameMessageBid :: Word64 - , buyNameMessageName :: Name + , buyNameMessageName :: Text , buyNameMessageValue :: Text , buyNameMessageBuyer :: Address } deriving (Eq, Show, Generic) instance Message BuyNameMessage instance Named BuyNameMessage -instance HasMessageType BuyName where +instance HasMessageType BuyNameMsg where messageType _ = "BuyName" -instance HasCodec BuyName where - encode BuyName {..} = +instance HasCodec BuyNameMsg where + encode BuyNameMsg {..} = let buyNameMessage = BuyNameMessage { buyNameMessageBid = unAmount buyNameBid , buyNameMessageName = buyNameName @@ -155,7 +148,7 @@ instance HasCodec BuyName where } in cs . toLazyByteString $ buyNameMessage decode = - let toBuyName BuyNameMessage {..} = BuyName + let toBuyName BuyNameMessage {..} = BuyNameMsg { buyNameBid = Amount buyNameMessageBid , buyNameName = buyNameMessageName , buyNameValue = buyNameMessageValue @@ -164,12 +157,11 @@ instance HasCodec BuyName where in bimap (formatMessageParseError . coerceProto3Error) toBuyName . fromByteString @BuyNameMessage -instance ValidateMessage BuyName where +instance ValidateMessage BuyNameMsg where validateMessage msg@Msg{..} = - let BuyName{buyNameName, buyNameValue} = msgData - Name name = buyNameName + let BuyNameMsg{buyNameName, buyNameValue} = msgData in sequenceA_ - [ nonEmptyCheck "Name" name + [ nonEmptyCheck "Name" buyNameName , nonEmptyCheck "Value" buyNameValue , isAuthorCheck "Owner" msg buyNameBuyer ] diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs index d3d59ac4..13911abb 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Query.hs @@ -3,22 +3,21 @@ module Nameservice.Modules.Nameservice.Query , querier ) where -import Data.Proxy -import Nameservice.Modules.Nameservice.Keeper (storeKey) -import Nameservice.Modules.Nameservice.Types (Name, Whois) -import Polysemy (Members) -import qualified Tendermint.SDK.BaseApp as BaseApp +import Nameservice.Modules.Nameservice.Store +import Nameservice.Modules.Nameservice.Types +import Polysemy (Members) +import Servant.API ((:>)) +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Store.Map as M -------------------------------------------------------------------------------- -- | Query API -------------------------------------------------------------------------------- -type NameserviceContents = '[(Name, Whois)] -type QueryApi = BaseApp.QueryApi NameserviceContents +type QueryApi = "whois" :> BaseApp.StoreLeaf (M.Map Name Whois) querier :: Members BaseApp.QueryEffs r => BaseApp.RouteQ QueryApi r -querier = - BaseApp.storeQueryHandlers (Proxy :: Proxy NameserviceContents) storeKey (Proxy :: Proxy r) +querier = BaseApp.storeQueryHandler whoisMap diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs index 3cbbc1b7..e1063e29 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Router.hs @@ -7,72 +7,60 @@ import Nameservice.Modules.Nameservice.Keeper (NameserviceEffs, buyName, deleteName, faucetAccount, setName) -import Nameservice.Modules.Nameservice.Messages (BuyName, DeleteName, - FaucetAccount, - SetName) +import Nameservice.Modules.Nameservice.Messages import Polysemy (Members, Sem) import Servant.API ((:<|>) (..)) import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, RouteTx, RoutingTx (..), - TxEffs, TypedMessage, + TypedMessage, incCount, withTimer) -import Tendermint.SDK.Modules.Bank (BankEffs) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) type MessageApi = - TypedMessage BuyName :~> Return () - :<|> TypedMessage SetName :~> Return () - :<|> TypedMessage DeleteName :~> Return () - :<|> TypedMessage FaucetAccount :~> Return () + TypedMessage BuyNameMsg :~> Return () + :<|> TypedMessage SetNameMsg :~> Return () + :<|> TypedMessage DeleteNameMsg :~> Return () + :<|> TypedMessage FaucetAccountMsg :~> Return () messageHandlers - :: Members BaseEffs r - => Members BankEffs r - => Members TxEffs r - => Members NameserviceEffs r + :: Members NameserviceEffs r + => Members BaseEffs r => RouteTx MessageApi r messageHandlers = buyNameH :<|> setNameH :<|> deleteNameH :<|> faucetH buyNameH - :: Members BaseEffs r - => Members TxEffs r - => Members BankEffs r - => Members NameserviceEffs r - => RoutingTx BuyName + :: Members NameserviceEffs r + => Members BaseEffs r + => RoutingTx BuyNameMsg -> Sem r () buyNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do incCount "buy_total" withTimer "buy_duration_seconds" $ buyName msgData setNameH - :: Members BaseEffs r - => Members TxEffs r - => Members NameserviceEffs r - => RoutingTx SetName + :: Members NameserviceEffs r + => Members BaseEffs r + => RoutingTx SetNameMsg -> Sem r () setNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do incCount "set_total" withTimer "set_duration_seconds" $ setName msgData deleteNameH - :: Members BaseEffs r - => Members TxEffs r - => Members BankEffs r - => Members NameserviceEffs r - => RoutingTx DeleteName + :: Members NameserviceEffs r + => Members BaseEffs r + => RoutingTx DeleteNameMsg -> Sem r () deleteNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do incCount "delete_total" withTimer "delete_duration_seconds" $ deleteName msgData faucetH - :: Members TxEffs r - => Members BankEffs r - => Members BaseEffs r - => RoutingTx FaucetAccount + :: Members NameserviceEffs r + => RoutingTx FaucetAccountMsg -> Sem r () faucetH (RoutingTx Tx{txMsg=Msg{msgData}}) = faucetAccount msgData diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Store.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Store.hs new file mode 100644 index 00000000..98e0a38d --- /dev/null +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Store.hs @@ -0,0 +1,38 @@ +module Nameservice.Modules.Nameservice.Store + ( Name(..) + , whoisMap + ) where + +import Control.Lens (iso) +import qualified Data.Aeson as A +import Data.Proxy +import Data.String.Conversions (cs) +import Data.Text (Text) +import GHC.TypeLits (symbolVal) +import Nameservice.Modules.Nameservice.Types +import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Store.Map as M + +data NameserviceNamespace + +store :: BaseApp.Store NameserviceNamespace +store = BaseApp.makeStore $ + BaseApp.KeyRoot $ cs . symbolVal $ Proxy @NameserviceName + +newtype Name = Name {unName :: Text} deriving (Eq, Show, A.ToJSON, A.FromJSON) + +instance BaseApp.RawKey Name where + rawKey = iso (\(Name n) -> cs n) (Name . cs) + +instance BaseApp.QueryData Name + +data WhoisMapKey = WhoisMapKey + +instance BaseApp.RawKey WhoisMapKey where + rawKey = iso (const "whoisMap") (const WhoisMapKey) + +instance BaseApp.IsKey WhoisMapKey NameserviceNamespace where + type Value WhoisMapKey NameserviceNamespace = M.Map Name Whois + +whoisMap :: M.Map Name Whois +whoisMap = M.makeMap WhoisMapKey store diff --git a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs index 131d9ada..8ba01d70 100644 --- a/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs +++ b/hs-abci-docs/nameservice/src/Nameservice/Modules/Nameservice/Types.hs @@ -2,23 +2,15 @@ module Nameservice.Modules.Nameservice.Types where -import Control.Lens (iso) import Data.Aeson as A import Data.Bifunctor (bimap) -import Data.String (IsString (..)) import Data.String.Conversions (cs) import Data.Text (Text) -import qualified Data.Text.Lazy as TL import Data.Word (Word64) import GHC.Generics (Generic) import Nameservice.Aeson (defaultNameserviceOptions) -import Proto3.Suite (HasDefault, Message, - MessageField, Named, - Primitive (..), fromByteString, +import Proto3.Suite (Message, Named, fromByteString, toLazyByteString) -import qualified Proto3.Suite.DotProto as DotProto -import qualified Proto3.Wire.Decode as Decode -import qualified Proto3.Wire.Encode as Encode import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Modules.Auth (Amount (..), CoinId (..)) @@ -29,22 +21,8 @@ import Tendermint.SDK.Types.Address (Address) type NameserviceName = "nameservice" -data NameserviceNamespace - -------------------------------------------------------------------------------- -newtype Name = Name Text deriving (Eq, Show, Generic, A.ToJSON, A.FromJSON, HasCodec) - -instance Primitive Name where - encodePrimitive n (Name txt) = Encode.text n . TL.fromStrict $ txt - decodePrimitive = Name . TL.toStrict <$> Decode.text - primType _ = DotProto.String -instance HasDefault Name -instance MessageField Name -instance IsString Name where - fromString = Name . fromString - -instance BaseApp.FromQueryData Name data Whois = Whois { whoisValue :: Text @@ -76,15 +54,6 @@ instance HasCodec Whois where } in bimap (cs . show) toWhois . fromByteString @WhoisMessage -instance BaseApp.RawKey Name where - rawKey = iso (\(Name n) -> cs n) (Name . cs) - -instance BaseApp.IsKey Name NameserviceNamespace where - type Value Name NameserviceNamespace = Whois - -instance BaseApp.Queryable Whois where - type Name Whois = "whois" - -------------------------------------------------------------------------------- -- Exceptions -------------------------------------------------------------------------------- @@ -136,7 +105,7 @@ instance BaseApp.Select Faucetted data NameClaimed = NameClaimed { nameClaimedOwner :: Address - , nameClaimedName :: Name + , nameClaimedName :: Text , nameClaimedValue :: Text , nameClaimedBid :: Amount } deriving (Eq, Show, Generic) @@ -152,7 +121,7 @@ instance BaseApp.ToEvent NameClaimed instance BaseApp.Select NameClaimed data NameRemapped = NameRemapped - { nameRemappedName :: Name + { nameRemappedName :: Text , nameRemappedOldValue :: Text , nameRemappedNewValue :: Text } deriving (Eq, Show, Generic) @@ -168,7 +137,7 @@ instance BaseApp.ToEvent NameRemapped instance BaseApp.Select NameRemapped data NameDeleted = NameDeleted - { nameDeletedName :: Name + { nameDeletedName :: Text } deriving (Eq, Show, Generic) nameDeletedAesonOptions :: A.Options diff --git a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs index e594f340..d1dc1f36 100644 --- a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -51,7 +51,7 @@ import Test.Hspec spec :: Spec spec = do - let satoshi = N.Name "satoshi" + let satoshi = "satoshi" faucetAmount = 1000 beforeAll (testInit faucetAmount) $ do @@ -68,7 +68,7 @@ spec = do it "Can create a name" $ \tenv -> do let val = "hello world" - msg = N.BuyName + msg = N.BuyNameMsg { buyNameBid = 0 , buyNameName = satoshi , buyNameValue = val @@ -100,19 +100,19 @@ spec = do , whoisPrice = 0 } foundWhois <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ - getWhois defaultQueryArgs { queryArgsData = satoshi } + getWhois defaultQueryArgs { queryArgsData = N.Name satoshi } foundWhois `shouldBe` expected it "Can query for a name that doesn't exist" $ const $ do - let nope = N.Name "nope" + let nope = "nope" resp <- RPC.runTendermintM rpcConfig $ - getWhois defaultQueryArgs { queryArgsData = nope } + getWhois defaultQueryArgs { queryArgsData = N.Name nope } ensureQueryResponseCode 2 resp it "Can set a name value" $ \tenv -> do let oldVal = "hello world" newVal = "goodbye to a world" - msg = N.SetName + msg = N.SetNameMsg { setNameName = satoshi , setNameOwner = signerAddress user1 , setNameValue = newVal @@ -141,12 +141,12 @@ spec = do , whoisPrice = 0 } foundWhois <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ - getWhois defaultQueryArgs { queryArgsData = satoshi } + getWhois defaultQueryArgs { queryArgsData = N.Name satoshi } foundWhois `shouldBe` expected it "Can fail to set a name" $ const $ do -- try to set a name without being the owner - let msg = N.SetName + let msg = N.SetNameMsg { setNameName = satoshi , setNameOwner = signerAddress user2 , setNameValue = "goodbye to a world" @@ -162,10 +162,10 @@ spec = do balance1 <- getUserBalance user1 balance2 <- getUserBalance user2 N.Whois{whoisPrice} <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ - getWhois defaultQueryArgs { queryArgsData = satoshi } + getWhois defaultQueryArgs { queryArgsData = N.Name satoshi } let purchaseAmount = whoisPrice + 1 newVal = "hello (again) world" - msg = N.BuyName + msg = N.BuyNameMsg { buyNameBid = purchaseAmount , buyNameName = satoshi , buyNameValue = newVal @@ -210,7 +210,7 @@ spec = do , whoisPrice = purchaseAmount } foundWhois <- fmap queryResultData . assertQuery . RPC.runTendermintM rpcConfig $ - getWhois defaultQueryArgs { queryArgsData = satoshi } + getWhois defaultQueryArgs { queryArgsData = N.Name satoshi } foundWhois `shouldBe` expected -- @NOTE: this is possibly a problem with the go application too @@ -221,7 +221,7 @@ spec = do -- buy let bid = 500 val = "hello (again) world" - msg = N.BuyName + msg = N.BuyNameMsg { buyNameBid = bid , buyNameName = satoshi , buyNameValue = val @@ -261,7 +261,7 @@ spec = do it "Can fail to buy a name" $ const $ do -- try to buy at a lower price - let msg = N.BuyName + let msg = N.BuyNameMsg { buyNameBid = 100 , buyNameName = satoshi , buyNameValue = "hello (again) world" @@ -276,7 +276,7 @@ spec = do ensureResponseCodes (0,1) resp it "Can delete names" $ \tenv -> do - let msg = N.DeleteName + let msg = N.DeleteNameMsg { deleteNameOwner = signerAddress user2 , deleteNameName = satoshi } @@ -298,14 +298,14 @@ spec = do fromEvent evdeletedLog `shouldBe` Right deletedLog respQ <- RPC.runTendermintM rpcConfig $ - getWhois defaultQueryArgs { queryArgsData = satoshi } + getWhois defaultQueryArgs { queryArgsData = N.Name satoshi } ensureQueryResponseCode 2 respQ it "Can fail a transfer" $ const $ do addr2Balance <- getUserBalance user2 let tooMuchToTransfer = addr2Balance + 1 - msg = B.Transfer + msg = B.TransferMsg { transferFrom = signerAddress user2 , transferTo = signerAddress user1 , transferCoinId = N.nameserviceCoinId @@ -324,7 +324,7 @@ spec = do balance2 <- getUserBalance user2 let transferAmount = 1 msg = - B.Transfer + B.TransferMsg { transferFrom = signerAddress user1 , transferTo = signerAddress user2 , transferCoinId = N.nameserviceCoinId @@ -367,7 +367,7 @@ faucetUser -> IO () faucetUser amount s@(Signer addr _) = void . assertTx .runTxClientM $ - let msg = N.FaucetAccount addr N.nameserviceCoinId amount + let msg = N.FaucetAccountMsg addr N.nameserviceCoinId amount opts = TxOpts { txOptsGas = 0 , txOptsSigner = s @@ -451,28 +451,28 @@ runTxClientM m = runReaderT m txClientConfig -- Nameservice Client buyName :: TxOpts - -> N.BuyName + -> N.BuyNameMsg -> TxClientM (TxClientResponse () ()) setName :: TxOpts - -> N.SetName + -> N.SetNameMsg -> TxClientM (TxClientResponse () ()) deleteName :: TxOpts - -> N.DeleteName + -> N.DeleteNameMsg -> TxClientM (TxClientResponse () ()) -- Bank Client transfer :: TxOpts - -> B.Transfer + -> B.TransferMsg -> TxClientM (TxClientResponse () ()) faucet :: TxOpts - -> N.FaucetAccount + -> N.FaucetAccountMsg -> TxClientM (TxClientResponse () ()) (buyName :<|> setName :<|> deleteName :<|> faucet) :<|> diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md b/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md new file mode 100644 index 00000000..c50562fb --- /dev/null +++ b/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md @@ -0,0 +1,77 @@ +--- +title: Foundations - Storage +--- + +# Database + +ABCI application depend on some kind of merkelized storage in order the achieve consensus on valid a application state. The SDK has two database options to interpret `StoreEffs`, an in-memory [avl-auth](https://github.com/oscoin/avl-auth) option as well as a persisted [iavl](https://github.com/tendermint/iavl) option. + +# Stores + +The most convenient way to partition a key-value store is by heavy use of prefixes -- for example, if you want to separate each module's keyspace, you can use prefix all of the keys that it manages by the module's unique name. If you want to partition storage within a module, say for a list or mapping, you can again use prefixes to create a unique keyspace. + +The definition of a `Store` is a unique keyspace. Implementation wise, it is effectively a list of prefixes to concatonate when creating keys. There are currently 6 ways of creating Stores: + +1. From a `KeyRoot`, which is basically defines a top level Store. +2. Using the `nestStore` function to mount one Store in another. +3. By creating a `Var`, which creates a keyspace with exactly one key. +4. By creating an `Array`, which creates a keyspace whose keys are type `Word64`. +5. By creating a `Map k v`, which creates a keyspace whose keys are type `k`. +6. By creating a `List`, which creates a linked list whose keyspace is internal. + + +Because a Store is a unique keyspace, it allows us to build a typed key-value storage on top of the raw ByteString interface. This is achieved by adding a phantom namespace type to the Store type and declaring an instance of the `IsKey` class: + + +~~~ haskell ignore +data Store ns = Store + { storePathFromRoot :: [ByteString] + } + +class RawKey k where + rawKey :: Iso' k BS.ByteString + +class RawKey k => IsKey k ns where + type Value k ns :: * +~~~ + +# Example + +Let's take the example of the `Auth` module which is responsible for maintaining a mapping `Address -> Account`. To declare the mapping, we first need to make a namespace and a root storage for the module: + +~~~ haskell ignore +data AuthNamespace + +store :: Store AuthNamespace +store = makeStore $ KeyRoot "auth" +~~~ + + +We then need to make a new keyspace for our accounts mapping, which looks like + +~~~ haskell ignore +data AccountsMapKey = AccountsMapKey + +instance RawKey AccountsMapKey where + rawKey = iso (const "accounts") (const AccountsMapKey) + +instance IsKey AccountsMapKey AuthNamespace where + type Value AccountsMapKey AuthNamespace = Map Address Account +~~~ + +This tells the compiler that the `AccountsMapKey` key type can only be used to access the mapping `Map Address Account`, it cannot be used to query any other types. + +We can then declare the mapping itself like + +~~~ haskell ignore +accountsMap :: Map Address Account +accountsMap = makeMap AccountsMapKey store +~~~ + +This both creates the mapping and mounts it inside of our module level store. The effect of this is that if you wanted to query the underlying raw key-value store for the account associated to the address `0xdeadbeef`, then the actual key looks something like + +~~~ haskell ignore +encodeUtf8 "auth" <> encodeUtf8 "accounts" <> bytesFromHex "0xdeafbeef" +~~~ + +While writing apps inside the SDK you do not need to worry about the explicit prefixing since everything is taken care of for you. However, if you are querying for state via an ABCI `query` message, the `key` field that is returned in the response will contain this full path. In the above example, if you wanted to recover the address from the key, you would need to know the prefixes that were applied. \ No newline at end of file diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md index ad964e18..54c9f31f 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/02-Types.md @@ -150,22 +150,6 @@ At is point, you can use the database operations exported by `Tendermint.SDK.Bas The [`cosmos-sdk`](https://github.com/cosmos/cosmos-sdk) assumes that you use `url` formatted queries with some possible query params. For example, to query a `Whois` value based on a `Name`, you might submit a `query` message with the route `nameservice/whois` and supply a value of type `Name` to specify as the `data` field. Our SDK makes the same assumption for compatability reasons. -In order to register the `Whois` type with the query service, you must implement the `Queryable` typeclass: - -~~~ haskell ignore -class HasCodec a => Queryable a where - type Name a :: Symbol -~~~ - -This means that any item which is queryable needs to have codecs via the `HasCodec` class. You also need to name the type, as this will match the leaf of the `url` used for querying. So for example, in the Nameservice app we have - -~~~ haskell -instance BA.Queryable Whois where - type Name Whois = "whois" -~~~ - -since `Whois` already implements the `HasCodec` class. - ### Error Types You might want to define a module specific error type that has a `throw`/`catch` interface. This error type should be accessible by any other dependent modules, and any uncaught error should eventually be converted into some kind of generic application error understandable by Tendermint. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md index c98aa5d1..3cb61e04 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/03-Message.md @@ -29,7 +29,7 @@ import Data.String.Conversions (cs) import Data.Text (Text) import Data.Word (Word64) import GHC.Generics (Generic) -import Nameservice.Modules.Nameservice.Types (Name(..)) +import Nameservice.Modules.Nameservice.Types import Proto3.Suite (Named, Message, fromByteString, toLazyByteString) import Tendermint.SDK.Types.Address (Address) import Tendermint.SDK.Types.Message (Msg(..), ValidateMessage(..), HasMessageType(..), @@ -46,49 +46,49 @@ For the puroposes of the tutorial, we will use the `proto3-suite` for the messag ~~~ haskell -data SetName = SetName - { setNameName :: Name +data SetNameMsg = SetNameMsg + { setNameName :: Text , setNameOwner :: Address , setNameValue :: Text } deriving (Eq, Show, Generic) -instance Message SetName -instance Named SetName +instance Message SetNameMsg +instance Named SetNameMsg -instance HasCodec SetName where +instance HasCodec SetNameMsg where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -data DeleteName = DeleteName +data DeleteNameMsg = DeleteNameMsg { deleteNameOwner :: Address - , deleteNameName :: Name + , deleteNameName :: Text } deriving (Eq, Show, Generic) -instance Message DeleteName -instance Named DeleteName +instance Message DeleteNameMsg +instance Named DeleteNameMsg -instance HasCodec DeleteName where +instance HasCodec DeleteNameMsg where encode = cs . toLazyByteString decode = first (formatMessageParseError . coerceProto3Error) . fromByteString -data BuyName = BuyName +data BuyNameMsg = BuyNameMsg { buyNameBid :: Amount - , buyNameName :: Name + , buyNameName :: Text , buyNameValue :: Text , buyNameBuyer :: Address } deriving (Eq, Show) data BuyNameMessage = BuyNameMessage { buyNameMessageBid :: Word64 - , buyNameMessageName :: Name + , buyNameMessageName :: Text , buyNameMessageValue :: Text , buyNameMessageBuyer :: Address } deriving (Eq, Show, Generic) instance Message BuyNameMessage instance Named BuyNameMessage -instance HasCodec BuyName where - encode BuyName {..} = +instance HasCodec BuyNameMsg where + encode BuyNameMsg {..} = let buyNameMessage = BuyNameMessage { buyNameMessageBid = unAmount buyNameBid , buyNameMessageName = buyNameName @@ -97,7 +97,7 @@ instance HasCodec BuyName where } in cs . toLazyByteString $ buyNameMessage decode = - let toBuyName BuyNameMessage {..} = BuyName + let toBuyName BuyNameMessage {..} = BuyNameMsg { buyNameBid = Amount buyNameMessageBid , buyNameName = buyNameMessageName , buyNameValue = buyNameMessageValue @@ -118,13 +118,13 @@ to associate each message to a tag to assist in parsing. So for example, we can ~~~ haskell -instance HasMessageType SetName where +instance HasMessageType SetNameMsg where messageType _ = "SetName" -instance HasMessageType DeleteName where +instance HasMessageType DeleteNameMsg where messageType _ = "DeleteName" -instance HasMessageType BuyName where +instance HasMessageType BuyNameMsg where messageType _ = "BuyName" ~~~ @@ -171,31 +171,28 @@ It is also possible to run dynamic checks on the transaction, i.e. checks that n Here are the validation instances for our message types, which use some of the combinators defined in the SDK ~~~ haskell -instance ValidateMessage SetName where +instance ValidateMessage SetNameMsg where validateMessage msg@Msg{..} = - let SetName{setNameName, setNameValue} = msgData - Name name = setNameName + let SetNameMsg{setNameName, setNameValue} = msgData in sequenceA_ - [ nonEmptyCheck "Name" name + [ nonEmptyCheck "Name" setNameName , nonEmptyCheck "Value" setNameValue , isAuthorCheck "Owner" msg setNameOwner ] -instance ValidateMessage DeleteName where +instance ValidateMessage DeleteNameMsg where validateMessage msg@Msg{..} = - let DeleteName{deleteNameName} = msgData - Name name = deleteNameName + let DeleteNameMsg{deleteNameName} = msgData in sequenceA_ - [ nonEmptyCheck "Name" name + [ nonEmptyCheck "Name" deleteNameName , isAuthorCheck "Owner" msg deleteNameOwner ] -instance ValidateMessage BuyName where +instance ValidateMessage BuyNameMsg where validateMessage msg@Msg{..} = - let BuyName{buyNameName, buyNameValue} = msgData - Name name = buyNameName + let BuyNameMsg{buyNameName, buyNameValue} = msgData in sequenceA_ - [ nonEmptyCheck "Name" name + [ nonEmptyCheck "Name" buyNameName , nonEmptyCheck "Value" buyNameValue , isAuthorCheck "Owner" msg buyNameBuyer ] diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md index da5a0c00..5b44c516 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/04-Keeper.md @@ -10,65 +10,77 @@ title: Nameservice - Keeper ## Tutorial.Nameservice.Keeper +In this section, we will make use of the `Store` types defined in `Nameservice.Modules.Nameservice.Store`. For an overview on how this is setup, see the `Storage` chapter in the `Foundations` section of the tutorial. + ~~~ haskell {-# LANGUAGE TemplateHaskell #-} module Tutorial.Nameservice.Keeper where -import Polysemy (Sem, Member, Members, makeSem, interpret) -import Polysemy.Error (Error, throw, mapError) -import Polysemy.Output (Output) -import Nameservice.Modules.Nameservice.Messages (DeleteName(..)) -import Nameservice.Modules.Nameservice.Types (Whois(..), Name, NameDeleted(..), NameserviceNamespace, NameserviceError(..)) +import Polysemy (Sem, Member, Members, makeSem) +import Polysemy.Error (Error, throw) +import Nameservice.Modules.Nameservice.Messages +import Nameservice.Modules.Nameservice.Store (Name(..), whoisMap) +import Nameservice.Modules.Nameservice.Types (Whois(..), NameDeleted(..), NameserviceError(..)) import qualified Tendermint.SDK.BaseApp as BA -import Tendermint.SDK.Modules.Auth (AuthEffs, Coin(..)) -import Tendermint.SDK.Modules.Bank (BankEffs, mint) +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import Tendermint.SDK.Modules.Bank (BankEffs, Coin(..), CoinId, mint) + + +nameserviceCoinId :: CoinId +nameserviceCoinId = "nameservice" ~~~ Generally a keeper is defined by a set of effects that the module introduces and depends on. In the case of Nameservice, we introduce the custom `Nameservice` effect: ~~~ haskell +type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] + data NameserviceKeeper m a where - PutWhois :: Name -> Whois -> NameserviceKeeper m () + BuyName :: BuyNameMsg -> NameserviceKeeper m () + DeleteName :: DeleteNameMsg -> NameserviceKeeper m () + SetName :: SetNameMsg -> NameserviceKeeper m () GetWhois :: Name -> NameserviceKeeper m (Maybe Whois) - DeleteWhois :: Name -> NameserviceKeeper m () makeSem ''NameserviceKeeper - -type NameserviceEffs = '[NameserviceKeeper, Error NameserviceError] ~~~ -where `makeSem` is from polysemy, it uses template Haskell to create the helper functions `putWhoIs`, `getWhois`, `deleteWhois`: +where `makeSem` is from polysemy, it uses template Haskell to create the helper functions `buyName`, `deleteName`, `setName`, `getWhois`: ~~~ haskell ignore -putWhois :: forall r. Member NameserviceKeeper r => Name -> Whois -> Sem r () -getWhois :: forall r. Member NameserviceKeeper r => Name -> Sem r (Maybe Whois) -deleteWhois :: forall r. Member NameserviceKeeper r => Name -> Sem r () +buyName :: BuyNameMsg -> NameserviceKeeper m () +deleteName :: DeleteNameMsg -> NameserviceKeeper m () +setName :: SetNameMsg -> NameserviceKeeper m () +getWhois :: Name -> NameserviceKeeper m (Maybe Whois) ~~~ -We can then write the top level function for example for deleting a name: +### Evaluating Module Effects + +Like we said before, all transactions must ultimately compile to the set of effects belonging to `TxEffs` and `BaseEffs`. In particular this means that we must interpret `NameserviceEffs` into more basic effects. To do this we follow the general pattern of first interpreting `NameserviceKeeper` effects, then finally interpreting `Error NameserviceError` in terms of `Error AppError`. Let's focus on the `DeleteName` summand of `NameserviceKeeper`. We can write an interpreting function as follows: ~~~ haskell -deleteName - :: Member (Output BA.Event) r - => Members AuthEffs r +deleteNameF + :: Members BA.TxEffs r + => Members BA.BaseEffs r => Members BankEffs r - => Members [NameserviceKeeper, Error NameserviceError] r - => DeleteName + => Member (Error NameserviceError) r + => DeleteNameMsg -> Sem r () -deleteName DeleteName{..} = do - mWhois <- getWhois deleteNameName +deleteNameF DeleteNameMsg{..} = do + mWhois <- M.lookup (Name deleteNameName) whoisMap case mWhois of Nothing -> throw $ InvalidDelete "Can't remove unassigned name." Just Whois{..} -> if whoisOwner /= deleteNameOwner then throw $ InvalidDelete "Deleter must be the owner." else do - mint deleteNameOwner (Coin "nameservice" whoisPrice) - deleteWhois deleteNameName - BA.emit NameDeleted - { nameDeletedName = deleteNameName - } + mint deleteNameOwner (Coin nameserviceCoinId whoisPrice) + M.delete (Name deleteNameName) whoisMap + let event = NameDeleted + { nameDeletedName = deleteNameName + } + BA.emit event + BA.logEvent event ~~~ The control flow should be pretty clear: @@ -76,47 +88,46 @@ The control flow should be pretty clear: 2. Check that the name is registered to the person trying to delete it, if not throw an error. 3. Refund the tokens locked in the name to the owner. 4. Delete the entry from the database. -5. Emit an event that the name has been deleted. +5. Emit an event that the name has been deleted and log this event. Taking a look at the class constraints, we see ~~~ haskell ignore -(Members NameserviceEffs, Member (Output Event) r) + ( Members BaseApp.TxEffs r + , Members BaseApp.BaseEffs r + , Members BankEffs r + , Member (Error NameserviceError) r + ) ~~~ -- The `NameserviceKeeper` effect is required because the function may manipulate the modules database with `deleteName`. +- The `TxEffs` effect is required because the function manipulates the `whoisMap` and emits an `Event`. +- The `BaseEffs` effect is required because the function has logging. - The `Error NameserviceError` effect is required because the function may throw an error. -- The `Auth` effect is required because the function will mint coins. -- The `Output Event` effect is required because the function may emit a `NameDeleted` event. +- The `BankEffs` effect is required because the function will mint coins. -### Evaluating Module Effects -Like we said before, all transactions must ultimately compile to the set of effects belonging to `TxEffs` and `BaseEffs`. For effects interpreted to `ReadStore` and `WriteStore`, this means that you will need to define something called a `StoreKey`. +Using this helper function and others, we can write our module's `eval` function by interpreting the `NameserviceEffs` in two steps: - -A `StoreKey` is effectively a namespacing inside the database, and is unique for a given module. In theory it could be any `ByteString`, but the natural definition in the case of Nameservice is would be something like - -~~~ haskell -storeKey :: BA.StoreKey NameserviceNamespace -storeKey = BA.StoreKey "nameservice" -~~~ - -With this `storeKey` it is possible to write the `eval` function to resolve the effects defined in Nameservice, namely the `NameserviceKeeper` effect and `Error NameserviceError`: - -~~~ haskell +~~~ haskell ignore eval :: Members BA.TxEffs r + => Members BankEffs r + => Members BA.BaseEffs r => forall a. Sem (NameserviceKeeper ': Error NameserviceError ': r) a -> Sem r a -eval = mapError BA.makeAppError . evalNameservice +eval = mapError BaseApp.makeAppError . evalNameservice where evalNameservice :: Members BA.TxEffs r + => Members BA.BaseEffs r + => Members BankEffs r + => Member (Error NameserviceError) r => Sem (NameserviceKeeper ': r) a -> Sem r a evalNameservice = interpret (\case - GetWhois name -> BA.get storeKey name - PutWhois name whois -> BA.put storeKey name whois - DeleteWhois name -> BA.delete storeKey name + ... + DeleteName msg -> deleteNameF msg + ... ) + ~~~ diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md index 706bb96e..c2a55b4a 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/05-Query.md @@ -9,36 +9,35 @@ title: Nameservice - Query ~~~ haskell module Tutorial.Nameservice.Query where -import Data.Proxy -import Nameservice.Modules.Nameservice.Keeper (storeKey) -import Nameservice.Modules.Nameservice.Types (Whois, Name) +import Nameservice.Modules.Nameservice.Types (Whois) +import Nameservice.Modules.Nameservice.Store (Name, whoisMap) import Polysemy (Members) -import Tendermint.SDK.BaseApp (QueryEffs, RouteQ, QueryApi, storeQueryHandlers) +import Tendermint.SDK.BaseApp (QueryEffs, StoreLeaf, RouteQ, storeQueryHandler) +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import Servant.API ((:>)) ~~~ The way to query application state is via the `query` message which uses a `url` like format. The SDK tries to abstract as much of this away as possible. For example, if you want to only serve state that you have registered with the store via the `IsKey` class, then things are very easy. If you need to make joins to serve requests, we support this as well and it's not hard, but we will skip this for now. -In the case we just want to serve data we have registered with the `IsKey` class, we simply need to declare some types +In the case we just want to serve data we have registered with the `IsKey` class, we simply need to declare some types: ```haskell -type NameserviceContents = '[(Name, Whois)] -type Api = QueryApi NameserviceContents +type Api = "whois" :> StoreLeaf (M.Map Name Whois) + ``` -- `NameserviceContents` is simply a type level list of the key value pairs you wish to serve. In this case there is only `Name -> Whois` -- `Api` is the list of leaves of valid url's for this module. When the type family `QueryApi` is applied, it will construct the leaves from the key value pairs based on the `IsKey` class. In this case you end up with only `"/whois"` endpoint, which accepts the `Name` in the `data` field of the `query` message encoded via the `HasCodec` class. +`Api` is the list of valid urls for this module. In this case, when unpacked it will create a single endpoint `/whois` that expects a value of type `Name` as the `data` field in the ABCI `query` object. Technically speaking it is a `Name` prefixed by some other storage related prefixes dictated by the module, but we will hold off on this for now. If you use the automatically generated client libraries, you don't need to worry about this. -To serve all the data registered with the `IsKey` class, we can use the `storeQueryHandlers` function, supplying a proxy for the store contents, the `storeKey` and a proxy for the effects used in serving requests. In this case because we are serving only types registered with the store, we will need to assume the `RawStore` and `Error AppError` effects. +To serve all the data, we can use the `storeQueryHandler` function by supplying the appropriate `store` we want to serve. ~~~ haskell querier :: Members QueryEffs r => RouteQ Api r -querier = - storeQueryHandlers (Proxy @NameserviceContents) storeKey (Proxy :: Proxy r) +querier = storeQueryHandler whoisMap ~~~ -Here `RouteT` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely vendored from that codebase. +Here `RouteQ` is a type family that can build a server from the `Api` type to handle incoming requests. It is similar to how `servant` works, and is largely vendored from that codebase. Note that more advanced queries are possible other than just serving what is in storage. For example you might want to use joins to fulfill requests or use query parameters in the url. These are all possible, but we won't go into details here as they are not used in the app. diff --git a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md index cfcdeb42..1bba738e 100644 --- a/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md +++ b/hs-abci-docs/nameservice/tutorial/Tutorial/Nameservice/09-Testing.md @@ -117,28 +117,28 @@ Once we have defined our monad capable of querying nonces, we can then generate -- Nameservice Client buyName :: TxOpts - -> N.BuyName + -> N.BuyNameMsg -> TxClientM (TxClientResponse () ()) setName :: TxOpts - -> N.SetName + -> N.SetNameMsg -> TxClientM (TxClientResponse () ()) deleteName :: TxOpts - -> N.DeleteName + -> N.DeleteNameMsg -> TxClientM (TxClientResponse () ()) -- Bank Client transfer :: TxOpts - -> B.Transfer + -> B.TransferMsg -> TxClientM (TxClientResponse () ()) faucet :: TxOpts - -> N.FaucetAccount + -> N.FaucetAccountMsg -> TxClientM (TxClientResponse () ()) (buyName :<|> setName :<|> deleteName :<|> faucet) :<|> diff --git a/hs-abci-docs/simple-storage/package.yaml b/hs-abci-docs/simple-storage/package.yaml index 52db0792..b39204bd 100644 --- a/hs-abci-docs/simple-storage/package.yaml +++ b/hs-abci-docs/simple-storage/package.yaml @@ -74,6 +74,7 @@ library: - polysemy-plugin - proto-lens - proto-lens-runtime + - servant - string-conversions - text - validation diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs index dd5efe20..4a4c634f 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage.hs @@ -9,7 +9,7 @@ module SimpleStorage.Modules.SimpleStorage import Data.Proxy import Polysemy (Members) -import SimpleStorage.Modules.SimpleStorage.Keeper hiding (storeKey) +import SimpleStorage.Modules.SimpleStorage.Keeper hiding (countVar) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Query import SimpleStorage.Modules.SimpleStorage.Router diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs index c985717e..008d55f7 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Keeper.hs @@ -1,46 +1,76 @@ {-# LANGUAGE TemplateHaskell #-} + module SimpleStorage.Modules.SimpleStorage.Keeper - ( SimpleStorageKeeper - , SimpleStorageEffs + ( SimpleStorageEffs + , SimpleStorageKeeper(..) , updateCount , getCount - , storeKey , eval + -- + , countVar ) where +import Control.Lens (iso) +import Crypto.Hash (SHA256 (..), + hashWith) +import Data.ByteArray (convert) +import Data.ByteString (ByteString) +import Data.String.Conversions (cs) import Polysemy (Members, Sem, interpret, makeSem) import Polysemy.Output (Output) import SimpleStorage.Modules.SimpleStorage.Types import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Store.Var as V -storeKey :: BaseApp.StoreKey SimpleStorageNamespace -storeKey = BaseApp.StoreKey "simple_storage" +type SimpleStorageEffs = '[SimpleStorageKeeper] data SimpleStorageKeeper m a where - PutCount :: Count -> SimpleStorageKeeper m () + UpdateCount :: Count -> SimpleStorageKeeper m () GetCount :: SimpleStorageKeeper m (Maybe Count) makeSem ''SimpleStorageKeeper -type SimpleStorageEffs = '[SimpleStorageKeeper] +eval + :: forall r. + Members BaseApp.TxEffs r + => Members BaseApp.BaseEffs r + => forall a. (Sem (SimpleStorageKeeper ': r) a -> Sem r a) +eval = interpret (\case + UpdateCount count -> updateCountF count + GetCount -> V.takeVar countVar + ) -updateCount - :: Members '[SimpleStorageKeeper, Output BaseApp.Event, BaseApp.Logger] r +updateCountF + :: Members '[BaseApp.WriteStore, Output BaseApp.Event, BaseApp.Logger] r => Count -> Sem r () -updateCount count = do - putCount count +updateCountF count = do + V.putVar count countVar let event = CountSet count BaseApp.emit event BaseApp.logEvent event -eval - :: forall r. - Members BaseApp.TxEffs r - => forall a. (Sem (SimpleStorageKeeper ': r) a -> Sem r a) -eval = interpret (\case - PutCount count -> do - BaseApp.put storeKey CountKey count - GetCount -> BaseApp.get storeKey CountKey - ) + +-------------------------------------------------------------------------------- + +data SimpleStorageNamespace + +store :: BaseApp.Store SimpleStorageNamespace +store = BaseApp.makeStore $ BaseApp.KeyRoot "simple_storage" + +data CountKey = CountKey + +instance BaseApp.RawKey CountKey where + rawKey = iso (\_ -> cs countKey) (const CountKey) + where + countKey :: ByteString + countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) + +instance BaseApp.IsKey CountKey SimpleStorageNamespace where + type Value CountKey SimpleStorageNamespace = V.Var Count + +countVar :: V.Var Count +countVar = V.makeVar CountKey store + +instance BaseApp.QueryData CountKey diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs index 741d868a..c243c39f 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Query.hs @@ -1,23 +1,19 @@ module SimpleStorage.Modules.SimpleStorage.Query - ( CountStoreContents - , QueryApi + ( QueryApi , querier ) where -import Data.Proxy import Polysemy (Members) -import SimpleStorage.Modules.SimpleStorage.Keeper (storeKey) -import SimpleStorage.Modules.SimpleStorage.Types (Count, CountKey) +import Servant.API ((:>)) +import SimpleStorage.Modules.SimpleStorage.Keeper (countVar) +import SimpleStorage.Modules.SimpleStorage.Types (Count) import qualified Tendermint.SDK.BaseApp as BaseApp +import qualified Tendermint.SDK.BaseApp.Store.Var as V -type CountStoreContents = '[(CountKey, Count)] - -type QueryApi = BaseApp.QueryApi CountStoreContents +type QueryApi = "count" :> BaseApp.StoreLeaf (V.Var Count) querier :: Members BaseApp.QueryEffs r => BaseApp.RouteQ QueryApi r -querier = - BaseApp.storeQueryHandlers (Proxy :: Proxy CountStoreContents) - storeKey (Proxy :: Proxy r) +querier = BaseApp.storeQueryHandler countVar diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs index f361ad7e..7172824f 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Router.hs @@ -3,16 +3,14 @@ module SimpleStorage.Modules.SimpleStorage.Router , messageHandlers ) where -import Polysemy (Member, Members, - Sem) +import Polysemy (Member, Sem) import SimpleStorage.Modules.SimpleStorage.Keeper (SimpleStorageKeeper, updateCount) import SimpleStorage.Modules.SimpleStorage.Message import SimpleStorage.Modules.SimpleStorage.Types (Count (..)) -import Tendermint.SDK.BaseApp ((:~>), BaseEffs, - Return, RouteTx, +import Tendermint.SDK.BaseApp ((:~>), Return, + RouteTx, RoutingTx (..), - TxEffs, TypedMessage) import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) @@ -23,15 +21,11 @@ type MessageApi = messageHandlers :: Member SimpleStorageKeeper r - => Members TxEffs r - => Members BaseEffs r => RouteTx MessageApi r messageHandlers = updateCountH updateCountH :: Member SimpleStorageKeeper r - => Members TxEffs r - => Members BaseEffs r => RoutingTx UpdateCountTx -> Sem r () updateCountH (RoutingTx Tx{txMsg}) = diff --git a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs index e62bbf07..67a7b19a 100644 --- a/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs +++ b/hs-abci-docs/simple-storage/src/SimpleStorage/Modules/SimpleStorage/Types.hs @@ -1,11 +1,7 @@ module SimpleStorage.Modules.SimpleStorage.Types where -import Control.Lens (iso) -import Crypto.Hash (SHA256 (..), hashWith) import qualified Data.Aeson as A import Data.Bifunctor (first) -import Data.ByteArray (convert) -import Data.ByteString (ByteString) import Data.Int (Int32) import qualified Data.Serialize as Serialize import qualified Data.Serialize.Text () @@ -14,30 +10,14 @@ import GHC.Generics (Generic) import qualified Tendermint.SDK.BaseApp as BaseApp import Tendermint.SDK.Codec (HasCodec (..)) -data SimpleStorageNamespace +type SimpleStorageName = "simple_storage" newtype Count = Count Int32 deriving (Eq, Show, A.ToJSON, A.FromJSON, Serialize.Serialize) -data CountKey = CountKey - instance HasCodec Count where encode = Serialize.encode decode = first cs . Serialize.decode -instance BaseApp.RawKey CountKey where - rawKey = iso (\_ -> cs countKey) (const CountKey) - where - countKey :: ByteString - countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) - -instance BaseApp.IsKey CountKey SimpleStorageNamespace where - type Value CountKey SimpleStorageNamespace = Count - -instance BaseApp.FromQueryData CountKey - -instance BaseApp.Queryable Count where - type Name Count = "count" - -------------------------------------------------------------------------------- -- Events -------------------------------------------------------------------------------- @@ -57,4 +37,3 @@ instance BaseApp.ToEvent CountSet instance BaseApp.Select CountSet -type SimpleStorageName = "simple_storage" diff --git a/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs index a292941e..6b13c38c 100644 --- a/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs +++ b/hs-abci-docs/simple-storage/test/SimpleStorage/Test/E2ESpec.hs @@ -55,7 +55,7 @@ spec = do it "can make sure the synchronous tx transaction worked and the count is now 4" $ \c -> do resp <- assertQuery . RPC.runTendermintM rpcConfig $ - getCount defaultQueryArgs { queryArgsData = SS.CountKey } + getCount defaultQueryArgs { queryArgsData = () } let foundCount = queryResultData resp foundCount `shouldBe` SS.Count c @@ -64,7 +64,7 @@ spec = do -------------------------------------------------------------------------------- getCount - :: QueryArgs SS.CountKey + :: QueryArgs () -> RPC.TendermintM (QueryClientResponse SS.Count) getAccount diff --git a/hs-abci-sdk/package.yaml b/hs-abci-sdk/package.yaml index d7ea8f21..ae5cb5df 100644 --- a/hs-abci-sdk/package.yaml +++ b/hs-abci-sdk/package.yaml @@ -116,9 +116,13 @@ library: - Tendermint.SDK.BaseApp.Router.Types - Tendermint.SDK.BaseApp.Router.Router - Tendermint.SDK.BaseApp.Store + - Tendermint.SDK.BaseApp.Store.Array - Tendermint.SDK.BaseApp.Store.IAVLStore + - Tendermint.SDK.BaseApp.Store.List + - Tendermint.SDK.BaseApp.Store.Map - Tendermint.SDK.BaseApp.Store.MemoryStore - Tendermint.SDK.BaseApp.Store.RawStore + - Tendermint.SDK.BaseApp.Store.Var - Tendermint.SDK.BaseApp.Transaction - Tendermint.SDK.BaseApp.Transaction.AnteHandler - Tendermint.SDK.BaseApp.Transaction.Cache @@ -159,6 +163,10 @@ tests: - Tendermint.SDK.Test.GasSpec - Tendermint.SDK.Test.MetricsSpec - Tendermint.SDK.Test.SimpleStorage + - Tendermint.SDK.Test.ArraySpec + - Tendermint.SDK.Test.ListSpec + - Tendermint.SDK.Test.MapSpec + - Tendermint.SDK.Test.VarSpec - Tendermint.SDK.Test.QuerySpec ghc-options: diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs index c21b99d2..ea52939b 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/AnteHandler.hs @@ -47,9 +47,9 @@ nonceAnteHandler = Endo $ result <- txApplication tx postMAcnt <- A.getAccount msgAuthor case postMAcnt of - Just acnt@A.Account{accountNonce} -> do - A.putAccount msgAuthor $ - acnt { A.accountNonce = accountNonce + 1} + Just A.Account{accountNonce} -> do + A.updateAccount msgAuthor $ \a -> + a { A.accountNonce = accountNonce + 1} -- @NOTE: no-op when no nonce is availble to update Nothing -> pure () pure result diff --git a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs index c2818c0b..c9373180 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Application/Module.hs @@ -39,7 +39,7 @@ data Module (name :: Symbol) (check :: *) (deliver :: *) (query :: *) (es :: Eff { moduleTxChecker :: T.RouteTx check r , moduleTxDeliverer :: T.RouteTx deliver r , moduleQuerier :: Q.RouteQ query r - , moduleEval :: forall s. (Members T.TxEffs s, Members (DependencyEffs deps) s) => forall a. Sem (es :& s) a -> Sem s a + , moduleEval :: forall s. (Members T.TxEffs s, Members BaseEffs s, Members (DependencyEffs deps) s) => forall a. Sem (es :& s) a -> Sem s a } type family ModuleEffs (m :: Component) :: EffectRow where @@ -122,6 +122,7 @@ instance (DependencyEffs deps ~ '[]) => Eval '[Module name check deliver query e instance ( Members (DependencyEffs deps) (Effs (m' ': ms) s) , Members T.TxEffs (Effs (m' ': ms) s) + , Members BaseEffs (Effs (m' ': ms) s) , Eval (m' ': ms) s ) => Eval (Module name check deliver query es deps ': m' ': ms) s where type Effs (Module name check deliver query es deps ': m' ': ms) s = es :& (Effs (m': ms)) s diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs index b802d72f..8eb48605 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp.hs @@ -28,8 +28,11 @@ module Tendermint.SDK.BaseApp , ReadStore , WriteStore , RawKey(..) - , IsKey(..) , StoreKey(..) + , IsKey(..) + , Store + , KeyRoot(..) + , makeStore , put , get , delete @@ -37,6 +40,7 @@ module Tendermint.SDK.BaseApp -- * Query Routes , Leaf , QA + , StoreLeaf -- * Errors , AppError(..) @@ -84,12 +88,10 @@ module Tendermint.SDK.BaseApp -- * Query , QueryEffs - , Queryable(..) - , FromQueryData(..) + , QueryData(..) , RouteQ - , QueryApi , QueryResult(..) - , storeQueryHandlers + , storeQueryHandler , EmptyQueryServer(..) , RouterError(ResourceNotFound) ) where diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs index 9394e35f..f37a284a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Errors.hs @@ -72,7 +72,7 @@ txResultAppError = lens g s -- | These errors originate from the SDK itself. The "sdk" namespace is reserved -- | for this error type and should not be used in modules or applications. data SDKError = - InternalError + InternalError Text -- ^ Something went wrong and we have no idea what. | ParseError Text -- ^ Parsing errors for SDK specific types, e.g. 'RawTransaction' or 'Msg', etc. @@ -82,7 +82,7 @@ data SDKError = | MessageValidation [Text] | SignatureRecoveryError Text | NonceException Word64 Word64 - | RawStoreInvalidOperation Text + | StoreError Text | GrpcError Text | UnknownAccountError Address deriving (Show) @@ -96,10 +96,10 @@ throwSDKError throwSDKError = throw . makeAppError instance IsAppError SDKError where - makeAppError InternalError = AppError + makeAppError (InternalError msg) = AppError { appErrorCode = 1 , appErrorCodespace = "sdk" - , appErrorMessage = "Internal Error" + , appErrorMessage = "Internal Error: " <> msg } makeAppError (ParseError msg) = AppError @@ -139,10 +139,10 @@ instance IsAppError SDKError where " but got " <> (cs . show $ toInteger found) <> "." } - makeAppError (RawStoreInvalidOperation operation) = AppError + makeAppError (StoreError msg) = AppError { appErrorCode = 8 , appErrorCodespace = "sdk" - , appErrorMessage = "Unsupported RawStore operation: `" <> operation <> "`" + , appErrorMessage = "Store Error: " <> msg } makeAppError (GrpcError msg) = AppError diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs index a56d82b7..d6a64060 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query.hs @@ -2,7 +2,8 @@ module Tendermint.SDK.BaseApp.Query ( serveQueryApplication -- * Re-Exports , HasQueryRouter(..) - , StoreQueryHandlers(..) + , StoreLeaf + , storeQueryHandler , QueryEffs , module Tendermint.SDK.BaseApp.Query.Types ) where @@ -16,7 +17,7 @@ import Tendermint.SDK.BaseApp.Errors (makeAppError, queryAppError) import Tendermint.SDK.BaseApp.Query.Effect (QueryEffs) import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..)) -import Tendermint.SDK.BaseApp.Query.Store (StoreQueryHandlers (..)) +import Tendermint.SDK.BaseApp.Query.Store import Tendermint.SDK.BaseApp.Query.Types import Tendermint.SDK.BaseApp.Router.Delayed (emptyDelayed) import Tendermint.SDK.BaseApp.Router.Router (runRouter) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs index bf8c632e..2d01948a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Router.hs @@ -21,8 +21,8 @@ import Servant.API.Modifiers (FoldLenient, unfoldRequestArgument) import Tendermint.SDK.BaseApp.Query.Effect (QueryEffs, runQuery) import Tendermint.SDK.BaseApp.Query.Types (EmptyQueryServer (..), - FromQueryData (..), Leaf, - QA, QueryArgs (..), + Leaf, QA, QueryArgs (..), + QueryData (..), QueryRequest (..), QueryResult (..)) import qualified Tendermint.SDK.BaseApp.Router as R @@ -104,7 +104,7 @@ instance (FromHttpApiData a, HasQueryRouter sublayout r) => HasQueryRouter (Capt ) hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @sublayout) pr nat . f -instance (FromQueryData a, HasQueryRouter sublayout r) => HasQueryRouter (QA a :> sublayout) r where +instance (QueryData a, HasQueryRouter sublayout r) => HasQueryRouter (QA a :> sublayout) r where type RouteQ (QA a :> sublayout) r = QueryArgs a -> RouteQ sublayout r diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs index c6cb965d..0234ee9a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Store.hs @@ -2,86 +2,167 @@ module Tendermint.SDK.BaseApp.Query.Store ( StoreLeaf - , StoreQueryHandlers(..) + , storeQueryHandler + --, StoreQueryHandlers(..) ) where -import Control.Lens (to, (^.)) +--import Control.Lens (to, (^.)) import Data.ByteArray.Base64String (fromBytes) import Data.Proxy -import Data.String.Conversions (cs) -import GHC.TypeLits (KnownSymbol, symbolVal) +--import Data.String.Conversions (cs) +import Data.Word (Word64) +--import GHC.TypeLits (KnownSymbol, symbolVal) import Polysemy (Member, Members, Sem) import Polysemy.Error (throw) import Polysemy.Tagged (Tagged) -import Servant.API ((:<|>) (..), (:>)) +import Servant.API ((:>)) import Tendermint.SDK.BaseApp.Errors (makeAppError) import Tendermint.SDK.BaseApp.Query.Effect (QueryEffs) -import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..), - methodRouter) -import Tendermint.SDK.BaseApp.Query.Types (QA, QueryArgs (..), - QueryResult (..), - Queryable (..)) -import Tendermint.SDK.BaseApp.Router (RouterError (..), - pathRouter) -import Tendermint.SDK.BaseApp.Store (IsKey (..), RawKey (..), - ReadStore, Scope (..), - StoreKey, get) +import Tendermint.SDK.BaseApp.Query.Router (HasQueryRouter (..)) +import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), + QueryData, + QueryResult (..)) +import Tendermint.SDK.BaseApp.Router (RouterError (..)) +import Tendermint.SDK.BaseApp.Store (RawKey (..), ReadStore, + Scope (..), makeKeyBytes) +import qualified Tendermint.SDK.BaseApp.Store.Array as A +import qualified Tendermint.SDK.BaseApp.Store.List as L +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import qualified Tendermint.SDK.BaseApp.Store.Var as V import Tendermint.SDK.Codec (HasCodec) +{- + +"account" :> StoreLeaf (Map Address Account) :<|> + + "count" :> StoreLeaf (Var Count) :<|> + + "counts" :> StoreLeaf (Array Count) + +-} + + data StoreLeaf a -instance (Queryable a, Member (Tagged 'QueryAndMempool ReadStore) r, KnownSymbol (Name a)) => HasQueryRouter (StoreLeaf a) r where +instance (QueryData k, HasCodec v, Member (Tagged 'QueryAndMempool ReadStore) r) => HasQueryRouter (StoreLeaf (M.Map k v)) r where - type RouteQ (StoreLeaf a) r = Sem r (QueryResult a) - routeQ _ _ = pathRouter (cs (symbolVal proxyPath)) . methodRouter - where proxyPath = Proxy :: Proxy (Name a) - hoistQueryRouter _ _ = ($) + type RouteQ (StoreLeaf (M.Map k v)) r = RouteQ (QA k :> Leaf v) r + routeQ _ = routeQ (Proxy @(QA k :> Leaf v)) + hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @(QA k :> Leaf v)) pr nat f -class StoreQueryHandler a ns h where - storeQueryHandler :: Proxy a -> StoreKey ns -> h +instance (HasCodec a, Member (Tagged 'QueryAndMempool ReadStore) r) => HasQueryRouter (StoreLeaf (V.Var a)) r where + + type RouteQ (StoreLeaf (V.Var a)) r = RouteQ (QA () :> Leaf a) r + routeQ _ = routeQ (Proxy @(QA () :> Leaf a)) + hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @(QA () :> Leaf a)) pr nat f + +instance (HasCodec a, Member (Tagged 'QueryAndMempool ReadStore) r) => HasQueryRouter (StoreLeaf (A.Array a)) r where + + type RouteQ (StoreLeaf (A.Array a)) r = RouteQ (QA Word64 :> Leaf a) r + routeQ _ = routeQ (Proxy @(QA Word64 :> Leaf a)) + hoistQueryRouter _ pr nat f = hoistQueryRouter (Proxy @(QA Word64 :> Leaf a)) pr nat f + +class StoreQueryHandler ns h where + storeQueryHandler :: ns -> h instance - ( IsKey k ns - , a ~ Value k ns - , HasCodec a + ( RawKey k + , HasCodec v , Members QueryEffs r ) - => StoreQueryHandler a ns (QueryArgs k -> Sem r (QueryResult a)) where - storeQueryHandler _ storeKey QueryArgs{..} = do + => StoreQueryHandler (M.Map k v) (QueryArgs k -> Sem r (QueryResult v)) where + storeQueryHandler m QueryArgs{..} = do let key = queryArgsData - mRes <- get storeKey key + mRes <- M.lookup key m case mRes of Nothing -> throw . makeAppError $ ResourceNotFound - Just (res :: a) -> pure $ QueryResult + Just (res :: v) -> pure $ QueryResult -- TODO: actually handle proofs { queryResultData = res , queryResultIndex = 0 - , queryResultKey = key ^. rawKey . to fromBytes + , queryResultKey = fromBytes . makeKeyBytes . M.makeFullStoreKey m $ key , queryResultProof = Nothing , queryResultHeight = 0 } -class StoreQueryHandlers (kvs :: [*]) ns r where - type QueryApi kvs :: * - storeQueryHandlers :: Proxy kvs -> StoreKey ns -> Proxy r -> RouteQ (QueryApi kvs) r +instance + ( HasCodec a + , Members QueryEffs r + ) + => StoreQueryHandler (A.Array a) (QueryArgs Word64 -> Sem r (QueryResult a)) where + storeQueryHandler as QueryArgs{..} = do + let i = queryArgsData + mRes <- as A.!! i + case mRes of + Nothing -> throw . makeAppError $ ResourceNotFound + Just (res :: a) -> pure $ QueryResult + -- TODO: actually handle proofs + { queryResultData = res + , queryResultIndex = 0 + , queryResultKey = fromBytes . makeKeyBytes . A.makeFullStoreKey as $ i + , queryResultProof = Nothing + , queryResultHeight = 0 + } instance - ( IsKey k ns - , a ~ Value k ns - , HasCodec a - , Members QueryEffs r - ) => StoreQueryHandlers '[(k,a)] ns r where - type QueryApi '[(k,a)] = QA k :> StoreLeaf a - storeQueryHandlers _ storeKey _ = storeQueryHandler (Proxy :: Proxy a) storeKey + ( HasCodec a + , Members QueryEffs r + ) + => StoreQueryHandler (L.List a) (QueryArgs Word64 -> Sem r (QueryResult a)) where + storeQueryHandler as QueryArgs{..} = do + let i = queryArgsData + mRes <- as L.!! i + case mRes of + Nothing -> throw . makeAppError $ ResourceNotFound + Just (res :: a) -> pure $ QueryResult + -- TODO: actually handle proofs + { queryResultData = res + , queryResultIndex = 0 + , queryResultKey = fromBytes . makeKeyBytes . L.makeFullStoreKey as $ i + , queryResultProof = Nothing + , queryResultHeight = 0 + } instance - ( IsKey k ns - , a ~ Value k ns - , HasCodec a - , StoreQueryHandlers ((k', a') ': as) ns r - , Members QueryEffs r - ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns r where - type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> StoreLeaf a) :<|> QueryApi ((k', a') ': as) - storeQueryHandlers _ storeKey pr = - storeQueryHandler (Proxy :: Proxy a) storeKey :<|> - storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) storeKey pr + ( HasCodec a + , Members QueryEffs r + ) + => StoreQueryHandler (V.Var a) (QueryArgs () -> Sem r (QueryResult a)) where + storeQueryHandler var QueryArgs{..} = do + mRes <- V.takeVar var + case mRes of + Nothing -> throw . makeAppError $ ResourceNotFound + Just (res :: a) -> pure $ QueryResult + -- TODO: actually handle proofs + { queryResultData = res + , queryResultIndex = 0 + , queryResultKey = fromBytes . makeKeyBytes . V.makeFullStoreKey $ var + , queryResultProof = Nothing + , queryResultHeight = 0 + } + +--class StoreQueryHandlers ns r where +-- type QueryApi kvs :: * +-- storeQueryHandlers :: Proxy kvs -> Store ns -> Proxy r -> RouteQ (QueryApi kvs) r +-- +--instance +-- ( IsKey k ns +-- , a ~ Value k ns +-- , HasCodec a +-- , Members QueryEffs r +-- ) => StoreQueryHandlers ns r where +-- type QueryApi (s :> StoreLeaf (M.Map k v)) = s :> QA k :> StoreLeaf a +-- storeQueryHandlers _ store _ = storeQueryHandler (Proxy :: Proxy a) store + +--instance +-- ( IsKey k ns +-- , a ~ Value k ns +-- , HasCodec a +-- , StoreQueryHandlers ((k', a') ': as) ns r +-- , Members QueryEffs r +-- ) => StoreQueryHandlers ((k,a) ': (k', a') : as) ns r where +-- type (QueryApi ((k, a) ': (k', a') : as)) = (QA k :> StoreLeaf a) :<|> QueryApi ((k', a') ': as) +-- storeQueryHandlers _ store pr = +-- storeQueryHandler (Proxy :: Proxy a) store :<|> +-- storeQueryHandlers (Proxy :: Proxy ((k', a') ': as)) store pr +-- diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs index 9c80284a..9eb9ec9a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Query/Types.hs @@ -4,7 +4,7 @@ module Tendermint.SDK.BaseApp.Query.Types Leaf , QA , EmptyQueryServer(..) - , FromQueryData(..) + , QueryData(..) -- * Query Application , QueryApplication @@ -14,21 +14,19 @@ module Tendermint.SDK.BaseApp.Query.Types , defaultQueryArgs , QueryResult(..) - -- * Store Queries - , Queryable(..) ) where -import Control.Lens (from, lens, (^.)) -import Data.ByteArray.Base64String (Base64String, toBytes) +import Control.Lens (from, lens, to, (^.)) +import Data.ByteArray.Base64String (Base64String, + fromBytes, toBytes) import Data.Int (Int64) import Data.Text (Text, breakOn, uncons) -import GHC.TypeLits (Symbol) +import Data.Word (Word64) import Network.ABCI.Types.Messages.FieldTypes (Proof, WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Request import qualified Network.ABCI.Types.Messages.Response as Response import Tendermint.SDK.BaseApp.Router.Types (HasPath (..)) import Tendermint.SDK.BaseApp.Store (RawKey (..)) -import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address) data Leaf (a :: *) @@ -95,21 +93,22 @@ data QueryResult a = QueryResult -------------------------------------------------------------------------------- --- | class representing objects which can be queried via the hs-abci query message. --- | Here the 'Name' is the leaf of the query url, e.g. if you can access a token --- | balance of type `Balance` at "token/balance", then 'Name Balance ~ "balance"'. -class HasCodec a => Queryable a where - type Name a :: Symbol - -- | This class is used to parse the 'data' field of the query request message. -- | The default method assumes that the 'data' is simply the key for the -- | value being queried. -class FromQueryData a where +class QueryData a where fromQueryData :: Base64String -> Either String a + toQueryData :: a -> Base64String default fromQueryData :: RawKey a => Base64String -> Either String a fromQueryData bs = Right (toBytes bs ^. from rawKey) -instance FromQueryData Address + default toQueryData :: RawKey a => a -> Base64String + toQueryData k = k ^. rawKey . to fromBytes + +instance QueryData Address +instance QueryData Text +instance QueryData Word64 +instance QueryData () data EmptyQueryServer = EmptyQueryServer diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Array.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Array.hs new file mode 100644 index 00000000..830e82b1 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Array.hs @@ -0,0 +1,256 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +module Tendermint.SDK.BaseApp.Store.Array + ( Array + , makeArray + , makeFullStoreKey + , append + , modifyAtIndex + , deleteWhen + , (!!) + , elemIndex + , toList + ) where + +import Control.Lens (iso, (^.)) +import qualified Data.ByteArray.HexString as Hex +import qualified Data.ByteString as BS +import Data.Maybe (fromMaybe) +import Data.String.Conversions (cs) +import Data.Word (Word64) +import Polysemy +import Polysemy.Error (Error) +import Prelude hiding (foldl, length, + (!!)) +import qualified Prelude as P (length) +import Tendermint.SDK.BaseApp.Errors (AppError, + SDKError (InternalError), + throwSDKError) +import Tendermint.SDK.BaseApp.Store.RawStore as S +import Tendermint.SDK.Codec (HasCodec (..)) + + + +-- | A 'Array a' is an appendable list whose elements can be accessed +-- | by their index. You can also delete from the list, in which case accessing +-- | that index will result in a `Nothing`. +data Array (a :: *) = Array + { arrayStore :: S.Store (Array a) } + +-- | Represents an index into a list +newtype Idx = Idx {unIdx :: Word64} deriving (Eq, Show, Ord, Num) + +instance S.RawKey Idx where + rawKey = iso elementKey unElementKey + +instance S.IsKey Idx (Array a) where + type Value Idx (Array a) = a + +-- Internal, used for accessing list length. +data LengthKey = LengthKey + +instance S.RawKey LengthKey where + rawKey = iso (const lengthKey) unLengthKey + +instance S.IsKey LengthKey (Array a) where + type Value LengthKey (Array a) = Word64 + +-- | Smart constuctor to make sure we're making a 'Array' from +-- | the appropriate key type. +makeArray + :: S.IsKey k ns + => S.Value k ns ~ Array a + => k + -> S.Store ns + -> S.Value k ns +makeArray k store = + let skr :: S.KeyRoot (Array a) + skr = S.KeyRoot $ k ^. S.rawKey + in Array $ S.nestStore store (S.makeStore skr) + +makeFullStoreKey + :: Array a + -> Word64 + -> S.StoreKey +makeFullStoreKey Array{..} i = + S.makeStoreKey arrayStore (Idx i) + +-- | Add an item to the end of the list. +append + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => a + -> Array a + -> Sem r () +append a as@Array{..} = do + n <- length as + writeAt (Idx n) a as + S.put arrayStore LengthKey (n + 1) + +-- | Access an item directly by its index. +(!!) + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => Array a + -> Word64 + -> Sem r (Maybe a) +as@Array{..} !! i = do + let n = Idx i + len <- length as + if Idx (len - 1) < n + then pure Nothing + else S.get arrayStore n + +infixl 9 !! + +-- | Modify a list at a particular index, return the +-- | updated value if the element was found. +modifyAtIndex + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => Word64 + -> (a -> a) + -> Array a + -> Sem r (Maybe a) +modifyAtIndex i f as = do + mRes <- as !! i + case mRes of + Nothing -> pure Nothing + Just res -> do + let a' = f res + writeAt (Idx i) a' as + pure (Just a') + +-- | Delete when the predicate evaluates to true. +deleteWhen + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => (a -> Bool) + -> Array a + -> Sem r () +deleteWhen p as@Array{..} = do + len <- length as + delete' 0 (len - 1) + where + delete' n end = + if n > end + then pure () + else do + mRes <- as !! n + case mRes of + Nothing -> delete' (n + 1) end + Just res -> + if p res + then do + S.delete arrayStore (Idx n) + delete' (n + 1) end + else delete' (n + 1) end + +-- | Get the first index where an element appears in the list. +elemIndex + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => Eq a + => a + -> Array a + -> Sem r (Maybe Word64) +elemIndex a as = do + len <- length as + elemIndex' 0 len + where + elemIndex' n len + | n == len = pure Nothing + | otherwise = do + mRes <- as !! n + let keepLooking = elemIndex' (n + 1) len + case mRes of + Nothing -> keepLooking + Just a' -> if a == a' then pure $ Just n else keepLooking + +foldl + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => (b -> a -> b) + -> b + -> Array a + -> Sem r b +foldl f b as = do + len <- length as + foldl' 0 len b + where + foldl' currentIndex end accum + | currentIndex == end = pure accum + | currentIndex < end = do + ma <- as !! currentIndex + case ma of + Nothing -> foldl' (currentIndex + 1) end accum + Just a -> foldl' (currentIndex + 1) end $! f accum a + | otherwise = error "Impossible case in Array foldl!" + +-- | View the 'Array' as a 'Array'. +toList + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => Array a + -> Sem r [a] +toList = foldl (flip (:)) [] + +-------------------------------------------------------------------------------- +-- Internal functions +-------------------------------------------------------------------------------- + +-- NOTE: Many things in this module are completely ad hoc, but tries to follow +-- the patterns set in https://github.com/cosmos/cosmos-sdk/blob/master/store/list/list.go +-- for future compatability, and because this ad-hoc-ness doesn't leak out. + +lengthKey :: BS.ByteString +lengthKey = Hex.toBytes "0x00" + +unLengthKey :: BS.ByteString -> LengthKey +unLengthKey bs + | bs == Hex.toBytes "0x00" = LengthKey + | otherwise = error $ "Couldn't parse LengthKey bytes: " <> cs bs + +elementKey + :: Idx + -> BS.ByteString +elementKey (Idx k) = + let padToNChars n a = + let nZeros = n - P.length a + in replicate nZeros '0' <> a + in Hex.toBytes "0x01" <> cs (padToNChars 20 $ show k) + +unElementKey + :: BS.ByteString + -> Idx +unElementKey bs = + let str = cs $ fromMaybe (error "Idx missing 0x01 prefix") $ + BS.stripPrefix (Hex.toBytes "0x01") bs + in Idx . read . dropWhile (== '0') $ str + +length + :: Members [Error AppError, S.ReadStore] r + => Array a + -> Sem r Word64 +length Array{..} = do + mLen <- S.get arrayStore LengthKey + pure $ fromMaybe 0 mLen + +writeAt + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => Idx + -> a + -> Array a + -> Sem r () +writeAt idx@(Idx i) a as@Array{..} = do + len <- length as + writeAt' len + where + writeAt' len + | i == len = do + S.put arrayStore idx a + S.put arrayStore LengthKey i + | i < len = + S.put arrayStore idx a + | otherwise = throwSDKError $ InternalError "Cannot write past list length index." diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs index c14802aa..3fb90455 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/IAVLStore.hs @@ -41,7 +41,7 @@ import Tendermint.SDK.BaseApp.Store.RawStore (CommitBlock (..), Transaction (..), Version (..), WriteStore (..), - makeRawKey) + makeKeyBytes) import Tendermint.SDK.Types.Effects ((:&)) data IAVLVersions = IAVLVersions @@ -60,11 +60,11 @@ evalWrite gc m = interpret (\case StorePut k v -> do - let setReq = defMessage & Api.key .~ makeRawKey k + let setReq = defMessage & Api.key .~ makeKeyBytes k & Api.value .~ v void . liftIO . runGrpc $ IAVL.set gc setReq StoreDelete k -> - let remReq = defMessage & Api.key .~ makeRawKey k + let remReq = defMessage & Api.key .~ makeKeyBytes k in void . liftIO . runGrpc $ IAVL.remove gc remReq ) m @@ -80,13 +80,13 @@ evalRead gc iavlVersion m = do version <- liftIO $ readIORef iavlVersion case version of Latest -> do - let getReq = defMessage & Api.key .~ makeRawKey k + let getReq = defMessage & Api.key .~ makeKeyBytes k res <- liftIO . runGrpc $ IAVL.get gc getReq case res ^. Api.value of "" -> pure Nothing val -> pure $ Just val Version v -> do - let getVerReq = defMessage & Api.key .~ makeRawKey k + let getVerReq = defMessage & Api.key .~ makeKeyBytes k & Api.version .~ fromInteger (toInteger v) res <- liftIO . runGrpc $ IAVL.getVersioned gc getVerReq case res ^. Api.value of diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/List.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/List.hs new file mode 100644 index 00000000..7b3e9770 --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/List.hs @@ -0,0 +1,363 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +module Tendermint.SDK.BaseApp.Store.List + ( List + , makeList + , makeFullStoreKey + , append + , delete + , deleteWhen + , foldl + , toList + , length + , elemIndex + , (!!) + ) where + +import Control.Lens (from, iso, to, view, + (^.)) +import Control.Monad (when) +import qualified Data.ByteArray.HexString as Hex +import Data.String.Conversions (cs) +import Data.Word (Word64) +import Polysemy (Members, Sem) +import Polysemy.Error (Error) +import Prelude hiding (foldl, length, + (!!)) +import Tendermint.SDK.BaseApp.Errors (AppError, + SDKError (InternalError), + throwSDKError) +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import qualified Tendermint.SDK.BaseApp.Store.RawStore as S +import Tendermint.SDK.Codec (HasCodec (..)) + +data List (a :: *) = List + { listStore :: S.Store (List a) + } + +newtype Idx = Idx {unIdx :: Word64} deriving (Eq, Show, Ord, Num) + +instance S.RawKey Idx where + rawKey = iso (\(Idx ma) -> ma ^. S.rawKey) + (\bs -> bs ^. from S.rawKey . to Idx) + +instance HasCodec Idx where + encode = view S.rawKey + decode = Right . view (from S.rawKey) + +data IdxKey = IdxKey + +instance S.RawKey IdxKey where + rawKey = + let k = Hex.toBytes "0x00" + in iso (const k) + (\bs -> if bs == k + then IdxKey + else error "Error parsing IdxKey" + ) + +instance S.IsKey IdxKey (List a) where + type Value IdxKey (List a) = M.Map Idx Idx + +data ValueKey = ValueKey + +instance S.RawKey ValueKey where + rawKey = + let k = Hex.toBytes "0x01" + in iso (const k) + (\bs -> if bs == k + then ValueKey + else error "Error parsing ValueKey" + ) + +instance S.IsKey ValueKey (List a) where + type Value ValueKey (List a) = M.Map Idx a + + +instance S.IsKey Idx (List a) where + type Value Idx (List a) = a + +makeList + :: S.IsKey key ns + => S.Value key ns ~ List a + => key + -> S.Store ns + -> S.Value key ns +makeList key store = + List $ S.nestStore store $ + S.makeStore . S.KeyRoot $ key ^. S.rawKey + +makeFullStoreKey + :: List a + -> Word64 + -> S.StoreKey +makeFullStoreKey List{..} i = + S.makeStoreKey listStore (Idx i) + +getIdxMap + :: List a + -> M.Map Idx Idx +getIdxMap List{..} = + M.makeMap IdxKey listStore + +getValueMap + :: List a + -> M.Map Idx a +getValueMap List{..} = + M.makeMap ValueKey listStore + +data HeadKey = HeadKey + +instance S.RawKey HeadKey where + rawKey = + let k = Hex.toBytes "0x02" + in iso (const k) + (\bs -> if bs == k + then HeadKey + else error "Error parsing HeadKey" + ) + +instance S.IsKey HeadKey (List a) where + type Value HeadKey (List a) = Idx + +append + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => a + -> List a + -> Sem r () +append a l@List{..} = do + mhd <- S.get listStore HeadKey + let valueMap = getValueMap l + case mhd of + Nothing -> do + S.put listStore HeadKey 0 + M.insert 0 a valueMap + Just hd -> do + let hd' = hd + 1 + idxMap = getIdxMap l + M.insert hd' hd idxMap + M.insert hd' a valueMap + S.put listStore HeadKey hd' + +-- | Delete the first occurence in the list. +delete + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => Eq a + => a + -> List a + -> Sem r () +delete a l@List{..} = do + mhd <- S.get listStore HeadKey + case mhd of + -- the list looks like [] + Nothing -> pure () + -- the list looks like (? : as) + Just hd -> do + let valueMap = getValueMap l + idxMap = getIdxMap l + a' <- assertLookup hd valueMap + mNext <- M.lookup hd idxMap + if a'== a + -- the list looks like (a : as) + then deleteHead l + -- the list looks like (b : as) + else delete' hd mNext + where + delete' prevIdx mcurrIdx = + case mcurrIdx of + Nothing -> pure () + Just currIdx -> do + let valueMap = getValueMap l + idxMap = getIdxMap l + a' <- assertLookup currIdx valueMap + mNext <- M.lookup currIdx idxMap + if a == a' + then snipNode prevIdx currIdx l + else delete' currIdx mNext + +-- | Delete an element whenever the predicate evaluates to 'True' +deleteWhen + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => HasCodec a + => (a -> Bool) + -> List a + -> Sem r () +deleteWhen p l@List{..} = do + mhd <- S.get listStore HeadKey + case mhd of + Nothing -> pure () + Just hd -> do + let valueMap = getValueMap l + a <- assertLookup hd valueMap + if p a + then do + deleteHead l + deleteWhen p l + else do + let idxMap = getIdxMap l + mNext <- M.lookup hd idxMap + deleteWhen' hd mNext + where + deleteWhen' prevIdx mcurrIdx = + case mcurrIdx of + Nothing -> pure () + Just currIdx -> do + let valueMap = getValueMap l + idxMap = getIdxMap l + a <- assertLookup currIdx valueMap + mNext <- M.lookup currIdx idxMap + when (p a) $ + snipNode prevIdx currIdx l + deleteWhen' currIdx mNext + +foldl + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => (b -> a -> b) + -> b + -> List a + -> Sem r b +foldl f b l@List{..} = do + mhd <- S.get listStore HeadKey + foldl' mhd b + where + foldl' mcurrentHead acc = + case mcurrentHead of + Nothing -> pure acc + Just hd -> do + let valMap = getValueMap l + idxMap = getIdxMap l + a <- assertLookup hd valMap + mNext <- M.lookup hd idxMap + foldl' mNext $! f acc a + +-- | View the 'List' as a 'List'. +toList + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => List a + -> Sem r [a] +toList = fmap reverse . foldl (flip (:)) [] + +length + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => List a + -> Sem r Word64 +length = foldl (\n _ -> n + 1) 0 + +elemIndex + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => Eq a + => a + -> List a + -> Sem r (Maybe Word64) +elemIndex a l@List{..} = do + mhd <- S.get listStore HeadKey + elemIndex' 0 mhd + where + elemIndex' i mcurrentHead = + case mcurrentHead of + Nothing -> pure Nothing + Just hd -> do + let valMap = getValueMap l + a' <- assertLookup hd valMap + if a == a' + then pure $ Just $ unIdx i + else do + let idxMap = getIdxMap l + mNext <- M.lookup hd idxMap + elemIndex' (i + 1) mNext + +(!!) + :: Members [Error AppError, S.ReadStore] r + => HasCodec a + => List a + -> Word64 + -> Sem r (Maybe a) +l@List{..} !! idx = do + mhd <- S.get listStore HeadKey + getAtIndex 0 mhd + where + getAtIndex currPos mhd = + case mhd of + Nothing -> pure Nothing + Just hd -> + if idx == currPos + then + let valMap = getValueMap l + in Just <$> assertLookup hd valMap + else do + let idxMap = getIdxMap l + mNext <- M.lookup hd idxMap + getAtIndex (currPos + 1) mNext + +infixl 9 !! + +-------------------------------------------------------------------------------- + +snipNode + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => Idx + -- ^ previous index + -> Idx + -- ^ current index (node to delete) + -> List a + -> Sem r () +snipNode prevIdx currIdx l = do + let idxMap = getIdxMap l + mNext <- M.lookup currIdx idxMap + case mNext of + -- (n) - (a) - [] ~> [] + Nothing -> M.delete prevIdx idxMap + -- (n) - (a) - rest ~> (n) ~> rest + Just next -> do + M.insert prevIdx next idxMap + deleteDetachedNode currIdx l + +deleteHead + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => List a + -> Sem r () +deleteHead l@List{..} = do + mhd <- S.get listStore HeadKey + case mhd of + Nothing -> pure () + Just hd -> do + let idxMap = getIdxMap l + mNext <- M.lookup hd idxMap + case mNext of + Nothing -> do + S.delete listStore HeadKey + Just next -> do + S.put listStore HeadKey next + deleteDetachedNode hd l + +deleteDetachedNode + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => Idx + -> List a + -> Sem r () +deleteDetachedNode idx l = + let valueMap = getValueMap l + idxMap = getIdxMap l + in do + M.delete idx valueMap + M.delete idx idxMap + +assertLookup + :: Members [S.ReadStore, Error AppError] r + => S.RawKey k + => HasCodec v + => k + -> M.Map k v + -> Sem r v +assertLookup k m = do + mRes <- M.lookup k m + case mRes of + Nothing -> throwSDKError $ + InternalError $ "Database integrity failed, lookup miss: " <> cs (k ^. S.rawKey) + Just res -> pure res diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Map.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Map.hs new file mode 100644 index 00000000..05cb26ae --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Map.hs @@ -0,0 +1,89 @@ +{-# LANGUAGE NoImplicitPrelude #-} + +module Tendermint.SDK.BaseApp.Store.Map + ( Map + , makeMap + , makeFullStoreKey + , insert + , lookup + , delete + , update + ) where + +import Control.Lens ((^.)) +import Polysemy (Member, Members, Sem) +import Polysemy.Error (Error) +import Prelude hiding (lookup) +import Tendermint.SDK.BaseApp.Errors (AppError) +import qualified Tendermint.SDK.BaseApp.Store.RawStore as S +import Tendermint.SDK.Codec (HasCodec (..)) + +data Map (k :: *) (v :: *) = Map + { mapStore :: S.Store (Map k v) + } + +instance S.RawKey k => S.IsKey k (Map k v) where + type Value k (Map k v) = v + +makeMap + :: S.IsKey key ns + => S.Value key ns ~ Map k v + => key + -> S.Store ns + -> S.Value key ns +makeMap key store = + let skr :: S.KeyRoot (Map k v) + skr = S.KeyRoot $ key ^. S.rawKey + in Map $ S.nestStore store (S.makeStore skr) + +makeFullStoreKey + :: S.RawKey k + => Map k v + -> k + -> S.StoreKey +makeFullStoreKey Map{..} = + S.makeStoreKey mapStore + +insert + :: Member S.WriteStore r + => S.RawKey k + => HasCodec v + => k + -> v + -> Map k v + -> Sem r () +insert k v Map{..} = + S.put mapStore k v + +lookup + :: Members [Error AppError, S.ReadStore] r + => S.RawKey k + => HasCodec v + => k + -> Map k v + -> Sem r (Maybe v) +lookup k Map{..} = + S.get mapStore k + +delete + :: Member S.WriteStore r + => S.RawKey k + => k + -> Map k v + -> Sem r () +delete k Map{..} = + S.delete mapStore k + +update + :: Members [Error AppError, S.ReadStore, S.WriteStore] r + => S.RawKey k + => HasCodec v + => (v -> Maybe v) + -> k + -> Map k v + -> Sem r () +update f k store = do + mv <- lookup k store + case mv of + Nothing -> pure () + Just v -> maybe (delete k store) (\a -> insert k a store) (f v) diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs index fd2da7b7..c41bb446 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/MemoryStore.hs @@ -7,6 +7,8 @@ module Tendermint.SDK.BaseApp.Store.MemoryStore , initDB -- * Eval , evalStoreEffs + , evalRead + , evalWrite ) where @@ -32,7 +34,7 @@ import Tendermint.SDK.BaseApp.Store.RawStore (CommitBlock (..), Transaction (..), Version (..), WriteStore (..), - makeRawKey) + makeKeyBytes) import Tendermint.SDK.Types.Effects ((:&)) @@ -61,10 +63,10 @@ evalWrite evalWrite DB{dbLatest} m = interpret (\case - StorePut k v -> do - liftIO . modifyIORef dbLatest $ AT.insert (makeRawKey k) v + StorePut k v -> + liftIO . modifyIORef dbLatest $ AT.insert (makeKeyBytes k) v StoreDelete k -> - liftIO . modifyIORef dbLatest $ AT.delete (makeRawKey k) + liftIO . modifyIORef dbLatest $ AT.delete (makeKeyBytes k) ) m evalRead @@ -80,10 +82,10 @@ evalRead DB{dbCommitted,dbLatest} iavlVersion m = do case version of Latest -> do tree <- liftIO $ readIORef dbLatest - pure $ AT.lookup (makeRawKey k) tree + pure $ AT.lookup (makeKeyBytes k) tree Version v -> do tree <- liftIO $ readIORef dbCommitted - pure (AT.lookup v tree >>= AT.lookup (makeRawKey k)) + pure (AT.lookup v tree >>= AT.lookup (makeKeyBytes k)) Genesis -> pure Nothing StoreProve _ -> pure Nothing ) m @@ -160,8 +162,8 @@ getRootHash db@DB{dbCommitted} = do case mcv of Nothing -> pure "" Just v -> do - cs <- readIORef dbCommitted - case AT.lookup v cs of + c <- readIORef dbCommitted + case AT.lookup v c of Nothing -> pure "" Just tree -> let AuthTreeHash hash = AT.merkleHash tree diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs index 551d4ffd..83b302d6 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/RawStore.hs @@ -4,7 +4,9 @@ module Tendermint.SDK.BaseApp.Store.RawStore ( -- * Effects StoreEffs + , Scope(..) , ReadStore(..) + , storeGet , get , prove , WriteStore(..) @@ -21,14 +23,18 @@ module Tendermint.SDK.BaseApp.Store.RawStore , commit -- * Types - , Scope(..) - , Version(..) , RawKey(..) , IsKey(..) - , RawStoreKey(..) - , makeRawKey , StoreKey(..) + , KeyRoot(..) + , makeKeyBytes , CommitResponse(..) + , Store + , nestStore + , makeStore + , makeStoreKey + + , Version(..) ) where import Control.Lens (Iso', iso, (^.)) @@ -36,6 +42,8 @@ import Data.ByteArray.Base64String (Base64String) import qualified Data.ByteString as BS import Data.Proxy import Data.String.Conversions (cs) +import Data.Text +import Data.Word (Word64) import Numeric.Natural (Natural) import Polysemy (Member, Members, Sem, makeSem) import Polysemy.Error (Error, catch, throw) @@ -47,32 +55,25 @@ import Tendermint.SDK.Codec (HasCodec (..)) import Tendermint.SDK.Types.Address (Address, addressFromBytes, addressToBytes) -data RawStoreKey = RawStoreKey - { rsStoreKey :: BS.ByteString - , rsKey :: BS.ByteString - } deriving (Eq, Show, Ord) - -makeRawKey :: RawStoreKey -> BS.ByteString -makeRawKey RawStoreKey{..} = rsStoreKey <> rsKey - -data ReadStore m a where - StoreGet :: RawStoreKey -> ReadStore m (Maybe BS.ByteString) - StoreProve :: RawStoreKey -> ReadStore m (Maybe BS.ByteString) - -makeSem ''ReadStore - -data WriteStore m a where - StorePut :: RawStoreKey -> BS.ByteString -> WriteStore m () - StoreDelete :: RawStoreKey -> WriteStore m () - -makeSem ''WriteStore +-------------------------------------------------------------------------------- +-- | Keys +-------------------------------------------------------------------------------- class RawKey k where rawKey :: Iso' k BS.ByteString +instance RawKey Text where + rawKey = iso cs cs + instance RawKey Address where rawKey = iso addressToBytes addressFromBytes +instance RawKey Word64 where + rawKey = iso encode (either (error "Error decoding Word64 RawKey") id . decode) + +instance RawKey () where + rawKey = iso (const "") (const ()) + class RawKey k => IsKey k ns where type Value k ns :: * prefix :: Proxy k -> Proxy ns -> BS.ByteString @@ -80,27 +81,77 @@ class RawKey k => IsKey k ns where default prefix :: Proxy k -> Proxy ns -> BS.ByteString prefix _ _ = "" -newtype StoreKey ns = StoreKey BS.ByteString +data StoreKey = StoreKey + { skPathFromRoot :: [BS.ByteString] + , skKey :: BS.ByteString + } deriving (Eq, Show, Ord) + +makeKeyBytes :: StoreKey -> BS.ByteString +makeKeyBytes StoreKey{..} = mconcat skPathFromRoot <> skKey -data CommitBlock m a where - CommitBlock :: CommitBlock m Base64String +-------------------------------------------------------------------------------- +-- | Store +-------------------------------------------------------------------------------- -makeSem ''CommitBlock +newtype KeyRoot ns = + KeyRoot BS.ByteString deriving (Eq, Show) + +data Store ns = Store + { storePathFromRoot :: [BS.ByteString] + } + +makeStore :: KeyRoot ns -> Store ns +makeStore (KeyRoot ns) = Store + { storePathFromRoot = [ns] + } + +nestStore :: Store parentns -> Store childns -> Store childns +nestStore (Store parentPath) (Store childPath) = + Store + { storePathFromRoot = parentPath ++ childPath + } + +makeStoreKey + :: forall k ns. + IsKey k ns + => Store ns + -> k + -> StoreKey +makeStoreKey (Store path) k = + StoreKey + { skKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey + , skPathFromRoot = path + } + + +-------------------------------------------------------------------------------- +-- | Read and Write Effects +-------------------------------------------------------------------------------- + + +data ReadStore m a where + StoreGet :: StoreKey -> ReadStore m (Maybe BS.ByteString) + StoreProve :: StoreKey -> ReadStore m (Maybe BS.ByteString) + +makeSem ''ReadStore + +data WriteStore m a where + StorePut :: StoreKey -> BS.ByteString -> WriteStore m () + StoreDelete :: StoreKey -> WriteStore m () + +makeSem ''WriteStore put :: forall k r ns. IsKey k ns => HasCodec (Value k ns) => Member WriteStore r - => StoreKey ns + => Store ns -> k -> Value k ns -> Sem r () -put (StoreKey sk) k a = - let key = RawStoreKey - { rsStoreKey = sk - , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey - } +put store k a = + let key = makeStoreKey store k val = encode a in storePut key val @@ -109,14 +160,11 @@ get IsKey k ns => HasCodec (Value k ns) => Members [ReadStore, Error AppError] r - => StoreKey ns + => Store ns -> k -> Sem r (Maybe (Value k ns)) -get (StoreKey sk) k = do - let key = RawStoreKey - { rsStoreKey = sk - , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey - } +get store k = do + let key = makeStoreKey store k mRes <- storeGet key case mRes of Nothing -> pure Nothing @@ -128,30 +176,32 @@ delete :: forall k ns r. IsKey k ns => Member WriteStore r - => StoreKey ns + => Store ns -> k -> Sem r () -delete (StoreKey sk) k = - let key = RawStoreKey - { rsStoreKey = sk - , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey - } +delete store k = + let key = makeStoreKey store k in storeDelete key prove :: forall k ns r. IsKey k ns => Member ReadStore r - => StoreKey ns + => Store ns -> k -> Sem r (Maybe BS.ByteString) -prove (StoreKey sk) k = - let key = RawStoreKey - { rsStoreKey = sk - , rsKey = prefix (Proxy @k) (Proxy @ns) <> k ^. rawKey - } +prove store k = + let key = makeStoreKey store k in storeProve key +-------------------------------------------------------------------------------- +-- | Consensus Effects +-------------------------------------------------------------------------------- + +data CommitBlock m a where + CommitBlock :: CommitBlock m Base64String + +makeSem ''CommitBlock data CommitResponse = CommitResponse { rootHash :: Base64String @@ -189,14 +239,18 @@ withSandbox m = let tryTx = m `catch` (\e -> rollback *> throw e) in finally (tryTx <* rollback) rollback -data Scope = Consensus | QueryAndMempool - data Version = Genesis | Version Natural | Latest deriving (Eq, Show) +-------------------------------------------------------------------------------- +-- | Store Effects +-------------------------------------------------------------------------------- + +data Scope = Consensus | QueryAndMempool + type StoreEffs = [ Tagged 'Consensus ReadStore , Tagged 'QueryAndMempool ReadStore diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Var.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Var.hs new file mode 100644 index 00000000..30978d8e --- /dev/null +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Store/Var.hs @@ -0,0 +1,72 @@ +module Tendermint.SDK.BaseApp.Store.Var + ( Var + , makeVar + , makeFullStoreKey + , takeVar + , unsafeTakeVar + , putVar + , deleteVar + ) where + +import Control.Lens ((^.)) +import Polysemy (Member, Members, Sem) +import Polysemy.Error (Error) +import Tendermint.SDK.BaseApp.Errors (AppError, + SDKError (StoreError), + throwSDKError) +import qualified Tendermint.SDK.BaseApp.Store.RawStore as S +import Tendermint.SDK.Codec (HasCodec (..)) + +data Var (a :: *) = Var + { varStore :: S.Store (Var a) } + +instance S.IsKey () (Var a) where + type Value () (Var a) = a + +makeVar + :: S.IsKey k ns + => S.Value k ns ~ Var a + => k + -> S.Store ns + -> S.Value k ns +makeVar key store = + Var $ S.nestStore store $ + S.makeStore . S.KeyRoot $ key ^. S.rawKey + +makeFullStoreKey + :: Var a + -> S.StoreKey +makeFullStoreKey Var{..} = + S.makeStoreKey varStore () + +takeVar + :: Members [S.ReadStore, Error AppError] r + => HasCodec a + => Var a + -> Sem r (Maybe a) +takeVar Var{..} = S.get varStore () + +unsafeTakeVar + :: Members [S.ReadStore, Error AppError] r + => HasCodec a + => Var a + -> Sem r a +unsafeTakeVar Var{..} = do + mRes <- S.get varStore () + case mRes of + Just a -> pure a + Nothing -> throwSDKError $ StoreError "Var key not found." + +putVar + :: Member S.WriteStore r + => HasCodec a + => a + -> Var a + -> Sem r () +putVar a Var{..} = S.put varStore () a + +deleteVar + :: Member S.WriteStore r + => Var a + -> Sem r () +deleteVar Var{..} = S.delete varStore () diff --git a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs index f6826609..3616d9dc 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/BaseApp/Transaction/Cache.hs @@ -15,20 +15,20 @@ import Data.Set (Set) import qualified Data.Set as Set import Polysemy (Member, Sem) import Polysemy.Tagged (Tagged, tag) -import Tendermint.SDK.BaseApp.Store.RawStore (RawStoreKey, Scope (..), +import Tendermint.SDK.BaseApp.Store.RawStore (Scope (..), StoreKey, WriteStore, storeDelete, storePut) data Cache = Cache - { keysToDelete :: Set RawStoreKey - , stateCache :: Map RawStoreKey ByteString + { keysToDelete :: Set StoreKey + , stateCache :: Map StoreKey ByteString } deriving (Eq, Show) emptyCache :: Cache emptyCache = Cache Set.empty Map.empty put - :: RawStoreKey + :: StoreKey -> ByteString -> Cache -> Cache @@ -40,7 +40,7 @@ put k v Cache{..} = data Deleted = Deleted get - :: RawStoreKey + :: StoreKey -> Cache -> Either Deleted (Maybe ByteString) get k Cache{..} = @@ -49,7 +49,7 @@ get k Cache{..} = else Right $ Map.lookup k stateCache delete - :: RawStoreKey + :: StoreKey -> Cache -> Cache delete k Cache{..} = diff --git a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs index 4dd91d58..734d6330 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Codec.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Codec.hs @@ -3,11 +3,17 @@ module Tendermint.SDK.Codec , defaultSDKAesonOptions ) where -import Data.Aeson (Options) -import Data.Aeson.Casing (aesonDrop, snakeCase) -import qualified Data.ByteString as BS -import Data.String.Conversions (cs) -import Data.Text (Text) +import Data.Aeson (Options) +import Data.Aeson.Casing (aesonDrop, snakeCase) +import Data.Bifunctor (first) +import qualified Data.ByteString as BS +import Data.Int (Int32, Int64) +import qualified Data.ProtoLens.Encoding.Bytes as PB +import Data.String.Conversions (cs) +import Data.Text (Text) +import Data.Word (Word32, Word64) + + -- | This class is used as a codec for all items stored in -- | the database as well as incoming transaction messages. @@ -19,6 +25,23 @@ instance HasCodec () where encode = const "" decode = const $ pure () + +instance HasCodec Word32 where + encode = PB.runBuilder . PB.putFixed32 + decode = first cs . PB.runParser PB.getFixed32 + +instance HasCodec Int32 where + encode = PB.runBuilder . PB.putFixed32 . PB.signedInt32ToWord + decode = first cs . PB.runParser (PB.wordToSignedInt32 <$> PB.getFixed32) + +instance HasCodec Word64 where + encode = PB.runBuilder . PB.putFixed64 + decode = first cs . PB.runParser PB.getFixed64 + +instance HasCodec Int64 where + encode = PB.runBuilder . PB.putFixed64 . PB.signedInt64ToWord + decode = first cs . PB.runParser (PB.wordToSignedInt64 <$> PB.getFixed64) + instance HasCodec String where encode = cs decode = Right . cs @@ -31,5 +54,6 @@ instance HasCodec BS.ByteString where encode = id decode = Right + defaultSDKAesonOptions :: String -> Options defaultSDKAesonOptions prefix = aesonDrop (length prefix) snakeCase diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs index 339efdaf..8a80f33e 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth.hs @@ -10,7 +10,7 @@ module Tendermint.SDK.Modules.Auth import Polysemy (Members) import Tendermint.SDK.Application.Module (Module (..), ModuleEffs) import Tendermint.SDK.BaseApp (EmptyTxServer (..)) -import Tendermint.SDK.Modules.Auth.Keeper hiding (storeKey) +import Tendermint.SDK.Modules.Auth.Keeper hiding (accountsMap) import Tendermint.SDK.Modules.Auth.Query import Tendermint.SDK.Modules.Auth.Types diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs index ce053568..25a9ea35 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Keeper.hs @@ -1,51 +1,77 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Modules.Auth.Keeper where +module Tendermint.SDK.Modules.Auth.Keeper + ( AuthEffs + , Accounts(..) + , createAccount + , updateAccount + , getAccount + , eval + -- stores + , accountsMap + ) where +import Control.Lens (iso) import Polysemy import Polysemy.Error (Error, mapError, throw) -import Tendermint.SDK.BaseApp (AppError, ReadStore, - StoreKey (..), WriteStore, - get, makeAppError, put) +import Tendermint.SDK.BaseApp (AppError, IsKey (..), + KeyRoot (..), RawKey (..), + ReadStore, Store, + WriteStore, makeAppError, + makeStore) +import qualified Tendermint.SDK.BaseApp.Store.Map as M import Tendermint.SDK.Modules.Auth.Types import Tendermint.SDK.Types.Address (Address) data Accounts m a where - PutAccount :: Address -> Account -> Accounts m () + CreateAccount :: Address -> Accounts m Account + UpdateAccount :: Address -> (Account -> Account) -> Accounts m () GetAccount :: Address -> Accounts m (Maybe Account) makeSem ''Accounts type AuthEffs = '[Accounts, Error AuthError] -storeKey :: StoreKey AuthNamespace -storeKey = StoreKey "auth" - eval :: Members [ReadStore, WriteStore, Error AppError] r => Sem (Accounts : Error AuthError : r) a -> Sem r a eval = mapError makeAppError . evalAuth where - evalAuth :: Members [ReadStore, WriteStore, Error AppError] r + evalAuth :: Members [ReadStore, WriteStore, Error AuthError, Error AppError] r => Sem (Accounts : r) a -> Sem r a evalAuth = interpret (\case - GetAccount addr -> - get storeKey addr - PutAccount addr acnt -> - put storeKey addr acnt + CreateAccount addr -> createAccountF addr + UpdateAccount addr f -> updateAccountF addr f + GetAccount addr -> getAccountF addr ) -------------------------------------------------------------------------------- -createAccount - :: Members [Accounts, Error AuthError] r +data AuthNamespace + +store :: Store AuthNamespace +store = makeStore $ KeyRoot "auth" + +data AccountsMapKey = AccountsMapKey + +instance RawKey AccountsMapKey where + rawKey = iso (const "accounts") (const AccountsMapKey) + +instance IsKey AccountsMapKey AuthNamespace where + type Value AccountsMapKey AuthNamespace = M.Map Address Account + +accountsMap :: M.Map Address Account +accountsMap = M.makeMap AccountsMapKey store + +createAccountF + :: Members [ReadStore, WriteStore, Error AppError, Error AuthError] r => Address -> Sem r Account -createAccount addr = do - mAcct <- getAccount addr +createAccountF addr = do + mAcct <- M.lookup addr accountsMap case mAcct of Just _ -> throw $ AccountAlreadyExists addr Nothing -> do @@ -53,5 +79,22 @@ createAccount addr = do { accountCoins = [] , accountNonce = 0 } - putAccount addr emptyAccount + M.insert addr emptyAccount accountsMap pure emptyAccount + +updateAccountF + :: Members [ReadStore, WriteStore, Error AppError, Error AuthError] r + => Address + -> (Account -> Account) + -> Sem r () +updateAccountF addr f = do + mAcct <- M.lookup addr accountsMap + case mAcct of + Nothing -> throw $ AccountNotFound addr + Just acct -> M.insert addr (f acct) accountsMap + +getAccountF + :: Members [ReadStore, Error AppError] r + => Address + -> Sem r (Maybe Account) +getAccountF addr = M.lookup addr accountsMap diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs index 6a116335..98e14e49 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Query.hs @@ -3,11 +3,12 @@ module Tendermint.SDK.Modules.Auth.Query , querier ) where -import Data.Proxy import Polysemy (Members) +import Servant.API ((:>)) import qualified Tendermint.SDK.BaseApp as BaseApp -import Tendermint.SDK.BaseApp.Query (QueryEffs) -import Tendermint.SDK.Modules.Auth.Keeper (storeKey) +import Tendermint.SDK.BaseApp.Query (QueryEffs, StoreLeaf) +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import Tendermint.SDK.Modules.Auth.Keeper (accountsMap) import Tendermint.SDK.Modules.Auth.Types (Account) import Tendermint.SDK.Types.Address (Address) @@ -15,12 +16,10 @@ import Tendermint.SDK.Types.Address (Address) -- | Query API -------------------------------------------------------------------------------- -type AuthContents = '[(Address, Account)] - -type Api = BaseApp.QueryApi AuthContents +type Api = "accounts" :> StoreLeaf (M.Map Address Account) querier :: Members QueryEffs r => BaseApp.RouteQ Api r querier = - BaseApp.storeQueryHandlers (Proxy :: Proxy AuthContents) storeKey (Proxy :: Proxy r) + BaseApp.storeQueryHandler accountsMap diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs index ca34bd9e..e0d43484 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Auth/Types.hs @@ -18,8 +18,7 @@ import GHC.Generics (Generic) import GHC.TypeLits (symbolVal) import qualified Proto.Modules.Auth as A import qualified Proto.Modules.Auth_Fields as A -import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..), - IsKey (..), Queryable (..)) +import Tendermint.SDK.BaseApp (AppError (..), IsAppError (..)) import Tendermint.SDK.Codec (HasCodec (..), defaultSDKAesonOptions) import Tendermint.SDK.Types.Address (Address (..)) @@ -30,27 +29,27 @@ import Web.HttpApiData (FromHttpApiData (..), type AuthName = "auth" -data AuthNamespace - -instance IsKey Address AuthNamespace where - type Value Address AuthNamespace = Account - -instance Queryable Account where - type Name Account = "account" - -------------------------------------------------------------------------------- -- Exceptions -------------------------------------------------------------------------------- data AuthError = - AccountAlreadyExists Address + AccountAlreadyExists Address + | AccountNotFound Address instance IsAppError AuthError where makeAppError (AccountAlreadyExists addr) = AppError { appErrorCode = 1 , appErrorCodespace = cs (symbolVal $ Proxy @AuthName) - , appErrorMessage = "Account Already Exists " <> (cs . show $ addr) <> "." + , appErrorMessage = "Account already exists " <> (cs . show $ addr) <> "." + } + + makeAppError (AccountNotFound addr) = + AppError + { appErrorCode = 2 + , appErrorCodespace = cs (symbolVal $ Proxy @AuthName) + , appErrorMessage = "Account not found for address " <> (cs . show $ addr) <> "." } -------------------------------------------------------------------------------- @@ -133,9 +132,6 @@ instance HasCodec Coin where coinAesonOptions :: JSON.Options coinAesonOptions = defaultSDKAesonOptions "coin" -instance Queryable Coin where - type Name Coin = "balance" - -------------------------------------------------------------------------------- data Account = Account diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs index aa7e56ed..8b5b4bfb 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Keeper.hs @@ -1,6 +1,14 @@ {-# LANGUAGE TemplateHaskell #-} -module Tendermint.SDK.Modules.Bank.Keeper where +module Tendermint.SDK.Modules.Bank.Keeper + ( BankEffs + , BankKeeper(..) + , getBalance + , transfer + , burn + , mint + , eval + ) where import Data.List (find) import Data.Maybe (fromMaybe) @@ -17,12 +25,14 @@ type BankEffs = '[BankKeeper, Error BankError] data BankKeeper m a where GetBalance :: Address -> Auth.CoinId -> BankKeeper m Auth.Coin - PutBalance :: Address -> Auth.Coin -> BankKeeper m () + Transfer :: Address -> Auth.Coin -> Address -> BankKeeper m () + Burn :: Address -> Auth.Coin -> BankKeeper m () + Mint :: Address -> Auth.Coin -> BankKeeper m () makeSem ''BankKeeper eval - :: Member (Error BaseApp.AppError) r + :: Members [BaseApp.Logger, Output BaseApp.Event, Error BaseApp.AppError] r => Members Auth.AuthEffs r => forall a. Sem (BankKeeper ': Error BankError ': r) a -> Sem r a eval = mapError BaseApp.makeAppError . evalBankKeeper @@ -30,72 +40,37 @@ eval = mapError BaseApp.makeAppError . evalBankKeeper evalBankKeeper :: forall r. Members Auth.AuthEffs r + => Members [BaseApp.Logger, Output BaseApp.Event, Error BankError ] r => forall a. Sem (BankKeeper ': r) a -> Sem r a evalBankKeeper = interpret (\case GetBalance addr coinId -> getCoinBalance addr coinId - PutBalance addr coins -> putCoinBalance addr coins + Transfer from coin to -> transferF from coin to + Burn addr coin -> burnF addr coin + Mint addr coin -> mintF addr coin ) - getCoinBalance - :: Members Auth.AuthEffs r - => Address - -> Auth.CoinId - -> Sem r Auth.Coin - getCoinBalance address cid = do - mAcnt <- Auth.getAccount address - let zeroBalance = Auth.Coin cid 0 - case mAcnt of - Nothing -> pure zeroBalance - Just (Auth.Account coins _) -> - let mCoin = find (\(Auth.Coin cid1 _) -> cid == cid1) coins - in pure $ fromMaybe zeroBalance mCoin - - replaceCoinValue - :: Auth.Coin - -> [Auth.Coin] - -> [Auth.Coin] - replaceCoinValue c [] = [c] - replaceCoinValue c@(Auth.Coin cid _) (c1@(Auth.Coin cid' _):rest) = - if cid' == cid - then c : rest - else c1 : replaceCoinValue c rest - - putCoinBalance - :: Members Auth.AuthEffs r - => Address - -> Auth.Coin - -> Sem r () - putCoinBalance address coin = do - mAcnt <- Auth.getAccount address - acnt <- case mAcnt of - Nothing -> Auth.createAccount address - Just a -> pure a - let updatedCoins = replaceCoinValue coin (Auth.accountCoins acnt) - updatedAcnt = acnt { Auth.accountCoins = updatedCoins } - Auth.putAccount address updatedAcnt - -------------------------------------------------------------------------------- -transfer - :: Members [BaseApp.Logger, Output BaseApp.Event] r - => Members BankEffs r +transferF + :: Members [BaseApp.Logger, Output BaseApp.Event, Error BankError] r + => Members Auth.AuthEffs r => Address -> Auth.Coin -> Address -> Sem r () -transfer addr1 (Auth.Coin cid amount) addr2 = do +transferF addr1 (Auth.Coin cid amount) addr2 = do -- check if addr1 has amt - (Auth.Coin _ addr1Bal) <- getBalance addr1 cid + (Auth.Coin _ addr1Bal) <- getCoinBalance addr1 cid if addr1Bal >= amount then do - (Auth.Coin _ addr2Bal) <- getBalance addr2 cid + (Auth.Coin _ addr2Bal) <- getCoinBalance addr2 cid let newCoinBalance1 = Auth.Coin cid (addr1Bal - amount) newCoinBalance2 = Auth.Coin cid (addr2Bal + amount) -- update both balances - putBalance addr1 newCoinBalance1 - putBalance addr2 newCoinBalance2 + putCoinBalance addr1 newCoinBalance1 + putCoinBalance addr2 newCoinBalance2 let event = TransferEvent { transferEventAmount = amount , transferEventCoinId = cid @@ -104,24 +79,65 @@ transfer addr1 (Auth.Coin cid amount) addr2 = do } BaseApp.emit event BaseApp.logEvent event - else throw (InsufficientFunds "Insufficient funds for transfer.") + else throw @BankError (InsufficientFunds "Insufficient funds for transfer.") -burn - :: Members BankEffs r +burnF + :: Members Auth.AuthEffs r + => Member (Error BankError) r => Address -> Auth.Coin -> Sem r () -burn addr (Auth.Coin cid amount) = do - (Auth.Coin _ bal) <- getBalance addr cid +burnF addr (Auth.Coin cid amount) = do + (Auth.Coin _ bal) <- getCoinBalance addr cid if bal < amount - then throw $ InsufficientFunds "Insufficient funds for burn." - else putBalance addr (Auth.Coin cid (bal - amount)) + then throw @BankError $ InsufficientFunds "Insufficient funds for burn." + else putCoinBalance addr (Auth.Coin cid (bal - amount)) + +mintF + :: Members Auth.AuthEffs r + => Address + -> Auth.Coin + -> Sem r () +mintF addr (Auth.Coin cid amount) = do + (Auth.Coin _ bal) <- getCoinBalance addr cid + putCoinBalance addr (Auth.Coin cid (bal + amount)) + +-------------------------------------------------------------------------------- -mint - :: Member BankKeeper r +getCoinBalance + :: Members Auth.AuthEffs r + => Address + -> Auth.CoinId + -> Sem r Auth.Coin +getCoinBalance address cid = do + mAcnt <- Auth.getAccount address + let zeroBalance = Auth.Coin cid 0 + case mAcnt of + Nothing -> pure zeroBalance + Just (Auth.Account coins _) -> + let mCoin = find (\(Auth.Coin cid1 _) -> cid == cid1) coins + in pure $ fromMaybe zeroBalance mCoin + +replaceCoinValue + :: Auth.Coin + -> [Auth.Coin] + -> [Auth.Coin] +replaceCoinValue c [] = [c] +replaceCoinValue c@(Auth.Coin cid _) (c1@(Auth.Coin cid' _):rest) = + if cid' == cid + then c : rest + else c1 : replaceCoinValue c rest + +putCoinBalance + :: Members Auth.AuthEffs r => Address -> Auth.Coin -> Sem r () -mint addr (Auth.Coin cid amount) = do - (Auth.Coin _ bal) <- getBalance addr cid - putBalance addr (Auth.Coin cid (bal + amount)) +putCoinBalance address coin = do + mAcnt <- Auth.getAccount address + acnt <- case mAcnt of + Nothing -> Auth.createAccount address + Just a -> pure a + let f a = a { Auth.accountCoins = replaceCoinValue coin $ Auth.accountCoins acnt } + Auth.updateAccount address f + diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs index aa768849..50d52902 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Messages.hs @@ -16,71 +16,71 @@ import Tendermint.SDK.Types.Address (Address, addressFromBytes, import Tendermint.SDK.Types.Message (HasMessageType (..), ValidateMessage (..)) -data Transfer = Transfer +data TransferMsg = TransferMsg { transferTo :: Address , transferFrom :: Address , transferCoinId :: CoinId , transferAmount :: Amount } deriving (Eq, Show, Generic) -instance Wrapped Transfer where - type Unwrapped Transfer = B.Transfer +instance Wrapped TransferMsg where + type Unwrapped TransferMsg = B.Transfer _Wrapped' = iso t f where - t Transfer {..} = + t TransferMsg {..} = P.defMessage & B.to .~ addressToBytes transferTo & B.from .~ addressToBytes transferFrom & B.cid .~ unCoinId transferCoinId & B.amount .~ unAmount transferAmount - f message = Transfer + f message = TransferMsg { transferTo = addressFromBytes $ message ^. B.to , transferFrom = addressFromBytes $ message ^. B.from , transferCoinId = CoinId $ message ^. B.cid , transferAmount = Amount $ message ^. B.amount } -instance HasMessageType Transfer where - messageType _ = "Transfer" +instance HasMessageType TransferMsg where + messageType _ = "TransferMsg" -instance HasCodec Transfer where +instance HasCodec TransferMsg where encode = P.encodeMessage . view _Wrapped' decode = bimap cs (view $ from _Wrapped') . P.decodeMessage -instance ValidateMessage Transfer where +instance ValidateMessage TransferMsg where validateMessage _ = Success () -------------------------------------------------------------------------------- -data Burn = Burn +data BurnMsg = BurnMsg { burnAddress :: Address , burnCoinId :: CoinId , burnAmount :: Amount } deriving (Eq, Show, Generic) -instance Wrapped Burn where - type Unwrapped Burn = B.Burn +instance Wrapped BurnMsg where + type Unwrapped BurnMsg = B.Burn _Wrapped' = iso t f where - t Burn {..} = + t BurnMsg {..} = P.defMessage & B.address .~ addressToBytes burnAddress & B.cid .~ unCoinId burnCoinId & B.amount .~ unAmount burnAmount - f message = Burn + f message = BurnMsg { burnAddress = addressFromBytes $ message ^. B.address , burnCoinId = CoinId $ message ^. B.cid , burnAmount = Amount $ message ^. B.amount } -instance HasMessageType Burn where - messageType _ = "Burn" +instance HasMessageType BurnMsg where + messageType _ = "BurnMsg" -instance HasCodec Burn where +instance HasCodec BurnMsg where encode = P.encodeMessage . view _Wrapped' decode = bimap cs (view $ from _Wrapped') . P.decodeMessage -instance ValidateMessage Burn where +instance ValidateMessage BurnMsg where validateMessage _ = Success () diff --git a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs index 1ccd6555..bbe9885a 100644 --- a/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs +++ b/hs-abci-sdk/src/Tendermint/SDK/Modules/Bank/Router.hs @@ -5,42 +5,38 @@ module Tendermint.SDK.Modules.Bank.Router import Polysemy (Members, Sem) import Servant.API ((:<|>) (..)) -import Tendermint.SDK.BaseApp ((:~>), BaseEffs, Return, - RouteTx, RoutingTx (..), - TxEffs, TypedMessage) +import Tendermint.SDK.BaseApp ((:~>), Return, RouteTx, + RoutingTx (..), + TypedMessage) import Tendermint.SDK.Modules.Auth (Coin (..)) import Tendermint.SDK.Modules.Bank.Keeper (BankEffs, burn, transfer) -import Tendermint.SDK.Modules.Bank.Messages (Burn (..), Transfer (..)) +import Tendermint.SDK.Modules.Bank.Messages import Tendermint.SDK.Types.Message (Msg (..)) import Tendermint.SDK.Types.Transaction (Tx (..)) type MessageApi = - TypedMessage Burn :~> Return () - :<|> TypedMessage Transfer :~> Return () + TypedMessage BurnMsg :~> Return () + :<|> TypedMessage TransferMsg :~> Return () messageHandlers ::Members BankEffs r - => Members TxEffs r - => Members BaseEffs r => RouteTx MessageApi r messageHandlers = burnH :<|> transferH transferH :: Members BankEffs r - => Members TxEffs r - => Members BaseEffs r - => RoutingTx Transfer + => RoutingTx TransferMsg -> Sem r () transferH (RoutingTx Tx{txMsg=Msg{msgData}}) = - let Transfer{..} = msgData + let TransferMsg{..} = msgData coin = Coin transferCoinId transferAmount in transfer transferFrom coin transferTo burnH :: Members BankEffs r - => RoutingTx Burn + => RoutingTx BurnMsg -> Sem r () burnH (RoutingTx Tx{txMsg=Msg{msgData}}) = - let Burn{..} = msgData + let BurnMsg{..} = msgData coin = Coin burnCoinId burnAmount in burn burnAddress coin diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/ArraySpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/ArraySpec.hs new file mode 100644 index 00000000..5ed5d714 --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/ArraySpec.hs @@ -0,0 +1,128 @@ +module Tendermint.SDK.Test.ArraySpec (spec) where + +import Control.Lens (iso) +import Data.ByteString (ByteString) +import Data.IORef (IORef, newIORef) +import Data.Maybe (fromJust, isJust) +import Data.String.Conversions (cs) +import Data.Word (Word64) +import Polysemy (Embed, Sem, runM) +import Polysemy.Error (Error, runError) +import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.BaseApp.Store (Version (..)) +import qualified Tendermint.SDK.BaseApp.Store.Array as A +import Tendermint.SDK.BaseApp.Store.MemoryStore as Mem +import Tendermint.SDK.Codec (HasCodec (..)) +import Test.Hspec + +spec :: Spec +spec = + beforeAll makeConfig $ + describe "Array Spec" $ do + + it "Can create an empty array" $ \config -> do + res <- runToIO config $ A.toList valArray + res `shouldBe` [] + + it "Can add an element to the array" $ \config -> do + res <- runToIO config $ do + let n = 1 + A.append n valArray + mi <- A.elemIndex n valArray + l <- A.toList valArray + pure (mi, l) + res `shouldBe` (Just 0, [1]) + runToIO config $ A.deleteWhen (const True) valArray + + it "Can add an element, modify it, then delete it" $ \config -> do + let n = 2 + m = 3 + + -- save the element and get its index + i <- runToIO config $ do + A.append n valArray + A.elemIndex n valArray + i `shouldSatisfy` isJust + + -- accessing at the index gets the value back again + n' <- runToIO config (valArray A.!! fromJust i) + Just n `shouldBe` n' + + -- modifying the element at the index is successful + mm <- runToIO config $ A.modifyAtIndex (fromJust i) (const m) valArray + mm `shouldBe` Just m + + -- deleting the element and trying to find it gives Nothing + res2 <- runToIO config $ do + A.deleteWhen (== m) valArray + A.elemIndex n valArray + res2 `shouldBe` Nothing + + -- modifying a deleted element gives Nothing + let k = 4 + mm' <- runToIO config $ A.modifyAtIndex (fromJust i) (const k) valArray + mm' `shouldBe` Nothing + + + + + +-------------------------------------------------------------------------------- +-- Store types +-------------------------------------------------------------------------------- + +data Namespace + +store :: BA.Store Namespace +store = BA.makeStore $ BA.KeyRoot "namespace" + +data ValArrayKey = ValArrayKey + +instance BA.RawKey ValArrayKey where + rawKey = iso (\_ -> cs valArrayKey) (const ValArrayKey) + where + valArrayKey :: ByteString + valArrayKey = cs $ ("valArray" :: String) + +instance BA.IsKey ValArrayKey Namespace where + type Value ValArrayKey Namespace = A.Array Val + +newtype Val = Val Word64 deriving (Eq, Show, Num, HasCodec) + +valArray :: A.Array Val +valArray = A.makeArray ValArrayKey store + +-------------------------------------------------------------------------------- +-- Interpreter +-------------------------------------------------------------------------------- + +type Effs = + [ BA.ReadStore + , BA.WriteStore + , Error BA.AppError + , Embed IO + ] + +data Config = Config + { configDB :: Mem.DB + , configVersion :: IORef Version + } + +makeConfig :: IO Config +makeConfig = do + db <- Mem.initDB + v <- newIORef Latest + pure $ Config db v + +runToIO + :: Config + -> forall a. + Sem Effs a + -> IO a +runToIO Config{configDB, configVersion} m = do + eRes <- + runM . + runError . + evalWrite configDB . + evalRead configDB configVersion $ m + either (error . show) pure eRes diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs index 838cabed..646fc9a9 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/IAVLStoreSpec.hs @@ -19,15 +19,15 @@ import Polysemy.Tagged (tag) import Tendermint.SDK.BaseApp.Errors (AppError (..), SDKError (InternalError), throwSDKError) import Tendermint.SDK.BaseApp.Store (IsKey (..), + KeyRoot (..), RawKey (..), ReadStore, - Scope (..), + Scope (..), Store, StoreEffs, - StoreKey (..), WriteStore, WriteStore, commit, commitBlock, delete, - get, put, + get, makeStore, put, withSandbox, withTransaction) import Tendermint.SDK.BaseApp.Store.IAVLStore (GrpcConfig (..), @@ -53,76 +53,76 @@ spec' (Proxy :: Proxy r) = do it "can fail to query an empty AuthTreeStore" $ \(driver :: Driver r) -> do Right mv <- runDriver driver $ tag @'QueryAndMempool $ - get storeKey IntStoreKey + get store IntStoreKey mv `shouldBe` Nothing it "can set a value and query the value" $ \(driver :: Driver r) -> do Right mv <- runDriver driver $ do - tag @'Consensus @WriteStore $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) - tag @'Consensus @ReadStore $ get storeKey IntStoreKey + tag @'Consensus @WriteStore $ (put store IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + tag @'Consensus @ReadStore $ get store IntStoreKey mv `shouldBe` Just (IntStore 1) it "can make changes and roll back" $ \(driver :: Driver r) -> do Right mv'' <- runDriver driver $ do void $ withTransaction $ - tag @'Consensus $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + tag @'Consensus $ (put store IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) withSandbox $ do - tag @'Consensus @WriteStore $ (put storeKey IntStoreKey (IntStore 5) :: Sem (WriteStore ': r) ()) - mv <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + tag @'Consensus @WriteStore $ (put store IntStoreKey (IntStore 5) :: Sem (WriteStore ': r) ()) + mv <- tag @'Consensus @ReadStore $ get store IntStoreKey liftIO (mv `shouldBe` Just (IntStore 5)) - tag @'Consensus @WriteStore$ (delete storeKey IntStoreKey :: Sem (WriteStore ': r) ()) - mv' <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + tag @'Consensus @WriteStore$ (delete store IntStoreKey :: Sem (WriteStore ': r) ()) + mv' <- tag @'Consensus @ReadStore $ get store IntStoreKey liftIO (mv' `shouldBe` Nothing) - tag @'Consensus @ReadStore $ get storeKey IntStoreKey + tag @'Consensus @ReadStore $ get store IntStoreKey mv'' `shouldBe` Just (IntStore 1) it "can roll back if an error occurs during a transaction" $ \driver -> do Left apperr <- runDriver driver $ do void $ withTransaction $ - tag @'Consensus $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + tag @'Consensus $ (put store IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) void $ withTransaction $ do - tag @'Consensus $ (put storeKey IntStoreKey (IntStore 6) :: Sem (WriteStore ': r) ()) - throwSDKError InternalError + tag @'Consensus $ (put store IntStoreKey (IntStore 6) :: Sem (WriteStore ': r) ()) + throwSDKError $ InternalError "SomeError" appErrorCode apperr `shouldBe` 1 Right mv <- runDriver driver $ - tag @'Consensus @ReadStore $ get storeKey IntStoreKey + tag @'Consensus @ReadStore $ get store IntStoreKey mv `shouldBe` Just (IntStore 1) it "can make changes with a transaction" $ \driver -> do Right (mv, _) <- runDriver driver . withTransaction $ do - tag @'Consensus $ (put storeKey IntStoreKey (IntStore 5) :: Sem (WriteStore ': r) ()) - tag @'Consensus @ReadStore $ get storeKey IntStoreKey + tag @'Consensus $ (put store IntStoreKey (IntStore 5) :: Sem (WriteStore ': r) ()) + tag @'Consensus @ReadStore $ get store IntStoreKey mv `shouldBe` Just (IntStore 5) it "can merge the scopes" $ \driver -> do -- set all to be initially the same value void $ runDriver driver $ withTransaction $ - tag @'Consensus $ (put storeKey IntStoreKey (IntStore 0) :: Sem (WriteStore ': r) ()) + tag @'Consensus $ (put store IntStoreKey (IntStore 0) :: Sem (WriteStore ': r) ()) -- mergeScopes so that all are using the latest version void $ runDriver driver $ commitBlock void $ runDriver driver $ do - res <- tag @'QueryAndMempool $ get storeKey IntStoreKey + res <- tag @'QueryAndMempool $ get store IntStoreKey liftIO (res `shouldBe` Just 0) void $ runDriver driver $ do - res <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + res <- tag @'Consensus @ReadStore $ get store IntStoreKey liftIO (res `shouldBe` Just 0) -- Make another change on Consensus that does not commit void $ runDriver driver $ do - tag @'Consensus $ (put storeKey IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) + tag @'Consensus $ (put store IntStoreKey (IntStore 1) :: Sem (WriteStore ': r) ()) void $ runDriver driver $ do - res <- tag @'QueryAndMempool @ReadStore $ get storeKey IntStoreKey + res <- tag @'QueryAndMempool @ReadStore $ get store IntStoreKey liftIO (res `shouldBe` Just 0) void $ runDriver driver $ do - res <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + res <- tag @'Consensus @ReadStore $ get store IntStoreKey liftIO (res `shouldBe` Just 1) -- commit the changes @@ -132,11 +132,11 @@ spec' (Proxy :: Proxy r) = do void $ runDriver driver $ commitBlock void $ runDriver driver $ do - res <- tag @'QueryAndMempool @ReadStore $ get storeKey IntStoreKey + res <- tag @'QueryAndMempool @ReadStore $ get store IntStoreKey liftIO (res `shouldBe` Just 1) void $ runDriver driver $ do - res <- tag @'Consensus @ReadStore $ get storeKey IntStoreKey + res <- tag @'Consensus @ReadStore $ get store IntStoreKey liftIO (res `shouldBe` Just 1) @@ -169,8 +169,8 @@ instance RawKey IntStoreKey where instance IsKey IntStoreKey "int_store" where type Value IntStoreKey "int_store" = IntStore -storeKey :: StoreKey "int_store" -storeKey = StoreKey "int_store" +store :: Store "int_store" +store = makeStore $ KeyRoot "int_store" type IAVLEffs = StoreEffs :& [Reader IAVLVersions, Reader GrpcClient, Error AppError, Resource, Embed IO] diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/ListSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/ListSpec.hs new file mode 100644 index 00000000..b3fca2dc --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/ListSpec.hs @@ -0,0 +1,187 @@ +module Tendermint.SDK.Test.ListSpec (spec) where + +import Control.Lens (iso) +import Data.ByteString (ByteString) +import Data.IORef (IORef, newIORef) +import Data.String.Conversions (cs) +import Data.Word (Word64) +import Polysemy (Embed, Sem, runM) +import Polysemy.Error (Error, runError) +import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.BaseApp.Store (Version (..)) +import qualified Tendermint.SDK.BaseApp.Store.List as L +import Tendermint.SDK.BaseApp.Store.MemoryStore as Mem +import Tendermint.SDK.Codec (HasCodec (..)) +import Test.Hspec + +spec :: Spec +spec = + + beforeAll makeConfig $ + describe "List Spec" $ do + + it "Can create an empty list" $ \config -> do + res <- runToIO config $ L.toList valList + res `shouldBe` [] + + it "Can make a singleton list then delete it" $ \config -> do + lInit <- runToIO config $ L.toList valList + lInit `shouldBe` [] + let n = 1 + res <- runToIO config $ do + L.append n valList + mi <- L.elemIndex n valList + me <- maybe (pure Nothing) (valList L.!! ) mi + l <- L.toList valList + len <- L.length valList + pure (mi, me , l, len) + res `shouldBe` (Just 0, Just n, [n], 1) + res' <- runToIO config $ do + L.delete n valList + i <- L.elemIndex n valList + len <- L.length valList + l <- L.toList valList + pure (i, len, l) + res' `shouldBe` (Nothing, 0, []) + runToIO config $ L.deleteWhen (const True) valList + + it "Can add two elements and delete the head" $ \config -> do + lInit <- runToIO config $ L.toList valList + lInit `shouldBe` [] + let n = 1 + m = 2 + res <- runToIO config $ do + L.append n valList + L.append m valList + mi <- L.elemIndex m valList + me <- maybe (pure Nothing) (valList L.!!) mi + l <- L.toList valList + len <- L.length valList + pure (mi, me, l, len) + res `shouldBe` (Just 0, Just m, [m,n], 2) + res' <- runToIO config $ do + L.delete m valList + i <- L.elemIndex m valList + len <- L.length valList + l <- L.toList valList + pure (i, len, l) + res' `shouldBe` (Nothing, 1, [n]) + runToIO config $ L.deleteWhen (const True) valList + + it "Can add add three elements and delete the second" $ \config -> do + lInit <- runToIO config $ L.toList valList + lInit `shouldBe` [] + let n = 1 + m = 2 + k = 3 + res <- runToIO config $ do + L.append n valList + L.append m valList + L.append k valList + mi <- L.elemIndex m valList + me <- maybe (pure Nothing) (valList L.!!) mi + l <- L.toList valList + len <- L.length valList + pure (mi, me, l, len) + res `shouldBe` (Just 1, Just m, [k,m,n], 3) + res' <- runToIO config $ do + L.delete m valList + i <- L.elemIndex m valList + len <- L.length valList + l <- L.toList valList + pure (i, len, l) + res' `shouldBe` (Nothing, 2, [k,n]) + runToIO config $ L.deleteWhen (const True) valList + + it "Can add add three elements and delete the third" $ \config -> do + lInit <- runToIO config $ L.toList valList + lInit `shouldBe` [] + let n = 1 + m = 2 + k = 3 + res <- runToIO config $ do + L.append n valList + L.append m valList + L.append k valList + mi <- L.elemIndex n valList + me <- maybe (pure Nothing) (valList L.!!) mi + l <- L.toList valList + len <- L.length valList + pure (mi, me, l, len) + res `shouldBe` (Just 2, Just n, [k,m,n], 3) + res' <- runToIO config $ do + L.delete n valList + i <- L.elemIndex n valList + len <- L.length valList + l <- L.toList valList + pure (i, len, l) + res' `shouldBe` (Nothing, 2, [k,m]) + runToIO config $ L.deleteWhen (const True) valList + + + + + + + + + +-------------------------------------------------------------------------------- +-- Store types +-------------------------------------------------------------------------------- + +data Namespace + +store :: BA.Store Namespace +store = BA.makeStore $ BA.KeyRoot "namespace" + +data ValListKey = ValListKey + +instance BA.RawKey ValListKey where + rawKey = iso (\_ -> cs valListKey) (const ValListKey) + where + valListKey :: ByteString + valListKey = cs $ ("valArray" :: String) + +instance BA.IsKey ValListKey Namespace where + type Value ValListKey Namespace = L.List Val + +newtype Val = Val Word64 deriving (Eq, Show, Num, HasCodec) + +valList :: L.List Val +valList = L.makeList ValListKey store + +-------------------------------------------------------------------------------- +-- Interpreter +-------------------------------------------------------------------------------- + +type Effs = + [ BA.ReadStore + , BA.WriteStore + , Error BA.AppError + , Embed IO + ] + +data Config = Config + { configDB :: Mem.DB + , configVersion :: IORef Version + } + +makeConfig :: IO Config +makeConfig = do + db <- Mem.initDB + v <- newIORef Latest + pure $ Config db v + +runToIO + :: Config + -> forall a. + Sem Effs a + -> IO a +runToIO Config{configDB, configVersion} m = do + eRes <- + runM . + runError . + evalWrite configDB . + evalRead configDB configVersion $ m + either (error . show) pure eRes diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/MapSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/MapSpec.hs new file mode 100644 index 00000000..1c08b316 --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/MapSpec.hs @@ -0,0 +1,107 @@ +module Tendermint.SDK.Test.MapSpec (spec) where + +import Control.Lens (iso) +import Data.ByteString (ByteString) +import Data.IORef (IORef, newIORef) +import Data.String.Conversions (cs) +import Data.Word (Word64) +import Polysemy (Embed, Sem, runM) +import Polysemy.Error (Error, runError) +import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.BaseApp.Store (Version (..)) +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import Tendermint.SDK.BaseApp.Store.MemoryStore as Mem +import Tendermint.SDK.Codec (HasCodec (..)) +import Test.Hspec + +spec :: Spec +spec = + beforeAll makeConfig $ + describe "Map Spec" $ do + + it "Can insert and delete from map" $ \config -> do + res <- runToIO config $ do + M.insert 1 1 valMap + v <- M.lookup 1 valMap + M.delete 1 valMap + v' <- M.lookup 1 valMap + pure (v,v') + res `shouldBe` (Just 1, Nothing) + + it "Can insert and update map" $ \config -> do + res <- runToIO config $ do + M.insert 1 1 valMap + M.update (const $ Just 2) 1 valMap + v <- M.lookup 1 valMap + M.update (const $ Nothing) 1 valMap + v' <- M.lookup 1 valMap + pure (v,v') + res `shouldBe` (Just 2, Nothing) + + +-------------------------------------------------------------------------------- +-- Store types +-------------------------------------------------------------------------------- + +data Namespace + +store :: BA.Store Namespace +store = BA.makeStore $ BA.KeyRoot "namespace" + +newtype Key = Key Word64 deriving (Eq, Show, Num) + +instance BA.RawKey Key where + rawKey = iso (\(Key k) -> encode k) + (either (const $ error "KeyError") Key . decode) + +newtype Val = Val Word64 deriving (Eq, Show, Num, HasCodec) + +data ValMapKey = ValMapKey + +instance BA.RawKey ValMapKey where + rawKey = iso (\_ -> cs valMapKey) (const ValMapKey) + where + valMapKey :: ByteString + valMapKey = cs $ ("valArray" :: String) + +instance BA.IsKey ValMapKey Namespace where + type Value ValMapKey Namespace = M.Map Key Val + + +valMap :: M.Map Key Val +valMap = M.makeMap ValMapKey store + +-------------------------------------------------------------------------------- +-- Interpreter +-------------------------------------------------------------------------------- + +type Effs = + [ BA.ReadStore + , BA.WriteStore + , Error BA.AppError + , Embed IO + ] + +data Config = Config + { configDB :: Mem.DB + , configVersion :: IORef Version + } + +makeConfig :: IO Config +makeConfig = do + db <- Mem.initDB + v <- newIORef Latest + pure $ Config db v + +runToIO + :: Config + -> forall a. + Sem Effs a + -> IO a +runToIO Config{configDB, configVersion} m = do + eRes <- + runM . + runError . + evalWrite configDB . + evalRead configDB configVersion $ m + either (error . show) pure eRes diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs index f78350d5..8fa74c65 100644 --- a/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/SimpleStorage.hs @@ -1,4 +1,5 @@ {-# LANGUAGE TemplateHaskell #-} +{-# OPTIONS_GHC -fno-warn-unused-top-binds #-} module Tendermint.SDK.Test.SimpleStorage ( SimpleStorage @@ -10,33 +11,36 @@ module Tendermint.SDK.Test.SimpleStorage , Count(..) ) where -import Control.Lens (iso, (^.)) -import Crypto.Hash (SHA256 (..), hashWith) -import Data.Bifunctor (first) -import Data.ByteArray (convert) -import qualified Data.ByteArray.Base64String as Base64 -import Data.ByteString (ByteString) -import Data.Int (Int32) +import Control.Lens (iso, (^.)) +import Crypto.Hash (SHA256 (..), hashWith) +import Data.Bifunctor (first) +import Data.ByteArray (convert) +import qualified Data.ByteArray.Base64String as Base64 +import Data.ByteString (ByteString) +import Data.Int (Int32) import Data.Proxy -import qualified Data.Serialize as Serialize -import Data.Serialize.Text () -import Data.String.Conversions (cs) -import Data.Validation (Validation (..)) -import Data.Word (Word64) -import GHC.Generics (Generic) -import GHC.TypeLits (symbolVal) +import qualified Data.Serialize as Serialize +import Data.Serialize.Text () +import Data.String.Conversions (cs) +import Data.Validation (Validation (..)) +import Data.Word (Word64) +import GHC.Generics (Generic) +import GHC.TypeLits (symbolVal) import Polysemy -import Polysemy.Error (Error, catch, throw) +import Polysemy.Error (Error, catch, throw) import Servant.API -import Tendermint.SDK.Application (Module (..), ModuleEffs) -import qualified Tendermint.SDK.BaseApp as BA -import Tendermint.SDK.Codec (HasCodec (..)) -import qualified Tendermint.SDK.Modules.Bank as B -import Tendermint.SDK.Types.Address (Address) -import Tendermint.SDK.Types.Message (HasMessageType (..), - Msg (..), - ValidateMessage (..)) -import Tendermint.SDK.Types.Transaction (Tx (..)) +import Tendermint.SDK.Application (Module (..), ModuleEffs) +import qualified Tendermint.SDK.BaseApp as BA +import qualified Tendermint.SDK.BaseApp.Store.Array as A +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import qualified Tendermint.SDK.BaseApp.Store.Var as V +import Tendermint.SDK.Codec (HasCodec (..)) +import qualified Tendermint.SDK.Modules.Bank as B +import Tendermint.SDK.Types.Address (Address) +import Tendermint.SDK.Types.Message (HasMessageType (..), + Msg (..), + ValidateMessage (..)) +import Tendermint.SDK.Types.Transaction (Tx (..)) -------------------------------------------------------------------------------- @@ -44,42 +48,19 @@ import Tendermint.SDK.Types.Transaction (Tx (..)) -------------------------------------------------------------------------------- type SimpleStorageName = "simple_storage" -data SimpleStorageNamespace - simpleStorageCoinId :: B.CoinId simpleStorageCoinId = B.CoinId . cs . symbolVal $ Proxy @SimpleStorageName newtype Count = Count Int32 deriving (Eq, Show, Ord, Num, Serialize.Serialize) -data CountKey = CountKey - instance HasCodec Count where encode = Serialize.encode decode = first cs . Serialize.decode -instance BA.RawKey CountKey where - rawKey = iso (\_ -> cs countKey) (const CountKey) - where - countKey :: ByteString - countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) - -instance BA.IsKey CountKey SimpleStorageNamespace where - type Value CountKey SimpleStorageNamespace = Count - -instance BA.FromQueryData CountKey - -instance BA.Queryable Count where - type Name Count = "count" +-- Count newtype AmountPaid = AmountPaid B.Amount deriving (Eq, Show, Num, Ord, HasCodec) --- for reporting how much paid to change count -instance BA.IsKey Address SimpleStorageNamespace where - type Value Address SimpleStorageNamespace = AmountPaid - -instance BA.Queryable AmountPaid where - type Name AmountPaid = "paid" - -------------------------------------------------------------------------------- -- Message Types -------------------------------------------------------------------------------- @@ -121,57 +102,115 @@ instance ValidateMessage UpdatePaidCountTx where -- Keeper -------------------------------------------------------------------------------- -storeKey :: BA.StoreKey SimpleStorageNamespace -storeKey = BA.StoreKey (cs . symbolVal $ Proxy @SimpleStorageName) data SimpleStorageKeeper m a where - PutCount :: Count -> SimpleStorageKeeper m () + UpdateCount :: Count -> SimpleStorageKeeper m () + UpdatePaidCount :: Address -> Count -> B.Amount -> SimpleStorageKeeper m () GetCount :: SimpleStorageKeeper m (Maybe Count) - StoreAmountPaid :: Address -> AmountPaid -> SimpleStorageKeeper m () + GetAllCounts :: SimpleStorageKeeper m [Count] makeSem ''SimpleStorageKeeper +-------------------------------------------------------------------------------- -updateCount - :: Member SimpleStorageKeeper r +data SimpleStorageNamespace + +store :: BA.Store SimpleStorageNamespace +store = BA.makeStore $ BA.KeyRoot (cs . symbolVal $ Proxy @SimpleStorageName) + +data CountKey = CountKey + +instance BA.RawKey CountKey where + rawKey = iso (\_ -> cs countKey) (const CountKey) + where + countKey :: ByteString + countKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("count" :: String) + +instance BA.IsKey CountKey SimpleStorageNamespace where + type Value CountKey SimpleStorageNamespace = V.Var Count + +countVar :: V.Var Count +countVar = V.makeVar CountKey store + +instance BA.QueryData CountKey + +data PaidKey = PaidKey + +instance BA.RawKey PaidKey where + rawKey = iso (const paidKey) (const PaidKey) + where + paidKey :: ByteString + paidKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("paid" :: String) + +instance BA.IsKey PaidKey SimpleStorageNamespace where + type Value PaidKey SimpleStorageNamespace = M.Map Address AmountPaid + +paidMap :: M.Map Address AmountPaid +paidMap = M.makeMap PaidKey store + +-- | Counts + +data CountsKey = CountsKey + +instance BA.RawKey CountsKey where + rawKey = iso (\_ -> cs countsKey) (const CountsKey) + where + countsKey :: ByteString + countsKey = convert . hashWith SHA256 . cs @_ @ByteString $ ("counts" :: String) + +instance BA.IsKey CountsKey SimpleStorageNamespace where + type Value CountsKey SimpleStorageNamespace = A.Array Count + +countsList :: A.Array Count +countsList = A.makeArray CountsKey store + +-------------------------------------------------------------------------------- + +type SimpleStorageEffs = '[SimpleStorageKeeper] + +eval + :: forall r. + Members BA.TxEffs r + => Members B.BankEffs r + => forall a. (Sem (SimpleStorageKeeper ': r) a -> Sem r a) +eval = interpret (\case + UpdateCount count -> updateCountF count + UpdatePaidCount addr count amt -> updatePaidCountF addr count amt + GetCount -> V.takeVar countVar + GetAllCounts -> A.toList countsList + ) + + +updateCountF + :: Members [Error BA.AppError, BA.ReadStore, BA.WriteStore] r => Count -> Sem r () -updateCount count = putCount count +updateCountF count = do + V.putVar count countVar + A.append count countsList -updatePaidCount + +updatePaidCountF :: forall r . - Member SimpleStorageKeeper r - => Members B.BankEffs r - => Members BA.BaseEffs r + Members B.BankEffs r + => Members [BA.ReadStore, BA.WriteStore, Error BA.AppError] r => Address -> Count -> B.Amount -> Sem r () -updatePaidCount from count amount = +updatePaidCountF from count amount = catch @_ @r ( do B.burn from (B.Coin simpleStorageCoinId amount) - updateCount count - storeAmountPaid from (AmountPaid amount) + updateCountF count + M.update (Just . \a -> a + AmountPaid amount) from paidMap ) (\(B.InsufficientFunds _) -> do let mintAmount = B.Coin simpleStorageCoinId (amount + 1) B.mint from mintAmount - updatePaidCount from count amount + updatePaidCountF from count amount ) -type SimpleStorageEffs = '[SimpleStorageKeeper] - -eval - :: forall r. - Members BA.TxEffs r - => forall a. (Sem (SimpleStorageKeeper ': r) a -> Sem r a) -eval = interpret (\case - PutCount count -> BA.put storeKey CountKey count - GetCount -> BA.get storeKey CountKey - StoreAmountPaid from amt -> BA.put storeKey from amt - ) - -------------------------------------------------------------------------------- -- Router -------------------------------------------------------------------------------- @@ -182,8 +221,6 @@ type MessageApi = messageHandlers :: Member SimpleStorageKeeper r - => Members B.BankEffs r - => Members BA.BaseEffs r => BA.RouteTx MessageApi r messageHandlers = updateCountH :<|> updatePaidCountH @@ -198,8 +235,6 @@ updateCountH (BA.RoutingTx Tx{txMsg}) = updatePaidCountH :: Member SimpleStorageKeeper r - => Members B.BankEffs r - => Members BA.BaseEffs r => BA.RoutingTx UpdatePaidCountTx -> Sem r () updatePaidCountH (BA.RoutingTx Tx{txMsg}) = @@ -212,10 +247,10 @@ updatePaidCountH (BA.RoutingTx Tx{txMsg}) = -- Server -------------------------------------------------------------------------------- -type CountStoreContents = - '[ (CountKey, Count) - , (Address, AmountPaid) - ] +type CountStoreApi = + "count" :> BA.StoreLeaf (V.Var Count) :<|> + "counts" :> BA.StoreLeaf (A.Array Count) :<|> + "amount_paid" :> BA.StoreLeaf (M.Map Address AmountPaid) type GetMultipliedCount = "manipulated" @@ -242,7 +277,7 @@ getMultipliedCount subtractor multiplier = do , queryResultHeight = 0 } -type QueryApi = GetMultipliedCount :<|> BA.QueryApi CountStoreContents +type QueryApi = GetMultipliedCount :<|> CountStoreApi querier :: forall r. @@ -250,9 +285,11 @@ querier => Member SimpleStorageKeeper r => BA.RouteQ QueryApi r querier = - let storeHandlers = BA.storeQueryHandlers (Proxy :: Proxy CountStoreContents) - storeKey (Proxy :: Proxy r) - in getMultipliedCount :<|> storeHandlers + getMultipliedCount :<|> + ( BA.storeQueryHandler countVar :<|> + BA.storeQueryHandler countsList :<|> + BA.storeQueryHandler paidMap + ) -------------------------------------------------------------------------------- -- Module Definition diff --git a/hs-abci-sdk/test/Tendermint/SDK/Test/VarSpec.hs b/hs-abci-sdk/test/Tendermint/SDK/Test/VarSpec.hs new file mode 100644 index 00000000..96ed41ef --- /dev/null +++ b/hs-abci-sdk/test/Tendermint/SDK/Test/VarSpec.hs @@ -0,0 +1,95 @@ +module Tendermint.SDK.Test.VarSpec (spec) where + +import Control.Lens (iso) +import Data.ByteString (ByteString) +import Data.IORef (IORef, newIORef) +import Data.String.Conversions (cs) +import Data.Word (Word64) +import Polysemy (Embed, Sem, runM) +import Polysemy.Error (Error, runError) +import qualified Tendermint.SDK.BaseApp as BA +import Tendermint.SDK.BaseApp.Store (Version (..)) +import Tendermint.SDK.BaseApp.Store.MemoryStore as Mem +import qualified Tendermint.SDK.BaseApp.Store.Var as V +import Tendermint.SDK.Codec (HasCodec (..)) +import Test.Hspec + +spec :: Spec +spec = + beforeAll makeConfig $ + describe "Var Spec" $ do + + it "Can create an empty var" $ \config -> do + res <- runToIO config $ V.takeVar valVar + res `shouldBe` Nothing + + it "Can put a val in a var and take it out" $ \config -> do + res <- runToIO config $ do + V.putVar 1 valVar + v <- V.takeVar valVar + v' <- V.unsafeTakeVar valVar + pure (v, v') + res `shouldBe` (Just 1, 1) + runToIO config $ V.deleteVar valVar + + +-------------------------------------------------------------------------------- +-- Store types +-------------------------------------------------------------------------------- + +data Namespace + +store :: BA.Store Namespace +store = BA.makeStore $ BA.KeyRoot "namespace" + +data ValVarKey = ValVarKey + +instance BA.RawKey ValVarKey where + rawKey = iso (\_ -> cs valVarKey) (const ValVarKey) + where + valVarKey :: ByteString + valVarKey = cs $ ("valVar" :: String) + +instance BA.IsKey ValVarKey Namespace where + type Value ValVarKey Namespace = V.Var Val + +newtype Val = Val Word64 deriving (Eq, Show, Num, HasCodec) + +valVar :: V.Var Val +valVar = V.makeVar ValVarKey store + + +-------------------------------------------------------------------------------- +-- Interpreter +-------------------------------------------------------------------------------- + +type Effs = + [ BA.ReadStore + , BA.WriteStore + , Error BA.AppError + , Embed IO + ] + +data Config = Config + { configDB :: Mem.DB + , configVersion :: IORef Version + } + +makeConfig :: IO Config +makeConfig = do + db <- Mem.initDB + v <- newIORef Latest + pure $ Config db v + +runToIO + :: Config + -> forall a. + Sem Effs a + -> IO a +runToIO Config{configDB, configVersion} m = do + eRes <- + runM . + runError . + evalWrite configDB . + evalRead configDB configVersion $ m + either (error . show) pure eRes diff --git a/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs b/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs index d4687e32..f5078fdd 100644 --- a/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs +++ b/hs-abci-test-utils/src/Tendermint/Utils/QueryClient/Class.hs @@ -1,7 +1,7 @@ {-# LANGUAGE UndecidableInstances #-} module Tendermint.Utils.QueryClient.Class where -import Control.Lens (to, (^.)) +import Control.Lens ((^.)) import Control.Monad.Reader (ReaderT) import qualified Data.ByteArray.Base64String as Base64 import qualified Data.ByteArray.HexString as Hex @@ -9,6 +9,7 @@ import Data.ByteString (ByteString) import Data.Proxy import Data.String.Conversions (cs) import Data.Text (Text, intercalate) +import Data.Word (Word64) import GHC.TypeLits (KnownSymbol, symbolVal) import Network.ABCI.Types.Messages.FieldTypes (WrappedVal (..)) import qualified Network.ABCI.Types.Messages.Request as Req @@ -20,9 +21,11 @@ import Tendermint.SDK.BaseApp.Errors (queryAppError) import Tendermint.SDK.BaseApp.Query.Store (StoreLeaf) import Tendermint.SDK.BaseApp.Query.Types (Leaf, QA, QueryArgs (..), - QueryResult (..), - Queryable (..)) -import Tendermint.SDK.BaseApp.Store (RawKey (..)) + QueryData (..), + QueryResult (..)) +import qualified Tendermint.SDK.BaseApp.Store.Array as A +import qualified Tendermint.SDK.BaseApp.Store.Map as M +import qualified Tendermint.SDK.BaseApp.Store.Var as V import Tendermint.SDK.Codec (HasCodec (decode)) import Tendermint.Utils.QueryClient.Types import Web.Internal.HttpApiData (ToHttpApiData (..)) @@ -81,10 +84,10 @@ instance (KnownSymbol sym, ToHttpApiData a, HasQueryClient m api, SBoolI (FoldRe pname :: Text pname = cs $ symbolVal (Proxy :: Proxy sym) -instance (RawKey k, HasQueryClient m a) => HasQueryClient m (QA k :> a) where +instance (QueryData k, HasQueryClient m a) => HasQueryClient m (QA k :> a) where type ClientQ m (QA k :> a) = QueryArgs k -> ClientQ m a genClientQ pm _ (q,qs) QueryArgs{..} = genClientQ pm (Proxy @a) - (q { Req.queryData = queryArgsData ^. rawKey . to Base64.fromBytes + (q { Req.queryData = toQueryData queryArgsData , Req.queryHeight = WrappedVal queryArgsHeight , Req.queryProve = queryArgsProve }, qs) @@ -134,12 +137,17 @@ leafGenClient (q,qs) = do } _ -> QueryError $ r ^. queryAppError -instance (RunQueryClient m, Queryable a, name ~ Name a, KnownSymbol name ) => HasQueryClient m (StoreLeaf a) where - type ClientQ m (StoreLeaf a) = m (QueryClientResponse a) - genClientQ _ _ (q,qs) = - let leaf = symbolVal (Proxy @(Name a)) - q' = q { Req.queryPath = Req.queryPath q <> "/" <> cs leaf } - in leafGenClient (q', qs) +instance (HasCodec a, RunQueryClient m) => HasQueryClient m (StoreLeaf (V.Var a)) where + type ClientQ m (StoreLeaf (V.Var a)) = ClientQ m (QA () :> Leaf a) + genClientQ pm _ = genClientQ pm (Proxy @(QA () :> Leaf a)) + +instance (HasCodec a, RunQueryClient m) => HasQueryClient m (StoreLeaf (A.Array a)) where + type ClientQ m (StoreLeaf (A.Array a)) = ClientQ m (QA Word64 :> Leaf a) + genClientQ pm _ = genClientQ pm (Proxy @(QA Word64 :> Leaf a)) + +instance (QueryData k, HasCodec v, RunQueryClient m) => HasQueryClient m (StoreLeaf (M.Map k v)) where + type ClientQ m (StoreLeaf (M.Map k v)) = ClientQ m (QA k :> Leaf v) + genClientQ pm _ = genClientQ pm (Proxy @(QA k :> Leaf v)) -- | Singleton type representing a client for an empty API. data EmptyQueryClient = EmptyQueryClient deriving (Eq, Show, Bounded, Enum) diff --git a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs index 709c4107..69f09091 100644 --- a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs +++ b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs @@ -2,7 +2,7 @@ module KVStore.Test.KVSpec (spec) where import Control.Concurrent (forkIO) import Control.Concurrent.MVar (MVar, modifyMVar_, - newMVar, readMVar) + newMVar) import Control.Lens (to, (^.)) import Control.Lens.Fold ((^?)) import Control.Monad (void) @@ -16,7 +16,7 @@ import qualified Data.ByteArray.HexString as Hex import Data.ByteString (ByteString) import Data.Default.Class (def) import Data.Either (isRight) -import Data.HashSet (fromList) +--import Data.HashSet (difference, fromList) import Data.String.Conversions (cs) import Data.Text (Text) import GHC.Generics (Generic) @@ -96,10 +96,12 @@ spec = do txResultHash `shouldBe` hash txResultWPHash `shouldBe` hash - it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do - expected <- readMVar mvex - res <- readMVar mvres - fromList res `shouldBe` fromList expected + + it "Can monitor all events" $ const pending + --it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do + -- expected <- readMVar mvex + -- res <- readMVar mvres + -- (fromList expected `difference` fromList res) `shouldBe` fromList [] encodeTx :: String -> Base64String encodeTx = Base64.fromBytes . cs @String @ByteString diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index 686a021a..b1548aab 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -75,6 +75,5 @@ tests: - lens-aeson - lens - text - - unordered-containers - string-conversions - hs-abci-sdk From e46dd33c8ca8b28b895e2fcb9996292f133caf4a Mon Sep 17 00:00:00 2001 From: Alexandre Esteves <2335822+alexfmpe@users.noreply.github.com> Date: Mon, 9 Mar 2020 15:46:38 +0000 Subject: [PATCH 68/70] Fix typos (#226) --- .../nameservice/tutorial/Foundations/04-Storage.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md b/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md index c50562fb..26f1321c 100644 --- a/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md +++ b/hs-abci-docs/nameservice/tutorial/Foundations/04-Storage.md @@ -4,15 +4,15 @@ title: Foundations - Storage # Database -ABCI application depend on some kind of merkelized storage in order the achieve consensus on valid a application state. The SDK has two database options to interpret `StoreEffs`, an in-memory [avl-auth](https://github.com/oscoin/avl-auth) option as well as a persisted [iavl](https://github.com/tendermint/iavl) option. +ABCI applications depend on some kind of merkelized storage in order to achieve consensus on a valid application state. The SDK has two database options to interpret `StoreEffs`, an in-memory [avl-auth](https://github.com/oscoin/avl-auth) option as well as a persisted [iavl](https://github.com/tendermint/iavl) option. # Stores The most convenient way to partition a key-value store is by heavy use of prefixes -- for example, if you want to separate each module's keyspace, you can use prefix all of the keys that it manages by the module's unique name. If you want to partition storage within a module, say for a list or mapping, you can again use prefixes to create a unique keyspace. -The definition of a `Store` is a unique keyspace. Implementation wise, it is effectively a list of prefixes to concatonate when creating keys. There are currently 6 ways of creating Stores: +The definition of a `Store` is a unique keyspace. Implementation wise, it is effectively a list of prefixes to concatenate when creating keys. There are currently 6 ways of creating Stores: -1. From a `KeyRoot`, which is basically defines a top level Store. +1. From a `KeyRoot`, which basically defines a top level Store. 2. Using the `nestStore` function to mount one Store in another. 3. By creating a `Var`, which creates a keyspace with exactly one key. 4. By creating an `Array`, which creates a keyspace whose keys are type `Word64`. @@ -74,4 +74,4 @@ This both creates the mapping and mounts it inside of our module level store. Th encodeUtf8 "auth" <> encodeUtf8 "accounts" <> bytesFromHex "0xdeafbeef" ~~~ -While writing apps inside the SDK you do not need to worry about the explicit prefixing since everything is taken care of for you. However, if you are querying for state via an ABCI `query` message, the `key` field that is returned in the response will contain this full path. In the above example, if you wanted to recover the address from the key, you would need to know the prefixes that were applied. \ No newline at end of file +While writing apps inside the SDK you do not need to worry about the explicit prefixing since everything is taken care of for you. However, if you are querying for state via an ABCI `query` message, the `key` field that is returned in the response will contain this full path. In the above example, if you wanted to recover the address from the key, you would need to know the prefixes that were applied. From a515e64b89e07ba5d9b7dbbdf7b386bd7b7f717e Mon Sep 17 00:00:00 2001 From: Alexandre Esteves <2335822+alexfmpe@users.noreply.github.com> Date: Tue, 10 Mar 2020 17:41:09 +0000 Subject: [PATCH 69/70] Hide literate haskell main from github markdown (#227) --- hs-abci-docs/nameservice/tutorial/README.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hs-abci-docs/nameservice/tutorial/README.md b/hs-abci-docs/nameservice/tutorial/README.md index db450596..08030f1e 100644 --- a/hs-abci-docs/nameservice/tutorial/README.md +++ b/hs-abci-docs/nameservice/tutorial/README.md @@ -7,7 +7,7 @@ title: Tutorial We're going to build an example application that mirrors the `golang` [cosmos-sdk](https://github.com/cosmos/cosmos-sdk) example application called [Nameservice](https://github.com/cosmos/sdk-tutorials/tree/master/nameservice). There is also a tutorial for that application which you can find [here](https://tutorials.cosmos.network/nameservice/tutorial/00-intro.html) for comparison. ## Application Specification -The Nameservice application is a simple marketplace for a name resolution service. Let us say that a `Name` resolves to type called `Whois` where +The Nameservice application is a simple marketplace for a name resolution service. Let us say that a `Name` resolves to type called `Whois` where ~~~ haskell ignore data Whois = Whois @@ -41,17 +41,18 @@ If you would like to start with something simpler, you can view the tutorial for This tutorial should teach you: 1. How to construct application specific modules. -2. How to enable a module to receive application specific transactions. +2. How to enable a module to receive application specific transactions. 3. How to compose modules and wire up an application. 4. How to add event logging, console logging, and other effects to module. 4. How to use the type system to control the capabilities of a module. -The SDK makes heavy use of the effects system brought to haskell by the [polysemy](https://hackage.haskell.org/package/polysemy-1.2.3.0) library. We're not going to explain how this library works here, there are several existing tutorials that do this already. Suffice it to say that polysemy encourages the application developer to develop modules that have well defined roles and scopes, and to prohibit certain modules from interfering with the roles and scopes of other modules unless explicitly allowed by the type system. +The SDK makes heavy use of the effects system brought to haskell by the [polysemy](https://hackage.haskell.org/package/polysemy-1.2.3.0) library. We're not going to explain how this library works here, there are several existing tutorials that do this already. Suffice it to say that polysemy encourages the application developer to develop modules that have well defined roles and scopes, and to prohibit certain modules from interfering with the roles and scopes of other modules unless explicitly allowed by the type system. It is also allows the application developer to construct modules without much regard for how they will plug into the SDK, leaving that job to the SDK itself. -(This tutorial is integrated as a literate haskell file, meaning that the following is necessary to ensure it compiles.) + From 69583e364bbb7aebf08b9e45601d34964dcfdc0c Mon Sep 17 00:00:00 2001 From: Martin Allen <31280145+blinky3713@users.noreply.github.com> Date: Wed, 11 Mar 2020 14:30:33 -0700 Subject: [PATCH 70/70] Ws event conduit (#228) * wip * appears to be working * tests pass * remove bad toJSON * update nameservice tests * use bracketP * hlint --- Makefile | 2 +- hs-abci-docs/nameservice/package.yaml | 6 +- .../test/Nameservice/Test/E2ESpec.hs | 129 +++++++-------- .../kv-test/KVStore/Test/KVSpec.hs | 151 +++++++++--------- hs-tendermint-client/package.yaml | 13 +- .../src/Network/Tendermint/Client.hs | 61 ++++++- .../Tendermint/Client/Internal/RPCClient.hs | 26 ++- 7 files changed, 216 insertions(+), 172 deletions(-) diff --git a/Makefile b/Makefile index 876ac36e..defa9c1d 100644 --- a/Makefile +++ b/Makefile @@ -93,7 +93,7 @@ deploy-nameservice-test: install ## run the nameservice docker network for testi # Tests ##################### -test-kv-store: install ## Run the test suite for the client interface +test-kv-store: ## Run the test suite for the client interface stack test hs-tendermint-client test-simple-storage: install ## Run the test suite for the simple-storage example application diff --git a/hs-abci-docs/nameservice/package.yaml b/hs-abci-docs/nameservice/package.yaml index 6652d8de..2f4b224d 100644 --- a/hs-abci-docs/nameservice/package.yaml +++ b/hs-abci-docs/nameservice/package.yaml @@ -183,17 +183,17 @@ tests: - -with-rtsopts=-N dependencies: - base >= 4.7 && < 5 + - conduit - data-default-class - hs-abci-sdk - hs-abci-test-utils + - hs-abci-types - hs-tendermint-client - hspec - aeson - mtl - nameservice + - resourcet - servant - text - unordered-containers - - lens-aeson - - lens - - string-conversions diff --git a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs index d1dc1f36..95251666 100644 --- a/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs +++ b/hs-abci-docs/nameservice/test/Nameservice/Test/E2ESpec.hs @@ -1,50 +1,51 @@ module Nameservice.Test.E2ESpec (spec) where -import Control.Concurrent (forkIO) -import Control.Concurrent.MVar (MVar, modifyMVar_, newMVar, - readMVar) -import Control.Lens ((^?)) -import Control.Monad (forM_, void) -import Control.Monad.Reader (ReaderT, runReaderT) -import qualified Data.Aeson as A -import Data.Aeson.Lens (key) -import Data.Default.Class (def) -import Data.HashSet (fromList) +import Control.Concurrent (forkIO) +import Control.Concurrent.MVar (MVar, modifyMVar_, + newMVar, readMVar) +import Control.Monad (forM_, void) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Reader (ReaderT, runReaderT) +import Control.Monad.Trans.Resource (runResourceT) +import qualified Data.Aeson as A +import Data.Conduit (awaitForever, + runConduit, (.|)) +import Data.Default.Class (def) +import Data.HashSet (fromList) import Data.Proxy -import Data.String.Conversions (cs) -import Data.Text (Text) +import Data.Text (Text) import Nameservice.Application -import qualified Nameservice.Modules.Nameservice as N -import Nameservice.Test.EventOrphans () -import qualified Network.Tendermint.Client as RPC -import Servant.API ((:<|>) (..)) -import qualified Tendermint.SDK.Application.Module as M -import Tendermint.SDK.BaseApp.Errors (AppError (..)) -import Tendermint.SDK.BaseApp.Events (Event (..), ToEvent (..)) -import Tendermint.SDK.BaseApp.Query (QueryArgs (..), - QueryResult (..), - defaultQueryArgs) -import qualified Tendermint.SDK.Modules.Auth as Auth -import qualified Tendermint.SDK.Modules.Bank as B -import Tendermint.SDK.Types.Address (Address) -import Tendermint.Utils.Client (ClientConfig (..), - EmptyTxClient (..), - HasQueryClient (..), - HasTxClient (..), - QueryClientResponse (..), - Signer (..), - TxClientResponse (..), - TxOpts (..), - defaultClientTxOpts) -import Tendermint.Utils.ClientUtils (assertQuery, assertTx, - deliverTxEvents, - ensureQueryResponseCode, - ensureResponseCodes, - rpcConfig) -import Tendermint.Utils.Events (FromEvent (..)) -import Tendermint.Utils.User (makeSignerFromUser, - makeUser) - +import qualified Nameservice.Modules.Nameservice as N +import Nameservice.Test.EventOrphans () +import qualified Network.ABCI.Types.Messages.FieldTypes as FT +import qualified Network.Tendermint.Client as RPC +import Servant.API ((:<|>) (..)) +import qualified Tendermint.SDK.Application.Module as M +import Tendermint.SDK.BaseApp.Errors (AppError (..)) +import Tendermint.SDK.BaseApp.Events (ToEvent (..)) +import Tendermint.SDK.BaseApp.Query (QueryArgs (..), + QueryResult (..), + defaultQueryArgs) +import qualified Tendermint.SDK.Modules.Auth as Auth +import qualified Tendermint.SDK.Modules.Bank as B +import Tendermint.SDK.Types.Address (Address) +import Tendermint.Utils.Client (ClientConfig (..), + EmptyTxClient (..), + HasQueryClient (..), + HasTxClient (..), + QueryClientResponse (..), + Signer (..), + TxClientResponse (..), + TxOpts (..), + defaultClientTxOpts) +import Tendermint.Utils.ClientUtils (assertQuery, assertTx, + deliverTxEvents, + ensureQueryResponseCode, + ensureResponseCodes, + rpcConfig) +import Tendermint.Utils.Events (FromEvent (..)) +import Tendermint.Utils.User (makeSignerFromUser, + makeUser) import Test.Hspec @@ -358,7 +359,7 @@ spec = do it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do expected <- readMVar mvex res <- readMVar mvres - fromList expected `shouldBe` fromList res + fromList (map A.toJSON expected) `shouldBe` fromList (map A.toJSON res) faucetUser @@ -486,38 +487,30 @@ faucet txApiDP = Proxy -- Test Init -data TestEnv = TestEnv (MVar [A.Value]) (MVar [A.Value]) [Text] +data TestEnv = TestEnv (MVar [FT.Event]) (MVar [FT.Event]) (MVar [Text]) testInit :: Auth.Amount -> IO TestEnv testInit faucetAmount = do forM_ [user1, user2] $ faucetUser faucetAmount - expectedEventsMVar <- newMVar [] - resultEventsMVar <- newMVar [] - pure $ TestEnv expectedEventsMVar resultEventsMVar [] + TestEnv <$> newMVar [] <*> newMVar [] <*> newMVar [] + addEventToCheck :: ToEvent a => TestEnv -> a -> IO () -addEventToCheck (TestEnv mvexpected mvres ses) ev = do - modifyMVar_ mvexpected $ \es -> pure $ es <> [A.toJSON . toEvent $ ev] - let evType = eventType (toEvent ev) +addEventToCheck (TestEnv mvexpected mvseen mveventTypes) ev = do + let appEv = toEvent ev + modifyMVar_ mvexpected $ pure . (appEv :) + ses <- readMVar mveventTypes + let evType = FT.eventType appEv if evType`elem` ses then pure () - else startNewListener evType + else do + _ <- startNewListener evType + modifyMVar_ mveventTypes $ pure . (evType :) where startNewListener evType = let subReq = RPC.RequestSubscribe ("tm.event = 'Tx' AND " <> evType <> " EXISTS") - forkTendermintM = void . forkIO . void . RPC.runTendermintM rpcConfig - in forkTendermintM $ RPC.subscribe subReq (handler evType) - handler evType res = case res ^? txEvents of - Nothing -> pure () - Just v -> case A.fromJSON v of - A.Error _ -> error ("Failed to parse\n" <> cs (A.encode v) ) - A.Success evs -> - let filterFn v' = evType == eventType v' - filteredEvs = filter filterFn evs - in modifyMVar_ mvres $ \es -> pure $ es <> map A.toJSON filteredEvs - txEvents = key "result" - . key "data" - . key "value" - . key "TxResult" - . key "result" - . key "events" + eventStorer = awaitForever $ \as -> + liftIO $ modifyMVar_ mvseen $ \es -> pure $ + RPC.txEventEvents as <> es + forkTendermintM = forkIO . RPC.runTendermintM rpcConfig . runResourceT . runConduit + in forkTendermintM $ RPC.subscribe subReq .| eventStorer diff --git a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs index 69f09091..0009cdf6 100644 --- a/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs +++ b/hs-tendermint-client/kv-test/KVStore/Test/KVSpec.hs @@ -1,28 +1,30 @@ module KVStore.Test.KVSpec (spec) where -import Control.Concurrent (forkIO) -import Control.Concurrent.MVar (MVar, modifyMVar_, - newMVar) -import Control.Lens (to, (^.)) -import Control.Lens.Fold ((^?)) -import Control.Monad (void) -import Control.Monad.Catch (try) -import qualified Data.Aeson as A -import Data.Aeson.Encode.Pretty (encodePretty) -import qualified Data.Aeson.Lens as A -import Data.ByteArray.Base64String (Base64String) -import qualified Data.ByteArray.Base64String as Base64 -import qualified Data.ByteArray.HexString as Hex -import Data.ByteString (ByteString) -import Data.Default.Class (def) -import Data.Either (isRight) ---import Data.HashSet (difference, fromList) -import Data.String.Conversions (cs) -import Data.Text (Text) -import GHC.Generics (Generic) -import qualified Network.ABCI.Types.Messages.Response as Response -import qualified Network.Tendermint.Client as RPC -import Tendermint.SDK.BaseApp.Events (Event (..), ToEvent (..)) +import Control.Concurrent (forkIO) +import Control.Concurrent.MVar (MVar, modifyMVar_, + newMVar, readMVar) +import Control.Lens ((^.)) +import Control.Monad (replicateM) +import Control.Monad.Catch (try) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Trans.Resource (runResourceT) +import qualified Data.Aeson as A +import Data.Aeson.Encode.Pretty (encodePretty) +import Data.ByteArray.Base64String (Base64String) +import qualified Data.ByteArray.Base64String as Base64 +import qualified Data.ByteArray.HexString as Hex +import Data.ByteString (ByteString) +import Data.Conduit (awaitForever, + runConduit, (.|)) +import Data.Default.Class (def) +import Data.Either (isRight) +import Data.HashSet (fromList) +import Data.String.Conversions (cs) +import Data.Text (Text) +import qualified Network.ABCI.Types.Messages.FieldTypes as FieldTypes +import qualified Network.ABCI.Types.Messages.Response as Response +import qualified Network.Tendermint.Client as RPC +import System.Random (randomIO) import Test.Hspec @@ -45,15 +47,17 @@ spec = do result `shouldSatisfy` isRight it "Can submit a async tx and the response code is 0 (success)" $ \tenv -> do - let asyncTxReq = RPC.RequestBroadcastTxAsync { RPC.requestBroadcastTxAsyncTx = encodeTx "abcd" } - addEventToCheck tenv $ mkAppEvent "abcd" + a <- replicateM 10 $ randomIO @Char + addEventToCheck tenv "name" + let asyncTxReq = RPC.RequestBroadcastTxAsync { RPC.requestBroadcastTxAsyncTx = encodeTx $ "name=" <> a } -- async returns nothing resp <- runRPC $ RPC.broadcastTxAsync asyncTxReq RPC.resultBroadcastTxCode resp `shouldBe` 0 it "Can submit a sync tx and the response code is 0 (success)" $ \tenv -> do - let txReq = RPC.RequestBroadcastTxSync { RPC.requestBroadcastTxSyncTx = encodeTx "efgh" } - addEventToCheck tenv $ mkAppEvent "efgh" + a <- replicateM 10 $ randomIO @Char + addEventToCheck tenv "name" + let txReq = RPC.RequestBroadcastTxSync { RPC.requestBroadcastTxSyncTx = encodeTx $ "name=" <> a } -- sync only returns a CheckTx resp <- runRPC $ RPC.broadcastTxSync txReq RPC.resultBroadcastTxCode resp `shouldBe` 0 @@ -61,8 +65,9 @@ spec = do it "Can submit a commit tx, make sure the response code is 0 (success), and get the result(s)" $ \tenv -> do -- /broadcast_tx_commit -- set name key - let broadcastTxReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeTx "name=satoshi" } - addEventToCheck tenv $ mkAppEvent "name" + addEventToCheck tenv "name" + a <- replicateM 10 $ randomIO @Char + let broadcastTxReq = RPC.RequestBroadcastTxCommit { RPC.requestBroadcastTxCommitTx = encodeTx $ "name=" <> a } broadcastResp <- runRPC $ RPC.broadcastTxCommit broadcastTxReq let deliverResp = RPC.resultBroadcastTxCommitDeliverTx broadcastResp deliverRespCode = deliverResp ^. Response._deliverTxCode @@ -78,10 +83,10 @@ spec = do RPC.abciQuery queryReq queryRespWProof <- fmap RPC.resultABCIQueryResponse . runRPC $ RPC.abciQuery queryReqWProof - let foundName = queryResp ^. Response._queryValue . to decodeName - foundNameWProof = queryRespWProof ^. Response._queryValue . to decodeName - foundName `shouldBe` "satoshi" - foundNameWProof `shouldBe` "satoshi" + let foundName = queryResp ^. Response._queryValue + foundNameWProof = queryRespWProof ^. Response._queryValue + decodeQuery foundName `shouldBe` a + decodeQuery foundNameWProof `shouldBe` a -- check with /tx endpoint (w+w/o proof) let hash = RPC.resultBroadcastTxCommitHash $ broadcastResp -- convert hex to base64 @@ -97,17 +102,11 @@ spec = do txResultWPHash `shouldBe` hash - it "Can monitor all events" $ const pending - --it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do - -- expected <- readMVar mvex - -- res <- readMVar mvres - -- (fromList expected `difference` fromList res) `shouldBe` fromList [] + it "Can monitor all events" $ \(TestEnv mvex mvres _) -> do + expected <- readMVar mvex + res <- readMVar mvres + fromList (map A.toJSON expected) `shouldBe` fromList (map A.toJSON res) -encodeTx :: String -> Base64String -encodeTx = Base64.fromBytes . cs @String @ByteString - -decodeName :: Base64String -> String -decodeName = cs @ByteString @String . Base64.toBytes runRPC :: forall a. RPC.TendermintM a -> IO a runRPC = RPC.runTendermintM rpcConfig @@ -120,49 +119,45 @@ runRPC = RPC.runTendermintM rpcConfig in RPC.Config baseReq (prettyPrint "RPC Request") (prettyPrint "RPC Response") host port tls -- See https://github.com/tendermint/tendermint/blob/master/abci/example/kvstore/kvstore.go#L101 -mkAppEvent :: Text -> App -mkAppEvent k = App "Cosmoshi Netowoko" k - -data App = App - { creator :: Text - , key :: Text - } deriving (Show, Eq, Generic) +mkAppEvent :: String -> FieldTypes.Event +mkAppEvent k = FieldTypes.Event + { eventType = "app" + , eventAttributes = + [ FieldTypes.KVPair (encode "creator") (encode "Cosmoshi Netowoko") + , FieldTypes.KVPair (encode "key") (encode k) + ] + } + where + encode = Base64.fromBytes . cs @String @ByteString -instance ToEvent App +encodeTx :: String -> Base64String +encodeTx = Base64.fromBytes . cs @_ @ByteString +decodeQuery :: Base64String -> String +decodeQuery = cs @ByteString . Base64.toBytes -- Test Init -data TestEnv = TestEnv (MVar [A.Value]) (MVar [A.Value]) [Text] +data TestEnv = TestEnv (MVar [FieldTypes.Event]) (MVar [FieldTypes.Event]) (MVar [Text]) testInit :: IO TestEnv -testInit = do - expectedEventsMVar <- newMVar [] - resultEventsMVar <- newMVar [] - pure $ TestEnv expectedEventsMVar resultEventsMVar [] - -addEventToCheck :: ToEvent a => TestEnv -> a -> IO () -addEventToCheck (TestEnv mvexpected mvres ses) ev = do - modifyMVar_ mvexpected $ \es -> pure $ es <> [A.toJSON . toEvent $ ev] - let evType = eventType (toEvent ev) +testInit = TestEnv <$> newMVar [] <*> newMVar [] <*> newMVar [] + +addEventToCheck :: TestEnv -> String -> IO () +addEventToCheck (TestEnv mvexpected mvseen mveventTypes) ev = do + let appEv = mkAppEvent ev + modifyMVar_ mvexpected $ pure . (appEv :) + ses <- readMVar mveventTypes + let evType = FieldTypes.eventType appEv if evType`elem` ses then pure () - else startNewListener evType + else do + _ <- startNewListener evType + modifyMVar_ mveventTypes $ pure . (evType :) where startNewListener evType = let subReq = RPC.RequestSubscribe ("tm.event = 'Tx' AND " <> evType <> " EXISTS") - forkTendermintM = void . forkIO . void . runRPC - in forkTendermintM $ RPC.subscribe subReq (handler evType) - handler evType res = case res ^? txEvents of - Nothing -> pure () - Just v -> case A.fromJSON v of - A.Error _ -> error ("Failed to parse\n" <> cs (A.encode v) ) - A.Success evs -> - let filterFn v' = evType == eventType v' - filteredEvs = filter filterFn evs - in modifyMVar_ mvres $ \es -> pure $ es <> map A.toJSON filteredEvs - txEvents = A.key "result" - . A.key "data" - . A.key "value" - . A.key "TxResult" - . A.key "result" - . A.key "events" + eventStorer = awaitForever $ \as -> + liftIO $ modifyMVar_ mvseen $ \es -> pure $ + RPC.txEventEvents as <> es + forkTendermintM = forkIO . runRPC . runResourceT . runConduit + in forkTendermintM $ RPC.subscribe subReq .| eventStorer diff --git a/hs-tendermint-client/package.yaml b/hs-tendermint-client/package.yaml index b1548aab..c6a25eb5 100644 --- a/hs-tendermint-client/package.yaml +++ b/hs-tendermint-client/package.yaml @@ -32,15 +32,22 @@ dependencies: - exceptions - data-default-class + library: source-dirs: src dependencies: - aeson-casing + - conduit - hs-abci-types - http-client - http-conduit + - lens + - lens-aeson - mtl - random + - resourcet + - stm + - stm-conduit - text - websockets - wuss @@ -69,11 +76,13 @@ tests: - -with-rtsopts=-N dependencies: - aeson-pretty + - conduit - hs-abci-types - hs-tendermint-client - hspec - - lens-aeson - lens - text + - random + - resourcet - string-conversions - - hs-abci-sdk + - unordered-containers diff --git a/hs-tendermint-client/src/Network/Tendermint/Client.hs b/hs-tendermint-client/src/Network/Tendermint/Client.hs index 8df188bd..431210b0 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client.hs @@ -8,8 +8,17 @@ module Network.Tendermint.Client ) where -import Control.Monad.Reader (ReaderT, +import Control.Concurrent (forkIO, + killThread) +import Control.Concurrent.STM.TQueue (newTQueueIO, + writeTQueue) +import Control.Lens ((^?)) +import Control.Monad.Catch (throwM) +import Control.Monad.IO.Class (liftIO) +import Control.Monad.Reader (ReaderT, ask, runReaderT) +import Control.Monad.STM (atomically) +import Control.Monad.Trans.Resource (ResourceT) import Data.Aeson (FromJSON (..), ToJSON (..), genericParseJSON, @@ -17,9 +26,13 @@ import Data.Aeson (FromJSON (..), import qualified Data.Aeson as Aeson import Data.Aeson.Casing (aesonDrop, snakeCase) +import qualified Data.Aeson.Lens as AL import qualified Data.ByteArray.Base64String as Base64 import Data.ByteArray.HexString (HexString) import Data.ByteString (ByteString) +import Data.Conduit (ConduitT, + bracketP) +import Data.Conduit.TQueue (sourceTQueue) import Data.Default.Class (Default (..)) import Data.Int (Int64) import Data.Text (Text) @@ -242,13 +255,49 @@ instance FromJSON ResultABCIInfo where -- Subscribe -------------------------------------------------------------------------------- +data TxResultEvent a = TxEvent + { txEventBlockHeight :: FieldTypes.WrappedVal Int64 + , txEventTxIndex :: Int64 + , txEventEvents :: a + } deriving (Generic) + +instance FromJSON (TxResultEvent [FieldTypes.Event]) where + parseJSON val = do + let mtxRes = val ^? AL.key "result" + . AL.key "data" + . AL.key "value" + . AL.key "TxResult" + . AL._Object + txRes <- maybe (fail "key not found: result.data.value.TxResult") pure mtxRes + height <- txRes Aeson..: "height" + idx <- txRes Aeson..: "index" + res' <- txRes Aeson..: "result" + es <- res' Aeson..: "events" + pure TxEvent + { txEventBlockHeight = height + , txEventTxIndex = idx + , txEventEvents = es + } + -- | invokes [/subscribe](https://tendermint.com/rpc/#subscribe) rpc call -- https://github.com/tendermint/tendermint/blob/master/rpc/core/events.go#L17 -subscribe :: RequestSubscribe -> (Aeson.Value -> IO ()) -> TendermintM ResultSubscribe -subscribe req handler = do - RPC.remoteWS (RPC.MethodName "subscribe") req handler - pure ResultSubscribe - +subscribe + :: RequestSubscribe + -> ConduitT () (TxResultEvent [FieldTypes.Event]) (ResourceT TendermintM) () +subscribe req = do + queue <- liftIO newTQueueIO + let handler (val :: Aeson.Value) = + let isEmptyResult = val ^? AL.key "result" == Just (Aeson.Object mempty) + in if isEmptyResult + then pure () + else case Aeson.eitherDecode . Aeson.encode $ val of + Left err -> throwM (RPC.ParsingException err) + Right a -> atomically $ writeTQueue queue a + cfg <- ask + bracketP + (forkIO $ RPC.remoteWS cfg (RPC.MethodName "subscribe") req handler) + killThread + (const $ sourceTQueue queue) newtype RequestSubscribe = RequestSubscribe { requestSubscribeQuery :: Text diff --git a/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs b/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs index 19a91b77..d2d2fec9 100644 --- a/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs +++ b/hs-tendermint-client/src/Network/Tendermint/Client/Internal/RPCClient.hs @@ -2,7 +2,7 @@ module Network.Tendermint.Client.Internal.RPCClient where import Control.Applicative ((<|>)) import Control.Exception (Exception) -import Control.Monad (forever, void) +import Control.Monad (forever) import Control.Monad.Catch (throwM) import Control.Monad.IO.Class (MonadIO, liftIO) import Control.Monad.Reader (MonadReader, ask) @@ -109,34 +109,32 @@ data Config = Config } remoteWS :: - ( MonadIO m - , MonadReader Config m - , FromJSON output + ( FromJSON output , ToJSON input ) - => MethodName + => Config + -> MethodName -> input -> (output -> IO ()) - -> m () -{-# INLINE remoteWS #-} -remoteWS method input handler = do - Config {..} <- ask + -> IO () +remoteWS Config{..} method input handler = do let host = BS.unpack cHost port = fromInteger $ toInteger cPort tlsPort = fromInteger $ toInteger port path = "/websocket" if tlsEnabled - then void . liftIO $ runSecureClient host tlsPort path ws - else void . liftIO $ WS.runClient host port path ws + then runSecureClient host tlsPort path ws + else WS.runClient host port path ws where ws c = do - rid <- abs <$> liftIO randomIO + rid <- abs <$> randomIO let rpcParams = Aeson.toJSON input rpcRequest = Request method rid rpcParams msg = WS.Binary $ Aeson.encode rpcRequest WS.sendDataMessage c msg - void . forever $ do - message <- WS.receiveData c >>= decodeRPCResponse + forever $ do + bs <- WS.receiveData c + message <- decodeRPCResponse bs handler message decodeRPCResponse bs = case Aeson.eitherDecodeStrict bs of Left err -> throwM $ ParsingException err
  • YY6NVqWuzePiKdb2>&CKavXI~MM%ljZS2zcql*CWX=N9*d|#Ij%hng# zE7+lJ?|G)jTme0cIskRNI|-h?Kr7U1XN@>A&ea4-X?>^I#3cSnJc?O+QHy>J$L zW1XCk{S@S7)M6SzRfMx-gI-}@W`O_t4>XZK$}p^3;7s)zmqZj&q%Rm zjTy?M>lfdL@y;Owl%Sm-%UAjP|Fj^$%aS7)TaS&2?YB(-pFccfeTln&xH8o9>X-%o z!0}}W6X?3qt=;#!d^H-4ua-Z&%BabZAYnkfLySFC{)8__kC;hKb<9x(jBUbyzVZo1 zh6Qtg=5bQ3qfX@p!(@=M#(;ND9R)Ll2;TW;|9NEA)UeW@u3s~8${Jx98_YN&eGo}3 zJD#tjLNp@4>pJ(#^OtEjcbikVcjwOl{~)_jYeW_OL1uHgR}&L6)%@M%;O-}s#=}H3 zVZLCxQztv04JB;r*w%zgGwlB`&Gm zb(WJ|Q2N&4h+wHm)5o^&Y#HzPKHd@rTS|uz@!u{~ZQ^J7WT~I53y$w(IkOX0#A#mL%nu zH%Qz@a&*|QKQ;ML|DVR7iWEl8uL{>;d3{z+V;xL(d8Q?6cr-&2LX3c){UKOg=~X#PEG8-qfESVw%VL`@V>I>FQ$Hd`6C7g z^0_nnQoQw4E8y^T-h}4}GoNP0{S1EqTani#sl%1MZhIO{)|+xB)uHT+0LuO~@xTgz zMWXWQ6p#u&1AJ9;8h4kZIn>=}z)_#;O)t0obS?#29k=GD%~@#dLN`MNNUB{I<)xCs z_E7?U?0Ex`SwUcVIe{=YIodX*Bpu4?7$8-$<6+hQpkdSp&FGi^mj&=GDoY{4K8rPN zK1+eXZlXA29$FS=$5q}I!4jCqeLp25W|DZTiK~X)!vTm`ui}>YbPlL<#|*UT9tNcG zi?lzqRD5GQ-_)pc3dAO9Oh4Ir{gm7VgjBm&?$19^{NLyNzsLXIXVy)^7Rl!%t85A#%9n zpZPWwM*ejxfBh%h2K0eeP#)v6&Sc|_)P#n+-sd5hsd35t^C8Ll&qvM-D=R&YwzxE{ zoRcjT69Kmx<#bmZ@XZuXeCcULnB8fMuEFKmG`^;*%+-F#5y6mEsnbY~Ru(DZ*pD7@ zIo}WX4%`t^r&AT{A1fRNzF;6?rEWE^Iv-$a_R3YC@6I-1p?H1UfoAe|b-14PUY#ra z#xEF+&e@-y52}ZG3p>X5t9=TW*mkvD%s=XkT|jOedKUVho2*Bo$swb#ojlI&-bC(C z5jJ&`r6_(s(z%#>m)Hx(0f05&zqVJ=#x~(H*j>=K*j}6*4QIG|J^O3Jc+m1ahk6d7 zAs)GjJqt4(*2wAUrOy);|Jx#%_O;;pURAQBFLl(hM8h(z3nGH-e2oSJ;Id!kJ=6Bt z_&o7?_Pd=CV_3i~eEqP8R!?g2Kcmah+*F-={n`BrAkorx-nnMv^sU#n?cZqndiBv9 zHWd#;q{3z^Lone4;Q`?pi?oN9{>z#`1)U5uXNP0*6CW&*N|C$hJdJL=ahO{K)5{YA z-=H@qn^fyfWZ1XRoI04=-}fO`kO-DNcjkyOip@CY4iY@A3R%Vg=KQR9^EctRV^xS zHX)`%+8f}96)SjQ4BO#+-d>?@fObHv>znyvHZlkG=}Mdt^^GyxRSyEFU~z4%X$lOB za+LVmg}IUJDFNxy&mP{)?VwtV>D&D3=T)XdEvkE!M3}ZOv-c~xi%)2p1_&3U052$e z^VL5a$E&$VfD>#N?b?decm|%n=3Q*&zw1f5_0SQLA?bz0I`Py7_}R{Xex&o##tf~9 zL=U+)*ETS3&nqYf9xYHC106;g2pu2Nxg|Y4Q)#EeH`^D9oPEksXDRQWO+RDRm_I(> z(UxLcU`N@x69Xcol(Xr-Pvv8UgK!j>abSQ2wo;;;dXpjEde=TTHGAT$&trYtpt?km5#^0+0tEpLh7t{{--aboVu#07Q&%{iTVo zvhBu9P037$K8Qc|{ODKs4(P6ZC-ve^EcHKj0X0?{TAlL07ybXe-IFMIOe#}G$_pm^ zn~xQOg$wk1&{oPor4HW~`}|wB?KEjgtrBR^iBDdN>XuT*aIx#+5W_nVXcQCTGg6xp z{*RiZ^8kA32wjw1#M~~(O%OTx)vKzkAQ73RNhzQ0VU=uOxRLY#(574!-zf_GK!Di% ziRrIVn?^B}nK0=N_fCO350b)u6Rcx@)0l|`A;#?uPLw>-`H>RH!I`6}c(i}CAS@2* z_OXW5sxOYfqpUAAF{&gLJ9!p;IJ{&>8R#A+BpY~OiGcvzYdoUw;{_G#jQVYIerm46 z`}cLqEb&e03?|w6FG0yq<~$^CgjelItF58OranJ^ zOq1HM^jm@MR)XzQWByTcSyrYwZ$RP)P2dN zxQiC1XxLUb0Ym6HamUphR59FP>P>OrgErpKkO~sa@Y(>%&PIHrZSzM+nJQ&x`ggRo z;@MeYi%QN;*v9;FSHP$Jj8|JxgULL59s#B_zcj;gADLEv*<&i4{Si%L=urIq02M0n z6cf~3(fHafnc$AAX{9+TagTQ~4!x?DoN*~Vd#`Nw03v(3-Mhc9a1eW%U$-66ftGb)sy~Y zC8hbbEwP)!Hb3-2b#q+$+%0SuI@d!`OHp{49myyS91Px)q9!)#LGRXaXWkuriDy&D zLpshksd=5iH&*^oicd(HZ0)SeJZSPiPfA&@R$=LqM-nc z(&3*G%O6dS7-RnSdym2@Tp$v}3m}oa)cvdnzse2NW}joGW>P;~{{uT+XObH~`)8v1 ze=l$}i0@Tay>An?y3ph%mz6KAjIu}R3L^92X~CgZR-1;z6RsHS)5?JD$ub-WqI2Oc z(LHdhNZ)XK42-tTStIr7?|vvDVuEAV8bAP8UmR~ZM?_2r)_ybtKpUhL2$43;qLa6z!SdtbWK6-iE@MAm4P-3}l33g^2W?#d;nH=DE$zL}`h=7M z8huZOS1mWq$}F=Wk7YKRP4~;`!fARMeO(fVtQyRoUD$7>n~FZdx4A)M3|ow~D=n^m zD8*!bQ{tN z?*WYaK+%6GKm$MuP%4lDv^M8+8gRl6D#Q4AXOd#WV)Wrp@?ftX9=>`JMs{f`o$4`3 z?#XX_lM9F3w~C+N%`CzHse1roePY)1h$(Z5eg94bSoo|C&!TBFg4<;!k|4wKb3vgt zWK{|?7*D8i)O?|ENOd;e>tK@q5tUrFzjAue(eB$qu!_#8e-bA;zYyA~2xsNh?dE6* z>7t3emKT^Q>x%*8rLTA!qiXjE~k-G0K9>iHEYkXt?2Uw~Lm4FY%9Z5GZilSg=iB-(JEDH|tZo=oF^o5q2>hxQAFCgNznQxFp8l)k z+00#^<*{V-8u=F$ypRBXxKeQ6Q5D}yoxrp4Yf{&bP&^P;}Jk&6oQV{!J%z*ZuI z3rs=PCRan-@yMLB{B=K8yd~NQDNhm00)2kEGgV|9@(i&SlP_Vy{nV(^k$=A% zQD_uvG~hn>m%M$|m~$VB)%w&bjx%I7p~3VzjH14yy7g#E4MIIScZOIbJa_GL8Mr2# z=!8S|_Gf|r5Azc;_nVf96f8{n5y5~NoEwMA$U-n<)}V8Q;PD~`1psCe^+6+ z4$JOz7+<~fCkd-s_Xc&ea$YRc`zc?g0hYVS9JU_*bhRmw?(rv6NBU42eq*Omiz+tK z)O;8%r&^LhSfeJpdlOGdeYg!OcgFc}I{fdpdo;K4)|wl{H>i8&gBZ!gAQqo&zkU-Z z?EZ!|*o6OOn>sQgR8B~;bT9qgRVnd$b)8Cp;Fiu*qeHId!QJUUJ~Jb1!$<4=N#Di^ zcw_>bjl#)%2%_KFx7H>l@fc1YJST|D7C^f4y%buRt(0W??3X^L@p=Ri6nfGJo;v;Z z5@LW>BHvK(uQxNF0azAXoX?AN(H9Y)x^m*N4EwS%uB~UcxZzr;YP{uJ+)xD(^j>@T zZT3wKtN<9wV67~Ndi`@7D(q;_6pZ}y^AIHRgxU-Pv8m6R zqwl|Z1fl~ftm3^hT34W-`lVDzxmV_>`SSFqWkE>}8Bzv;>&ZpNCjG5_KZ_4`yZlet zYeNN&PW4yciPMUvf$Spj(j~o%3r*@?%G4`-8aGzb6=>rZWalZHjiRTlnFIb{os zelmn8z~PIg>_De-2w>luw6@xQ0nEIgH(1blB;KAFpr>kBmKDPP5f9v`GXo&x&%7D}efpa#^0>K=FEdHJ?G8S#b*I_O;;T&ex z&91v#bY^DH(JM0gQ)pO$y9ha;xNBCOF|k;3GYrWZE@<7$3q^W(<3j>jU43`!){oU% z0}d0Lify_fF{ zFfL_74q3wu&W&Ak4-cM}#nXSUXpZ@<{~@^W1@ORDKJb;L3#e(9P-Fmu5luViAdAQN zB=p~Z`x%5MyPefd#93EM0TJfqfXcUV0H9Ca4-_Lc7cs`_CwVcPPBe7aD6mhiW`8U; zgA`2PR2mHmA0)QFV`&PU{{{YdtIsv)*R|U5& zX`r3PLau4~MB2nYF$oP~WwYl7piD>I-P#0|gG3>V)$z{jr)eKbcebatJ_;Vm*Z)jp zhMtPHqG_vrF44k$g%^j%@QwMBJYvR5{gPbA^Sw%f&@bY9USi^5F5JI7`ynhbu&?U^ z$hgdYs(Y8|A{pNLM8dc7*efI2V7$()=tsxF6n%}86>n3=MxK+EB%N$2WbT3fr1AVo zhFiy7!!-d$SiVvS#|`$YkYlCBUXA@&kXBc-%8K1y95#YRzw4_$rJFN8vjxhpsowu; zh?ePb#n|$T^^ey6{@x?q zTz^H(R*+t0yRlNWvmZktdopwc@eG1TXD|F%JKoTdC^64!JKTa?W*nBKz9lG}nBRmb zVCeFsF1|MJBmaq;_{EO6&lEBjQwf%&ue7+k0>l-ZCUl%;BBmR^xeSj(*kR)2Cru0;QoX>q zIg8J@A^Tce+elRbJgS6vt6OgReZCM6!IP&4LfRj?MO4?Y$c7eIz-a*`tPW(TmwxTkgepw)1H=+b43o)J5t2&LYSp$h~%XKQ@T3Y z0KulQ7Ztuo=W+S-zmp>`@?C6$Q%zzb2XwCGXns?^;mf0;F`@BvAOgSe7;FfR48j3SmkF6P^(7D|~* z=CC;5)4dr6M6z2E)5QR50H$%Ws6zkTjYTu*U6@oGRisCH-sRMU*}&M2m;Z%o>v_OI z8(u)uvJpzeRHEc|v4K8%MP0Y#2W@=Od}$Q0#OvLVYYdOQl9o|f#jF*w-j9j|u3QV@ zgrD?<>)DZoFlh{AKlr}%`;FOEn`?zAm(6Od^!2?{F*o)-k-069 zfj9+GBG-w3VkpgOa9<-M#AQLLmp1ena`EmE4g4W3%~m*P4;{)2Wj8@eW`C$YgLt1D zKt#<4zI0G)00}|X_Fs}i;-4o!ar*i9QPU{UL3~V%ph-{`3zOK738&$0B6hx6%;>L- za_Z=%P`8-G0KNS)d|}sJk&4FdnvvZ%H*5X+AqJ#^=BZDuuc*e(b2RS~&B6Z$y4}N` zgA(l9lCx8c9}wJB%MAiQnb5{qLGqCjUoCw#53neBvA(5J|77^@y-=r4R(oDxosmj1 zE#!09ad>j|d|EQqZZvZEXF`ok_wO|W_n46_sXZ?;wB!+H3f_9-lFyxE>L=!)NAF#9UM3YpLN9H{HV>D0OT;mz z;7u?P_^5Ep611e6Ti6_WIlZxW^Y&uxCRI%J*)3(%I?BE#apm;QB|P*(*nVPYM(?VX zB4um|7fA`tei+FMXi#}J_D-5G`X#SsEF#LQCrO=RZ)~%v@ivuW;*3ZB`tC>~o`~vf&T(TFJU0+atE;MY6W1IVW!kx40WMI8xl$zn{ zp5ihgBFYs=f(;Guvj=F!Y^$z8P%N>lZv7eJKV8{kue zKx8*GL&V`__yXyg+j;0mOD_T%T~s0 zfr|YEW_cX}Z%E?VZ7XSg)SK1|nY-98KJL8FU!BA-kKYpR!>{Mw_Z)8WvfWkydfwS| z>cAtdJU`>tHRLmNQ@51a7eNf;OzP3w=L@?E-v@lgB7MR|cg`h)`VPA_4h|k-Oegn* zx4n7JQplJqS^L}=F#55~Qqx-jQRF{bB_Cjd<=*1C%XCd0oqBc`w)1t>KdO19pT!j$ zN!r_24{5;Ci@AQ)nROO@93V`RcG}n2vJ&BKRG`P7%MZVc7EKbI-JyI}Q99jOFArMo`OKR;KapA;@ZyE>4Ki&Nu~`rsxuW!)NtB_Gplf2G zHAKOQ2BT(~?o?u*W41O|o*LKvU}uTZ_it8&JTQPQN;?JrrEZ(T114Z1mlKxzN0!SQe&?LDU^a#(%* zExjdkU7;x@w%C*Hs*jLrcx$$<;xP06d9tccjy%uL2b;aEDdl_{3S?iDyM94<&^Q1( zd_3#TKAxq-swpcMF*AChY8$Y~`L@tN$SSORsRyehOV-OThtM>03u2A+JA zC|n)Lpjmx)n4(v%D508(!3N;K`z1VPv(s-bH?1DyI#`|vpO_uawDP|EzAyEWH`zs# zKlp+ov3G&o`SynQjb7MlUp|b(+?5c60bEMNSEn;n;vQ!|SGrLz-%~esxftk}omg>@ zS!iLm>k8o_(WmbKgzMpg7so-vvnraQOUmMqW)|_n24$2^_COnsi~XWtX!6bS+5chh zt)r^k-gbYbQx+gdhtkp@xo8wYBm|^ex}_G~p>#_sNH@~mAs`^#U6PA#_)YeHPwelz z$2jAhzkg#e93Uubt>;;D&ilTv>-unb$b+0ruZo3aTX|@{|8x!g`ar4_#DJri^wM36 zL%?Gz_B07`P#q4%@1Zgfu)nO(s)H#GXTB{c_ET>w^gDfe)eliAuEEi1zKy7p%Nb2Z z#)0B@dFFPK**9sdIxE?Qi8!AY&gY=nCo0Z)l@)PZgsxQE7bBysnKaOeS)%v^ijdyU z&-}3bW8lMX)e%H#Hkgkl7^1L*jPW>j^lWyeBwtl{pie|TZKzuwludEA+6Rh*n71ey z4NR!|bp0D56CSuanpIZKREeC~kw9Ad{gT*|V{ELTdbP#p`A21R0tL!6heZ_@YT`Y+ zAe_6bujKD?Wb&(V&TP@Bma{sCZqO}l!vt~%(!Iv`6~8Gg*#>;?zBmeUZ4z-g!~x08 zF{|&+LN|q-!M>g=;i99OK6_YzLo=KyP5P{!ho?9zs6j z8qtZ9PrKG+W#q6wwRDzFgXUe|f}So%)vdf}u3_-7rz&;!5fe@#GDha3qdM{_EAAPR z-4UWX&u(ziQ}NUnzN>j}t07c=mv>9=JA}FKv`9d?FWhEGA6xyQA*ikXQSdB6;}M#? z+Y+O_bU;hwz&_5>k)OQ_dfV+^7~wlqwJ<9Pa_6wK&;RxWO6gy(@Ot zZ1YCZFk`elnb*4KEkQ3d0|m#q^>Gcg)}OP2<`IO`siP+-mNOs)*>qt==7Rm}T?R}y z^f~x-lF<+L<)e_g&1oXxXmC&5!BfVPg@?{+U%KsErnQJ<5g>$PK)V%kyZF%J`uw2P zGR-PVlmVfn;qWtl9l`uO+eT00tax8)Bzml~~LVhOfdRUoNBRSkkQYRm2NS1{qBGdS;3Lt?7KfZYDi|asY89bW=W6KqX{Y zA^$Du0(FB#bqg6@Fxf*gH;aj4N2X_wpR;)4ePd_w+~Ym{541Yf10Nb`yaKrF9SQ^P z-4lIMGaASQ`eK$*zH*+!c?gJ3%088I{#*!`LB4l z!s~?d>PWEW8vE0;RNI^N1G1~`Bamd63BqV~$jwG`qgo6}!jLjzT0r)$;}NVH#O%NM zne@WKp*Y+MgtKFeWZ<$U|BbEqyHRt$Pmm-0jG>FNK-c;bXq6cyszuN4b$eYi|57@M zJ8IAH$IJ-aiBB3&UMN=naWs~VV|={?`Yifcj-bOtSK=>L<=o&{Mok@1D(7B(kTg46 z>!)Q&vt&6T;l_~BovGOV))@P;qO9YSAAR9qyik2CtCHNb$Wh8{4Bgu=+8pVbF$`=U z55~@~pXi=x&F=H=%iDQTmIcv3a1k1GCH>!Dv%1l1HM>B&Z#m4Ui*Krt-Cuhl4pLo3E@^4< z$_Dd^Mw45YZHddk-#1zZvrnuuZO9bCM=K8T#janPANLL`iPTjsi&p2|-pO!&9x=ca znIKI-yS{>HZDC)1%z?ayvc1!ErJw!!6ge=?S8h>+;BXb}_8(8CRenZNf5%(u(&czR zwA8VJ(tdK?ePsZhZx`DX*q0ac?xA~4bqfFnPbu~!beeDWDs0IIJuU1X-rw<&qFPif zym)r{Y^6B=YeMqZv_y>dLF@b8$3Iu6MRQ`l<8xTfJ&6q_!LINHRjmOxh*9&vM`j2E=&5l;Bpn zH}|SF%5F~V+foj;BJ?`_J;%)m5QV$vNWD3mWccf`{q>&w`vsE>7fq^HdVTl-oo$x3 z3Vr6)-+aj6uSj@XH;kTp*x;?QjQUXct~281@q*jWD<$^@za0_uU{BPxGi2xas7Qs9 z1^3(9qi`c@4)CnoXhAG;+g$Z5=RYdbnicd+`P6rAa&H)@;8_e+pZ-ERfD;coO~sYc z^*rg05PF9dg8OaA%oTn5N%&cgf0b0NkTc%HdA7aGMsin8{OObAZu?D-ezL!Afk7~U zt@S*LpRGHw`Qo_z?*0w_=A%EI%1jG9)y{0N zlm0bx`v1TE5S_hGq@G=n+M)ix|Hpt> z^Zb7I`u~6V|HI23$-U>ykr{MmRHw=`t1D_A_nh_2s7dwn+RyYSd;IQN;i&@jeZ0G| z*!4I)Wg{EnOdNluPyWZU@PGai@*Qb8fSu)Se(Z3%C&8bgZ=b1ldC*4T1=>wDT9?~N zvg#GDvYB+Oq5t3d_y2Qq_&U@3>ts#q8uo#-r`i8`l{h4fE zdar2B_|d1jLs;eEu+bzN}utUC2`)-jp&G_i>Iw$%)M{BNsxm9SG5$xW50m&Mo zLguF+6tyyL(o$_=d>TJV-Mirtle@{j9hcEmS1O$*ihN>8fLV`d^5blN(=Y#drNJuu z0BP}INJd2@IXaO@&HI;b3sDpK^Cre#~>de&y=#lJj+Nbexg6s$G%`fI;B z=leIQVa_1`)mD@28Hk`NY=9rS?y|c zvAgKwPA%<_$ss5bECv3W%I91j+!W`{IJLE$WI0ZE(7wC@WOeFT|N2Dj$b zfoMgW-xqzVMz?3lZIzDFc=Yj3CrpAy2{zNTQu3*e>xuvUWeZM5D%Nf$<`pDFbw%d+ z8BO;IQRveRE1d7sjYkVbq$oQYyp9m-v*llGs{hi2{jaSy8@+>HpG4BQ3#6$3V%Ds( z3jzl6kFgRIZH|PMTZV~*)s><$nHh&k@e1uOr~BtM3Fp?^Y(c9%Ke^Jdao|rfDuj2F z1!yUH5|ms$&hxkik^b8S5}l>7P1R|=-Xr>fE+#=p&HUx5&$+Dk#z*&gw;jR%_TvxG zXKRs`Q`lLCihJw?Wpl{qM`C$h(C4_4rshktODfE%*xcD!3Jr#fZ@=ru>;H360hbU^ z9z>*L85I3_P#uf4YM+8hVYuxaS`kBV0GOpR=e^5{?&y`^WZ^@)y*bkbsKI>UM{P69 z3to3De4g*JWr^pRaG9L9$D47PyyyuY(ItcM{ytC(t@)r*M1CfC-X$2?s&{Y{|Lfi@ z3piPgqZX5V^Bj6X(p1zCw659|2w%rK&wXq@r_+v_hfVcW6pJR$Jsu`CPH%*i$1)Pc zC-*j<%|8dwnm460Ke7M&!iYxU!$&V$E|gUJ(wOv=EE1y=WAiJim=!@WejsO+ih|B? z=#*7*gTFwL`koIvFN?BuX8^Mn?DTsAlr8ssk%7exR7!U$ zv*Ap;O9OhUq$FTp(9Qr)mdA2NCl^Jy2mmu%* zl^N?XMh?7NOul+#pV1H8oHNK`qAZ7zdPdjk_D6$!o>w94yP(kfxYFfVtBGz7;3wpv ztmR=0kl;TzK5o5tO!OJ93|Ye0y`1dJCqA@4>4~51br?(2|R3>l|Aed`R|EZHpCzRjQgD zdjmbqP#;$OIt^M|Ps+i02Leiafgc@d@C!J-p?xg&|EGWxKzxMmZwJKReX4iyZhK8{ zsF;%LUB4r`Ty&*s#De{xy{?k5Ul*Jn9848Df%p|V6Iz39HIII}aS;+t>!Bf?uCx5Y zHM7F~Xytu#^39LKe3bh1w&umPSBD>dd-2gU?WQj4RyM&&w)AYb;z=yO|Lr*YuT4l) z5{+5APTBQrZ&wA`uj*VZmmAFT73K@AXKT`tB_U60t@h^|563la06V4c!)UJJZzefE zTZ|j-`=ElDV91zZpSSHRq_ z-GUfeGu6gg67ZYP@6O)ydN%>`Qd*dk?QD7!PU-p@Y{DWs^-0;jUg||1`vba z;G}RF1yeQGof!~Wm6}pJFggxu~ ze}oCjI{BvU3BzPLV`W*S+PBJyo%iSV)54%+Ls=js$FM$473=l?@xrN zEm^f{nUYT_V)ve9_r6^VYqL^$^CX6Mh9_!Pol{_2qR01*YRS(C!PCAkwSB7-hV*k| zeDe1-&;R^*OWqW=rqzu1Y#Zj;$1McF%`Kvj%m0#ojDv3+~;$QJU-1)kG*G3NYIGEJ08BpUL>0)q=m;VxsiAE(6L$31N_k`!nu&0`zp!P)-Si z+p=nZX>qac)tE*Ddb7)y5PEuB<2*++n!`gP@t z_wJmN%z}uIRi~kb%Hv@9CCh8S31OH5;#aUT^nQ;J-YNjXuD`cZDpMpvHXUXlbQwEa z`}TsQo5WYG5a2v7Z{5$~`Zo=MSG;K(@jwOyJS0ckc03u$z8f)hj@trb@F)pc^pTV} z&c8~_|M^n>{mDV7Xv*X4w>}Scs~E87hhF{C+0wHb^9nt7&nP~ʬ&fGPD$eM{w$ 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Р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