|
| 1 | +--- |
| 2 | +tags: ["core", "architecture"] |
| 3 | +--- |
| 4 | + |
| 5 | +# Transactions |
| 6 | + |
| 7 | +Every contract invocation is wrapped into a transaction. If you know about transactions in SQL |
| 8 | +databases, you can consider them as the same basic concept. You execute multiple operations in a |
| 9 | +single transaction, and if one of them fails, the whole transaction is rolled back. |
| 10 | + |
| 11 | +In our case, these operations are invocations of contract entrypoints. If one of the invocations in |
| 12 | +the chain fails, the whole transaction is usually rolled back. Failing in this context means that |
| 13 | +the contract entrypoint returns an error or panics. |
| 14 | + |
| 15 | +## Dispatching Submessages |
| 16 | + |
| 17 | +Now let's move onto the `messages` field of the |
| 18 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html). Some contracts |
| 19 | +are fine only talking with themselves. But many want to move tokens or call into other contracts for |
| 20 | +more complex actions. This is where messages come in. We return |
| 21 | +[`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html), which is a |
| 22 | +serializable representation of any external call the contract can make. |
| 23 | + |
| 24 | +This may be hard to understand at first. "Why can't I just call another contract?", you may ask. |
| 25 | +However, we do this to prevent one of the most widespread and hardest to detect security holes in |
| 26 | +Ethereum contracts - reentrancy. We do this by following the actor model, which doesn't nest |
| 27 | +function calls, but returns messages that will be executed later. This means all state that is |
| 28 | +carried over between one call and the next happens in storage and not in memory. For more |
| 29 | +information on this design, I recommend you read [our docs on the Actor Model](actor-model.mdx). |
| 30 | + |
| 31 | +A common request was the ability to get the result from one of the messages you dispatched. For |
| 32 | +example, you want to create a new contract with |
| 33 | +[`WasmMsg::Instantiate`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.WasmMsg.html#variant.Instantiate), |
| 34 | +but then you need to store the address of the newly created contract in the caller. This is possible |
| 35 | +with `messages` and replies. This makes use of |
| 36 | +[`CosmosMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/enum.CosmosMsg.html) as above, but it |
| 37 | +wraps it inside a [`SubMsg`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsg.html) |
| 38 | +envelope. |
| 39 | + |
| 40 | +What are the semantics of a submessage execution? First, we create a sub-transaction context around |
| 41 | +the state, allowing it to read the latest state written by the caller, but write to yet-another |
| 42 | +cache. If `gas_limit` is set, it is sandboxed to how much gas it can use until it aborts with |
| 43 | +`OutOfGasError`. This error is caught and returned to the caller like any other error returned from |
| 44 | +contract execution (unless it burned the entire gas limit of the transaction). |
| 45 | + |
| 46 | +If it returns success, the temporary state is committed (into the caller's cache), and the |
| 47 | +[`Response`](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.Response.html) is processed as |
| 48 | +normal. Once the response is fully processed, this may then be intercepted by the calling contract |
| 49 | +(for `ReplyOn::Always` and `ReplyOn::Success`). On an error, the subcall will revert any partial |
| 50 | +state changes due to this message, but not revert any state changes in the calling contract. The |
| 51 | +error may then be intercepted by the calling contract (for `ReplyOn::Always` and `ReplyOn::Error`). |
| 52 | +In this case, the message's error doesn't abort the whole transaction. |
| 53 | + |
| 54 | +Note, that error doesn't abort the whole transaction _if and only if_ the `reply` is called - so in |
| 55 | +case of `ReplyOn::Always` and `ReplyOn::Error`. If the submessage is called with `ReplyOn::Success` |
| 56 | +or `ReplyOn::Never`, the error in a subsequent call would result in failing whole transaction and |
| 57 | +not commit the changes for it. The rule here is as follows: if for any reason you want your message |
| 58 | +handling to succeed on submessage failure, you always have to reply on failure. |
| 59 | + |
| 60 | +## Preventing rollbacks in case of failure |
| 61 | + |
| 62 | +If you don't want your entire transaction to be rolled back in case of a failure, you can split the |
| 63 | +logic into multiple messages. This can be two contracts, a contract executing itself or a contract |
| 64 | +that sends a message to a Cosmos SDK module. Then use the `reply_on` field in the message you send. |
| 65 | +Set the field to one of the following values and instead of rolling back the transaction, you will |
| 66 | +receive a message containing the error: |
| 67 | + |
| 68 | +- `ReplyOn::Always` |
| 69 | +- `ReplyOn::Error` |
| 70 | + |
| 71 | +That way you can handle the error and decide what to do next, whether you want to propagate the |
| 72 | +error, retry the operation, ignore it, etc. |
| 73 | + |
| 74 | +The default value `ReplyOn::Success` means the caller is not ready to handle an error in the message |
| 75 | +execution and the entire transaction is reverted on error. |
| 76 | + |
| 77 | +## Order of execution and rollback procedure |
| 78 | + |
| 79 | +Submessages handling follows _depth first_ order rules. Let's see the following example scenario: |
| 80 | + |
| 81 | +```mermaid |
| 82 | +
|
| 83 | + sequenceDiagram |
| 84 | + Note over Contract1: Contract1 returns two submessages:<br/> 1. Execute Contract2<br/> 2. Execute Contract4 |
| 85 | + Contract1->>Contract2: 1. Execute |
| 86 | + Note over Contract2: Contract2 returns one submessage:<br/> 1. Execute Contract3 |
| 87 | + Contract2->>Contract3: 2. Execute |
| 88 | + Contract3->>Contract2: 3. Response |
| 89 | + Note over Contract2: Contract2 can handle the Response<br/>in the reply entrypoint or leave it |
| 90 | + Contract2->>Contract1: 4. Response |
| 91 | + Note over Contract1: Contract1 can handle the Response<br/>in the reply entrypoint or leave it |
| 92 | + Contract1->>Contract4: 5. Execute |
| 93 | + Contract4->>Contract1: 6. Response |
| 94 | + Note over Contract1: Contract1 can handle the Response<br/>in the reply entrypoint or leave it |
| 95 | +
|
| 96 | +``` |
| 97 | + |
| 98 | +**Note1:** The |
| 99 | +[msg_responses](https://docs.rs/cosmwasm-std/latest/cosmwasm_std/struct.SubMsgResponse.html#structfield.msg_responses) |
| 100 | +of the response are not forwarded down the call path. It means that for e.g. if `Contract2` will not |
| 101 | +explicitly handle response from `Contract3` and forward any data, then `Contract1` will never learn |
| 102 | +about results from `Contract3`. |
| 103 | + |
| 104 | +**Note2:** If `Contract2` returns an error, the error message can be handled by the `Contract1` |
| 105 | +reply entry-point and prevent the whole transaction from rollback. In such a case only the |
| 106 | +`Contract2` and `Contract3` states changes are reverted. |
0 commit comments