Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Minor tutorial fixes #237

Merged
merged 1 commit into from
Apr 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions hs-abci-docs/nameservice/tutorial/Foundations/01-Overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ title: Foundations - Overview

# Overview

The SDK relies heavily on two abstractions to facilitate application development, effects systems and Modules.
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
The effects system is backed by a library called `polysemy` which we mentioned in the introduction. An application basically has five layers of effects

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. **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 two different core options available in the SDK (distinguished by an in-memory versus production database), but the more advanced developer might wish to write their own.
5. **Core effects**: These are largely internal and used to interpret the other effects to `IO`. There are two different core options available in the SDK (distinguished by 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.

Expand Down
10 changes: 5 additions & 5 deletions hs-abci-docs/nameservice/tutorial/Foundations/02-Effects.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ These effects are:
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 exceptions.
4. `Error AppError` -- allows for errors of type `AppError` to be thrown or caught.
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.
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

Expand Down Expand Up @@ -57,10 +57,10 @@ where

## QueryEffs

`QueryEffs` are used to interpret queries and are defined as
`QueryEffs` are used to interpret queries and are defined as

~~~ haskell ignore
type QueryEffs =
type QueryEffs =
[ ReadStore
, Error AppError
]
Expand Down Expand Up @@ -95,7 +95,7 @@ There are a few effects lists that appear so frequently at different points in t

## 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`.
`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

Expand Down
8 changes: 4 additions & 4 deletions hs-abci-docs/nameservice/tutorial/Foundations/03-Modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ Let's take a look at the type parameters

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.
- `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.
- `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`.
- `moduleEval` is the natural transformation that specifies how to interpret 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`.

Expand All @@ -59,8 +59,8 @@ Note that in the event that a `Module` is _abstract_, meaning it doesn't have an
~~~ haskell ignore
data ModuleList (ms :: [Component]) r where
NilModules :: Modules '[] r
(:+) :: Module name check deliver query es r
-> Modules ms r
(:+) :: Module name check deliver query es r
-> Modules ms r
-> Modules (Module name check deliver query es ': ms) r
~~~

Expand Down
2 changes: 1 addition & 1 deletion hs-abci-docs/nameservice/tutorial/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This means that users can buy and sell entries in a shared mapping of type `Name

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.
2. `Token` - Allows users to manage their tokens, things like transfering or burning.
3. `Nameservice` - Controls the shared `Name -> Value` mapping described above.

## How to Read this Tutorial
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,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
This section is where we sketch the definition of the Nameservice module and application. It's to everyone's benefit if module structures follow a similar file hierarchy as the Nameservice module, or any module found in the SDK. In the case of Nameservice this roughly translates to

```
├── Nameservice
Expand All @@ -26,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.
The reason why we suggest this is that each of these haskell modules is buiding up one of the core components of our definition of a module, and it provides a nice logical split between these pieces.
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ 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 as a mapping

~~~ haskell ignore
owner :: Tendermint.SDK.Types.Address -> Account
Expand Down Expand Up @@ -148,7 +148,7 @@ At this point, you can use the database operations exported by `Tendermint.SDK.B

### 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.
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 compatibility reasons.

### Error Types

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ title: Nameservice - Message
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.
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 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.
Expand Down Expand Up @@ -42,7 +42,7 @@ import Tendermint.SDK.Codec (HasCodec(..))

### Message Definitions

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`:
For the purposes 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
Expand Down Expand Up @@ -131,7 +131,7 @@ instance HasMessageType BuyNameMsg where

## 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
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.
Expand Down Expand Up @@ -161,14 +161,14 @@ isAuthorCheck
-> (msg -> Address)
-> V.Validation [MessageSemanticError] ()
isAuthorCheck fieldName Msg{msgAuthor, msgData} getAuthor
| getAuthor msgData /= msgAuthor =
| 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
Here are the validation instances for our message types, which use some of the combinators defined in the SDK

~~~ haskell
instance ValidateMessage SetNameMsg where
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: Nameservice - 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.
The Router is where you specify 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
Expand Down Expand Up @@ -44,10 +44,10 @@ type MessageApi =

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 the operator which denotes alternative, so our router is composed of 3 handlers in this case.
- `TypedMessage` is a combinator that specifies the message type we want to accept. We require 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.
- `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

Expand All @@ -72,7 +72,7 @@ In the case of our actual application, all the transactions return `()` for both

## 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`.
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:

Expand Down Expand Up @@ -120,4 +120,4 @@ deleteNameH (RoutingTx Tx{txMsg=Msg{msgData}}) = do
~~~


[Next: Module](Module.md)
[Next: Module](Module.md)
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ nameserviceModule = Module

~~~

Here We are using `defaultCheckTx` as our transaction checker, which is a static, message validating handler defined as:
Here we are using `defaultCheckTx` as our transaction checker, which is a static, message validating handler defined as:

~~~ haskell ignore
defaultCheckTxHandler
Expand All @@ -56,20 +56,20 @@ 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 in the serve type is `Return ()`

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 with 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.
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 with all the return values changed to `Return ()`. In this particular 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 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
...
ModuleEffs Nameservice r
ModuleEffs Nameservice r
~ ( Members NameserviceEffs r
, Members (DependencyEffs '[Bank] r)
, Members TxEffs r
, Members BaseEffs r
)
~ ( Members NameserviceEffs r
, Members BankEffs r
, Members BankEffs r
, Members TxEffs r
, Members BaseEffs r
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ title: Nameservice - Application

## From Modules to App

The `App` type in `Network.ABCI.Server` is defined as
The `App` type in `Network.ABCI.Server` is defined as

~~~ haskell ignore
newtype App m = App
Expand All @@ -28,7 +28,7 @@ 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`.
- `core` is the set of core effects that are used to interpret `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`:
Expand Down Expand Up @@ -99,7 +99,7 @@ Finally we're able to define our application that runs in the `CoreEffs` context

~~~ haskell
app :: App (Sem CoreEffs)
app = makeApp handlersContext
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 the `Sem CoreEffs` in our app. We can simple use the `createIOApp` function:
Expand Down
Loading