Bringing the Web Assembly (Wasm) VM and superior key management to the Cosmos SDK to increase user functionality.

Aaron Craelius
Jun 26 · 11 min read

By Aaron Craelius, Gregory Landua, and Ethan Frey.

Photo c/o Gregory Landua, taken at Full Node, Berlin.

Regen Network — along with our compatriots Shane Vitarana of TruStory, Ethan Frey of IOV, Jehan Tremback of Althea, and Pedro Gomes of WalletConnect — combined forces to become Team Gaians for the Cosmos Berlin HackAtom. This blog will review what we created and what it can be used for.

Our team focused on hacking towards a clear use case: user-friendly participation in smart contracts about ecological health. In order to create usable functionality, the team had to overcome some key challenges: create a smart contracting framework for the Cosmos SDK and improved key management.

And that is exactly what was accomplished.

In 36 hours, the team pulled together an incredibly powerful and flexible, smart contracting module using WebAssembly (Wasm) and key management modules that interact to create a smart contracting platform that is more powerful and flexible than Ethereum, and built in the Cosmos SDK, making interoperability with other chains easy.

Team Gaians, made up of Aaron Craelius, Shane Vitarana, Ethan Frey, Jehan, and Pedro Gomes, present the winning Hackathon work to Jae Kwon and Jack Zampolin. Photo by Gregory Landua.

Let’s walk through the functionality that was built using the use-case we considered — a simple three-party contract for Regen Network involving a farmer, funder, and verifier. The funder and land steward create a contract that sends money to the farmer if the approved third-party verifier certifies that the land steward successfully implemented regenerative practices. In a real-world scenario, our contract might be a bit more complex, possibly involving decentralized verification using satellite data, but for now, this is a minimum viable contract.

Smart contracts about ecological health are the basis of Regen Network’s platform and the inspiration behind CosmWasm. Image from Regen Network.

The need to manage keys and tokens is one of the biggest barriers preventing non-technical users from adopting blockchain-based applications. We have heard time and time again that we cannot expect farmers and agricultural field agents to be comfortable managing private keys and filling up a wallet with tokens just to pay for gas to verify contracts.

“I lost my private keys over there somewhere.” Photo by Bec Ritchie on Unsplash.

We added several new features to the Cosmos SDK which we believe help solve this problem: key groups, delegated fees, and delegated actions.

Delegated Fees

One of the most basic usability enhancements we wanted to add was delegated fees.

Our UX team has confronted us several times with the fact that current blockchain design requires users like agricultural field agents to have a live wallet filled up with tokens — likely on their smartphone — just to send verification reports to contracts. This is simply untenable for non-technical users.

So we looked at where fees are being handled in the AnteHandler of the Cosmos SDK and made a few changes. In our fork, the Cosmos StdTx type now includes a field for FeeAccount. To check whether fees have been delegated to the account that actually signed the transaction, we created a delegation module that allows any account to delegate a fee allowance to any other account. Fee allowances can then be defined by implementing the FeeAllowance interface:

// FeeAllowance defines a permission for one account to use another account's balance
// to pay fees
type FeeAllowance interface {
// Accept checks whether this allowance allows the provided fees to be spent,
// and optionally updates the allowance or deletes it entirely
Accept(fee sdk.Coins, block abci.Header) (allow bool, updated FeeAllowance, delete bool)
}

This interface allows for quite a bit of flexibility as to how fees can be delegated. For example, a user could specify a daily or monthly limit.

For our use case, the verifier of the contract could be delegated a certain daily fee allowance by their employer and there would be no need for these agents to worry about tokens at all. Only the management of the organization would need to worry about keeping the company wallet filled and delegating appropriate allowances to its employees.

Delegated Actions

This solves part of the problem that our field verifier might encounter. This user can have a key on their phone that never touches tokens, but this also brings up many questions:

  • When the farmer and funder write their contract, what is the verifier key that actually signs the claim?
  • Do we know which particular field agent will actually sign the claim?
  • Does everything need to get signed by the main corporate key?
  • Do all field agents need to walk around with a phone that has the master key referenced in the contract?

A better solution would allow an organization to delegate some permissions — such as signing a verification claim — to particular employees. To solve this we came up with a generalized solution for delegated actions.

With delegated signing permissions, a smart phone-wielding verifier can participate in the verification of ecological outcomes without having any knowledge or responsibility for private key management. Photo by Tyler Mullins on Unsplash.

Delegated actions allow any user to delegate a permission to perform any action on their behalf to another user. We can do this with the Capability interface which allows for any level of permission granularity desired:

type Capability interface {
// MsgType returns the type of Msg's that this capability can accept
MsgType() sdk.Msg
// Accept determines whether this grant allows the provided action, and if
// so provides an upgraded capability grant
Accept(msg sdk.Msg, block abci.Header) (allow bool, updated Capability, delete bool)
}

This interface is pretty flexible and lets a Cosmos SDK developer define a capability for any sdk.Msg. Because capabilities can send back an updated state, we can write capabilities that set a total spend limit and are automatically deleted when that amount of coins has been spent. By default, every capability grant can also have an expiration time.

Key Groups

Another component of our generalized solution to the organizational and individual key management problem is key groups.

Key groups are basically multi-signature wallets that allow keys to be added or removed. Every member of a group can be assigned a weight and the group as a whole has a decision threshold (the number of weighted votes that need to be acquired to take an action). Key groups can include other groups or even contracts as their group members.

Groups can make proposals on any action that can be taken on the blockchain and send them back to the main app router via the delegation module that allows groups to execute actions that have been delegated to them.

To simplify the management of our verification organization, there could be a top-level key group for the owners of the company. That top-level group would delegate fees and the ability to sign specific contracts to some of their employees. In turn, each user could have a key group that includes the keys for all of their devices. Key groups could even include a trusted third-party provider that signs using normal username/password methods to create a multi-factor authentication scenario that requires a device plus the third-party to sign.

We believe these functionalities together form a solid basis for greatly simplifying key management for individuals and organizations and we hope they will enable wide adoption of Cosmos-based Dapps by non-technical users.

The other part of our user scenario involves a simple multi-party smart contract. Our hackathon team was pretty ambitious so while one part of the team was implementing the key management solutions described above, another part was integrating a WebAssembly (Wasm) virtual machine into the Cosmos SDK.

We chose to implement our smart contracting framework using a Wasm virtual machine because there is already a good ecosystem of tooling around Wasm and this would allow us to use a sophisticated language like Rust.

We originally explored Perlin Networks Life as our Wasm VM because it has built-in gas metering code, but we ran into some issues — possibly just due to lack of examples — and switched to Wasmer, which was straightforward to work with.

To integrate the Wasm VM with the rest of our Cosmos blockchain, we came up with an interesting model where a contract can act like any other account in the blockchain, sending messages back to the main message router as if the contract were a user that had signed a transaction. The messages that a contract sends are passed as the return value of the contract’s send function and then run after the contract returns so there is no issue of re-entrance.

To know whether a contract has permission to execute a specific action we needed another layer on top of Cosmos’s BaseApp router. This functionality is handled by the delegation module described above which checks if the action can be executed directly by the contract of if this action was delegated by some other account.

We defined a relatively simple API for smart contracts that covered our farmer + funder + verifier use case. This could be expanded as needed in the future, but was kept to minimal complexity needed to perform the task at hand.

HackAtom Berlin 2019 in full swing. Photo by Gregory Landua.

To run a contract, there are three steps:

  1. Upload the smart contract Wasm code. This is a large chunk (dozens of KB for now, but it can be optimized) and is stored one time, allowing us to instantiate multiple contract instances for a single piece of code without needing to upload that code multiple times.
  2. Create a contract, which is one instance of the code. This creates a new account for this instance, then moves any sent tokens to the contract and calls the init method exported by the Wasm code.
  3. Once there is a contract instance, you can call the contract any number of times, in order to execute the contract logic implemented in its send method exported from the Wasm code.

Both MsgCreateContract and MsgSendContract take a set of tokens to transfer to the contract, as well as a raw []byte with a contract-specific message. This message is known to the client and the Wasm code, but doesn't need to be known to the SDK code — just like Tendermint is agnostic to transaction bytes. For simplicity, we chose to settle on JSON as the serialization format. We were all quite happy with this, but other methods could be used in the future!

Looking at an example contract

When we pass information to the contract, we create a JSON message containing the contract_address, sender, and sent_funds from the SDK, along with the raw app-dependent bytes sent to the contract. This information is passed to both SendParams and InitParams:

#[derive(Serialize, Deserialize)]
pub struct InitParams<'a> {
contract_address: String,
sender: String,
#[serde(borrow)]
msg: &'a RawValue,
sent_funds: u64,
}
#[derive(Serialize, Deserialize)]
pub struct SendParams<'a> {
contract_address: String,
sender: String,
#[serde(borrow)]
msg: &'a RawValue,
sent_funds: u64,
}

Each contract can then define it’s own format for parsing the custom message, such as RegenInitMsg. Note that we pass sender and contract_address as standard Cosmos bech32 address strings, eg. cosmos1q.....

#[derive(Serialize, Deserialize)]
struct RegenInitMsg {
verifier: String,
beneficiary: String,
}

When the contract wants to react to these incoming messages, it needs some state to make its decisions. We export functions get_state() and set_state() to the Wasm contract, which allows it to set and get arbitrary data in a dedicated key in the contract substore. We could extend this API in the future, but it sufficed for now. We also define the contract's state as a JSON structure, which is initialized in the init call, and can be used to control execution of the send call.

#[derive(Serialize, Deserialize)]
struct RegenState {
verifier: String,
beneficiary: String,
payout: u64,
funder: String,
}

Now that we see the functionality that the framework exposes to a contract, we can see how easy it is to write some logic. init just stores the information on who can execute the contract. Note that it uses a mix of information passed by the SDK such as params.sent_funds and user-specified content such as msg.verifier and msg.beneficiary:

pub fn init(params: InitParams) -> Result<Vec<CosmosMsg>, Error> {
let msg: RegenInitMsg = from_str(params.msg.get())?;
set_state(to_vec(&RegenState {
verifier: msg.verifier,
beneficiary: msg.beneficiary,
payout: params.sent_funds,
funder: params.sender
})?);
Ok(Vec::new())
}

send can now compare the stored state with the SDK-verified params.sender to control whether to release the funds or not. Note that the return value of both init and send is Result<Vec<CosmosMsg>, Error>, which means it can return an Error code, or a (possibly empty) list of Cosmos messages to dispatch on success. In send, we use both of these features:

pub fn send(params: SendParams) -> Result<Vec<CosmosMsg>, Error> {
let mut state: RegenState = from_slice(&get_state())?;
let funds = state.payout + params.sent_funds;
state.payout = 0;
set_state(to_vec(&state)?);
if params.sender == state.verifier {
Ok(vec![CosmosMsg::SendTx {
from_address: params.contract_address,
to_address: state.beneficiary,
amount: vec![SendAmount {
denom: "earth".into(),
amount: funds.to_string(),
}],
}])
} else {
bail!("Unauthorized")
}
}

If the caller of MsgSendContract is the same as the verifier set in MsgCreateContract, then we will dispatch a message moving the funds inside the contract to the beneficiary specified in init. If it is called by any other user, then it will return an unauthorized error. (The observant reader may notice we hardcoded the token denomination to be earth, which we did to simplify parsing...with a bit of polish outside of the Hackatom, we can use proper sdk.Coins types).

Of course, this example is quite simple, but you can quickly see that a large variety of custom escrows, authorization, and key management solutions re possible to build with this framework, using only a few lines of Rust. Making that Rust easy to work with involved quite a bit of work wrangling Wasm and cgo bindings, and figuring how to pass arbitrary json through the wasm function call interface, which is defined in go-speak as f(args ...int32) int32. Details of that and how to interface Go, Rust, and Wasm through low-level C-bindings is quite interesting for anyone working to integrate Wasm into their go applications — but we will save that another blog post.

Since building these features at the Hackathon, there has been a rising interest withinin the Cosmos community, beyond Regen Network, in bringing these features to production level. As a follow-up to some discussions online, we have created two community groups to work on key management and WASM with the goal of producing PR’s to be merged into the Cosmos SDK:

Until now, most development on Cosmos SDK has happened directly via the Cosmos team, albeit with quite a large number of contributions from different community groups. The idea of a community group or working group is to create a space where members of different organizations can collaborate on common problems without the ownership of the process falling under either organization. For these community groups, we have created a simple structure involving a dedicated GitHub organization and a Telegram channel for live discussion.

This model could very possibly be replicated for other community efforts. We welcome community members interested in moving these features to production to join these groups and collaborate. As a near-term goal, Regen Network is intending to deploy at least the key management functionality described above to its live regen-test-1001 testnet, also as a test of this live upgrade PR. We hope these contributions bring long-lasting benefit to the community and are useful to many projects.

Team “Gaians” hard at work. Jehan, Ethan Frey, Pedro Gomes, Shane Vitarana, Aaron Craelius from left foreground, clockwise. Photo by Gregory Landua.

Regen Network

A blockchain network of ecological knowledge changing the economics of regenerative agriculture to reverse global warming. Learn more: https://regen.network

Aaron Craelius

Written by

CTO of regen.network, https://github.com/aaronc

Regen Network

A blockchain network of ecological knowledge changing the economics of regenerative agriculture to reverse global warming. Learn more: https://regen.network

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade