Quick start
This section will quickly introduce the development of Rooch through a counter example.
What is counter
The counter is a small counting program. It contains an initial value. We can increment its value through an increment instruction to achieve the purpose of counting.
We can use any programming language to implement this small program. This tutorial will use the Move language to write it and let it run successfully on Rooch.
Create counter project
Before we create the counter contract program, we can use the CLI command provided by Rooch to initialize an empty project:
rooch move new quick_start_counter
For detailed methods, please refer to Creating a Rooch Move Contract.
Next, create a counter.move
file in the sources
directory to write our contract code.
Contract writing
module quick_start_counter::quick_start_counter {
use moveos_std::account_storage;
use moveos_std::context::Context;
struct Counter has key {
count_value: u64
}
fun init(ctx: &mut Context, account: &signer) {
account_storage::global_move_to(ctx, account, Counter { count_value: 0 });
}
entry fun increase(ctx: &mut Context) {
let counter = account_storage::global_borrow_mut<Counter>(ctx, @quick_start_counter);
counter.count_value = counter.count_value + 1;
}
}
With a simple 17 lines of code, a simple counter function is implemented on Rooch Move.
What follows is a detailed description of what each line of code does.
Module declaration
In the Move language, contracts are usually written in modules. Simple contracts can be completed through one module, while complex contracts may be composed of multiple modules. Our counter contract is very simple, so there is only one quick_start_counter
module.
Modules usually contain data types and functions required to implement the current module functions.
module quick_start_counter::quick_start_counter {
In line 1, we declare a quick_start_counter
module using the module
keyword.
In the Move-based blockchain system, modules are uniquely identified by address, that is, an address can only have one module of the same type, and modules of the same type cannot be released multiple times. The same contract can be issued by multiple addresses. In order to identify who issued the contract module in the huge blockchain system, it needs to be uniquely identified by the address
.
In the Move-based blockchain system, modules are uniquely identified by address, that is, an address can only have one module with the same name, and modules with the same name cannot be published multiple times. The same contract can be published by multiple addresses. In order to identify who published the contract module in the blockchain system, it needs to be uniquely identified by the address
.
Therefore, the syntax for declaring a Move module is module address::module_name
.
Import module
use moveos_std::account_storage;
use moveos_std::context::Context;
In lines 2~3, it is a statement that imports modules. To implement a counter contract on Rooch, we need to use some functional libraries of the Rooch. This contract uses functional modules such as the account_storage
and context
provided by the MoveOS Standard Library.
Define data structure
struct Counter has key {
count_value: u64
}
We define a Counter
type structure to record the count value. The structure only contains a u64
type field count_value
.
We want to record the value of the Counter
type into Rooch's global storage, so we need to provide a key
ability for this type so that Move can find the data through the key.
Initialization function
fun init(ctx: &mut Context, account: &signer) {
account_storage::global_move_to(ctx, account, Counter { value: 0 })
}
Move provides a specific initialization function init
to automatically initialize the contract to ensure that some necessary operations have been performed after the contract is released.
For example, we hope that once the counter contract is released, the contract will automatically initialize the counter for us so that its count value is 0
.
Line 9 is the function signature of the init
function, which accepts two parameters ctx
and account
.
Increasing function
Next, we need to define an incrementing function that will increment the counter value by 1
every time it is executed.
entry fun increase(ctx: &mut Context) {
let counter = account_storage::global_borrow_mut<Counter>(ctx, @quick_start_counter);
counter.count_value = counter.count_value + 1;
}
Line 13 is the signature of the increase
function, which accepts a context parameter.
In line 14, the global_borrow_mut
instruction of the account storage provided by Rooch is called to obtain a mutable reference of type Counter
, and the return value of the function is bound to the counter
variable. By borrowing a mutable reference from Counter
, we can modify its value.
The global_borrow_mut
function accepts a context parameter and an address parameter to borrow the Counter
resource. For simplicity, we directly use the address of the published counter module as the borrower.
In line 15, we obtain the field value of the Counter
structure through member operations and perform an increment operation.
At this point, we have implemented the incrementing logic of the counter.
Entry function
The entry function is a function modified by the entry
keyword.
For security reasons, the Move virtual machine prohibits external (command line, etc.) direct calls to functions that operate module data. Instead, a method called an entry function
is provided to indirectly call logical functions. The entry function is the contract exposed to the outside what an interface.
Note: In order to simplify the counter contract demonstrated in this example as much as possible, we merged the logical operation and entry function into one
increase
function. In actual development, it is recommended to encapsulate the logic and entry function separately into different functions.
As you can see, the increase
function on line 13 is modified with the entry
keyword, so it becomes an entry function. With the entry function, we can perform the counter increment operation in the command line or other clients.
Special note that the context parameters of the
increase
function are parameters automatically passed by Move VM. They need to be passed explicitly when writing the contract. However, for external calls, they do not need to be passed manually, so when calling theincrease
function on the command line, they do not need to pass any arguments.
Demo counter program in Rooch's CLI
- First check whether the currently activated Rooch network is the
dev
network:
$ rooch env list
Env Alias | RPC URL | Websocket URL | Active Env
---------------------------------------------------------------------------------------------------------------------------------------------------------
local | http://0.0.0.0:50051 | Null |
dev | https://dev-seed.rooch.network:443/ | Null | True
Note that if the dev
environment of Active Env
is not True
, use the rooch env switch --alias dev
command to switch to the development network.
- Open another terminal, switch to the root directory of the
counter
project, and compile the contract:
$ rooch move build
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
INCLUDING DEPENDENCY MoveStdlib
INCLUDING DEPENDENCY MoveosStdlib
INCLUDING DEPENDENCY RoochFramework
BUILDING quick_start_counter
Success
- Publish the counter contract to Rooch:
$ rooch move publish
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
UPDATING GIT DEPENDENCY https://github.com/rooch-network/rooch.git
INCLUDING DEPENDENCY MoveStdlib
INCLUDING DEPENDENCY MoveosStdlib
INCLUDING DEPENDENCY RoochFramework
BUILDING quick_start_counter
Publish modules to address: 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81
{
"sequence_info": {
"tx_order": "17128",
"tx_order_signature": {
"auth_validator_id": "0",
"payload": "0x00b5d56a8178299d628e0d341fff4b97904f7ff80f9e3e8aca10f0d1d9ac65e5736757caec9be17aa54b1da860b183a6d1574f1db9ba632910fb308c4d6283d30eca2ecf17ab26b7c32ddba10913e6f74d43b5258905bef0f88b6d744d73bc9ce9"
},
"tx_accumulator_root": "0xb408f55b7e003f54fa37a87ccaa25937bedb7d04d3d8a06f745de4e30b25a3cf"
},
"execution_info": {
"tx_hash": "0xa45998b0a7d979597d0ed4021c2ffbe778d2e0889d3151ad29493e3d91f177a6",
"state_root": "0x0a5f095123b459749938b4fc436c06c429066f6a48a8f014bba6360471b6d03b",
"event_root": "0x8be169d5a1b2bdace1ff085fae4753e37f69095ec543a83ef34cae23e9ebb747",
"gas_used": 1644906,
"status": {
"type": "executed"
}
},
......
When you see executed
in the execution_info.status
of the output result, it means that the counter contract has been successfully released and the counter has been initialized.
- We use the resource search command provided by Rooch to obtain information about
Counter
resources.
The syntax is rooch resource --address address_of_published_resource --resource resource_type
:
$ rooch resource --address 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81 --resource 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter
{
"value": "0x0000000000000000",
"value_type": "0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter",
"decoded_value": {
"abilities": 8,
"type": "0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter",
"value": {
"count_value": "0"
}
}
}
Pay attention to the value
attribute. You can see that in the output information of the Counter
resource, the field count_value
of Counter is indeed 0
.
- Then we call the counter’s increment function:
The syntax is rooch move run --function address_published_by_the_module::module_name::entry_function_name --sender-account address_to_send_the_current_transaction
.
Note: In the blockchain system, performing certain operations is usually performed by sending a transaction to the blockchain system to perform the corresponding operation.
$ rooch move run --function 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::increase --sender-account 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81
{
"sequence_info": {
"tx_order": "17160",
"tx_order_signature": {
"auth_validator_id": "0",
"payload": "0x00f3809e65ee7aef9d9f4f7f79820ad6a25d44aed583c74300f30a0550f8256e371546e4515125636646acb3e625bb5d02db933656218832103300861b4a2b9d07ca2ecf17ab26b7c32ddba10913e6f74d43b5258905bef0f88b6d744d73bc9ce9"
},
"tx_accumulator_root": "0x89ace9f8d15707a008f7237566761e28743a4c73d9f5b19622c65e4a072850bc"
},
"execution_info": {
"tx_hash": "0x19139e0f8779c43e7feb989553d66e41eab2840705417898dee376b3ea2ea3d0",
"state_root": "0xb876495e3c144700fb315cf2697a990d25578b4b832f9b34bb74e4f233d4faf2",
"event_root": "0x414343554d554c41544f525f504c414345484f4c4445525f4841534800000000",
"gas_used": 12769,
"status": {
"type": "executed"
}
},
......
If you see executed
in the status
, it proves that the increase
function call was successful.
- Check again to see if the counter value is what we expected:
$ rooch resource --address 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81 --resource 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter
{
"value": "0x0100000000000000",
"value_type": "0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter",
"decoded_value": {
"abilities": 8,
"type": "0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter",
"value": {
"count_value": "1"
}
}
}
As you can see, the value of the Counter
's value
field has indeed been updated to 1
.
At this point, you already know how to write contracts in Rooch and how to call contracts from the command line.
Next, let's increase the difficulty a little bit and we will demonstrate how to run and use the counter contract program in the front end.
Quick experience
The source code of this example is stored in the rooch/examples/quick_start_counter
directory. To make testing easier, we change the address in the Move.toml
file to the wildcard character _
.
If you run it directly using the example we provided, the executed Shell command will be a little different:
$ rooch move build --named-addresses quick_start_counter=default
$ rooch move publish --named-addresses quick_start_counter=default
# 注意 `0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81` 是我演示的地址,当你实际运行时,需要更改为你自己的钱包地址
$ rooch resource --address 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81 --resource 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter
$ rooch move run --function 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::increase --sender-account default
$ rooch resource --address 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81 --resource 0xc4a286bef174e126ef24363a0799c069504d0f132f713bf4762ad127c799df81::quick_start_counter::Counter
Note: The
default
in the command represents the default address in the Rooch configuration. If you want to use another address, you can also directly pass the address starting with0x
.