HackMD
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
Publish Note

Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note

Your note will be visible on your profile and discoverable by anyone.
Your note is now live.
This note is visible on your profile and discoverable online.
Everyone on the web can find and read all notes of this public team.
See published notes
Unpublish note
Please check the box to agree to the Community Guidelines.
View profile
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# WebAssembly integration in Cosmos-SDK This is a proposal for how to integrate the wasmer engine into the cosmos-sdk. Not on the technical details of the runtime implementation, but on how it connects to the rest of the SDK framework and all modules build upon it. ## External Requirements This is based on work with the wasmer.io engine, including the following points: * Exposing gas metering in go API * Repeatable gas metering (fixed backend compiler options) * Testing serialization in go API * Benchmarks to correllate wasmer gas to sdk gas usage It also assumes the following build system: * Configuration to build rust to WebAssembly * Small wasm binaries (<100KB typical) * Deterministic builds (so we can map rust to on-chain wasm) These are done in other repositories and are out of the scope of this document. ---------------------------------- ## High-Level Design This first section, covers high-level design and architecure decisions. Any code here is just illustrative pseudo-code. The main point to focus on are the concepts. ## Where to Integrate WebAssembly? When we discuss Web Assembly, most people see it as a magic fix that allows us to upload sandboxed, user-defined code *anywhere*. The truth is that while Web Assembly allows us to upload sandboxed functions to run, we need to design the connections (arguments, return value, and environment) with one specific case in mind. As Aaron put it well, anywhere we accept a Go *interface* in the SDK, we could place a WASM adapter there. But just as a struct only implements one interface, we need to concentrate on one interface to adapt. The majority of the use cases could be covered by a properly designed adapter for *Handler* and this is where we will focus our work: However, there are a number of other places where we could potentially provide another interface for custom web assembly contracts. To be added after the original integration work: * Payment Channels * Atomic Swap * Escrow (with arbiter) * Automatic payment distribution based on shares * Multi-party escrow triggered by calculations - querying other data on the chain * Programmable NFTs: just as ethereum NFTs include some custom code to perform actions Some other important use cases that will need different adapters: * **IBC verification function** The current ICS23 spec mentions wasm uploaded code to verify external proofs. This should be easily creatable with a custom interface . * **Delegation Rewards** Support different commission to different validators based on on-chain rules. * **Signature Verification** Allow uploading new algorithms, like `ed25519` or `BLS`. These would obviously need to be enabled by a governance vote. ## The Handler Interface For the work below, we are just looking at the "Handler" interface. In go, this is `type Handler func(ctx Context, msg Msg) Result`. In addition to the message to process, it gets a Context that allows it to view and mutate state: ```go func (c Context) Context() context.Context { return c.ctx } func (c Context) MultiStore() MultiStore { return c.ms } func (c Context) BlockHeight() int64 { return c.header.Height } func (c Context) BlockTime() time.Time { return c.header.Time } func (c Context) ChainID() string { return c.chainID } func (c Context) TxBytes() []byte { return c.txBytes } func (c Context) Logger() log.Logger { return c.logger } func (c Context) VoteInfos() []abci.VoteInfo { return c.voteInfo } func (c Context) GasMeter() GasMeter { return c.gasMeter } func (c Context) BlockGasMeter() GasMeter { return c.blockGasMeter } func (c Context) IsCheckTx() bool { return c.checkTx } func (c Context) MinGasPrices() DecCoins { return c.minGasPrice } func (c Context) EventManager() *EventManager { return c.eventManager } ``` It is clearly not desirable to expose all this to arbitrary, unaudited code, but we can grab a subset of this to expose to the wasm contract. Readonly: * Block Context * BlockHeight * BlockTime * ChainID * Message Data * Signer (who authorized this message) * Tokens Sent with message * User-defined data * Contract State * Contract Address * Contract Account (just balance or more info?) Read/Write: * A sandboxed sub-store * Events ### Security Concerns We clearly cannot just pass in a Controller to another module into an unknown wasm contract. But we do need some way to allow a wasm contract to integrate with other modules to make it interesting. Above we allow it access to its internal state and ability to read its own balance. In terms of security, we can view the wasm contract as a "client" on-chain with its own account and address. It can theoretically read any on-chain data, and send any message "signed" by its own address, without opening up security holes. Provided these messages are processed just like external messages, and that gas limits are enforced in CPU time and those queries. ### Calling Other Modules Ethereum provides a nice dispatch model, where a contract can make arbitrary calls to the public API of any other contract. However, we have seen many issues and bugs, especially related to re-entrancy attacks. To simplify this, we propose that the contract cannot directly call any other contract, but instead returns a list of messages, which will be dispatched and validated *after contract execution* but in *the same transaction*. This means that if they fail, the contract will also roll back, but we don't allow any cycles or re-entrancy possibilities. We could conceive of this as something like: `ProcessMessage(info ReadOnlyInfo, db SubStore) []Msg`. Note that we also want to allow it to return a `Result` and `Events`, so this may end up with a much larger pseudo-function signature, like: `ProcessMessage(info ReadOnlyInfo, db SubStore) (*Result, []Event, []Msg, error)` This allows the contract to easily move the tokens it controls (via `SendMsg`) or even vote, stake tokens, or take any other action its account has authority to do. The potential actions increase with the delegation work being done as part of Key Managament. ### Querying Other Modules While it is great to change state in other modules, the design until this point leave the contract blind. Sure, it can emit a message in order to stake some tokens, but it cannot check its current stake, or the number of tokens available to withdraw. To do so, we need to expose some interface to query other modules. Going along with the client analogy above , we definitely **do not** want to allow *write* access to the other substores. Instead we can allow something like the high level `abci_query` interface, where the smart contract would send a path and data (key), and receive the requested object - likely serialized as json rather than amino for ease of parsing in the smart contract. Whether we wrap the existing `Query` interface or provide a different interface just for WASM contract is an open question, which we touch in the next section. ## Upgradeability This can be a **major problem** or even **blocker** for enabling Web Assembly contracts, unless we make some very conscious design decisions, both in the WASM interfaces, as well as the SDK as a whole. If we allow the contract to query arbitrary data in other modules, this contract is dependent on those not changing. If the chain upgrades (gracefully or hardfork) and the queries return data in a different format, or change the path they respond to, then the contract will break. The same is true with the format of the messages we return. If cosmos-sdk modifies the format of the staking message, after an upgrade the module will continue to emit staking messages in the old format, which will fail - leave the contract broken and fund stuck. One proposed solution was to allow us to "upgrade contracts" as well, but I find this highly problematic. A core pillar of most smart contract designs is immutability, which is what allows us to trust them. If the author could change them *after* I send it my funds, then there can be no trustless execution. Maybe we then decide that governance can update the contracts, or only change them in the context of a hard-fork. This provides safe-guards, but the issue arises that the contract author and those updating the application code, and the validators all are different entities. Do we now need to contact every contract author and involve them in preparing every upgrade? Or will the validators just re-wrire contracts as they see fit? Seems extremely risky in any case. One alternative here is to either freeze every API that a Handler touches in cosmos-sdk, as well as every data structure exposed over `abci_query`. But this would have the effect of a huge stagnation of the codebase and very determental to innovation. Another alternative, and what we propose here, is to limit the interfaces exposed to the wasm contracts to a minimalistic subset of all possible functionality. And provide a fixed format with strong immutability guarantees. This will likely require some wrapper between the structs used in the wasm interface, and those used elsewhere in the sdk. As we noticed, even the transaction type changed in the upgrade to Gaia 2.0. We could, for example, expose a custom SendMsg, `{type: 'send', to, from, amount}` and then in the Golang wrapper code (which can be updated easily during a hard-fork), we translate this *well-defined*, *immutable*, and *forward-comaptible* message definition into the actual structure used by the cosmos-sdk, before dispatching this to other modules. We would like-wise have to provide clear definitions for a minimal set of queries we want to expose, and then make sure to translate fields from the current struct into this static definition. This means we would have to manually enable each Message or Query we would want to expose to all Web Assembly contract, and provide strong guarantees to each of them *forever*. We could easily add new message types, or queries, such that new contracts deployed after version X could make use of them, but all the types that were exposed to the first contract deployed on the system must remain valid for the lifetime of the chain (including any hardforks, dump-state-and-reset, etc.). **WARNING** Even with a buffer class, this will have a noticeable strong impact on a number of development practices in the core cosmos-sdk team, especially related to version and migration, and we need to have a clear and open discussion on possible approaches here. Relevant link (recommended by Aaron): https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/Spec_ulation.md ---------------------------------- ## Specification This second section attempts to codify the architecture defined above with a number of concrete implementation details, function signatures, naming choices, etc. We should discuss the details here - both names and functionality. ## Definitions **Contract** is as some wasm code uploaded to the system, *possibly* with some "global" state, initialized at the creation of the contract. **Instance** is one instantiation of the contract. This contains a reference to the contract, as well as some "local" state to this instance, initialized at the creation of the instance. Example: we could upload a generic "ERC20 mintable" contract, and many people could create independent instances of the same bytecode, where the local data defines the token name, the issuer, the max issuance, etc. * First you **create** a *contract* * Then you **instantiate** an *instance* * Finally users **invoke** the *instance* *Contracts* are immutible (code/logic is fixed), but *instances* are mutible (state changes) ## Serialization There are two pieces of data that must be considered here. **Message Data**, which is arbitrary binary data passed in the transaction by the end user signing it, and **Context Data**, which is passed in by the cosmos sdk runtime, providing some guaranteed context. Context data may include the signer's address, the instance's address, number of tokens sent, block height, and any other information a contract may need to control the internal logic. **Message Data** comes from a binary transaction and must be serialized. The most standard and flexible codec is (unfortunately) JSON. This allows the contract to define any schema it wants, and the client can easily provide the proper data. We recommend using a `string` field in the `InvokeMsg`, to contain the user-defined *message data*. **Contact Data** comes from the go runtime and can either be serialized by sdk and deserialized by the contract, or we can try to do some ffi magic and use the same memory layout for the struct in Go and Wasm and avoid any serialization overhead. Note that the context data struct will be well-defined at compile time and guaranteed not to change between invocations (the same cannot be said for *message data*). In spite of possible performance gains or compiler guarantees with C-types, I would recommend using JSON for this as well. Or another well-defined binary format, like protobuf. However, I will document some links below for those who would like to research the shared struct approach. * [repr( c )](https://doc.rust-lang.org/nomicon/other-reprs.html) is a rust directive to produce cannonical C-style memory layouts. This is typically used in FFI (which wasm calls are). * [wasm-ffi](https://github.com/DeMille/wasm-ffi) demos how to pass structs between wasm/rust and javascript painlessly. Not into golang, but it provides a nice explanation and design overview. * [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen/) also tries to convert types and you can [read some success and limitations of the approach](https://github.com/rustwasm/wasm-bindgen/issues/111) * [cgo](https://golang.org/cmd/cgo/#hdr-Go_references_to_C) has some documentation about accessing C structs from Go (which is what we get with the repr( c ) directive) ## State Access **Contract State** is globally accessible to all instances of the contract. This is optional and I would consider a read-only singleton as sufficient for most customizations of the contract (using a contract with different configurations), and more secure than other access modes. This should be discussed in the light of actual use cases. Maybe it is unneeded, maybe write access is needed. We can set contract state upon *creation*. This is a unique db key that cannot be accessed excpet by this contract code. I would conceive of this as a data section baked into the binary, which is actually probably the better approach. **Instance State** is accessible only by one instance of the contract, with full read-write access. This can contain either a singleton (one key - simple contract or config) or a kvstore (subspace with many keys that can be accessed - like erc20 instance holding balances for many accounts). Sometimes the contract may want one or the other or even both (config + much data) access modes. We can set the instance state upon *instantiation*. We can read and modify it upon *invocation*. This is a unique "prefixed db" subspace that can only be accessed by this instance. The read-only contract state should suffice for shared data between all instances. (Discuss this design in light of all use cases) ## Function Definitions As discussed above, all data structures passed between web assembly and the cosmos-sdk will be sent in their JSON representation. For simplicity, I will show them as Go structs in this section, and we can assume a cannonical snake_case conversion of the field name. This allows us to be explicit about types. Function: `Process(request Request) Response` This Read-Only info is available to every contract: ```go type Request struct { Block BlockInfo Message MessageInfo Contract ContractInfo } type BlockInfo struct { Height int64 Time int64 // ??? seconds (nanoseconds) since unix epoch??? ChainID string } type MessageInfo struct { Signer string // bech32 encoding of authorizing address TokensSent CoinInfo // money transfered by sdk as part of this message (before execution) UserData string // arbitrary data set in transaction } type ContractInfo struct { Address string // bech32 encoding of address this contract controls Balance []CoinInfo } type CoinInfo struct { Amount string // encoing of decimal value, eg. "12.3456" Denom string // type, eg. "ATOM" } ``` This is the information the contract can return: ```go type Response struct { Result ABCIResult // Msg struct is an "interface" discussed in a later section Messages []Msg } type ABCIResult struct { Data string // hex-encoded Log string Code int32 // non-zero on error CodeSpace string // non-empty on error Events []Event } // do we give all this power to wasm, or provide a simpler interface (eg. fixing type) type Event struct { Type string Attributes []struct{ Key string Value string } } ``` Note that I intentionally redefine a number of core types, rather than importing them from sdk/types. This is to guarantee immutibility. These types will be passed to and from the contract, and the contract adapter code (in go) can convert them to the go types used in the rest of the app. But these are decoupled, so they can remain constant while other parts of the sdk evolve. ## Exposed imports **TODO** Precise imports it can expect to be able to call (eg. query modules, modify local state) ## Define Query and Message types **TODO** After upgrade discussion above, some fixed subset of types here

Import from clipboard

Paste your markdown or webpage here...

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template has been removed or transferred.
Upgrade
All
  • All
  • Team
No template.

Create a template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

Slide Example

API Docs

Edit in VSCode

Install browser extension

Contacts

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Get Full History Access

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

Note content is identical to the latest version.
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully