How to Set Up the Dependencies For Running Local Terra: All You Need to Know
In this tutorial-based article, we are going to set up the dependencies for running Local Terra. So, you will be guided through all the installations and commands necessary to start your interaction with local Terra smart contracts. Installing Go, Docker, Terrad, and git cloning local Terra, as well as terra-core repositories, are some of the main steps we are going to take in this tutorial.
Installing the Dependencies
For running local terra the main step is to install all of the dependencies required. The first step is to install go. You need to be careful that the version should be higher than 1.17.5.
The Dependencies for Running Local Terra #1: Go
To install Go, you should find the latest version for your operating system using this link. Then, the next step is to check the version to make sure it meets the requirement of our version (higher than 1.17.5). To do so, enter the following command in the terminal:
go version
Result:
go version go1.18.1 linux/amd64
If you see a result like the above, your installation has been successful. You should also have the Rust programming language installed using the below command (Notice that this command is used for Linux OS) in the terminal:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
For other operating systems, follow the instructions on this link. Now, it is time to git clone the terra-core repository. In the terminal, enter the following command:
git clone https://github.com/terra-money/core terra-core
After that, change the directory to the terra-core folder by entering the below command in the terminal:
cd terra-core
To install the downloaded repository, enter the following in the terminal:
git checkout main
make install
And, the most important step is to git clone the local Terra repository. We follow the
same steps for this one as well:
git clone --depth 1 https://www.github.com/terra-money/LocalTerra
cd LocalTerra
The Dependencies for Running Local Terra #2: Docker
One of the required dependencies for running local Terra is Docker. You should also have docker installed on your operating system. Here we explain the
process for Linux:
For the installation guide in other operating systems than Linux, follow the instructions on the Docker.
Installation Guide on Linux
This process consists of 5 steps that are described in the followings.
1. Dependencies:
At first, update the system to get the list of available packages and their version numbers. To do so, enter the following command in the terminal:
sudo apt update
To prepare for the installation run these commands in the terminal one after the other:
sudo apt -y install apt-transport-https ca-certificates curl software-properties-common
sudo apt -y remove docker docker-engine docker.io containerd runc
2. Add Docker’s Official GPG Key:
Also run the following commands in the terminal:
curl -fsSL https://download.docker.com/linux/ubuntu/gpg |
sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
3. Add the Docker Repository to Linux:
To add the Docker repository for Linux, enter the following commands in the terminal:
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/dockerarchive-keyring.gpg] https://download.docker.com/linux/ubuntu bionic stable" |
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg]
https://download.docker.com/linux/ubuntu bionic stable
4. Install the Docker Engine and the Docker Compose:
And finally, It is time to install docker by running these 2 commands in the terminal one after the other.
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io
5. Checking the Installation:
To start with checking the installation, use the command below:
sudo usermod -aG docker
USER newgrp docker
To make sure the installation has been successful, run the following command in the terminal:
docker version
Result:
Client: Docker Engine - Community
Version: 20.10.14
API version: 1.41
Go version: go1.16.15
Git commit: a224086
Built: Thu Mar 24 01:47:57 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server:
Engine:
Version: 20.10.14
API version: 1.41 (minimum version 1.12)
Go version: go1.16.15
Git commit: 87a90dc
Built: Thu Mar 24 17:15:03 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: v1.5.11
GitCommit: 3df54a852345ae127d1fa3092b95168e4a88e2f8
runc:
Version: 1.0.3
GitCommit:
docker-init:
Version: 0.19.0
GitCommit: de40ad
After installation for every use, you need to first sign in and run the following commands in the terminal:
sudo usermod -aG docker
docker login
docker-compose up
Run the above commands in the local Terra directory terminal.
Result:
terrad_1 | 1:47PM INF indexed block height=143 module=txindex
terrad_1 | 1:47PM INF Timed out dur=4987.108435 height=144 module=consensus round=0 step=1
terrad_1 | 1:47PM INF received proposal module=consensus
proposal={"Type":32,"block_id":{"hash":"F4DD36C8FB8D539F3F28ABB8BB7969A099AEE78B707FAE7F3C7DC8D36BF38B9D","parts":
{"hash":"F92FDEA4153F172B082676706A3B35F54A0EF3D6639FA23A3FD88FFBB40ACA6E","total":1}},"height":144,"pol_round":-
1,"round":0,"signature":"nDA4YtVFM/a8TXCLMwTwydkfJSwOoI57zNkkKieyv0xy/
zbnCVIM10cpzh78+0wym1jT3fj16ZqH5OiFE3kaAg==","timestamp":"2022-05-
07T13:47:21.846543194Z"}
terrad_1 | 1:47PM INF received complete proposal block hash=F4DD36C8FB8D539F3F28ABB8BB7969A099AEE78B707FAE7F3C7DC8D36BF38B9D
height=144 module=consensus
terrad_1 | 1:47PM INF finalizing commit of block hash=F4DD36C8FB8D539F3F28ABB8BB7969A099AEE78B707FAE7F3C7DC8D36BF38B9D
height=144 module=consensus num_txs=0 root=EFAA4A74DD1E73032E0FF553A37CCDFAD443AF0EE395516EB247948ADAF45A21
terrad_1 | 1:47PM INF minted coins from module account
amount=226570495uluna from=mint module=x/bank
terrad_1 | 1:47PM INF executed block height=144 module=state
num_invalid_txs=0 num_valid_txs=0
terrad_1 | 1:47PM INF commit synced
commit=436F6D6D697449447B5B313138203835203538203234362036322032392
03133203136302033372031393920343920393520373020323030203231312032323
12032353320323039203231382031393720313538203134392032313720322039203
13238203232352031362031363420313339203131392032325D3A39307D
terrad_1 | 1:47PM INF committed state app_hash=76553AF63E1D0-
DA025C7315F46C8D3DDFDD1DAC59E95D9020980E110A48B7716 height=144
module=state num_txs=0
terrad_1 | 1:47PM INF indexed block height=144 module=txindex
Now, you should install cargo:
To install cargo, run these 3 commands in the terminal separately:
cargo install cargo-generate --features vendored-openssl
cargo install cargo-run-scrip
For the following command in the terminal, instead of YourProjectName, you can enter any name you want for your project to be created (make sure you run this command in the directory of local Terra you have just git cloned):
cargo generate --git https://github.com/CosmWasm/cw-template.git --name YourProjectName
Result:
Generating template ...
[ 1/36] Done: .cargo/config
[ 2/36] Done: .cargo
[ 3/36] Skipped: .circleci/config.yml
[ 4/36] Done: .circleci
[ 1/36] Done: .cargo/config
[ 2/36] Done: .cargo
[ 3/36] Skipped: .circleci/config.yml
[ 4/36] Done: .circleci
[ 5/36] Done: .editorconfig
[ 6/36] Done: .github/workflows/Basic.yml
[ 7/36] Done: .github/workflows
[ 8/36] Done: .github
[ 9/36] Done: .gitignore
[10/36] Done: .gitpod.Dockerfile
[11/36] Done: .gitpod.yml
[ 1/36] Done: .cargo/config
[ 2/36] Done: .cargo
[ 3/36] Skipped: .circleci/config.yml
[ 4/36] Done: .circleci
[ 5/36] Done: .editorconfig
[ 6/36] Done: .github/workflows/Basic.yml
[ 7/36] Done: .github/workflows
[ 8/36] Done: .github
[ 9/36] Done: .gitignore
[10/36] Done: .gitpod.Dockerfile
[11/36] Done: .gitpod.yml
[12/36] Done: Cargo.lock
[13/36] Done: Cargo.toml
[14/36] Done: Developing.md
[15/36] Done: Importing.md
[16/36] Done: LICENSE
[17/36] Done: NOTICE
[ 1/36] Done: .cargo/config
[ 2/36] Done: .cargo
[ 3/36] Skipped: .circleci/config.yml
[ 4/36] Done: .circleci
[ 5/36] Done: .editorconfig
[ 6/36] Done: .github/workflows/Basic.yml
[ 7/36] Done: .github/workflows
[ 8/36] Done: .github
[ 9/36] Done: .gitignore
[10/36] Done: .gitpod.Dockerfile
[11/36] Done: .gitpod.yml
[12/36] Done: Cargo.lock
[13/36] Done: Cargo.toml
[14/36] Done: Developing.md
[15/36] Done: Importing.md
[16/36] Done: LICENSE
[17/36] Done: NOTICE
[18/36] Done: Publishing.md
[19/36] Done: README.md
[20/36] Done: examples/schema.rs
[21/36] Done: examples
[22/36] Done: rustfmt.toml
[23/36] Done: schema/count_response.json
[24/36] Done: schema/execute_msg.json
[ 1/36] Done: .cargo/config
[ 2/36] Done: .cargo
[ 3/36] Skipped: .circleci/config.yml
[ 4/36] Done: .circleci
[ 5/36] Done: .editorconfig
[ 6/36] Done: .github/workflows/Basic.yml
[ 7/36] Done: .github/workflows
[ 8/36] Done: .github
[ 9/36] Done: .gitignore
[10/36] Done: .gitpod.Dockerfile
[11/36] Done: .gitpod.yml
[12/36] Done: Cargo.lock
[13/36] Done: Cargo.toml
[14/36] Done: Developing.md
[15/36] Done: Importing.md
[16/36] Done: LICENSE
[17/36] Done: NOTICE
[18/36] Done: Publishing.md
[19/36] Done: README.md
[20/36] Done: examples/schema.rs
[21/36] Done: examples
[22/36] Done: rustfmt.toml
[23/36] Done: schema/count_response.json
[24/36] Done: schema/execute_msg.json
[25/36] Done: schema/instantiate_msg.json
[26/36] Done: schema/query_msg.json
[27/36] Done: schema/state.json
[28/36] Done: schema
[29/36] Done: src/contract.rs
[30/36] Done: src/error.rs
[31/36] Done: src/helpers.rs
[32/36] Done: src/integration_tests.rs
[33/36] Done: src/lib.rs
[34/36] Done: src/msg.rs
[35/36] Done: src/state.rs
[36/36] Done: src
Moving generated files into: `/home/mohamad/LocalTerra/my-project/np`...
Initializing a fresh Git repository
Done! New project created /home/mohamad/LocalTerra/my-project/np
CosmWasm Smart Contracts on Local Terra
In this section, we are going to get familiar with CosmWasm smart contracts written inRust programming language for interacting with local Terra through Python or JavaScript. These smart contracts have different functions that we need to send specific JSON messages to interact with them. In the end, we are going to create the artifacts folder containing the necessary binary files to interact with local Terra.
If you open the YourProjectName folder and in its src folder, the Msg.rs and contract.rs alongside with other Rust script files are found. The Msg.rs contract is related to the 3 kinds of messages we can send to our contract and it is composed of 3 main parts:
-
1. InstantiateMsg struct:
-
2. ExecuteMsg enum:
-
3. QueryMsg Query Msg:
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
pub count: i32,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
Increment {},
Reset { count: i32 },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
// GetCount returns the current count as a json-encoded number
GetCount {},
}
// We define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
pub count: i32,
}
On Dependencies for Running Local Terra: What is Inside the Contract.rs File?
Another important Rust file is Contract.rs. In the next part, we are going to refer to the functions of this contract and use them in python. The important functions that we are going to interact with later using the python scripts are instantiated, execute, try increment, try_reset, and query_count. We will later see how we can interact with these functions with the JSON messages sent to them through python codes. As you can see the programming language that these smart contracts are written in is Rust.
use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use cw2::set_contract_version;
use crate::error::ContractError;
use crate::msg::{CountResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE};
// version info for migration info
const CONTRACT_NAME: &str = "crates.io:my-project";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: InstantiateMsg,
) -> Result {
let state = State {
count: msg.count,
owner: info.sender.clone(),
};
set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
STATE.save(deps.storage, &state)?;
Ok(Response::new()
.add_attribute("method", "instantiate")
.add_attribute("owner", info.sender)
.add_attribute("count", msg.count.to_string()))
}
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
deps: DepsMut,
_env: Env,
info: MessageInfo,
msg: ExecuteMsg,
) -> Result {
match msg {
ExecuteMsg::Increment {} => try_increment(deps),
ExecuteMsg::Reset { count } => try_reset(deps, info, count),
}
}
pub fn try_increment(deps: DepsMut) -> Result {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
state.count += 1;
Ok(state)})?;
Ok(Response::new().add_attribute("method", "try_increment"))
}
}
pub fn try_reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result {
STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
if info.sender != state.owner {
return Err(ContractError::Unauthorized {});
}
state.count = count;
Ok(state)
})?;
Ok(Response::new().add_attribute("method", "reset"))
}
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult {
match msg {
QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
}
}
fn query_count(deps: Deps) -> StdResult {
let state = STATE.load(deps.storage)?;
Ok(CountResponse { count: state.count })
}
#[cfg(test)]
mod tests {
use super::*;
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::{coins, from_binary};
#[test]
fn proper_initialization() {
let mut deps = mock_dependencies(&[]);
let msg = InstantiateMsg { count: 17 };
let info = mock_info("creator", &coins(1000, "earth"));
// we can just call .unwrap() to assert this was a success
let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
assert_eq!(0, res.messages.len());
// it worked, let's query the state
let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_binary(&res).unwrap();
assert_eq!(17, value.count);
}
#[test]
fn increment() {
let mut deps = mock_dependencies(&coins(2, "token"));
let msg = InstantiateMsg { count: 17 };
let info = mock_info("creator", &coins(2, "token"));
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
// beneficiary can release it
let info = mock_info("anyone", &coins(2, "token"));
let msg = ExecuteMsg::Increment {};
let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
// should increase counter by 1
let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_binary(&res).unwrap();
assert_eq!(18, value.count);
}
#[test]
fn reset() {
let mut deps = mock_dependencies(&coins(2, "token"));
let msg = InstantiateMsg { count: 17 };
let info = mock_info("creator", &coins(2, "token"));
let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
// beneficiary can release it
let unauth_info = mock_info("anyone", &coins(2, "token"));
let msg = ExecuteMsg::Reset { count: 5 };
let res = execute(deps.as_mut(), mock_env(), unauth_info, msg);
match res {
Err(ContractError::Unauthorized {}) => {}
_ => panic!("Must return unauthorized error"),
}
// only the original creator can reset the counter
let auth_info = mock_info("creator", &coins(2, "token"));
let msg = ExecuteMsg::Reset { count: 5 };
let _res = execute(deps.as_mut(), mock_env(), auth_info, msg).unwrap();
// should now be 5
let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
let value: CountResponse = from_binary(&res).unwrap();
assert_eq!(5, value.count);
}
}
Creating a project folder using cargo
Now, let’s run the following command in the terminal to create binaries and other dependency files so that we can eventually interact with local Terra using python Terra SDK.
cargo run-script optimize
Result:
Running script 'optimize': '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/rustoptimizer:0.12.5
'
Info: RUSTC_WRAPPER=sccache
Info: sccache stats before build
Compile requests 0
Compile requests executed 0
Cache hits 0
Cache misses 0
Cache timeouts 0
Cache read errors 0
Forced recaches 0
Cache write errors 0
Compilation failures 0
Cache errors 0
Non-cacheable compilations 0
Non-cacheable calls 0
Non-compilation calls 0
Unsupported compiler calls 0
Average cache write 0.000 s
Average cache read miss 0.000 s
Average cache read hit 0.000 s
Failed distributed compilations 0
Cache location Local disk: "/root/.cache/sccache"
Cache size 0 bytes
Max cache size 10 GiB
Building contract in /code ...
Finished release [optimized] target(s) in 0.16s
Creating intermediate hash for my_project.wasm ...
ec1944cdda3c5f8f6968d59b990d6f92800f65d9655a53a45ee29984f6c5d882
./target/wasm32-unknown-unknown/release/my_project.wasm
Optimizing my_project.wasm ...
Creating hashes ...
fa445512e5b3274ddda6474335d8515c660c5fcdf03b8b2d7f468c12f3e55564
my_project.wasm
Info: sccache stats after build
Compile requests 0
Compile requests executed 0
Cache hits 0
Cache misses 0
Cache timeouts 0
Cache read errors 0
Forced recaches 0
Cache write errors 0
Compilation failures 0
Cache errors 0
Non-cacheable compilations 0
Non-cacheable calls 0
Non-compilation calls 0
Unsupported compiler calls 0
Average cache write 0.000 s
Average cache read miss 0.000 s
Average cache read hit 0.000 s
Failed distributed compilations 0
Cache location Local disk: "/root/.cache/sccache"
Cache size 0 bytes
Max cache size 10 GiB
done
Finished, status of exit status: 0
And in the project name folder, we are going to see that a new folder has been created called artifacts. Now, we are ready to write our python codes to interact with local Terra and use CosmWasm based contracts written in Rust.
Conclusion:In this tutorial, we have managed to install the dependencies required to run local Terra, such as GO and Rust programming languages, Docker, Cargo, and some Github repositories like terra-core and local-terra. We have also connected to Local
Terra network using the docker-compose up command. In the end, we have created
our project folder and files using the cargo generate command.
Moreover, we have taken a quick look at some of the important smart contracts
written in Rust programming language and got familiar with their functions so that
we can interact with them later using our python scripts. We also created the artifacts
folder using the cargo command to have everything set up for interacting with the
local Terra.
Download this Article in PDF format
Need More Rust Learning?
To fully take advantage of this article, you’d better get familiar with Rust Language applied to CosmWasm smart contracts. And the good news is that we have a complete tutorial for it.