|
| 1 | +--- |
| 2 | +tags: ["core"] |
| 3 | +--- |
| 4 | + |
| 5 | +# Contract Semantics |
| 6 | + |
| 7 | +## TODO tkulik: Check if the info does not overlap with already created docs |
| 8 | + |
| 9 | +This document aims to clarify the semantics of how a CosmWasm contract interacts with its |
| 10 | +environment. There are two main types of actions: _mutating_ actions, which are able to modify the |
| 11 | +state of the blockchain, and _query_ actions, which are run on a single node with read-only access |
| 12 | +to the data. |
| 13 | + |
| 14 | +## Definitions |
| 15 | + |
| 16 | +**Contract** is as some wasm code uploaded to the system, initialized at the creation of the |
| 17 | +contract. This has no state except that which is contained in the wasm code (eg. static constants) |
| 18 | + |
| 19 | +**Instance** is one instantiation of the contract. This contains a reference to the contract, as |
| 20 | +well as some "local" state to this instance, initialized at the creation of the instance. This state |
| 21 | +is stored in the kvstore, meaning a reference to the code plus a reference to the (prefixed) data |
| 22 | +store uniquely defines the smart contract. |
| 23 | + |
| 24 | +Example: we could upload a generic "ERC20 mintable" contract, and many people could create |
| 25 | +independent instances of the same bytecode, where the local data defines the token name, the issuer, |
| 26 | +the max issuance, etc. |
| 27 | + |
| 28 | +- First you **create** a _contract_ |
| 29 | +- Then you **instantiate** an _instance_ |
| 30 | +- Finally users **invoke** the _instance_ |
| 31 | + |
| 32 | +## Execution |
| 33 | + |
| 34 | +In the section below, we will discuss how the `execute` call works, but the same semantics apply to |
| 35 | +any other _mutating_ action - `instantiate`, `migrate`, `sudo`, etc. |
| 36 | + |
| 37 | +### SDK Context |
| 38 | + |
| 39 | +Before looking at CosmWasm, we should look at the semantics enforced by the blockchain framework we |
| 40 | +integrate with - the [Cosmos SDK](https://v1.cosmos.network/sdk). It is based upon the |
| 41 | +[Tendermint BFT](https://tendermint.com/core/) Consensus Engine. Let us first look how they process |
| 42 | +transactions before they arrive in CosmWasm. |
| 43 | + |
| 44 | +First, the Tendermint engine will seek 2/3+ consensus on a list of transactions to be included in |
| 45 | +the next block. This is done _without executing them_. They are simply subjected to a minimal |
| 46 | +pre-filter by the Cosmos SDK module, to ensure they are validly formatted transactions, with |
| 47 | +sufficient gas fees, and signed by an account with sufficient fees to pay it. Notably, this means |
| 48 | +many transactions that error may be included in a block. |
| 49 | + |
| 50 | +Once a block is committed, the transactions are then fed to the Cosmos SDK sequentially in order to |
| 51 | +execute them. Each one returns a result or error along with event logs, which are recorded in the |
| 52 | +`TxResults` section of the next block. The `AppHash` (or merkle proof or blockchain state) after |
| 53 | +executing the block is also included in the next block. |
| 54 | + |
| 55 | +The Cosmos SDK `BaseApp` handles each transaction in an isolated context. It first verifies all |
| 56 | +signatures and deducts the gas fees. It sets the "Gas Meter" to limit the execution to the amount of |
| 57 | +gas paid for by the fees. Then it makes an isolated context to run the transaction. This allows the |
| 58 | +code to read the current state of the chain (after the last transaction finished), but it only |
| 59 | +writes to a cache, which may be committed or rolled back on error. |
| 60 | + |
| 61 | +A transaction may consist of multiple messages and each one is executed in turn under the same |
| 62 | +context and same gas limit. If all messages succeed, the context will be committed to the underlying |
| 63 | +blockchain state and the results of all messages will be stored in the `TxResult`. If one message |
| 64 | +fails, all later messages are skipped and all state changes are reverted. This is very important for |
| 65 | +atomicity. That means Alice and Bob can both sign a transaction with 2 messages: Alice pays Bob 1000 |
| 66 | +ATOM, Bob pays Alice 50 ETH, and if Bob doesn't have the funds in his account, Alice's payment will |
| 67 | +also be reverted. This is just like a DB Transaction typically works. |
| 68 | + |
| 69 | +[`x/wasm`](https://github.com/CosmWasm/wasmd/tree/master/x/wasm) is a custom Cosmos SDK module, |
| 70 | +which processes certain messages and uses them to upload, instantiate, and execute smart contracts. |
| 71 | +In particular, it accepts a properly signed |
| 72 | +[`MsgExecuteContract`](https://github.com/CosmWasm/wasmd/blob/master/proto/cosmwasm/wasm/v1/tx.proto), |
| 73 | +routes it to |
| 74 | +[`Keeper.Execute`](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/keeper.go), which |
| 75 | +loads the proper smart contract and calls `execute` on it. Note that this method may either return a |
| 76 | +success (with data and events) or an error. In the case of an error here, it will revert the entire |
| 77 | +transaction in the block. This is the context we find ourselves in when our contract receives the |
| 78 | +`execute` call. |
| 79 | + |
| 80 | +### Basic Execution |
| 81 | + |
| 82 | +When we implement a contract, we provide the following entry point: |
| 83 | + |
| 84 | +```rust template="core" |
| 85 | +#[cfg_attr(not(feature = "library"), entry_point)] |
| 86 | +pub fn execute( |
| 87 | + deps: DepsMut, |
| 88 | + env: Env, |
| 89 | + info: MessageInfo, |
| 90 | + msg: ExecuteMsg, |
| 91 | +) -> Result<Response, ContractError> { |
| 92 | + // [...] |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +With [`DepsMut`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.DepsMut.html), this can |
| 97 | +read and write to the backing |
| 98 | +[`Storage`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/trait.Storage.html), as well as use the |
| 99 | +[`Api`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/trait.Api.html) to validate addresses, and |
| 100 | +use [`QuerierWrapper`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.QuerierWrapper.html) |
| 101 | +the state of other contracts or native modules. Once it is done, it returns either `Ok(Response)` or |
| 102 | +`Err(ContractError)`. |
| 103 | + |
| 104 | +If it returns `Err`, this error is converted to a string representation, and it's returned to the |
| 105 | +SDK module. _All state changes are reverted_ and `x/wasm` returns this error message, which will |
| 106 | +_generally_ abort the transaction, and return the error message to the external caller. |
| 107 | + |
| 108 | +If it returns `Ok`, the |
| 109 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) object is parsed |
| 110 | +and processed. |
| 111 | + |
| 112 | +In the Cosmos SDK, a transaction returns a number of events to the user, along with an optional data |
| 113 | +"result". This result is hashed into the next block hash to be provable and can return some |
| 114 | +essential state (although in general client apps rely on Events more). This result is more commonly |
| 115 | +used to pass results between contracts or modules in the sdk. |
| 116 | + |
| 117 | +### Dispatching Submessages |
| 118 | + |
| 119 | +Now let's move onto the `messages` field of the |
| 120 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html). Some contracts |
| 121 | +are fine only talking with themselves. But many want to move tokens or call into other contracts for |
| 122 | +more complex actions. This is where messages come in. We return |
| 123 | +[`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html), which is a |
| 124 | +serializable representation of any external call the contract can make. |
| 125 | + |
| 126 | +This may be hard to understand at first. "Why can't I just call another contract?", you may ask. |
| 127 | +However, we do this to prevent one of most widespread and hardest to detect security holes in |
| 128 | +Ethereum contracts - reentrancy. We do this by following the actor model, which doesn't nest |
| 129 | +function calls, but returns messages that will be executed later. This means all state that is |
| 130 | +carried over between one call and the next happens in storage and not in memory. For more |
| 131 | +information on this design, I recommend you read |
| 132 | +[our docs on the Actor Model](architecture/actor-model.mdx). |
| 133 | + |
| 134 | +A common request was the ability to get the result from one of the messages you dispatched. For |
| 135 | +example, you want to create a new contract with |
| 136 | +[`WasmMsg::Instantiate`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.WasmMsg.html#variant.Instantiate), |
| 137 | +but then you need to store the address of the newly created contract in the caller. this is possible |
| 138 | +with `messages`. It also solves a similar use-case of capturing the error results, so if you execute |
| 139 | +a message from e.g. a cron contract, it can store the error message and mark the message as run, |
| 140 | +rather than aborting the whole transaction. It also allows for limiting the gas usage of the |
| 141 | +submessage (this is not intended to be used for most cases, but is needed for eg. the cron job to |
| 142 | +protect it from an infinite loop in the submessage burning all gas and aborting the transaction). |
| 143 | + |
| 144 | +This makes use of |
| 145 | +[`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html) as above, but it |
| 146 | +wraps it inside a [`SubMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsg.html) |
| 147 | +envelope. |
| 148 | + |
| 149 | +What are the semantics of a submessage execution. First, we create a sub-transaction context around |
| 150 | +the state, allowing it to read the latest state written by the caller, but write to yet-another |
| 151 | +cache. If `gas_limit` is set, it is sandboxed to how much gas it can use until it aborts with |
| 152 | +`OutOfGasError`. This error is caught and returned to the caller like any other error returned from |
| 153 | +contract execution (unless it burned the entire gas limit of the transaction). What is more |
| 154 | +interesting is what happens on completion. |
| 155 | + |
| 156 | +If it return success, the temporary state is committed (into the caller's cache), and the |
| 157 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) is processed as |
| 158 | +normal. Once the response is fully processed, this may then be intercepted by the calling contract |
| 159 | +(for `ReplyOn::Always` and `ReplyOn::Success`). On an error, the subcall will revert any partial |
| 160 | +state changes due to this message, but not revert any state changes in the calling contract. The |
| 161 | +error may then be intercepted by the calling contract (for `ReplyOn::Always` and `ReplyOn::Error`). |
| 162 | +_In this case, the messages error doesn't abort the whole transaction_ |
| 163 | + |
| 164 | +Note, that error doesn't abort the whole transaction _if and only if_ the `reply` is called - so in |
| 165 | +case of `ReplyOn::Always` and `ReplyOn::Error`. If the submessage is called with `ReplyOn::Success` |
| 166 | +(or `ReplyOn::Never`, which makes it effectively a normal message), the error in subsequent call |
| 167 | +would result in failing whole transaction and not commit the changes for it. The rule here is as |
| 168 | +follows: if for any reason you want your message handling to succeed on submessage failure, you |
| 169 | +always have to reply on failure. |
| 170 | + |
| 171 | +Obviously - on the successful processing of sub-message, if the reply is not called (in particular |
| 172 | +`ReplyOn::Error`), the whole transaction is assumed to succeed, and is committed. |
| 173 | + |
| 174 | +#### Handling the Reply |
| 175 | + |
| 176 | +In order to make use of `messages`, the calling contract must have an extra entry point: |
| 177 | + |
| 178 | +```rust filename="contract.rs" template="core" |
| 179 | +#[cfg_attr(not(feature = "library"), entry_point)] |
| 180 | +pub fn reply(deps: DepsMut, env: Env, reply: Reply) -> StdResult<Response> { |
| 181 | + // [...] |
| 182 | +} |
| 183 | +``` |
| 184 | + |
| 185 | +After the `message` is finished, the caller will get a chance to handle the result. It will get the |
| 186 | +original `id` of the subcall so it can switch on how to process this, and the `Result` of the |
| 187 | +execution, both success and error. Note that it includes all events returned by the submessage, |
| 188 | +which applies to native sdk modules (like Bank) as well as the data returned from below. This and |
| 189 | +the original call id provide all context to continue processing it. If you need more state, you must |
| 190 | +save some local context to the store (under the `id`) before returning the `message` in the original |
| 191 | +`execute`, and load it in `reply`. We explicitly prohibit passing information in contract memory, as |
| 192 | +that is the key vector for reentrancy attacks, which are a large security surface area in Ethereum. |
| 193 | + |
| 194 | +The `reply` call may return `Err` itself, in which case it is treated like the caller errored, and |
| 195 | +aborting the transaction. However, on successful processing, `reply` may return a normal |
| 196 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html), which will be |
| 197 | +processed as normal - events added to the EventManager, and all `messages` dispatched as described |
| 198 | +above. |
| 199 | + |
| 200 | +## TODO tkulik: This paragraph is related to the issue from first PR |
| 201 | + |
| 202 | +The one _critical difference_ with `reply`, is that we _do not drop data_. If `reply` returns |
| 203 | +`data: Some(value)` in the |
| 204 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) object, we will |
| 205 | +overwrite the `data` field returned by the caller. That is, if `execute` returns |
| 206 | +`data: Some(b"first thought")` and the `reply` (with all the extra information it is privy to) |
| 207 | +returns `data: Some(b"better idea")`, then this will be returned to the caller of `execute` (either |
| 208 | +the client or another transaction), just as if the original `execute` and returned |
| 209 | +`data: Some(b"better idea")`. If `reply` returns `data: None`, it will not modify any previously set |
| 210 | +data state. If there are multiple messages all setting this, only the last one is used (they all |
| 211 | +overwrite any previous `data` value). As a consequence, you can use `data: Some(b"")` to clear |
| 212 | +previously set data. This will be represented as a JSON string instead of `null` and handled as any |
| 213 | +other `Some` value. |
| 214 | + |
| 215 | +#### Order and Rollback |
| 216 | + |
| 217 | +## TODO tkulik: make sure that the order is properly described here |
| 218 | + |
| 219 | +Submessages follow a _depth first_ order rules, with their replies considered as an immediate |
| 220 | +additional message call. Here is a simple example. Contract **A** returns messages **S1** and |
| 221 | +**S2**, and message **M1**. Submessage **S1** returns submessage **N1**. The order will be: **S1, |
| 222 | +N1, reply(S1), S2, reply(S2), M1**. |
| 223 | + |
| 224 | +Please keep in mind that submessage `execution` and `reply` can happen within the context of another |
| 225 | +submessage. For example `contract-A--submessage --> contract-B--submessage --> contract-C`. Then |
| 226 | +`contract-B` can revert the state for `contract-C` and itself by returning `Err` in the submessage |
| 227 | +`reply`, but not revert contract-A or the entire transaction. It just ends up returning `Err` to |
| 228 | +contract-A's `reply` function. |
| 229 | + |
| 230 | +Note that errors are not handled with `ReplyOn::Success`, meaning, in such a case, an error will be |
| 231 | +treated just like a normal `message` returning an error. This diagram may help explain. Imagine a |
| 232 | +contract returned two submesssages - (a) with `ReplyOn::Success` and (b) with `ReplyOn::Error`: |
| 233 | + |
| 234 | +| processing a) | processing b) | reply called | may overwrite result from reply | note | |
| 235 | +| ------------- | ------------- | ------------ | ------------------------------- | ------------------------------------------------- | |
| 236 | +| ok | ok | a) | a) | returns success | |
| 237 | +| err | err | none | none | returns error (abort parent transaction) | |
| 238 | +| err | ok | none | none | returns error (abort parent transaction) | |
| 239 | +| ok | err | a)b) | a)b) | if both a) and b) overwrite, only b) will be used | |
| 240 | + |
| 241 | +## Query Semantics |
| 242 | + |
| 243 | +Until now, we have focused on the |
| 244 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) object, which |
| 245 | +allows us to execute code in other contracts via the actor model. That is, each contract is run |
| 246 | +sequentially, one after another, and no nested calls are possible. This is essential to avoid |
| 247 | +reentrancy, which is when calling into another contract can change my state while I am in the middle |
| 248 | +of a transaction. |
| 249 | + |
| 250 | +However, there are many times we need access to information from other contracts in the middle of |
| 251 | +processing, such as determining the contract's bank balance before sending funds. To enable this, we |
| 252 | +have exposed the _read only_ |
| 253 | +[`QuerierWrapper`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.QuerierWrapper.html) to |
| 254 | +enable _synchronous_ calls in the middle of the execution. By making it read-only (and enforcing |
| 255 | +that in the VM level), we can prevent the possibility of reentrancy, as the query cannot modify any |
| 256 | +state or execute our contract. |
| 257 | + |
| 258 | +When we "make a query", we serialize a |
| 259 | +[`QueryRequest` struct](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.QueryRequest.html) |
| 260 | +that represents all possible calls, and then pass that over FFI to the runtime, where it is |
| 261 | +interpreted in the `x/wasm` SDK module. This is extensible with blockchain-specific custom queries |
| 262 | +just like [`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html) |
| 263 | +accepts custom results. |
| 264 | + |
| 265 | +While this is flexible and needed encoding for the cross-language representation, this is a bit of |
| 266 | +mouthful to generate and use when I just want to find my bank balance. To help that, we often use |
| 267 | +[`QuerierWrapper`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.QuerierWrapper.html). |
0 commit comments