Skip to content

Rename Features to Capabilities #1387

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ and this project adheres to
- cosmwasm-std: Make `Decimal{,256}::DECIMAL_PLACES` a public `u32` value.
- cosmwasm-crypto: Bumped `k256` `0.10.4 -> 0.11` and `digest` `0.9 -> 0.10`
([#1374]).
- cosmwasm-vm: Rename features to capabilities, including
1. `features_from_csv` to `capabilities_from_csv`;
2. `CacheOptions::supported_features` to
`CacheOptions::available_capabilities`;
3. `MockInstanceOptions::supported_features` to
`MockInstanceOptions::available_capabilities`
4. `Instance::required_features` to `Instance::required_capabilities`
5. `AnalysisReport::required_features` to
`AnalysisReport::required_capabilities`.

[#1374]: https://github.com/CosmWasm/cosmwasm/pull/1374

Expand Down
2 changes: 1 addition & 1 deletion contracts/hackatom/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ fn make_init_msg() -> (InstantiateMsg, String) {
#[test]
fn proper_initialization() {
let mut deps = mock_instance(WASM, &[]);
assert_eq!(deps.required_features().len(), 0);
assert_eq!(deps.required_capabilities().len(), 0);

let verifier = String::from("verifies");
let beneficiary = String::from("benefits");
Expand Down
4 changes: 2 additions & 2 deletions contracts/staking/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ fn proper_initialization() {
);
let (instance_options, memory_limit) = mock_instance_options();
let mut deps = Instance::from_code(WASM, backend, instance_options, memory_limit).unwrap();
assert_eq!(deps.required_features().len(), 1);
assert!(deps.required_features().contains("staking"));
assert_eq!(deps.required_capabilities().len(), 1);
assert!(deps.required_capabilities().contains("staking"));

let creator = String::from("creator");
let msg = InstantiateMsg {
Expand Down
108 changes: 108 additions & 0 deletions docs/CAPABILITIES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Capabilities

Capabilities are a mechanism to negotiate functionality between a contract and
an environment (i.e. the chain that embeds cosmwasm-vm/[wasmvm]) in a very
primitive way. The contract defines required capabilities. The environment
defines it's capabilities. If the required capabilities are all available, the
contract can be used. Doing this check when the contract is first stored ensures
missing capabilities are detected early and not when a user tries to execute a
certain code path.

## Origin and Disambiguation

Before August 2022, we had two types of "features": app level features in the
CosmWasm VM and Cargo's build system features. In order to avoid the confusion,
the former have been renamed to capabilities.

Capabilities can be implemented in any language that compiles to Wasm whereas
features are Rust build system specific.

## Required capabilities

The contract defines required capabilities using marker export functions that
take no arguments and return no value. The name of the export needs to start
with "requires\_" followed by the name of the capability. When creating a new
capability, do yourself a favor and keep the name all lower ASCII
alphanumerical.

An example of such markers in cosmwasm-std are those:

```rust
#[cfg(feature = "iterator")]
#[no_mangle]
extern "C" fn requires_iterator() -> () {}

#[cfg(feature = "staking")]
#[no_mangle]
extern "C" fn requires_staking() -> () {}

#[cfg(feature = "stargate")]
#[no_mangle]
extern "C" fn requires_stargate() -> () {}
```

which in Wasm compile to this:

```
# ...
(export "requires_staking" (func 181))
(export "requires_stargate" (func 181))
(export "requires_iterator" (func 181))
# ...
(func (;181;) (type 12)
nop)
# ...
(type (;12;) (func))
```

As mentioned above, the Cargo features are independent of the capabilities we
talk about and it is perfectly fine to have a requires\_\* export that is
unconditional in a library or a contract.

The marker export functions can be executed, but the VM does not require such a
call to succeed. So a contract can use no-op implementation or crashing
implementation.

## Available capabilities

An instance of the main `Cache` has `available_capabilities` in its
`CacheOptions`. This value is set in the caller, such as
[here](https://github.com/CosmWasm/wasmvm/blob/v1.0.0-rc.0/libwasmvm/src/cache.rs#L75)
and
[here](https://github.com/CosmWasm/wasmvm/blob/v1.0.0-rc.0/libwasmvm/src/cache.rs#L62).
`capabilities_from_csv` takes a comma separated list and returns a set of
capabilities. This capabilities list is set
[in keeper.go](https://github.com/CosmWasm/wasmd/blob/v0.27.0-rc0/x/wasm/keeper/keeper.go#L100)
and
[in app.go](https://github.com/CosmWasm/wasmd/blob/v0.27.0-rc0/app/app.go#L475-L496).

## Common capabilities

Here is a list of capabilities created in the past. Since capabilities can be
created between contract and environment, we don't know them all in the VM.

- `iterator` is for storage backends that allow range queries. Not all types of
databases do that. There are trees that don't allow it and Secret Network does
not support iterators for other technical reasons.
- `stargate` is for messages and queries that came with the Cosmos SDK upgrade
"Stargate". It primarily includes protobuf messages and IBC support.
- `staking` is for chains with the Cosmos SDK staking module. There are Cosmos
chains that don't use this (e.g. Tgrade).

## What's a good capability?

A good capability makes sense to be disabled. The examples above explain why the
capability is not present in some environments.

Also when the environment adds new functionality in a way that does not break
existing contracts (such as new queries), capabilities can be used to ensure the
contract checks the availability early on.

When functionality is always present in the VM (such as a new import implemented
directly in the VM, see [#1299]), we should not use capability. They just create
fragmentation in the CosmWasm ecosystem and increase the barrier for adoption.
Instead the `check_wasm_imports` check is used to validate this when the
contract is stored.

[wasmvm]: https://github.com/CosmWasm/wasmvm
[#1299]: https://github.com/CosmWasm/cosmwasm/pull/1299
102 changes: 2 additions & 100 deletions docs/FEATURES.md
Original file line number Diff line number Diff line change
@@ -1,100 +1,2 @@
# Features

Features are a mechanism to negotiate functionality between a contract and an
environment (i.e. the chain that embeds cosmwasm-vm/[wasmvm]) in a very
primitive way. The contract defines required features. The environment defines
supported features. If the required features are all supported, the contract can
be used. Doing this check when the contract is first stored ensures missing
features are detected early and not when a user tries to execute a certain code
path.

## Disambiguation

This document is about app level features in the CosmWasm VM. Features should
not be confused with Cargo's build system features, even when connected.
Features can be implemented in any language that compiles to Wasm.

## Required features

The contract defines required features using a marker export function that takes
no arguments and returns no value. The name of the export needs to start with
"requires\_" followed by the name of the feature. Do yourself a favor and keep
the name all lower ASCII alphanumerical.

An example of such markers in cosmwasm-std are those:

```rust
#[cfg(feature = "iterator")]
#[no_mangle]
extern "C" fn requires_iterator() -> () {}

#[cfg(feature = "staking")]
#[no_mangle]
extern "C" fn requires_staking() -> () {}

#[cfg(feature = "stargate")]
#[no_mangle]
extern "C" fn requires_stargate() -> () {}
```

which in Wasm compile to this:

```
# ...
(export "requires_staking" (func 181))
(export "requires_stargate" (func 181))
(export "requires_iterator" (func 181))
# ...
(func (;181;) (type 12)
nop)
# ...
(type (;12;) (func))
```

As mentioned above, the Cargo features are independent of the features we talk
about and it is perfectly fine to have a requires\_\* export that is
unconditional in a library or a contract.

The marker export functions can be executed, but the VM does not require such a
call to succeed. So a contract can use no-op implementation or crashing
implementation.

## Supported features

An instance of the main `Cache` has `supported_features` in its `CacheOptions`.
This value is set in the caller, such as
[here](https://github.com/CosmWasm/wasmvm/blob/v1.0.0-rc.0/libwasmvm/src/cache.rs#L75)
and
[here](https://github.com/CosmWasm/wasmvm/blob/v1.0.0-rc.0/libwasmvm/src/cache.rs#L62).
`features_from_csv` takes a comma separated list and returns a set of features.
This features list is set
[in keeper.go](https://github.com/CosmWasm/wasmd/blob/v0.27.0-rc0/x/wasm/keeper/keeper.go#L100)
and
[in app.go](https://github.com/CosmWasm/wasmd/blob/v0.27.0-rc0/app/app.go#L475-L496).

## Common features

Here is a list of features created in the past. Since features can be created
between contract and environment, we don't know them all in the VM.

- `iterator` is for storage backends that allow range queries. Not all types of
databases do that. There are trees that don't allow it and Secret Network does
not support iterators for other technical reasons.
- `stargate` is for messages and queries that came with the Cosmos SDK upgrade
"Stargate". It primarily includes protobuf messages and IBC support.
- `staking` is for chains with the Cosmos SDK staking module. There are Cosmos
chains that don't use this (e.g. Tgrade).

## What's a good feature?

A good feature makes sense to be disabled. The examples above explain why the
feature is not present in some environments.

When functionality is always present in the VM (such as a new import implemented
directly in the VM, see [#1299]), we should not use features. They just create
fragmentation in the CosmWasm ecosystem and increase the barrier for adoption.
Instead the `check_wasm_imports` check is used to validate this when the
contract is stored.

[wasmvm]: https://github.com/CosmWasm/wasmvm
[#1299]: https://github.com/CosmWasm/cosmwasm/pull/1299
Features have been renamed to capabilities. See
[CAPABILITIES.md](./CAPABILITIES.md) in the same folder.
2 changes: 1 addition & 1 deletion packages/std/src/math/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1334,7 +1334,7 @@ mod tests {
];

// The regular std::ops::Mul is our source of truth for these tests.
for (x, y) in test_data.iter().cloned() {
for (x, y) in test_data.into_iter() {
assert_eq!(x * y, x.checked_mul(y).unwrap());
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/std/src/math/decimal256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1453,7 +1453,7 @@ mod tests {
];

// The regular std::ops::Mul is our source of truth for these tests.
for (x, y) in test_data.iter().cloned() {
for (x, y) in test_data.into_iter() {
assert_eq!(x * y, x.checked_mul(y).unwrap());
}
}
Expand Down
8 changes: 4 additions & 4 deletions packages/vm/benches/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use cosmwasm_vm::testing::{
mock_backend, mock_env, mock_info, mock_instance_options, MockApi, MockQuerier, MockStorage,
};
use cosmwasm_vm::{
call_execute, call_instantiate, features_from_csv, Cache, CacheOptions, Checksum, Instance,
call_execute, call_instantiate, capabilities_from_csv, Cache, CacheOptions, Checksum, Instance,
InstanceOptions, Size,
};

Expand Down Expand Up @@ -123,7 +123,7 @@ fn bench_cache(c: &mut Criterion) {

let options = CacheOptions {
base_dir: TempDir::new().unwrap().into_path(),
supported_features: features_from_csv("iterator,staking"),
available_capabilities: capabilities_from_csv("iterator,staking"),
memory_cache_size: MEMORY_CACHE_SIZE,
instance_memory_limit: DEFAULT_MEMORY_LIMIT,
};
Expand Down Expand Up @@ -163,7 +163,7 @@ fn bench_cache(c: &mut Criterion) {
group.bench_function("instantiate from fs", |b| {
let non_memcache = CacheOptions {
base_dir: TempDir::new().unwrap().into_path(),
supported_features: features_from_csv("iterator,staking"),
available_capabilities: capabilities_from_csv("iterator,staking"),
memory_cache_size: Size(0),
instance_memory_limit: DEFAULT_MEMORY_LIMIT,
};
Expand Down Expand Up @@ -229,7 +229,7 @@ pub fn bench_instance_threads(c: &mut Criterion) {
c.bench_function("multi-threaded get_instance", |b| {
let options = CacheOptions {
base_dir: TempDir::new().unwrap().into_path(),
supported_features: features_from_csv("iterator,staking"),
available_capabilities: capabilities_from_csv("iterator,staking"),
memory_cache_size: MEMORY_CACHE_SIZE,
instance_memory_limit: DEFAULT_MEMORY_LIMIT,
};
Expand Down
29 changes: 15 additions & 14 deletions packages/vm/examples/check_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,26 @@ use std::io::Read;

use clap::{App, Arg};

use cosmwasm_vm::features_from_csv;
use cosmwasm_vm::capabilities_from_csv;
use cosmwasm_vm::internals::{check_wasm, compile};

const DEFAULT_SUPPORTED_FEATURES: &str = "iterator,staking,stargate";
const DEFAULT_AVAILABLE_CAPABILITIES: &str = "iterator,staking,stargate";

pub fn main() {
eprintln!("`check_contract` will be removed from the next version of `cosmwasm-vm` - please use `cw-check-contract` instead.");
eprintln!("> cargo install cw-check-contract");

let matches = App::new("Contract checking")
.version("0.1.0")
.long_about("Checks the given wasm file (memories, exports, imports, supported features, and non-determinism).")
.long_about("Checks the given wasm file (memories, exports, imports, available capabilities, and non-determinism).")
.author("Mauro Lacy <mauro@lacy.com.es>")
.arg(
Arg::with_name("FEATURES")
Arg::with_name("CAPABILITIES")
// `long` setting required to turn the position argument into an option 🤷
.long("supported-features")
.value_name("FEATURES")
.help("Sets the supported features that the desired target chain supports")
.long("available-capabilities")
.aliases(&["FEATURES", "supported-features"]) // Old names
.value_name("CAPABILITIES")
.help("Sets the available capabilities that the desired target chain has")
.takes_value(true)
)
.arg(
Expand All @@ -32,12 +33,12 @@ pub fn main() {
)
.get_matches();

// Supported features
let supported_features_csv = matches
.value_of("FEATURES")
.unwrap_or(DEFAULT_SUPPORTED_FEATURES);
let supported_features = features_from_csv(supported_features_csv);
println!("Supported features: {:?}", supported_features);
// Available capabilities
let available_capabilities_csv = matches
.value_of("CAPABILITIES")
.unwrap_or(DEFAULT_AVAILABLE_CAPABILITIES);
let available_capabilities = capabilities_from_csv(available_capabilities_csv);
println!("Available capabilities: {:?}", available_capabilities);

// File
let path = matches.value_of("WASM").expect("Error parsing file name");
Expand All @@ -48,7 +49,7 @@ pub fn main() {
file.read_to_end(&mut wasm).unwrap();

// Check wasm
check_wasm(&wasm, &supported_features).unwrap();
check_wasm(&wasm, &available_capabilities).unwrap();

// Compile module
compile(&wasm, None, &[]).unwrap();
Expand Down
5 changes: 3 additions & 2 deletions packages/vm/examples/multi_threaded_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use tempfile::TempDir;
use cosmwasm_std::{coins, Empty};
use cosmwasm_vm::testing::{mock_backend, mock_env, mock_info, MockApi, MockQuerier, MockStorage};
use cosmwasm_vm::{
call_execute, call_instantiate, features_from_csv, Cache, CacheOptions, InstanceOptions, Size,
call_execute, call_instantiate, capabilities_from_csv, Cache, CacheOptions, InstanceOptions,
Size,
};

// Instance
Expand All @@ -27,7 +28,7 @@ const THREADS: usize = SAVE_WASM_THREADS + INSTANTIATION_THREADS;
pub fn main() {
let options = CacheOptions {
base_dir: TempDir::new().unwrap().into_path(),
supported_features: features_from_csv("iterator,staking"),
available_capabilities: capabilities_from_csv("iterator,staking"),
memory_cache_size: MEMORY_CACHE_SIZE,
instance_memory_limit: DEFAULT_MEMORY_LIMIT,
};
Expand Down
Loading