在我們開始使用代碼之前盘榨,我們首先需要一個想法郊愧。你看過Steven Spielberg的“Ready Player One”電影嗎朴译?如果你確實已經(jīng)知道了綠洲是什么井佑!精彩,對吧眠寿?讓我們想象一下躬翁,我們是將要建立下一個Oasis的團隊的一部分,我們的任務是將區(qū)塊鏈技術集成到其中盯拱,或者至少表明可以完成盒发。我們要做的是一個小型的dApp,它會有玩家狡逢。每個玩家都可以玩游戲宁舰,并且可以從中獲得硬幣。隨著硬幣奢浑,他可以去市場購買物品蛮艰,這會給體力,能力雀彼,生命 壤蚜,升級。當然徊哑,Oasis合同是我們的主要起點袜刷。
開發(fā)前準備
為了能夠開發(fā)EOS dApps,您需要使用C / C ++的背景实柠,因為這是用于EOS Smart Contracts的編程語言
- 會使用C / C ++ - ?
一個IDE水泉,您可以在其中編寫智能合約。我們使用VS Code和C / C ++擴展窒盐,但是你可以選擇最適合你的草则。CLion也是一個非常不錯的選擇
- IDE - **Visual Studio代碼 ** - ?
當然,你需要一個本地testnet節(jié)點蟹漓,但你知道如何構建一個炕横。
- 一個工作的本地測試網(wǎng)絡節(jié)點 - ?
配置開發(fā)環(huán)境
在編寫我們的第一份合同之前,我們需要在開發(fā)過程中設置一些我們需要的東西葡粒。
步驟1 - 啟動節(jié)點
nodeos -e -p eosio --plugin eosio :: wallet_api_plugin --plugin eosio :: chain_api_plugin --plugin eosio :: account_history_api_plugin --resync
步驟2 - 創(chuàng)建一個電子錢包
它將存儲我們在簽署交易時使用的密鑰
# 1. Create new wallet with the name "oasis"
cleos wallet create -n oasis
# 2. Generate two pair of keys (use the command twice)
cleos create key
# 3. Import the generated private keys in the wallet(you need to specify the wallet at the end)
# {private_key_1} - This will be the OwnerKey
# {private_key_2} - This will be the ActiveKey
cleos wallet import {private_key_1} -n oasis
cleos wallet import {private_key_2} -n oasis
# 4. Add the private key of the "eosio" account into your wallet
# Note: With the latest version of EOSIO the private key is automatically added to your wallet
cleos wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 -n oasis
注意:不要忘記保存您的密鑰和錢包密碼
EOS.IO智能合約在帳戶上運行份殿。因此,需要賬戶將交易轉(zhuǎn)移或以其他方式推送至區(qū)塊鏈嗽交。讓我們將我們的帳戶命名為“ anorak ”卿嘲。聽起來很熟悉?
# Create the account using the public keys
cleos create account eosio anorak {public_OwnerKey} {public_ActiveKey}
# "eosio" is the name of the account who will create the new one
# "anorak" is the name of the new account
項目'綠洲'
現(xiàn)在是開始開發(fā)我們的dApp的時候了夫壁!創(chuàng)建一個名為Oasis的項目文件夾拾枣。里面添加兩個主要的子文件夾 - contracts 和 tests。是的,我們也要寫tests梅肤。將會有四個子文件夾:Players, Games, Marketplace , Oasis司蔬。
Player智能合約
玩家將成為我們的第一個EOS智能合約。每個玩家將擁有用戶名姨蝴,等級俊啼,生命值,能量值左医,金幣授帕,背包和天賦。他將能夠用金幣從商店購買物品炒辉,并將其添加到他的背包豪墅,生命值,能量值或天賦中黔寇。為了獲得金幣,他需要與綠洲的其他玩家一起玩游戲
在Players文件夾中創(chuàng)建Players.hpp和Players.cpp文件
Player.hpp是包含由.cpp文件引用的變量斩萌,常量和函數(shù)的頭文件缝裤。
Player.cpp文件是包含合同功能的源文件。
我們來深入看一下EOS合約的基本結構
Players.hpp
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include <string>
Players.cpp
在合約開始時颊郎,我們設置了我們要使用的所有包含憋飞。在我們的例子中,Players.hpp已經(jīng)擁有了它們姆吭,所以在我們的合同實現(xiàn)中只包含頭文件就足夠了榛做。大多數(shù)情況下,當你使用頭文件時你會這樣做内狸。
將所有內(nèi)容都包裝在名稱空間中是一種很好的做法检眯,因為它與Oasis一起顯示在這里。
#include "Players.hpp"
namespace Oasis {
using namespace eosio;
using std::string;
class Players : public contract {
using contract::contract;
public:
Players(account_name self):contract(self) {}
//@abi action
void add(const account_name account, string& username, uint64_t level) {
}
//@abi action
void update(const account_name account) {
}
//@abi action
void getplayer(const account_name account) {
}
private:
//@abi table player i64
struct player {
uint64_t account_name;
string username;
uint64_t level;
uint64_t health_points = 1000;
uint64_t energy_points = 1000;
uint64_t primary_key() const { return account_name; }
EOSLIB_SERIALIZE(player, (account_name)(username)(level)(health_points)(energy_points))
};
typedef multi_index<N(player), player> playerIndex;
};
EOSIO_ABI(Players, (add)(update)(getplayer))
}
該Players 繼承contract 昆淡,并使用它的構造函數(shù)(using contract::contract)锰瘸。
在開發(fā)EOS dApp時,您應該了解的一件重要事情是昂灵,智能合約以操作和共享內(nèi)存數(shù)據(jù)庫訪問的形式相互通信避凝。一個合同可以讀取另一個合同數(shù)據(jù)庫的狀態(tài),只要它包含在具有異步可達的事務的讀取范圍內(nèi)眨补。這可以通過使用兩種通信模式之一來實現(xiàn) - inline 和deferred管削。您可以將它們視為同步和異步
從EOS.IO文檔:
inline - 內(nèi)聯(lián)保證與當前交易函數(shù)一起執(zhí)行或展開; 無論成功或失敗,都不會收到任何通知撑螺。內(nèi)聯(lián)與原始交易函數(shù)具有相同的范圍和權限
deferred - 延期將在稍后由生產(chǎn)者自行決定; 可以傳達通信結果或者可以簡單地超時含思。延期可以延伸到不同的范圍,并承擔發(fā)送它們的合同的權力实蓬。
在我們合同的類體中茸俭,有兩種類型的訪問修飾符 - public和private吊履。在公共部分是構造函數(shù)和所有動作。一個動作表示智能合約中的單個操作调鬓。在我們的合同中艇炎,我們add,update和getplayer腾窝。我們稍后會看看他們缀踪。與此同時,您應該已經(jīng)注意到虹脯,在每個函數(shù)之前驴娃,我們都有“ // @ abi action ”。它是eosiocpp腳本的指示標志循集,它將為我們的智能合約生成.abi文件唇敞。
在private中,我們保存我們不希望從Player合約外部訪問的所有內(nèi)容咒彤。這里我們正在初始化multi_index疆柔。這是什么,為什么我們需要它镶柱?
//@abi table player i64
struct player {
uint64_t account_name;
string username;
uint64_t level;
uint64_t health_points = 1000;
uint64_t energy_points = 1000;
uint64_t primary_key() const { return username; }
EOSLIB_SERIALIZE(player, (account_name)(username)(level)(health_points)(energy_points))
};
typedef multi_index<N(player), player> playerIndex;
Actions 實現(xiàn)
add 負責創(chuàng)建一個玩家
/@abi action
void add(const account_name account, string& username) {
/**
* We require that only the owner of an account can use this action
* or somebody with the account authorization
*/
require_auth(account);
/**
* We access the "player" table as creating an object of type "playerIndex"
* As parameters we pass code & scope - _self from the parent contract
*/
playerIndex players(_self, _self);
/**
* We must verify that the account doesn't exist yet
* If the account is not found the iterator variable should be players.end()
*/
auto iterator = players.find(account);
eosio_assert(iterator == players.end(), "Address for account already exists");
/**
* We add the new player in the table
* The first argument is the payer of the storage which will store the data
*/
players.emplace(account, [&](auto& player) {
player.account_name = account;
player.username = username;
player.level = 1;
player.health_points = 1000;
player.energy_points = 1000;
});
}
update 負責更新玩家level, health and energy points . 實現(xiàn)和add相似旷档,只是在更新玩家狀態(tài)時第一個參數(shù)時迭代器
//@abi action
void update(account_name account, uint64_t level, int64_t healthPoints, int64_t energyPoints) {
require_auth(account);
playerIndex players(_self, _self);
auto iterator = players.find(account);
eosio_assert(iterator != players.end(), "Address for account not found");
/**
* We add the new player in the table
* The first argument is the payer of the storage which will store the data
*/
players.modify(iterator, account, [&](auto& player) {
player.level = level;
if ((player.health_points - healthPoints) < 0) {
player.health_points = 0;
} else {
player.health_points -= healthPoints;
}
if ((player.energy_points - energyPoints) < 0) {
player.energy_points = 0;
} else {
player.energy_points -= energyPoints;
}
});
}
getplayer 是一個查詢操作,任何人都可以訪問 歇拆,不需要認證
//@abi action
void getplayer(const account_name account) {
playerIndex players(_self, _self);
auto iterator = players.find(account);
eosio_assert(iterator != players.end(), "Address for account not found");
/**
* The "get" function returns a constant reference to the object
* containing the specified secondary key
*/
auto currentPlayer = players.get(account);
print("Username: ", currentPlayer.username.c_str(), " Level: ", currentPlayer.level, " Health: ", currentPlayer.health_points, " Energy: ", currentPlayer.energy_points);
}
Player智能合約部署
生成wast和abi
//生成wast
eosiocpp -o Players.wast Players.cpp
//生成abi
eosiocpp -g Players.abi Players.cpp
部署
# cleos set contract {account} {path_to_contract_folder} {path_to_.wast_file} {path_to_.abi_file}
cleos set contract anorak ./contracts/Players ./contracts/Players/Players.wast ./contracts/Players/Players.abi
Reading WAST...
Assembling WASM...
Publishing contract...
executed transaction: b3fd7968d08f3e1cece14fed9632c8c678e89f5832b356a3c4c651aa4d3fe159 5668 bytes 10000 cycles
# eosio <= eosio::setcode "000000004073e9340000cf590061736d0100000001cf011d60037f7e7f0060057f7e7e7e7e0060027f7e0060000060057e7...
# eosio <= eosio::setabi "000000004073e934000406706c6179657200050c6163636f756e745f6e616d650675696e74363408757365726e616d65067...
測試
創(chuàng)建一個玩家
# cleos push action {account} {action_name} '{data}' -p {account}@active
cleos push action anorak add '["anorak","art3mis"]' -p anorak@active
executed transaction: 9b099bd072d090424bff9cff3e0473a2ccde98d3554cbffee2392172e4221055 252 bytes 1000 cycles
# anorak <= anorak::add {"account":"anorak","username":"art3mis"}
查詢玩家
# cleos push action {account} {action} {data}
# account - The account providing the contract to execute
# action - The action to execute on the contract
# data - The arguments to the contract
cleos push action anorak getplayer '["anorak"]'
executed transaction: 5ecdf264952837e61671b31e935eec6365af8c6430f00f3eafd274e7ca81910d 156 bytes 1000 cycles
# anorak <= anorak::getplayer {"account":"anorak"}
>> Username: art3mis Level: 1 Health: 1000 Energy: 1000
更新玩家狀態(tài)
cleos push action anorak update '["anorak",4,75,110]' -p anorak@active
executed transaction: 0da802b784e9bc2d56d84582e269eae213f6255ca2bbe83c32b6baf9fafc1f54 268 bytes 1000 cycles
# anorak <= anorak::update {"account":"anorak","level":4,"healthPoints":75,"energyPoints":110}
驗證更新
cleos push action anorak getplayer '["anorak"]' -p anorak@active
executed transaction: a82c8356aaaea39ddb544d0fc5486f567c4bbdbb887b752aaf21ff0dd9ee8772 156 bytes 1000 cycles
# anorak <= anorak::getplayer {"account":"anorak"}
>> Username: art3mis Level: 4 Health: 925 Energy: 890
總結
from:https://infinitexlabs.com/eos-development-tutorial-part-1/