diff --git a/INTEGRATION.md b/INTEGRATION.md new file mode 100644 index 0000000000..82f2830c03 --- /dev/null +++ b/INTEGRATION.md @@ -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`. 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. \ No newline at end of file diff --git a/go.mod b/go.mod index a199b9b514..25bfe342a9 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index cf7f31a206..4c484989ec 100644 --- a/go.sum +++ b/go.sum @@ -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=