diff --git a/src/pages/core/semtantics.mdx b/src/pages/core/semtantics.mdx new file mode 100644 index 00000000..10b2f949 --- /dev/null +++ b/src/pages/core/semtantics.mdx @@ -0,0 +1,261 @@ +--- +tags: ["core"] +--- + +# Contract Semantics + +This document aims to clarify the semantics of how a CosmWasm contract interacts with its +environment. There are two main types of actions: _mutating_ actions, which are able to modify the +state of the blockchain, and _query_ actions, which are run on a single node with read-only access +to the data. + +## Definitions + +**Contract** is as some wasm code uploaded to the system, initialized at the creation of the +contract. This has no state except that which is contained in the wasm code (eg. static constants) + +**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. This state +is stored in the kvstore, meaning a reference to the code plus a reference to the (prefixed) data +store uniquely defines the smart contract. + +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_ + +## Execution + +In the section below, we will discuss how the `execute` call works, but the same semantics apply to +any other _mutating_ action - `instantiate`, `migrate`, `sudo`, etc. + +### SDK Context + +Before looking at CosmWasm, we should look at the semantics enforced by the blockchain framework we +integrate with - the [Cosmos SDK](https://v1.cosmos.network/sdk). It is based upon the +[Tendermint BFT](https://tendermint.com/core/) Consensus Engine. Let us first look how they process +transactions before they arrive in CosmWasm. + +First, the Tendermint engine will seek 2/3+ consensus on a list of transactions to be included in +the next block. This is done _without executing them_. They are simply subjected to a minimal +pre-filter by the Cosmos SDK module, to ensure they are validly formatted transactions, with +sufficient gas fees, and signed by an account with sufficient fees to pay it. Notably, this means +many transactions that error may be included in a block. + +Once a block is committed, the transactions are then fed to the Cosmos SDK sequentially in order to +execute them. Each one returns a result or error along with event logs, which are recorded in the +`TxResults` section of the next block. The `AppHash` (or merkle proof or blockchain state) after +executing the block is also included in the next block. + +The Cosmos SDK `BaseApp` handles each transaction in an isolated context. It first verifies all +signatures and deducts the gas fees. It sets the "Gas Meter" to limit the execution to the amount of +gas paid for by the fees. Then it makes an isolated context to run the transaction. This allows the +code to read the current state of the chain (after the last transaction finished), but it only +writes to a cache, which may be committed or rolled back on error. + +A transaction may consist of multiple messages and each one is executed in turn under the same +context and same gas limit. If all messages succeed, the context will be committed to the underlying +blockchain state and the results of all messages will be stored in the `TxResult`. If one message +fails, all later messages are skipped and all state changes are reverted. This is very important for +atomicity. That means Alice and Bob can both sign a transaction with 2 messages: Alice pays Bob 1000 +ATOM, Bob pays Alice 50 ETH, and if Bob doesn't have the funds in his account, Alice's payment will +also be reverted. This is just like a DB Transaction typically works. + +[`x/wasm`](https://github.com/CosmWasm/wasmd/tree/master/x/wasm) is a custom Cosmos SDK module, +which processes certain messages and uses them to upload, instantiate, and execute smart contracts. +In particular, it accepts a properly signed +[`MsgExecuteContract`](https://github.com/CosmWasm/wasmd/blob/master/proto/cosmwasm/wasm/v1/tx.proto), +routes it to +[`Keeper.Execute`](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/keeper.go), which +loads the proper smart contract and calls `execute` on it. Note that this method may either return a +success (with data and events) or an error. In the case of an error here, it will revert the entire +transaction in the block. This is the context we find ourselves in when our contract receives the +`execute` call. + +### Basic Execution + +When we implement a contract, we provide the following entry point: + +```rust template="core" +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn execute( + deps: DepsMut, + env: Env, + info: MessageInfo, + msg: ExecuteMsg, +) -> Result { + // [...] + Ok(Response::new()) +} +``` + +With [`DepsMut`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.DepsMut.html), this can +read and write to the backing +[`Storage`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/trait.Storage.html), as well as use the +[`Api`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/trait.Api.html) to validate addresses, and +use [`QuerierWrapper`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.QuerierWrapper.html) +the state of other contracts or native modules. Once it is done, it returns either `Ok(Response)` or +`Err(ContractError)`. + +If it returns `Err`, this error is converted to a string representation, and it's returned to the +SDK module. _All state changes are reverted_ and `x/wasm` returns this error message, which will +_generally_ abort the transaction, and return the error message to the external caller. + +If it returns `Ok`, the +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) object is parsed +and processed. + +In the Cosmos SDK, a transaction returns a number of events to the user, along with an optional data +"result". This result is hashed into the next block hash to be provable and can return some +essential state (although in general client apps rely on Events more). This result is more commonly +used to pass results between contracts or modules in the sdk. + +### Dispatching Submessages + +Now let's move onto the `messages` field of the +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html). Some contracts +are fine only talking with themselves. But many want to move tokens or call into other contracts for +more complex actions. This is where messages come in. We return +[`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html), which is a +serializable representation of any external call the contract can make. + +This may be hard to understand at first. "Why can't I just call another contract?", you may ask. +However, we do this to prevent one of most widespread and hardest to detect security holes in +Ethereum contracts - reentrancy. We do this by following the actor model, which doesn't nest +function calls, but returns messages that will be executed later. This means all state that is +carried over between one call and the next happens in storage and not in memory. For more +information on this design, I recommend you read +[our docs on the Actor Model](architecture/actor-model.mdx). + +A common request was the ability to get the result from one of the messages you dispatched. For +example, you want to create a new contract with +[`WasmMsg::Instantiate`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.WasmMsg.html#variant.Instantiate), +but then you need to store the address of the newly created contract in the caller. This is possible +with `messages`. It also solves a similar use-case of capturing the error results, so if you execute +a message from e.g. a cron contract, it can store the error message and mark the message as run, +rather than aborting the whole transaction. It also allows for limiting the gas usage of the +submessage. + +This makes use of +[`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html) as above, but it +wraps it inside a [`SubMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsg.html) +envelope. + +What are the semantics of a submessage execution. First, we create a sub-transaction context around +the state, allowing it to read the latest state written by the caller, but write to yet-another +cache. If `gas_limit` is set, it is sandboxed to how much gas it can use until it aborts with +`OutOfGasError`. This error is caught and returned to the caller like any other error returned from +contract execution (unless it burned the entire gas limit of the transaction). What is more +interesting is what happens on completion. + +If it return success, the temporary state is committed (into the caller's cache), and the +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) is processed as +normal. Once the response is fully processed, this may then be intercepted by the calling contract +(for `ReplyOn::Always` and `ReplyOn::Success`). On an error, the subcall will revert any partial +state changes due to this message, but not revert any state changes in the calling contract. The +error may then be intercepted by the calling contract (for `ReplyOn::Always` and `ReplyOn::Error`). +_In this case, the messages error doesn't abort the whole transaction_ + +Note, that error doesn't abort the whole transaction _if and only if_ the `reply` is called - so in +case of `ReplyOn::Always` and `ReplyOn::Error`. If the submessage is called with `ReplyOn::Success` +(or `ReplyOn::Never`, which makes it effectively a normal message), the error in subsequent call +would result in failing whole transaction and not commit the changes for it. The rule here is as +follows: if for any reason you want your message handling to succeed on submessage failure, you +always have to reply on failure. + +Obviously - on the successful processing of sub-message, if the reply is not called (in particular +`ReplyOn::Error`), the whole transaction is assumed to succeed, and is committed. + +#### Handling the Reply + +In order to make use of `messages`, the calling contract must have an extra entry point: + +```rust filename="contract.rs" template="core" +#[cfg_attr(not(feature = "library"), entry_point)] +pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult { + // [...] + Ok(Response::new()) +} +``` + +Once the submessage handling is finished, the caller will get a chance to handle the result. It will +get the original `id` of the subcall and the `Result` of the execution, both success and error. Note +that it includes all events returned by the submessage, which applies to native sdk modules (like +Bank) as well as the contracts. If you need more state, you must save some local context to the +store (under the `id`) before returning the `message` in the original `execute`, and load it in +`reply`. We explicitly prohibit passing information in contract memory, as that is the key vector +for reentrancy attacks, which are a large security surface area in Ethereum. + +The `reply` call may return `Err` itself, in which case it is treated like the caller errored, and +aborting the transaction. However, on successful processing, `reply` may return a normal +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html), which will be +processed as normal - events added to the `EventManager`, and all `messages` dispatched as described +above. When `Err` is returned by any message handler in the given transaction, the whole transaction +is reverted. + +### TODO tkulik: NOT TRUE: THERE'S NO EVIDENCE FOR THAT - MSGS ARE HANDLED BY WASMD, ONLY SINGLE RESPONSE IS RETURNED + +The responses emitted by the submessage are gathered in the +[`msg_responses`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsgResponse.html#structfield.msg_responses) +field of the +[SubMsgResponse](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsgResponse.html) +structure. **Wasmd** allows chains to translate a single contract message into multiple SDK +messages. In that case all the message responses from each are concatenated into this flattened +`Vec`. + +#### Order and rollback + +Submessages follow a _depth first_ order rules: + +```mermaid + + sequenceDiagram + Note over Contract1: Contract1 returns two submessages:
1. `Execute Contract2`
2. `Execute Contract4` + Contract1->>Contract2: 1. Execute + Note over Contract2: Contract2 returns one submessage:
1. `Execute Contract3` + Contract2->>Contract3: 2. Execute + Contract3->>Contract2: 3. Response + Note over Contract2: Contract2 can handle the Response
in the reply entrypoint or leave it + Contract2->>Contract1: 4. Response + Note over Contract1: Contract1 can handle the Response
in the reply entrypoint or leave it + Contract1->>Contract4: 5. Execute + Contract4->>Contract1: 6. Response + Note over Contract1: Contract1 can handle the Response
in the reply entrypoint or leave it + +``` + +**Note:** The field `data` of the submessages field in the response are not forwarded down the\ +call path. It means that for e.g. if `Contract2` will not explicitly handle response from +`Contract3` and forward any data, then `Contract1` will never learn about results from `Contract3`. + +## Query Semantics + +Until now, we have focused on the +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) object, which +allows us to execute code in other contracts via the actor model. That is, each contract is run +sequentially, one after another, and no nested calls are possible. This is essential to avoid +reentrancy, which is when calling into another contract can change my state while I am in the middle +of a transaction. + +However, there are many times we need access to information from other contracts in the middle of +processing, such as determining the contract's bank balance before sending funds. To enable this, we +have exposed the _read only_ +[`QuerierWrapper`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.QuerierWrapper.html) to +enable _synchronous_ calls in the middle of the execution. By making it read-only (and enforcing +that in the VM level), we can prevent the possibility of reentrancy, as the query cannot modify any +state or execute our contract. + +When we "make a query", we serialize a +[`QueryRequest` struct](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.QueryRequest.html) and +then pass that over FFI to the runtime, where it is interpreted in the `x/wasm` SDK module. This is +extensible with blockchain-specific custom queries. + +## Sylvia + +The goal of Sylvia library is to enhance user experience and simplify the process of creating smart +contracts. Once we learn how CosmWasm works it's good to spend some time on writing your contracts +without the serialization/deserialization overhead. For more info check the +[Sylvia docs here](../sylvia.mdx)