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
    • Invite by email
      Invitee

      This note has no invitees

    • 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
    • Note Insights
    • 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 Versions and GitHub Sync Note Insights Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
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
  • Invite by email
    Invitee

    This note has no invitees

  • 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