-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added legal informatio to SUMMARY.md
- Loading branch information
Showing
5 changed files
with
1,095 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
# Actor model | ||
|
||
This section describes the fundaments of CosmWasm smart contracts architecture, which determines how do they communicate | ||
with each other. I want to go through this before teaching step by step how to create multiple contracts relating to each | ||
other, to give you a grasp of what to expect. Don't worry if it will not be clear after the first read - I suggest going | ||
through this chapter once now and maybe giving it another take in the future when you know the practical part of this. | ||
|
||
The whole thing described here is officially documented in the | ||
[SEMANTICS.md](https://github.com/CosmWasm/cosmwasm/blob/main/SEMANTICS.md), of the `cosmwasm` repository. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,326 @@ | ||
# Actors in blockchain | ||
|
||
Previously we were talking about actors mostly in the abstraction of any | ||
blockchain-specific terms. However, before we would dive into the code, we need | ||
to establish some common language, and to do so we would look at contracts from | ||
the perspective of external users, instead of their implementation. | ||
|
||
In this part, I would use the `wasmd` binary to communicate with the malaga | ||
testnet. To properly set it up, check the [Quick start with | ||
`wasmd`](../wasmd-quick-start.md). | ||
|
||
## Blockchain as a database | ||
|
||
It is kind of starting from the end, but I would start with the state part of | ||
the actor model. Relating to traditional systems, there is one particular thing | ||
I like to compare blockchain with - it is a database. | ||
|
||
Going back to the previous section we learned that the most important part of | ||
a contract is its state. Manipulating the state is the only way to persistently | ||
manifest work performed to the world. But What is the thing which purpose is to | ||
keep the state? It is a database! | ||
|
||
So here is my (as a contract developer) point of view on contracts: it is a distributed | ||
database, with some magical mechanisms to make it democratic. Those "magical | ||
mechanisms" are crucial for BC's existence and they make they are reasons why even | ||
use blockchain, but they are not relevant from the contract creator's point of | ||
view - for us, everything that matters is the state. | ||
|
||
But you can say: what about the financial part?! Isn't blockchain (`wasmd` in particular) | ||
the currency implementation? With all of those gas costs, sending funds seems | ||
very much like a money transfer, not database updates. And yes, you are kind of right, | ||
but I have a solution for that too. Just imagine, that for every native token (by | ||
"native tokens" we meant tokens handled directly by blockchain, in contradiction | ||
to for example cw20 tokens) there is a special database bucket (or table if you prefer) | ||
with mapping of address to how much of a token the address possesses. You can query | ||
this table (querying for token balance), but you cannot modify it directly. To modify | ||
it you just send a message to a special build-in bank contract. And everything | ||
is still a database. | ||
|
||
But if blockchain is a database, then where are smart contracts stored? | ||
Obviously - in the database itself! So now imagine another special table - this | ||
one would contain a single table of code-ids mapped to blobs of wasm binaries. And | ||
again - to operate on this table, you use "special contract" which is not accessible | ||
from another contract, but you can use it via `wasmd` binary. | ||
|
||
Now there is a question - why do I even care about BC being a DB? So the reason | ||
is that it makes reasoning about everything in blockchain very natural. Do you | ||
remember that every message in the actor model is transactional? It perfectly | ||
matches traditional database transactions (meaning: every message starts a new | ||
transaction)! Also, when we later talk about migrations, it would turn out, that | ||
migrations in CosmWasm are very much equivalents of schema migrations in | ||
traditional databases. | ||
|
||
So, the thing to remember - blockchain is very similar to a database, having some | ||
specially reserved tables (like native tokens, code repository), with a special | ||
bucket created for every contract. A contract can look at every table in every | ||
bucket in the whole blockchain, but it can modify the only one he created. | ||
|
||
## Compile the contract | ||
|
||
I will not go into the code for now, but to start with something we need compiled | ||
contract binary. The `cw4-group` contract from | ||
[cw-plus](https://github.com/CosmWasm/cw-plus) is simple enough to work with, for | ||
now, so we will start with compiling it. Start with cloning the repository: | ||
|
||
```bash | ||
$ git clone [email protected]:CosmWasm/cw-plus.git | ||
``` | ||
|
||
Then go to `cw4-group` contract and build it: | ||
|
||
```bash | ||
$ cd cw-plus/contracts/cw4-group | ||
$ 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/workspace-optimizer:0.12.6 | ||
``` | ||
|
||
Your final binary should be located in the | ||
`cw-plus/artifacts` folder (`cw-plus` being where you cloned your repository). | ||
|
||
## Contract code | ||
|
||
When the contract binary is built, the first interaction with CosmWasm is uploading | ||
it to the blockchain (assuming you have your wasm binary in the working directory): | ||
|
||
```bash | ||
$ wasmd tx wasm store ./cw4-group.wasm --from wallet $TXFLAG -y -b block | ||
``` | ||
|
||
As a result of such an operation you would get json output like this: | ||
|
||
``` | ||
.. | ||
logs: | ||
.. | ||
- events: | ||
.. | ||
- attributes: | ||
- key: code_id | ||
value: "1069" | ||
type: store_code | ||
``` | ||
|
||
I ignored most of not fields as they are not relevant for now - what we care | ||
about is the event emitted by blockchain with information about `code_id` of | ||
stored contract - in my case the contract code was stored in blockchain under | ||
the id of `1069`. I can now look at the code by querying for it: | ||
|
||
```bash | ||
$ wasmd query wasm code 1069 code.wasm | ||
``` | ||
|
||
And now the important thing - the contract code is not an actor. So, what is a | ||
contract code? I think that the easiest way to think about that is a `class` or | ||
a `type` in programming. It defines some stuff about what can be done, but the | ||
class itself is in most cases not very useful unless we create an instance | ||
of a type, on which we can call class methods. So now let's move forward to | ||
instances of such contract classes. | ||
|
||
## Contract instance | ||
|
||
Now we have a contract code, but what we want is an actual contract itself. | ||
To create it, we need to instantiate it. Relating to analogy to programming, | ||
instantiation is calling a constructor. To do that, I would send an | ||
instantiate message to my contract: | ||
|
||
```bash | ||
$ wasmd tx wasm instantiate 1069 '{"members": []}' --from wallet --label "Group 1" --no-admin $TXFLAG -y | ||
``` | ||
|
||
What I do here is create a new contract and immediately call the `Instantiate` | ||
message on it. The structure of such a message is different for every contract | ||
code. In particular, the `cw4-group` Instantiate message contains two fields: | ||
|
||
* `members` field which is the list of initial group members optional `admin` | ||
* field which defines an address of who can add or remove | ||
a group member | ||
|
||
In this case, I created an empty group with no admin - so which could never | ||
change! It may seem like a not very useful contract, but it serves us as a | ||
contract example. | ||
|
||
As the result of instantiating, I got the result: | ||
|
||
``` | ||
.. | ||
logs: | ||
.. | ||
- events: | ||
.. | ||
- attributes: | ||
- key: _contract_address | ||
value: wasm1u0grxl65reu6spujnf20ngcpz3jvjfsp5rs7lkavud3rhppnyhmqqnkcx6 | ||
- key: code_id | ||
value: "1069" | ||
type: instantiate | ||
``` | ||
|
||
As you can see, we again look at `logs[].events[]` field, looking for | ||
interesting event and extracting information from it - it is the common case. | ||
I will talk about events and their attributes in the future but in general, | ||
it is a way to notify the world that something happened. Do you remember the | ||
KFC example? If a waiter is serving our dish, he would put a tray on the bar, | ||
and she would yell (or put on the screen) the order number - this would be | ||
announcing an event, so you know some summary of operation, so you can go and | ||
do something useful with it. | ||
|
||
So, what use can we do with the contract? We obviously can call it! But first | ||
I want to tell you about addresses. | ||
|
||
## Addresses in CosmWasm | ||
|
||
Address in CosmWasm is a way to refer to entities in the blockchain. There are | ||
two types of addresses: contract addresses, and non-contracts. The difference | ||
is that you can send messages to contract addresses, as there is some smart | ||
contract code associated with them, and non-contracts are just users of the | ||
system. In an actor model, contract addresses represent actors, and | ||
non-contracts represent clients of the system. | ||
|
||
When operating with blockchain using `wasmd`, you also have an address - you | ||
got one when you added the key to `wasmd`: | ||
|
||
```bash | ||
# add wallets for testing | ||
$ wasmd keys add wallet3 | ||
- name: wallet3 | ||
type: local | ||
address: wasm1dk6sq0786m6ayg9kd0ylgugykxe0n6h0ts7d8t | ||
pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"Ap5zuScYVRr5Clz7QLzu0CJNTg07+7GdAAh3uwgdig2X"}' | ||
mnemonic: "" | ||
``` | ||
|
||
You can always check your address: | ||
|
||
```bash | ||
$ wasmd keys show wallet | ||
- name: wallet | ||
type: local | ||
address: wasm1um59mldkdj8ayl5gknp9pnrdlw33v40sh5l4nx | ||
pubkey: '{"@type":"/cosmos.crypto.secp256k1.PubKey","key":"A5bBdhYS/4qouAfLUH9h9+ndRJKvK0co31w4lS4p5cTE"}' | ||
mnemonic: "" | ||
``` | ||
|
||
Having an address is very important because it is a requirement for being able | ||
to call anything. When we send a message to a contract it always knows the | ||
address which sends this message so it can identify it - not to mention that | ||
this sender is an address that would play a gas cost. | ||
|
||
## Querying the contract | ||
|
||
So, we have our contract, let's try to do something with it - query would be the | ||
easiest thing to do. Let's do it: | ||
|
||
```bash | ||
$ wasmd query wasm contract-state smart wasm1u0grxl65reu6spujnf20ngcpz3jvjfsp5rs7lkavud3rhppnyhmqqnkcx6 '{ "list_members": {} }' | ||
data: | ||
members: [] | ||
``` | ||
|
||
The `wasm...` string is the contract address, and you have to substitute it with | ||
your contract address. `{ "list_members": {} }` is query message we send to | ||
contract. Typically, CW smart contract queries are in the form of a single JSON | ||
object, with one field: the query name (`list_members` in our case). The value | ||
of this field is another object, being query parameters - if there are any. | ||
`list_members` query handles two parameters: `limit`, and `start_after`, which | ||
are both optional and which support result pagination. However, in our case of | ||
an empty group they don't matter. | ||
|
||
The query result we got is in human-readable text form (if we want to get the | ||
JSON from - for example, to process it further with `jq`, just pass the | ||
`-o json` flag). As you can see response contains one field: `members` which is | ||
an empty array. | ||
|
||
So, can we do anything more with this contract? Not much. But let's try to do | ||
something with a new one! | ||
|
||
## Executions to perform some actions | ||
|
||
The problem with our previous contract is that for the `cw4-group` contract, | ||
the only one who can perform executions on it is an admin, but our contract | ||
doesn't have one. This is not true for every smart contract, but it is the | ||
nature of this one. | ||
|
||
So, let's make a new group contract, but this time we would | ||
make ourselves an admin. First, check our wallet address: | ||
|
||
```bash | ||
$ wasmd keys show wallet | ||
``` | ||
|
||
And instantiate a new group contract - this time with proper admin: | ||
|
||
```bash | ||
$ wasmd tx wasm instantiate 1069 '{"members": [], "admin": "wasm1um59mldkdj8ayl5gknp9pnrdlw33v40sh5l4nx"}' --from wallet --label "Group 1" --no-admin $TXFLAG -y | ||
.. | ||
logs: | ||
- events: | ||
.. | ||
- attributes: | ||
- key: _contract_address | ||
value: wasm1n5x8hmstlzdzy5jxd70273tuptr4zsclrwx0nsqv7qns5gm4vraqeam24u | ||
- key: code_id | ||
value: "1069" | ||
type: instantiate | ||
``` | ||
|
||
You may ask, why do we pass some kind of `--no-admin` flag, if we just said, we | ||
want to set an admin to the contract? The answer is sad and confusing, but... | ||
it is a different admin. The admin we want to set is one checked by the | ||
contract itself and managed by him. The admin which is declined with | ||
`--no-admin` flag, is a wasmd-level admin, which can migrate the contract. You | ||
don't need to worry about the second one at least until you learn about | ||
contract migrations - until then you can always pass the `--no-admin` flag to | ||
the contract. | ||
|
||
Now let's query our new contract for the member's list: | ||
|
||
```bash | ||
$ wasmd query wasm contract-state smart wasm1n5x8hmstlzdzy5jxd70273tuptr4zsclrwx0nsqv7qns5gm4vraqeam24u '{ "list_members": {} }' | ||
data: | ||
members: [] | ||
``` | ||
|
||
Just like before - no members initially. Now check an admin: | ||
|
||
``` | ||
$ wasmd query wasm contract-state smart wasm1n5x8hmstlzdzy5jxd70273tuptr4zsclrwx0nsqv7qns5gm4vraqeam24u '{ "admin": {} }' | ||
data: | ||
admin: wasm1um59mldkdj8ayl5gknp9pnrdlw33v40sh5l4nx | ||
``` | ||
|
||
So, there is an admin, it seems like the one we wanted to have there. So now we | ||
would add someone to the group - maybe ourselves? | ||
|
||
```bash | ||
wasmd tx wasm execute wasm1n5x8hmstlzdzy5jxd70273tuptr4zsclrwx0nsqv7qns5gm4vraqeam24u '{ "update_members": { "add": [{ "addr": "wasm1um59mldkdj8ayl5gkn | ||
p9pnrdlw33v40sh5l4nx", "weight": 1 }], "remove": [] } }' --from wallet $TXFLAG -y | ||
``` | ||
|
||
The message for modifying the members is `update_members` and it has two | ||
fields: members to remove, and members to add. Members to remove are | ||
just addresses. Members to add have a bit more complex structure: they | ||
are records with two fields: address and weight. Weight is not relevant | ||
for us now, it is just metadata stored with every group member - for | ||
us, it would always be 1. | ||
|
||
Let's query the contract again to check if our message changed anything: | ||
|
||
```bash | ||
$ wasmd query wasm contract-state smart wasm1n5x8hmstlzdzy5jxd70273tuptr4zsclrwx0nsqv7qns5gm4vraqeam24u '{ "list_members": {} }' | ||
data: | ||
members: | ||
- addr: wasm1um59mldkdj8ayl5gknp9pnrdlw33v40sh5l4nx | ||
weight: 1 | ||
``` | ||
|
||
As you can see, the contract updated its state. This is basically how | ||
it works - sending messages to contracts causes them to update the state, | ||
and the state can be queried at any time. For now, to keep things simple | ||
we were just interacting with the contract directly by `wasmd`, but as described | ||
before - contracts can communicate with each other. However, to investigate | ||
this we need to understand how to write contracts. Next time we will look | ||
at the contract structure and we will map it part by part to what we have learned | ||
until now. |
Oops, something went wrong.