Skip to content

Commit

Permalink
Merge branch 'main' into upgrade-nextra
Browse files Browse the repository at this point in the history
  • Loading branch information
abefernan committed Jan 13, 2025
2 parents 6d586bf + 637dce4 commit a2d0e77
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/pages/core/architecture/_meta.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export default {
semantics: "Semantics",
"actor-model": "Actor Model",
events: "Events",
gas: "Gas",
Expand Down
70 changes: 70 additions & 0 deletions src/pages/core/architecture/semantics.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
tags: ["core"]
---

# Contract Semantics

This document provides an explanation of the semantics governing the interaction between a CosmWasm
contract and its environment.

## Definitions

**Contract** is a [WebAssembly](https://webassembly.org/) code stored in the blockchain's state.
This has no state except that which is contained in the binary code (eg. static constants).

**Instance** refers to a specific instantiation of a contract. It includes a reference to the
contract's binary as well as a state unique to the instance, which is initialized upon its creation.
This state is stored in the key-value store on the blockchain's state. Consequently, a reference to
the contract's binary, combined with a reference to the prefixed data store, uniquely identifies the
smart contract.

Example: we could upload a generic "ERC20 mintable" contract, and many people could create
independent instances based on the same bytecode, where the local data defines the token name, the
issuer, the max issuance, etc.

1. First you **store** a _contract_,
2. Then you **instantiate** an _instance_,
3. Finally users **invoke** the _instance_.

### 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 similar to how a database 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.
It accepts a properly signed
[`CosmWasm message`](https://github.com/CosmWasm/wasmd/blob/master/proto/cosmwasm/wasm/v1/tx.proto),
routes it to [`Keeper`](https://github.com/CosmWasm/wasmd/blob/master/x/wasm/keeper/keeper.go),
which loads the proper smart contract and calls one of the entry-point 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.

For more information on contracts design visit [entry points chapter](../entrypoints.mdx).
76 changes: 76 additions & 0 deletions src/pages/core/architecture/transactions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,51 @@ In our case, these operations are invocations of contract entrypoints. If one of
the chain fails, the whole transaction is usually rolled back. Failing in this context means that
the contract entrypoint returns an error or panics.

## 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 the 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](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` and replies. 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).

If it returns 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 message's 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`, the error in a 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.

## Preventing rollbacks in case of failure

If you don't want your entire transaction to be rolled back in case of a failure, you can split the
Expand All @@ -28,3 +73,34 @@ error, retry the operation, ignore it, etc.

The default value `ReplyOn::Success` means the caller is not ready to handle an error in the message
execution and the entire transaction is reverted on error.

## Order of execution and rollback procedure

Submessages handling follows _depth first_ order rules. Let's see the following example scenario:

```mermaid
sequenceDiagram
Note over Contract1: Contract1 returns two submessages:<br/> 1. Execute Contract2<br/> 2. Execute Contract4
Contract1->>Contract2: 1. Execute
Note over Contract2: Contract2 returns one submessage:<br/> 1. Execute Contract3
Contract2->>Contract3: 2. Execute
Contract3->>Contract2: 3. Response
Note over Contract2: Contract2 can handle the Response<br/>in the reply entrypoint or leave it
Contract2->>Contract1: 4. Response
Note over Contract1: Contract1 can handle the Response<br/>in the reply entrypoint or leave it
Contract1->>Contract4: 5. Execute
Contract4->>Contract1: 6. Response
Note over Contract1: Contract1 can handle the Response<br/>in the reply entrypoint or leave it
```

**Note1:** The
[msg_responses](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsgResponse.html#structfield.msg_responses)
of 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`.

**Note2:** If `Contract2` returns an error, the error message can be handled by the `Contract1`
reply entry-point and prevent the whole transaction from rollback. In such a case only the
`Contract2` and `Contract3` states changes are reverted.
24 changes: 24 additions & 0 deletions src/pages/core/entrypoints/reply.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,29 @@ pub fn reply(deps: DepsMut, env: Env, msg: cosmwasm_std::Reply) -> StdResult<Res
}
```

## Handling the reply

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 it in the local store during
the `execute` entry-point call, and load it in `reply`.

The `reply` call may return `Err` itself, in which case it is treated like the caller errored, and
aborting the sub-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 a message handler, all changes made by the handler up to the reply
entry-point that returns the `Ok` response are reverted. More information can be found in the
following section.

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`.

[its own page]: ../architecture/actor-model
[transactions]: ../architecture/transactions

0 comments on commit a2d0e77

Please sign in to comment.