Skip to content

Commit

Permalink
Merge pull request #114 from CosmWasm/document-integration
Browse files Browse the repository at this point in the history
Document integration
  • Loading branch information
ethanfrey authored May 25, 2020
2 parents 9aaad07 + 55368f1 commit 4ab0544
Show file tree
Hide file tree
Showing 3 changed files with 182 additions and 1 deletion.
179 changes: 179 additions & 0 deletions INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# Integration

If you want to use Wasm in your own app, here is how you can get this working
quickly and easily. First, check to make sure you fit the pre-requisites,
then integrate the `x/wasm` module as described below, and finally, you
can add custom messages and queries to your custom Go/SDK modules, exposing
them to any chain-specific contract.

## Prerequisites

The pre-requisites of integrating `x/wasm` into your custom app is to be using
a compatible version of the Cosmos SDK, and to accept some limits to the
hardware it runs on.

| wasmd | Cosmos SDK |
|:-----:|:----------:|
| v0.7 | v0.38 |
| v0.8 | v0.38 |

We currently only support Intel/AMD64 CPUs and OSX or Linux (with glibc - not muslc like alpine).
This limit comes from the Rust dll we use to run the wasm code. There are open issues
for adding [ARM support](https://github.com/CosmWasm/go-cosmwasm/issues/53),
adding [Windows support](https://github.com/CosmWasm/go-cosmwasm/issues/28),
and [compiling static binaries](https://github.com/CosmWasm/go-cosmwasm/issues/45), so it works on both glibc and muslc systems.
However, these issues are not high on the roadmap and unless you are championing
them, please count on the current limits for the near future.

## Quick Trial

The simplest way to try out CosmWasm is simply to run `wasmd` out of the box,
and focus on writing, uploading, and using your custom contracts. There is
plenty that can be done there, and lots to learn.

Once you are happy with it and want to use a custom Cosmos SDK app,
you may consider simply forking `wasmd`. *I highly advise against this*.
You should try one of the methods below.

## Integrating wasmd

### As external module

The simplest way to use `wasmd` is just to import `x/wasm` and wire it up
in `app.go`. You now have access to the whole module and you custom modules
running side by side. (But the CosmWasm contracts will only have access
to `bank` and `staking`... more below on [customization](#Adding-Custom-Hooks)).

The requirement here is that you have imported the standard sdk modules
from the Cosmos SDK, and enabled them in `app.go`. If so, you can just look
at [`wasmd/app/app.go`](https://github.com/CosmWasm/wasmd/blob/master/app/app.go#)
for how to do so (just search there for lines with `wasm`).

### Copied into your app

Sometimes, however, you will need to copy `x/wasm` into your app. This should
be in limited cases, and makes upgrading more difficult, so please take the
above path if possible. This is required if you have either disabled some key
SDK modules in your app (eg. using PoA not staking and need to disable those
callbacks and feature support), or if you have copied in the core `x/*` modules
from the Cosmos SDK into your application and customized them somehow.

In either case, your best approach is to copy the `x/wasm` module from the
latest release into your application. Your goal is to make **minimal changes**
in this module, and rather add your customizations in a separate module.
This is due to the fact that you will have to copy and customize `x/wasm`
from upstream on all future `wasmd` releases, and this should be as simple
as possible.

If, for example, you have forked the standard SDK libs, you just want to
change the imports (from eg. `github.com/cosmos/cosmos-sdk/x/bank` to
`github.com/YOUR/APP/x/bank`), and adjust any calls if there are compiler
errors due to differing APIs (maybe you use Decimals not Ints for currencies?).

By the end of this, you should be able to run the standard CosmWasm contracts
in your application, alongside all your custom logic.

## Adding custom hooks

Once you have gotten this integration working and are happy with the
flexibility it offers you, you will probably start wishing for deeper
integration with your custom SDK modules. "It sure is nice to have custom
tokens with a bonding curve from my native token, but I would love
to trade them on the exchange I wrote as a Go module. Or maybe use them
to add options to the exchange."

At this point, you need to dig down deeper and see how you can add this
power without forking either CosmWasm or `wasmd`.

### Calling contracts from native code

This is perhaps the easiest part. Let's say your native exchange module
wants to call into a token that lives as a CosmWasm module. You need to
pass the `wasm.Keeper` into your `exchange.Keeper`. If you know the format
for sending messages and querying the contract (exported as json schema
from each contract), and have a way of configuring addresses of supported
token contracts, your exchange code can simply call `wasm.Keeper.Execute`
with a properly formatted message to move funds, or `wasm.Keeper.SmartQuery`
to check balances.

If you look at the unit tests in [`x/wasm/internal/keeper`](https://github.com/CosmWasm/wasmd/tree/master/x/wasm/internal/keeper),
it should be pretty straight forward.

### Extending the Contract Interface

If you want to let the contracts access your native modules, the first
step is to define a set of Messages and Queries that you want to expose,
and then add them as `CosmosMsg::Custom` and `QueryRequest::Custom`
variants. You can see an example of the [bindings for Terra](https://github.com/CosmWasm/terra-contracts/tree/master/packages/bindings).

Once you have those bindings, use them to build a
[simple contact using much of the API](https://github.com/CosmWasm/terra-contracts/tree/master/contracts/maker).
Don't worry too much about the details, this should be usable, but mainly
you will want to upload it to your chain and use for integration tests
with your native Cosmos SDK modules. Once that is solid, then add more
and more complex contracts.

You will then likely want to add a `mocks` package so you can provide
mocks for the functionality of your native modules when unit testing
the contracts (provide static data for exchange rates when your contracts
query it). You can see an example of [mocks for Terra contracts](https://github.com/CosmWasm/terra-contracts/tree/master/packages/mocks).

What these three steps provide is basically a chain-specific extension to the CosmWasm contract SDK.
Any CosmWasm contract can import you library (bindings and mocks) and easily get started using
your custom, chain-specific extensions just as easily as using the standard CosmWasm interfaces.
What is left is actually wiring them up in your chain so they work as desired.

Note, in order to ensure that no one tries to run the contracts on an unsupported chain,
you will want to include a `requires_XYZ` directive in your `bindings` library, this will
mean that only blockchain apps that explicitly declare their support for the `XYZ` extensions
(please rename XYZ to your project name) will allow the contract to be uploaded, and others
get error messages upon upload, not while running a critical feature later on.
You just need to add [a line like this](https://github.com/CosmWasm/terra-contracts/blob/master/packages/bindings/src/lib.rs#L13-L16)
to your binding library to add the requirement to any contract that imports your `bindings` lib.

### Calling into the SDK

Before I show how this works, I want to remind you, if you have copied `x/wasm`,
please **do not make these changes to `x/wasm`**.

We will add a new module, eg. `x/contracts`, that will contain custom
bindings between CosmWasm contracts and your native modules. There are two entry points
for you to use. The first is
[`CustomQuerier`](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/x/wasm/internal/keeper/query_plugins.go#L35),
which allows you to handle your custom queries. The second is
[`CustomEncoder`](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/x/wasm/internal/keeper/handler_plugin.go#L30)
which allows you to convert the `CosmosMsg::Custom(YourMessage)` types to `[]sdk.Msg` to be dispatched.

Writing stubs for these is rather simple. You can look at the `reflect_test.go` file to see this in action.
In particular, here [we define a `CustomQuerier`](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/x/wasm/internal/keeper/reflect_test.go#L355-L385),
and here [we define a `CustomHandler`](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/x/wasm/internal/keeper/reflect_test.go#L303-L353).
This code is responsible to take `json.RawMessage` from the raw bytes serialized from your custom types in rust and parse it into
Go structs. Then take these go structs and properly convert them for your custom SDK modules.

You can look at the implementations for the `staking` module to see how to build these for non-trivial
cases, including passing in the `Keeper` via a closure. Here we
[encode staking messages](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/x/wasm/internal/keeper/handler_plugin.go#L114-L192).
Note that withdraw returns 2 messages, which is an option you can use if needed to translate into native messages.
When we [handle staking queries](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/x/wasm/internal/keeper/query_plugins.go#L109-L172)
we take in a `Keeper in the closure` and dispatch the custom `QueryRequest` from the contract to the native `Keeper` interface,
then encodes a response. When defining the return types, note that for proper parsing in the Rust contract, you
should properly name the JSON fields and use the `omitempty` keyword if Rust expects `Option<T>`. You must also use
`omitempty` and pointers for all fields that correspond to a Rust `enum`, so exactly one field is serialized.

### Wiring it all together

Once you have writen and tested these custom callbacks for your module, you need to enable it in your application.
The first step is to write an integration test with a contract compiled with your custom SDK to ensure it works properly,
then you need to configure this in `app.go`.

For the test cases, you must
[define the supported feature set](https://github.com/CosmWasm/wasmd/blob/ade03a1d39a9b8882e9a1ce80572d39d57bb9bc3/x/wasm/internal/keeper/reflect_test.go#L52)
to include your custom name (remember `requires_XYZ` above?). Then, when creating `TestInput`,
you can [pass in your custom encoder and querier](https://github.com/CosmWasm/wasmd/blob/ade03a1d39a9b8882e9a1ce80572d39d57bb9bc3/x/wasm/internal/keeper/reflect_test.go#L52).
Run a few tests with your compiled contract, ideally exercising the majority of the interfaces to ensure that all parsing between the contract and
the SDK is implemented properly.

Once you have tested this and are happy with the results, you can wire it up in `app.go`.
Just edit [the default `NewKeeper` constructor](https://github.com/CosmWasm/wasmd/blob/v0.8.0-rc1/app/app.go#L257-L258)
to have the proper `supportedFeatures` and pass in the `CustomEncoder` and `CustomQuerier` as the last two arguments to `NewKeeper`.
Now you can compile your chain and upload your custom contracts on it.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/CosmWasm/wasmd
go 1.13

require (
github.com/CosmWasm/go-cosmwasm v0.8.0-alpha3
github.com/CosmWasm/go-cosmwasm v0.8.0
github.com/btcsuite/btcd v0.0.0-20190807005414-4063feeff79a // indirect
github.com/cosmos/cosmos-sdk v0.38.3
github.com/golang/mock v1.4.3 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ github.com/CosmWasm/go-cosmwasm v0.8.0-alpha2.0.20200519190609-972a954b9b43 h1:K
github.com/CosmWasm/go-cosmwasm v0.8.0-alpha2.0.20200519190609-972a954b9b43/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc=
github.com/CosmWasm/go-cosmwasm v0.8.0-alpha3 h1:pa1gVDCGvM+5TBYepUhP1JHnpfOXwpBbFNSzYcXGXOU=
github.com/CosmWasm/go-cosmwasm v0.8.0-alpha3/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc=
github.com/CosmWasm/go-cosmwasm v0.8.0 h1:xoufklhgJQ7Sy6xwZw9hRbnXl79ylvxlpGJDcPz9Ipo=
github.com/CosmWasm/go-cosmwasm v0.8.0/go.mod h1:gAFCwllx97ejI+m9SqJQrmd2SBW7HA0fOjvWWJjM2uc=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
Expand Down

0 comments on commit 4ab0544

Please sign in to comment.