diff --git a/EntryPoints.md b/EntryPoints.md deleted file mode 100644 index f65cfed42..000000000 --- a/EntryPoints.md +++ /dev/null @@ -1,83 +0,0 @@ -# Defining Entry Points to Wasm - -## Exports - -`exports` are the functions that we export to the outside world. After -compilation these will be the only entry points that the application can call -into the web assembly contract. In general, the runtime will define a fixed set -of `exports` that must be implemented by the contract. - -To make an export in rust code, you can add the following lines: - -```rust -#[no_mangle] -pub extern "C" fn double(n: i32) -> i32 { - n * 2 -} -``` - -Note that you need the `#[no_mangle]` directive to keep the naming, and declare -it as `pub extern "C"` to create a proper C ABI, which is the standard interface -for Web Assembly, as well as FFI. - -## Imports - -If we want to interact with the outside world, the smart contract needs to -define a set of `imports`. These are function signatures we expect to be -implemented by the environment and provided when the VM is instantiated. If the -proper imports are not provided, you will receive an error upon instantiating -the contract. - -```rust -extern "C" { - fn c_read() -> *mut c_char; - fn c_write(string: *mut c_char); -} -``` - -The above expects the runtime to provide read/write access to some (persistent) -singleton. Notably, the contract has no knowledge how it is stored, and no way -to "jailbreak" or access other element of the database. - -## Memory Management - -If you look closely, you will see every function definition in `exports` accepts -a fixed number of arguments of type `i32` and returns one result of type `i32` -(or `void`). With such limitations, how can one pass in a `struct` to the -contract, or even a serialized byte array (eg. json blob). And how can we return -a string back? - -There is one more way in which the runtime can interact with a smart contract -instance. It can directly read and write to the linear memory of the smart -contract. In general, contracts are expected to export two well-defined -functions: - -```rust -#[no_mangle] -pub extern "C" fn allocate(size: usize) -> *mut c_void { - let mut buffer = Vec::with_capacity(size); - let pointer = buffer.as_mut_ptr(); - mem::forget(buffer); - pointer as *mut c_void -} - -#[no_mangle] -pub extern "C" fn deallocate(pointer: *mut c_void, capacity: usize) { - unsafe { - let _ = Vec::from_raw_parts(pointer, 0, capacity); - } -} -``` - -`allocate` heap allocates `size` bytes and tells the wasm alloc library not to -clear it (forget), after which it returns the pointer (integer offset) to the -caller. The caller can now safely write up to `size` bytes to the given offset, -eg. `copy(data, vm.Memory[offset:offset+size])`. Of course, this passes the -responsibility of freeing the memory from the wasm code to the caller, so make -sure to call `deallocate()` on the memory reference after the function call -finishes. - -We can explore more complex and idiomatic ways of passing data between the -environment and the wasm contract, but for now, just ensure you export these two -functions and the runtime will make use of them to get `string` and `[]byte` -into and out of the wasm contract. diff --git a/MIGRATING.md b/MIGRATING.md deleted file mode 100644 index 8ed661310..000000000 --- a/MIGRATING.md +++ /dev/null @@ -1,831 +0,0 @@ -# Migrating Contracts - -This guide explains what is needed to upgrade contracts when migrating over -major releases of `cosmwasm`. Note that you can also view the -[complete CHANGELOG](./CHANGELOG.md) to understand the differences. - -## 0.13 -> 0.14 (unreleased) - -- The minimum Rust supported version for 0.14 is 1.50.0. Verify your Rust - version is >= 1.50.0 with: `rustc --version` - -- Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): - - ``` - [dependencies] - cosmwasm-std = "0.14.0" - cosmwasm-storage = "0.14.0" - # ... - - [dev-dependencies] - cosmwasm-schema = "0.14.0" - cosmwasm-vm = "0.14.0" - # ... - ``` - -- Rename the `init` entry point to `instantiate`. Also, rename `InitMsg` to - `InstantiateMsg`. - -- Rename the `handle` entry point to `execute`. Also, rename `HandleMsg` to - `ExecuteMsg`. - -- Rename `InitResponse`, `HandleResponse` and `MigrateResponse` to `Response`. - The old names are still supported (with a deprecation warning), and will be - removed in the next version. Also, you'll need to add the `submessages` field - to `Response`. - -- Remove `from_address` from `BankMsg::Send`, which is now automatically filled - with the contract address: - - ```rust - // before - ctx.add_message(BankMsg::Send { - from_address: env.contract.address, - to_address: to_addr, - amount: balance, - }); - - // after - ctx.add_message(BankMsg::Send { - to_address: to_addr, - amount: balance, - }); - ``` - -- Use the new entry point system. From `lib.rs` remove - - ```rust - #[cfg(target_arch = "wasm32")] - cosmwasm_std::create_entry_points!(contract); - - // or - - #[cfg(target_arch = "wasm32")] - cosmwasm_std::create_entry_points_with_migration!(contract); - ``` - - Then add the macro attribute `#[entry_point]` to your `contract.rs` as - follows: - - ```rust - use cosmwasm_std::{entry_point, … }; - - // … - - #[entry_point] - pub fn init( - _deps: DepsMut, - _env: Env, - _info: MessageInfo, - _msg: InitMsg, - ) -> StdResult { - // … - } - - #[entry_point] - pub fn execute( - _deps: DepsMut, - _env: Env, - _info: MessageInfo, - _msg: ExecuteMsg, - ) -> StdResult { - // … - } - - // only if you have migrate - #[entry_point] - pub fn migrate( - deps: DepsMut, - env: Env, - _info: MessageInfo, - msg: MigrateMsg, - ) -> StdResult { - // … - } - - #[entry_point] - pub fn query(_deps: Deps, _env: Env, _msg: QueryMsg) -> StdResult { - // … - } - ``` - -- Since `Response` contains a `data` field, converting `Context` into `Response` - always succeeds. - - ```rust - // before - pub fn init(deps: DepsMut, env: Env, info: MessageInfo, msg: InitMsg) -> Result { - // … - let mut ctx = Context::new(); - ctx.add_attribute("Let the", "hacking begin"); - Ok(ctx.try_into()?) - } - - // after - pub fn init(deps: DepsMut, env: Env, info: MessageInfo, msg: InitMsg) -> Result { - // … - let mut ctx = Context::new(); - ctx.add_attribute("Let the", "hacking begin"); - Ok(ctx.into()) - } - ``` - -- Remove the `info: MessageInfo` field from the `migrate` entry point: - - ```rust - // Before - pub fn migrate( - deps: DepsMut, - env: Env, - _info: MessageInfo, - msg: MigrateMsg, - ) -> StdResult { - // ... - } - - // After - pub fn migrate(deps: DepsMut, env: Env, msg: MigrateMsg) -> StdResult { - // ... - } - ``` - - `MessageInfo::funds` was always empty since [MsgMigrateContract] does not have - a funds field. `MessageInfo::sender` should not be needed for authentication - because the chain checks permissions before calling `migrate`. If the sender's - address is needed for anything else, this should be expressed as part of the - migrate message. - - [msgmigratecontract]: - https://github.com/CosmWasm/wasmd/blob/v0.15.0/x/wasm/internal/types/tx.proto#L86-L96 - -- Add mutating helper methods to `Response` that can be used instead of creating - a `Context` that is later converted to a response: - - ```rust - // before - pub fn handle_impl(deps: DepsMut, env: Env, info: MessageInfo) -> Result { - // ... - - // release counter_offer to creator - let mut ctx = Context::new(); - ctx.add_message(BankMsg::Send { - to_address: state.creator, - amount: state.counter_offer, - }); - - // release collateral to sender - ctx.add_message(BankMsg::Send { - to_address: state.owner, - amount: state.collateral, - }); - - // .. - - ctx.add_attribute("action", "execute"); - Ok(ctx.into()) - } - - - // after - pub fn execute_impl(deps: DepsMut, env: Env, info: MessageInfo) -> Result { - // ... - - // release counter_offer to creator - let mut resp = Response::new(); - resp.add_message(BankMsg::Send { - to_address: state.creator, - amount: state.counter_offer, - }); - - // release collateral to sender - resp.add_message(BankMsg::Send { - to_address: state.owner, - amount: state.collateral, - }); - - // .. - - resp.add_attribute("action", "execute"); - Ok(resp) - } - ``` - -- If necessary, add a wildcard arm to the `match` of now non-exhaustive message - types `BankMsg`, `BankQuery`, `WasmMsg` and `WasmQuery`. - -## 0.12 -> 0.13 - -- The minimum Rust supported version for 0.13 is 1.47.0. Verify your Rust - version is >= 1.47.0 with: `rustc --version` - -- Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): - - ``` - [dependencies] - cosmwasm-std = "0.13.0" - cosmwasm-storage = "0.13.0" - # ... - - [dev-dependencies] - cosmwasm-schema = "0.13.0" - cosmwasm-vm = "0.13.0" - # ... - ``` - -## 0.11 -> 0.12 - -- Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): - - ``` - [dependencies] - cosmwasm-std = "0.12.0" - cosmwasm-storage = "0.12.0" - # ... - - [dev-dependencies] - cosmwasm-schema = "0.12.0" - cosmwasm-vm = "0.12.0" - # ... - ``` - -- In your contract's `.cargo/config` remove `--features backtraces`, which is - now available in Rust nightly only: - - ```diff - @@ -1,6 +1,6 @@ - [alias] - wasm = "build --release --target wasm32-unknown-unknown" - wasm-debug = "build --target wasm32-unknown-unknown" - -unit-test = "test --lib --features backtraces" - +unit-test = "test --lib" - integration-test = "test --test integration" - schema = "run --example schema" - ``` - - In order to use backtraces for debugging, run - `RUST_BACKTRACE=1 cargo +nightly unit-test --features backtraces`. - -- Rename the type `Extern` to `Deps`, and radically simplify the - `init`/`handle`/`migrate`/`query` entrypoints. Rather than - `&mut Extern`, use `DepsMut`. And instead of `&Extern`, use - `Deps`. If you ever pass eg. `foo(api: A)` around, you must now use - dynamic trait pointers: `foo(api: &dyn Api)`. Here is the quick search-replace - guide on how to fix `contract.rs`: - - _In production (non-test) code:_ - - - `` => `` - - `&mut Extern` => `DepsMut` - - `&Extern` => `Deps` - - `&mut deps.storage` => `deps.storage` where passing into `state.rs` helpers - - `&deps.storage` => `deps.storage` where passing into `state.rs` helpers - - On the top, remove `use cosmwasm_std::{Api, Extern, Querier, Storage}`. Add - `use cosmwasm_std::{Deps, DepsMut}`. - - _In test code only:_ - - - `&mut deps,` => `deps.as_mut(),` - - `&deps,` => `deps.as_ref(),` - - You may have to add `use cosmwasm_std::{Storage}` if the compile complains - about the trait - - _If you use cosmwasm-storage, in `state.rs`:_ - - - `` => `` - - `` => `` - - ` `<` - - `&mut S` => `&mut dyn Storage` - - `&S` => `&dyn Storage` - -- If you have any references to `ReadonlyStorage` left after the above, please - replace them with `Storage` - -## 0.10 -> 0.11 - -- Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): - - ``` - [dependencies] - cosmwasm-std = "0.11.0" - cosmwasm-storage = "0.11.0" - # ... - - [dev-dependencies] - cosmwasm-schema = "0.11.0" - cosmwasm-vm = "0.11.0" - # ... - ``` - -- Contracts now support any custom error type `E: ToString + From`. - Previously this has been `StdError`, which you can still use. However, you can - now create a much more structured error experience for your unit tests that - handels exactly the error cases of your contract. In order to get a convenient - implementation for `ToString` and `From`, we use the crate - [thiserror](https://crates.io/crates/thiserror), which needs to be added to - the contracts dependencies in `Cargo.toml`. To create the custom error, create - an error module `src/errors.rs` as follows: - - ```rust - use cosmwasm_std::{CanonicalAddr, StdError}; - use thiserror::Error; - - // thiserror implements Display and ToString if you - // set the `#[error("…")]` attribute for all cases - #[derive(Error, Debug)] - pub enum MyCustomError { - #[error("{0}")] - // let thiserror implement From for you - Std(#[from] StdError), - // this is whatever we want - #[error("Permission denied: the sender is not the current owner")] - NotCurrentOwner { - expected: CanonicalAddr, - actual: CanonicalAddr, - }, - #[error("Messages empty. Must reflect at least one message")] - MessagesEmpty, - } - ``` - - Then add `mod errors;` to `src/lib.rs` and `use crate::errors::MyCustomError;` - to `src/contract.rs`. Now adapt the return types as follows: - - - `fn init`: `Result`, - - `fn migrate` (if you have it): `Result`, - - `fn handle`: `Result`, - - `fn query`: `Result`. - - If one of your funtions does not use the custom error, you can continue to use - `StdError` as before. I.e. you can have `handle` returning - `Result` and `query` returning - `StdResult`. - - You can have a top-hevel `init`/`migrate`/`handle`/`query` that returns a - custom error but some of its implementations only return errors from the - standard library (`StdResult` aka. - `Result`). Then use `Ok(std_result?)` to convert - between the result types. E.g. - - ```rust - pub fn handle( - deps: &mut Extern, - env: Env, - msg: HandleMsg, - ) -> Result { - match msg { - // conversion to Result - HandleMsg::Bond {} => Ok(bond(deps, env)?), - // this already returns Result - HandleMsg::_BondAllTokens {} => _bond_all_tokens(deps, env), - } - } - ``` - - or - - ```rust - pub fn init( - deps: &mut Extern, - env: Env, - msg: InitMsg, - ) -> Result { - // … - - let mut ctx = Context::new(); - ctx.add_attribute("Let the", "hacking begin"); - Ok(ctx.try_into()?) - } - ``` - - Once you got familiar with the concept, you can create different error types - for each of the contract's functions. - - You can also try a different error library than - [thiserror](https://crates.io/crates/thiserror). The - [staking development contract](https://github.com/CosmWasm/cosmwasm/tree/main/contracts/staking) - shows how this would look like using [snafu](https://crates.io/crates/snafu). - -- Change order of arguments such that `storage` is always first followed by - namespace in `Bucket::new`, `Bucket::multilevel`, `ReadonlyBucket::new`, - `ReadonlyBucket::multilevel`, `PrefixedStorage::new`, - `PrefixedStorage::multilevel`, `ReadonlyPrefixedStorage::new`, - `ReadonlyPrefixedStorage::multilevel`, `bucket`, `bucket_read`, `prefixed` and - `prefixed_read`. - - ```rust - // before - let mut bucket = bucket::<_, Data>(b"data", &mut store); - - // after - let mut bucket = bucket::<_, Data>(&mut store, b"data"); - ``` - -- Rename `InitResponse::log`, `MigrateResponse::log` and `HandleResponse::log` - to `InitResponse::attributes`, `MigrateResponse::attributes` and - `HandleResponse::attributes`. Replace calls to `log` with `attr`: - - ```rust - // before - Ok(HandleResponse { - log: vec![log("action", "change_owner"), log("owner", owner)], - ..HandleResponse::default() - }) - - // after - Ok(HandleResponse { - attributes: vec![attr("action", "change_owner"), attr("owner", owner)], - ..HandleResponse::default() - }) - ``` - -- Rename `Context::add_log` to `Context::add_attribute`: - - ```rust - // before - let mut ctx = Context::new(); - ctx.add_log("action", "release"); - ctx.add_log("destination", &to_addr); - - // after - let mut ctx = Context::new(); - ctx.add_attribute("action", "release"); - ctx.add_attribute("destination", &to_addr); - ``` - -- Add result type to `Bucket::update` and `Singleton::update`: - - ```rust - // before - bucket.update(b"maria", |mayd: Option| { - let mut d = mayd.ok_or(StdError::not_found("Data"))?; - old_age = d.age; - d.age += 1; - Ok(d) - }) - - // after - bucket.update(b"maria", |mayd: Option| -> StdResult<_> { - let mut d = mayd.ok_or(StdError::not_found("Data"))?; - old_age = d.age; - d.age += 1; - Ok(d) - }) - ``` - -- Remove all `canonical_length` arguments from mock APIs in tests: - - ```rust - // before - let mut deps = mock_dependencies(20, &[]); - let mut deps = mock_dependencies(20, &coins(123456, "gold")); - let deps = mock_dependencies_with_balances(20, &[(&rich_addr, &rich_balance)]); - let api = MockApi::new(20); - - // after - let mut deps = mock_dependencies(&[]); - let mut deps = mock_dependencies(&coins(123456, "gold")); - let deps = mock_dependencies_with_balances(&[(&rich_addr, &rich_balance)]); - let api = MockApi::default(); - ``` - -- Add `MessageInfo` as separate arg after `Env` for `init`, `handle`, `migrate`. - Add `Env` arg to `query`. Use `info.sender` instead of `env.message.sender` - and `info.sent_funds` rather than `env.message.sent_funds`. Just changing the - function signatures of the 3-4 export functions should be enough, then the - compiler will warn you anywhere you use `env.message` - - ```rust - // before - pub fn init( - deps: &mut Extern, - env: Env, - msg: InitMsg, - ) { - deps.storage.set( - CONFIG_KEY, - &to_vec(&State { - verifier: deps.api.canonical_address(&msg.verifier)?, - beneficiary: deps.api.canonical_address(&msg.beneficiary)?, - funder: deps.api.canonical_address(&env.message.sender)?, - })?, - ); - } - - // after - pub fn init( - deps: &mut Extern, - _env: Env, - info: MessageInfo, - msg: InitMsg, - ) { - deps.storage.set( - CONFIG_KEY, - &to_vec(&State { - verifier: deps.api.canonical_address(&msg.verifier)?, - beneficiary: deps.api.canonical_address(&msg.beneficiary)?, - funder: deps.api.canonical_address(&info.sender)?, - })?, - ); - } - ``` - -- Test code now has `mock_info` which takes the same args `mock_env` used to. - You can just pass `mock_env()` directly into the function calls unless you - need to change height/time. -- One more object to pass in for both unit and integration tests. To do this - quickly, I just highlight all copies of `env` and replace them with `info` - (using Ctrl+D in VSCode or Alt+J in IntelliJ). Then I select all `deps, info` - sections and replace that with `deps, mock_env(), info`. This fixes up all - `init` and `handle` calls, then just add an extra `mock_env()` to the query - calls. - - ```rust - // before: unit test - let env = mock_env(creator.as_str(), &[]); - let res = init(&mut deps, env, msg).unwrap(); - - let query_response = query(&deps, QueryMsg::Verifier {}).unwrap(); - - // after: unit test - let info = mock_info(creator.as_str(), &[]); - let res = init(&mut deps, mock_env(), info, msg).unwrap(); - - let query_response = query(&deps, mock_env(), QueryMsg::Verifier {}).unwrap(); - - // before: integration test - let env = mock_env("creator", &coins(1000, "earth")); - let res: InitResponse = init(&mut deps, env, msg).unwrap(); - - let query_response = query(&mut deps, QueryMsg::Verifier {}).unwrap(); - - // after: integration test - let info = mock_info("creator", &coins(1000, "earth")); - let res: InitResponse = init(&mut deps, mock_env(), info, msg).unwrap(); - - let query_response = query(&mut deps, mock_env(), QueryMsg::Verifier {}).unwrap(); - ``` - -## 0.9 -> 0.10 - -- Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): - - ``` - [dependencies] - cosmwasm-std = "0.10.0" - cosmwasm-storage = "0.10.0" - # ... - - [dev-dependencies] - cosmwasm-schema = "0.10.0" - cosmwasm-vm = "0.10.0" - # ... - ``` - -Integration tests: - -- Calls to `Api::human_address` and `Api::canonical_address` now return a pair - of result and gas information. Change - - ```rust - // before - verifier: deps.api.canonical_address(&verifier).unwrap(), - - // after - verifier: deps.api.canonical_address(&verifier).0.unwrap(), - ``` - - The same applies for all calls of `Querier` and `Storage`. - -All Tests: - -All usages of `mock_env` will have to remove the first argument (no need of -API). - -```rust -// before -let env = mock_env(&deps.api, "creator", &coins(1000, "earth")); - -// after -let env = mock_env("creator", &coins(1000, "earth")); -``` - -Contracts: - -- All code that uses `message.sender` or `contract.address` should deal with - `HumanAddr` not `CanonicalAddr`. Many times this means you can remove a - conversion step. - -## 0.8 -> 0.9 - -- Update CosmWasm dependencies in Cargo.toml (skip the ones you don't use): - - ``` - [dependencies] - cosmwasm-std = "0.9.0" - cosmwasm-storage = "0.9.0" - # ... - - [dev-dependencies] - cosmwasm-schema = "0.9.0" - cosmwasm-vm = "0.9.0" - # ... - ``` - -`lib.rs`: - -- The C export boilerplate can now be reduced to the following code (see e.g. in - [hackatom/src/lib.rs](https://github.com/CosmWasm/cosmwasm/blob/0a5b3e8121/contracts/hackatom/src/lib.rs)): - - ```rust - mod contract; // contains init, handle, query - // maybe additional modules here - - #[cfg(target_arch = "wasm32")] - cosmwasm_std::create_entry_points!(contract); - ``` - -Contract code and uni tests: - -- `cosmwasm_storage::get_with_prefix`, `cosmwasm_storage::set_with_prefix`, - `cosmwasm_storage::RepLog::commit`, `cosmwasm_std::ReadonlyStorage::get`, - `cosmwasm_std::ReadonlyStorage::range`, `cosmwasm_std::Storage::set` and - `cosmwasm_std::Storage::remove` now return the value directly that was wrapped - in a result before. -- Error creator functions are now in type itself, e.g. - `StdError::invalid_base64` instead of `invalid_base64`. The free functions are - deprecated and will be removed before 1.0. -- Remove `InitResponse.data` in `init`. Before 0.9 this was not stored to chain - but ignored. -- Use `cosmwasm_storage::transactional` instead of the removed - `cosmwasm_storage::transactional_deps`. -- Replace `cosmwasm_std::Never` with `cosmwasm_std::Empty`. - -Integration tests: - -- Replace `cosmwasm_vm::ReadonlyStorage` with `cosmwasm_vm::Storage`, which now - contains all backend storage methods. -- Storage getters (and iterators) now return a result of - `(Option>, u64)`, where the first component is the element and the - second one is the gas cost. Thus in a few places `.0` must be added to access - the element. - -## 0.7.2 -> 0.8 - -### Update wasm code - -`Cargo.toml` dependencies: - -- Update to `schemars = "0.7"` -- Replace `cosmwasm = "0.7"` with `cosmwasm-std = "0.8"` -- Replace `cosmwasm-vm = "0.7"` with `cosmwasm-vm = "0.8"` -- Replace `cw-storage = "0.2"` with `cosmwasm-storage = "0.8"` -- Remove explicit `snafu` dependency. `cosmwasm_std` still uses it internally - but doesn't expose snafu specifics anymore. See more details on errors below. - -(Note: until release of `0.8`, you need to use git references for all -`cosmwasm_*` packages) - -`Cargo.toml` features: - -- Replace `"cosmwasm/backtraces"` with `"cosmwasm-std/backtraces"` - -Imports: - -- Replace all `use cosmwasm::X::Y` with `use cosmwasm_std::Y`, except for mock -- Replace all `use cosmwasm::mock::Y` with `use cosmwasm_std::testing::Y`. This - should only be used in test code. -- Replace `cw_storage:X` with `cosmwasm_storage::X` -- Replace `cosmwasm_std::Response` with `cosmwasm_std::HandleResponse` and - `cosmwasm_std::InitResponse` (different type for each call) - -`src/lib.rs`: - -This has been re-written, but is generic boilerplate and should be (almost) the -same in all contracts: - -- copy the new version from - [`contracts/queue`](https://github.com/CosmWasm/cosmwasm/blob/main/contracts/queue/src/lib.rs) -- Add `pub mod XYZ` directives for any modules you use besides `contract` - -Contract Code: - -- Add query to extern: - - Before: `my_func(deps: &Extern, ...` - - After: `my_func(deps: &Extern, ...` - - Remember to add `use cosmwasm_std::Querier;` -- `query` now returns `StdResult` instead of `Result>` - - You can also replace `to_vec(...)` with `to_binary(...)` -- No `.context(...)` is required after `from_slice` and `to_vec`, they return - proper `cosmwasm_std::Error` variants on errors. -- `env.message.signer` becomes `env.message.sender`. -- If you used `env.contract.balance`, you must now use the querier. The - following code block should work: - - ```rust - // before (in env) - let foo = env.contract.balance; - - // after (query my balance) - let contract_addr = deps.api.human_address(&env.contract.address)?; - let balance = deps.querier.query_all_balances(&contract_addr)?; - let foo = balance.amount; - ``` - -- Update the `CosmosMsg` enums used: - - - `CosmosMsg::Send{}` => `CosmosMsg::Bank(BankMsg::Send{})` - - `CosmosMsg::Opaque{ data }` => `CosmosMsg::Native{ msg }` - - `CosmosMsg::Contract` => `CosmosMsg::Wasm(WasmMsg::Execute{})` - -- Complete overhaul of `cosmwasm::Error` into `cosmwasm_std::StdError`: - - Auto generated snafu error constructor structs like `NotFound`/`ParseErr`/… - have been privatized in favour of error generation helpers like - `not_found`/`parse_err`/… - - All error generator functions now return errors instead of results, such - that e.g. `return unauthorized();` becomes `return Err(unauthorized());` - - Error cases don't contain `source` fields anymore. Instead source errors are - converted to standard types like `String`. For this reason, both - `snafu::ResultExt` and `snafu::OptionExt` cannot be used anymore. An error - wrapper now looks like `.map_err(invalid_base64)` and an `Option::None` to - error mapping looks like `.ok_or_else(|| not_found("State"))`. - - Backtraces became optional. Use `RUST_BACKTRACE=1` to enable them for unit - tests. - - `Utf8Err`/`Utf8StringErr` merged into `StdError::InvalidUtf8` - - `Base64Err` renamed into `StdError::InvalidBase64` - - `ContractErr`/`DynContractErr` merged into `StdError::GenericErr`, thus both - `contract_err` and `dyn_contract_err` must be replaced with `generic_err`. - - The unused `ValidationErr` was removed - -At this point `cargo wasm` should pass. - -### Update test code - -Both: - -- Update all imports from `cosmwasm::mock::*` to `cosmwasm_std::testing::*` -- Use `from_binary` not `from_slice` on all query responses (update imports) - - `from_slice(res.as_slice())` -> `from_binary(&res)` -- Replace `coin("123", "FOO")` with `coins(123, "FOO")`. We renamed it to coins - to be more explicit that it returns `Vec`, and now accept a `u128` as - the first argument for better type-safety. `coin` is now an alias to - `Coin::new` and returns one `Coin`. -- Remove the 4th argument (contract balance) from all calls to `mock_env`, this - is no longer stored in the environment. -- `dependencies` was renamed to `mock_dependencies`. `mock_dependencies` and - `mock_instance` take a 2nd argument to set the contract balance (visible for - the querier). If you need to set more balances, use `mock_XX_with_balances`. - The follow code block explains: - - ```rust - // before: balance as last arg in mock_env - let mut deps = dependencies(20); - let env = mock_env(&deps.api, "creator", &coins(15, "earth"), &coins(1015, "earth")); - - // after: balance as last arg in mock_dependencies - let mut deps = mock_dependencies(20, &coins(1015, "earth")); - let env = mock_env(&deps.api, "creator", &coins(15, "earth")); - ``` - -Unit Tests: - -- Replace `dependencies` with `mock_dependencies` - -Integration Tests: - -- We no longer check errors as strings but have rich types: - - Before: - `match err { ContractResult::Err(msg) => assert_eq!(msg, "Unauthorized"), ... }` - - After: `match err { Err(StdError::Unauthorized{ .. }) => {}, ... }` -- Remove all imports / use of `ContractResult` -- You must specify `CosmosMsg::Native` type when calling - `cosmwasm_vm::testing::{handle, init}`. You will want to - `use cosmwasm_std::{HandleResult, InitResult}` or - `use cosmwasm_std::{HandleResponse, InitResponse}`. If you don't use custom - native types, simply update calls as follows: - - `let res = init(...)` => `let res: InitResult = init(...)` - - `let res = init(...).unwrap()` => - `let res: InitResponse = init(...).unwrap()` - - `let res = handle(...)` => `let res: HandleResult = handle(...)` - - `let res = handle(...).unwrap()` => - `let res: HandleResponse = handle(...).unwrap()` - -### Update schema code - -All helper functions have been moved into a new `cosmwasm-schema` package. - -- Add `cosmwasm-schema = "0.8"` to `[dev-dependencies]` in `Cargo.toml` -- Remove `serde_json` `[dev-dependency]` if there, as cosmwasm-schema will - handle JSON output internally. -- Update `examples/schema.rs` to look - [more like queue](https://github.com/CosmWasm/cosmwasm/blob/main/contracts/queue/examples/schema.rs), - but replacing all the imports and type names with those you currently have. -- Regenerate schemas with `cargo schema` - -### Polishing - -After so many changes, remember to let the linters do their jobs. - -- `cargo fmt` -- `cargo clippy` diff --git a/README.md b/README.md index d49a33f42..4df51f432 100644 --- a/README.md +++ b/README.md @@ -1,331 +1,14 @@ # CosmWasm -[![CircleCI](https://circleci.com/gh/CosmWasm/cosmwasm/tree/main.svg?style=shield)](https://circleci.com/gh/CosmWasm/cosmwasm/tree/main) +[![Tests](https://github.com/line/cosmwasm/actions/workflows/tests.yml/badge.svg)](https://github.com/line/cosmwasm/actions/workflows/tests.yml) -**WebAssembly Smart Contracts for the Cosmos SDK.** +**WebAssembly Smart Contracts for the LBM** +This repository is forked from [CosmWasm/cosmwasm](https://github.com/CosmWasm/cosmwasm). -## Packages +For more detail, see the original documents. -The following packages are maintained here: - -| Crate | Usage | Download | Docs | -| ---------------- | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | -| cosmwasm-crypto | Internal only | [![cosmwasm-crypto on crates.io](https://img.shields.io/crates/v/cosmwasm-crypto.svg)](https://crates.io/crates/cosmwasm-crypto) | [![Docs](https://docs.rs/cosmwasm-crypto/badge.svg)](https://docs.rs/cosmwasm-crypto) | -| cosmwasm-derive | Internal only | [![cosmwasm-derive on crates.io](https://img.shields.io/crates/v/cosmwasm-derive.svg)](https://crates.io/crates/cosmwasm-derive) | [![Docs](https://docs.rs/cosmwasm-derive/badge.svg)](https://docs.rs/cosmwasm-derive) | -| cosmwasm-schema | Contract development | [![cosmwasm-schema on crates.io](https://img.shields.io/crates/v/cosmwasm-schema.svg)](https://crates.io/crates/cosmwasm-schema) | [![Docs](https://docs.rs/cosmwasm-schema/badge.svg)](https://docs.rs/cosmwasm-schema) | -| cosmwasm-std | Contract development | [![cosmwasm-std on crates.io](https://img.shields.io/crates/v/cosmwasm-std.svg)](https://crates.io/crates/cosmwasm-std) | [![Docs](https://docs.rs/cosmwasm-std/badge.svg)](https://docs.rs/cosmwasm-std) | -| cosmwasm-storage | Contract development | [![cosmwasm-storage on crates.io](https://img.shields.io/crates/v/cosmwasm-storage.svg)](https://crates.io/crates/cosmwasm-storage) | [![Docs](https://docs.rs/cosmwasm-storage/badge.svg)](https://docs.rs/cosmwasm-storage) | -| cosmwasm-vm | Host environments | [![cosmwasm-vm on crates.io](https://img.shields.io/crates/v/cosmwasm-vm.svg)](https://crates.io/crates/cosmwasm-vm) | [![Docs](https://docs.rs/cosmwasm-vm/badge.svg)](https://docs.rs/cosmwasm-vm) | - -## Overview - -To get that contract to interact with a system needs many moving parts. To get -oriented, here is a list of the various components of the CosmWasm ecosystem: - -**Standard library:** - -This code is compiled into Wasm bytecode as part of the smart contract. - -- [cosmwasm-std](https://github.com/CosmWasm/cosmwasm/tree/main/packages/std) - - A crate in this workspace. Provides the bindings and all imports needed to - build a smart contract. -- [cosmwasm-storage](https://github.com/CosmWasm/cosmwasm/tree/main/packages/storage) - - A crate in this workspace. This optional addition to `cosmwasm-std` includes - convenience helpers for interacting with storage. - -**Building contracts:** - -- [cosmwasm-template](https://github.com/CosmWasm/cosmwasm-template) - A - starter-pack to get you quickly building your custom contract compatible with - the cosmwasm system. -- [cosmwasm-examples](https://github.com/CosmWasm/cosmwasm-examples) - Some - sample contracts (build with cosmwasm-template) for use and inspiration. - Please submit your contract via PR. -- [cosmwasm-opt](https://github.com/CosmWasm/cosmwasm-opt) - A docker image and - scripts to take your Rust code and produce the smallest possible Wasm output, - deterministically. This is designed both for preparing contracts for - deployment as well as validating that a given deployed contract is based on - some given source code, allowing a - [similar contract verification algorithm](https://medium.com/coinmonks/how-to-verify-and-publish-on-etherscan-52cf25312945) - as Etherscan. -- [serde-json-wasm](https://github.com/CosmWasm/serde-json-wasm) - A custom json - library, forked from `serde-json-core`. This provides an interface similar to - `serde-json`, but without ay floating-point instructions (non-deterministic) - and producing builds around 40% of the code size. - -**Executing contracts:** - -- [cosmwasm-vm](https://github.com/CosmWasm/cosmwasm/tree/main/packages/vm) - A - crate in this workspace. Uses the [wasmer](https://github.com/wasmerio/wasmer) - engine to execute a given smart contract. Also contains code for gas metering, - storing, and caching wasm artifacts. -- [go-cosmwasm](https://github.com/CosmWasm/go-cosmwasm) - High-level go - bindings to all the power inside `cosmwasm-vm`. Easily allows you to upload, - instantiate and execute contracts, making use of all the optimizations and - caching available inside `cosmwasm-vm`. -- [wasmd](https://github.com/CosmWasm/wasmd) - A basic Cosmos SDK app to host - WebAssembly smart contracts. - -Ongoing work is currently tracked -[on this project board](https://github.com/orgs/CosmWasm/projects/1) for all of -the blockchain / contract work. - -## Creating a Smart Contract - -You can see some examples of contracts under the `contracts` directory, which -you can look at. - -If you want to get started building you own, the simplest way is to go to the -[cosmwasm-template](https://github.com/CosmWasm/cosmwasm-template) repository -and follow the instructions. This will give you a simple contract along with -tests, and a properly configured build environment. From there you can edit the -code to add your desired logic and publish it as an independent repo. - -## API entry points - -WebAssembly contracts are basically black boxes. The have no default entry -points, and no access to the outside world by default. To make them useful, we -need to add a few elements. - -If you haven't worked with WebAssembly before, please read an overview on -[how to create imports and exports](./EntryPoints.md) in general. - -### Exports - -The required exports provided by the cosmwasm smart contract are: - -```rust -extern "C" fn allocate(size: usize) -> u32; -extern "C" fn deallocate(pointer: u32); - -extern "C" fn instantiate(env_ptr: u32, info_ptr: u32, msg_ptr: u32) -> u32; -extern "C" fn execute(env_ptr: u32, info_ptr: u32, msg_ptr: u32) -> u32; -extern "C" fn query(env_ptr: u32, msg_ptr: u32) -> u32; -extern "C" fn migrate(env_ptr: u32, info_ptr: u32, msg_ptr: u32) -> u32; -``` - -`allocate`/`deallocate` allow the host to manage data within the Wasm VM. If -you're using Rust, you can implement them by simply -[re-exporting them from cosmwasm::exports](https://github.com/CosmWasm/cosmwasm/blob/v0.6.3/contracts/hackatom/src/lib.rs#L5). -`instantiate`, `execute` and `query` must be defined by your contract. - -### Imports - -The imports provided to give the contract access to the environment are: - -```rust -// This interface will compile into required Wasm imports. -// A complete documentation those functions is available in the VM that provides them: -// https://github.com/CosmWasm/cosmwasm/blob/0.7/lib/vm/src/instance.rs#L43 -// -extern "C" { - fn db_read(key: u32) -> u32; - fn db_write(key: u32, value: u32); - fn db_remove(key: u32); - - // scan creates an iterator, which can be read by consecutive next() calls - #[cfg(feature = "iterator")] - fn db_scan(start: u32, end: u32, order: i32) -> u32; - #[cfg(feature = "iterator")] - fn db_next(iterator_id: u32) -> u32; - - fn canonicalize_address(source: u32, destination: u32) -> u32; - fn humanize_address(source: u32, destination: u32) -> u32; - - /// Verifies message hashes against a signature with a public key, using the - /// secp256k1 ECDSA parametrization. - /// Returns 0 on verification success, 1 on verification failure, and values - /// greater than 1 in case of error. - fn secp256k1_verify(message_hash_ptr: u32, signature_ptr: u32, public_key_ptr: u32) -> u32; - - /// Verifies a message against a signature with a public key, using the - /// ed25519 EdDSA scheme. - /// Returns 0 on verification success, 1 on verification failure, and values - /// greater than 1 in case of error. - fn ed25519_verify(message_ptr: u32, signature_ptr: u32, public_key_ptr: u32) -> u32; - - /// Verifies a batch of messages against a batch of signatures and public keys, using the - /// ed25519 EdDSA scheme. - /// Returns 0 on verification success, 1 on verification failure, and values - /// greater than 1 in case of error. - fn ed25519_batch_verify(messages_ptr: u32, signatures_ptr: u32, public_keys_ptr: u32) -> u32; - - /// Executes a query on the chain (import). Not to be confused with the - /// query export, which queries the state of the contract. - fn query_chain(request: u32) -> u32; -} - -``` - -(from -[imports.rs](https://github.com/CosmWasm/cosmwasm/blob/0.7/src/imports.rs)) - -You could actually implement a WebAssembly module in any language, and as long -as you implement these functions, it will be interoperable, given the JSON data -passed around is the proper format. - -Note that these `*c_void` pointers refers to a Region pointer, containing the -offset and length of some Wasm memory, to allow for safe access between the -caller and the contract: - -```rust -/// Refers to some heap allocated data in Wasm. -/// A pointer to an instance of this can be returned over FFI boundaries. -/// -/// This struct is crate internal since the VM defined the same type independently. -#[repr(C)] -pub struct Region { - pub offset: u32, - /// The number of bytes available in this region - pub capacity: u32, - /// The number of bytes used in this region - pub length: u32, -} -``` - -(from -[memory.rs](https://github.com/CosmWasm/cosmwasm/blob/main/src/memory.rs#L7-L13)) - -## Implementing the Smart Contract - -If you followed the [instructions above](#Creating), you should have a runable -smart contract. You may notice that all of the Wasm exports are taken care of by -`lib.rs`, which should shouldn't need to modify. What you need to do is simply -look in `contract.rs` and implement `instantiate` and `execute` functions, -defining your custom `InstantiateMsg` and `ExecuteMsg` structs for parsing your -custom message types (as json): - -```rust -pub fn instantiate( - deps: &mut Deps, - env: Env, - info: MessageInfo, - msg: InstantiateMsg, -) -> StdResult {} - -pub fn execute( - deps: &mut Deps, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { } - -pub fn migrate( - deps: &mut Deps, - env: Env, - info: MessageInfo, - msg: ExecuteMsg, -) -> Result { } - -pub fn query( - deps: &Deps, - env: Env, - msg: QueryMsg, -) -> StdResult { } -``` - -The low-level `c_read` and `c_write` imports are nicely wrapped for you by a -`Storage` implementation (which can be swapped out between real Wasm code and -test code). This gives you a simple way to read and write data to a custom -sub-database that this contract can safely write as it wants. It's up to you to -determine which data you want to store here: - -```rust -pub trait Storage { - fn get(&self, key: &[u8]) -> Option>; - fn set(&mut self, key: &[u8], value: &[u8]); -} -``` - -## Testing the Smart Contract (rust) - -For quick unit tests and useful error messages, it is often helpful to compile -the code using native build system and then test all code except for the -`extern "C"` functions (which should just be small wrappers around the real -logic). - -If you have non-trivial logic in the contract, please write tests using rust's -standard tooling. If you run `cargo test`, it will compile into native code -using the `debug` profile, and you get the normal test environment you know and -love. Notably, you can add plenty of requirements to `[dev-dependencies]` in -`Cargo.toml` and they will be available for your testing joy. As long as they -are only used in `#[cfg(test)]` blocks, they will never make it into the -(release) Wasm builds and have no overhead on the production artifact. - -Note that for tests, you can use the `MockStorage` implementation which gives a -generic in-memory hashtable in order to quickly test your logic. You can see a -[simple example how to write a test](https://github.com/CosmWasm/cosmwasm/blob/81b6702d3994c8c34fb51c53176993b7e672860b/contracts/hackatom/src/contract.rs#L70-L88) -in our sample contract. - -## Testing the Smart Contract (wasm) - -You may also want to ensure the compiled contract interacts with the environment -properly. To do so, you will want to create a canonical release build of the -`.wasm` file and then write tests in with the same VM tooling we will -use in production. This is a bit more complicated but we added some tools to -help in -[cosmwasm-vm](https://github.com/CosmWasm/cosmwasm/tree/main/packages/vm) which -can be added as a `dev-dependency`. - -You will need to first compile the contract using `cargo wasm`, then load this -file in the integration tests. Take a -[look at the sample tests](https://github.com/CosmWasm/cosmwasm/blob/main/contracts/hackatom/tests/integration.rs) -to see how to do this... it is often quite easy to port a unit test to an -integration test. - -## Production Builds - -The above build process (`cargo wasm`) works well to produce wasm output for -testing. However, it is quite large, around 1.5 MB likely, and not suitable for -posting to the blockchain. Furthermore, it is very helpful if we have -reproducible build step so others can prove the on-chain wasm code was generated -from the published rust code. - -For that, we have a separate repo, -[rust-optimizer](https://github.com/CosmWasm/rust-optimizer) that provides a -[docker image](https://hub.docker.com/r/CosmWasm/rust-optimizer/tags) for -building. For more info, look at -[rust-optimizer README](https://github.com/CosmWasm/rust-optimizer/blob/master/README.md#usage), -but the quickstart guide is: - -```sh -docker run --rm -v "$(pwd)":/code \ - --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ - --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ - cosmwasm/rust-optimizer:0.10.8 -``` - -It will output a highly size-optimized build as `contract.wasm` in `$CODE`. With -our example contract, the size went down to 126kB (from 1.6MB from -`cargo wasm`). If we didn't use serde-json, this would be much smaller still... - -## Benchmarking - -You may want to compare how long the contract takes to run inside the Wasm VM -compared to in native rust code, especially for computationally intensive code, -like hashing or signature verification. - -**TODO** add instructions - -## Developing - -The ultimate auto-updating guide to building this project is the CI -configuration in `.circleci/config.yml`. - -For manually building this repo locally during development, here are a few -commands. They assume you use a stable Rust version by default and have a -nightly toolchain installed as well. - -**Workspace** - -```sh -./devtools/check_workspace.sh -``` - -**Contracts** - -| Step | Description | Command | -| ---- | -------------------------------- | -------------------------------------- | -| 1 | fast checks, rebuilds lock files | `./devtools/check_contracts_fast.sh` | -| 2 | medium fast checks | `./devtools/check_contracts_medium.sh` | -| 3 | slower checks | `./devtools/check_contracts_full.sh` | +- [REDAME.md](https://github.com/CosmWasm/cosmwasm/blob/main/README.md) +- [SEMANTICS.md](https://github.com/CosmWasm/cosmwas/blob/main/SEMANTICS.md) +- [EntryPoints.md](https://github.com/CosmWasm/cosmwasm/blob/main/EntryPoints.md) +- [MIGRATING.md](https://github.com/CosmWasm/cosmwasm/blob/main/MIGRATING.md) +- [IBC.md](https://github.com/CosmWasm/cosmwasm/blob/main/IBC.md)