目錄
EOS開(kāi)發(fā)(1) -- 安裝及常用工具介紹
EOS開(kāi)發(fā)(2) -- 發(fā)布智能合約
EOS開(kāi)發(fā)(3) -- currency合約分析
前一段時(shí)間一直在看如何在eos上進(jìn)行開(kāi)發(fā),作為一個(gè)dapp開(kāi)發(fā)者,最主要的就是去寫(xiě)智能合約和區(qū)塊鏈交互,這次分析的主要分支是在dawn-2.x
穩(wěn)定版本,以后eos結(jié)構(gòu)可能也會(huì)變讲逛,但是不會(huì)有特別大的變化
合約結(jié)構(gòu)
可以看到在contracts合約目錄下有個(gè)currency目錄,這個(gè)目錄里的內(nèi)容就是我們所要分析的
主要分析是后面三個(gè)文件,
abi
應(yīng)用程序二進(jìn)制接口文件,hpp
c++頭文件,cpp
c++代碼邏輯文件abi 文件主要是定義接口和整體的結(jié)構(gòu)
hpp 主要是去引入一些依賴和做一些結(jié)構(gòu)的聲明,設(shè)計(jì)上的事情都放在這里
cpp 主要就是合約的邏輯了,對(duì)于不同的事件 去應(yīng)用不同的處理邏輯,還包括合約初始化的方法
hpp頭文件分析
//eos的頭文件依賴
#include <eoslib/eos.hpp>
//定義token的頭文件依賴 比如token結(jié)構(gòu)體
#include <eoslib/token.hpp>
//操作區(qū)塊鏈的底層方法的頭文件
#include <eoslib/db.hpp>
namespace currency {
//定義一個(gè)currency token結(jié)構(gòu) 泛型第一個(gè)代表token的類型,第二個(gè)是token_name
typedef eosio::token<uint64_t,N(currency)> currency_tokens;
/**
* 在用戶表里存的每一條記錄(可以理解成我們數(shù)據(jù)庫(kù)里的每一行)
* @abi table
*/
struct account {
//構(gòu)造函數(shù)
account( currency_tokens b = currency_tokens() ):balance(b){}
//key 是 constant 因?yàn)槊總€(gè)用戶只在每個(gè)currency里存在一條
const uint64_t key = N(account);
//每個(gè)賬戶里的余額
currency_tokens balance;
//驗(yàn)證余額是否為空方法 c++ struct類似于class 不過(guò)默認(rèn)對(duì)成員的訪問(wèn)是public的 class是private的
bool is_empty()const { return balance.quantity == 0; }
};
//賬戶表記錄每個(gè)用戶當(dāng)前的狀態(tài)
using accounts = eosio::table<N(defaultscope),N(currency),N(account),account,uint64_t>;
//一般token都會(huì)涉及到轉(zhuǎn)賬的動(dòng)作 這個(gè)就是轉(zhuǎn)賬的結(jié)構(gòu)體
//@abi action
struct transfer {
/**
* 從哪個(gè)賬戶轉(zhuǎn)賬
*/
account_name from;
/**
* 到哪個(gè)賬戶
*/
account_name to;
/**
* 多少數(shù)量的該token
*/
currency_tokens quantity;
};
//這個(gè)接口是給第三方調(diào)用的 為了查某個(gè)用戶的賬戶信息 如果賬戶不存在 則會(huì)構(gòu)造一個(gè)默認(rèn)的賬戶返回(我的理解currency token 為0)
inline account get_account( account_name owner ) {
account owned_account;
accounts::get( owned_account, owner );
return owned_account;
}
}
- 整體分析下來(lái)沒(méi)有什么難以理解的,首先要定義一個(gè)currency token那么需要引入token結(jié)構(gòu)體,并聲明一個(gè)currency_token
- 然后轉(zhuǎn)賬設(shè)計(jì)到賬戶 需要定義賬戶的表結(jié)構(gòu) 包括賬戶名和賬戶余額
- 賬戶信息需要存儲(chǔ)在區(qū)塊鏈上所以引入賬戶table accounts
- 賬戶之間會(huì)有轉(zhuǎn)賬 所以聲明一個(gè)transfer action
- 然后提供一個(gè)接口給第三方 查看某個(gè)用戶的余額,區(qū)塊鏈上的數(shù)據(jù)公開(kāi)透明的,這里的賬戶名owner不是真正的名字,其實(shí)是一個(gè)hash值,如下
typedef unsigned long long uint64_t;
typedef uint64_t account_name;
abi文件分析
{
"types": [{
"new_type_name": "account_name",
"type": "name"
}
],
"structs": [{
"name": "transfer",
"base": "",
"fields": {
"from": "account_name",
"to": "account_name",
"quantity": "uint64"
}
},{
"name": "account",
"base": "",
"fields": {
"key": "name",
"balance": "uint64"
}
}
],
"actions": [{
"action_name": "transfer",
"type": "transfer"
}
],
"tables": [{
"table_name": "account",
"type": "account",
"index_type": "i64",
"key_names" : ["key"],
"key_types" : ["name"]
}
]
}
- 可以看到abi里有四種結(jié)構(gòu)
types
,structs
,actions
,tables
- 我看到很多合約types都是一樣的,所以暫時(shí)先這樣寫(xiě),不做分析,等官方文檔通知
- structs可以理解成定義的所有結(jié)構(gòu),包括轉(zhuǎn)賬的transfer結(jié)構(gòu),account的schema結(jié)構(gòu)
- action對(duì)應(yīng)的是合約的所有事件 這里只有轉(zhuǎn)賬一種
- tables對(duì)應(yīng)的是我們所涉及到在區(qū)塊鏈上數(shù)據(jù)的存儲(chǔ),這里賬戶的信息會(huì)存在區(qū)塊鏈上
cpp文件分析
#include <currency/currency.hpp> /// defines transfer struct (abi)
namespace TOKEN_NAME {
using namespace eosio;
// 創(chuàng)建賬戶的時(shí)候 如果檢查余額為空則刪除
void store_account( account_name account_to_store, const account& a ) {
//這個(gè)就是上面hpp文件里的判斷余額是否為空方法
if( a.is_empty() ) {
accounts::remove( a, account_to_store );
} else {
//不為空就存到區(qū)塊鏈上
accounts::store( a, account_to_store );
}
}
void apply_currency_transfer( const TOKEN_NAME::transfer& transfer_msg ) {
//做一些通知
require_notice( transfer_msg.to, transfer_msg.from );
//做一些權(quán)限的檢查
require_auth( transfer_msg.from );
//解析message
auto from = get_account( transfer_msg.from );
auto to = get_account( transfer_msg.to );
//下面的-= +=操作符是重載過(guò)的 會(huì)自動(dòng)做上溢出 和下溢出的assert檢查
from.balance -= transfer_msg.quantity;
to.balance += transfer_msg.quantity;
// 更新區(qū)塊鏈上的值
store_account( transfer_msg.from, from );
store_account( transfer_msg.to, to );
}
} // namespace TOKEN_NAME
using namespace TOKEN_NAME;
//下面的c代碼是整個(gè)程序的入口點(diǎn)
extern "C" {
//init是在合約發(fā)布或者更新的時(shí)候去調(diào)用的,如果賬戶不存在則發(fā)現(xiàn)1億的token
void init() {
account owned_account;
if ( !accounts::get( owned_account, N(currency) )) {
store_account( N(currency), account( currency_tokens(1000ull*1000ull*1000ull) ) );
}
}
//apply方法實(shí)現(xiàn)了該合約對(duì)不同事件對(duì)應(yīng)處理程序的分發(fā)
void apply( uint64_t code, uint64_t action ) {
//code 是這個(gè)合約
if( code == N(currency) ) {
//事件是transfer 轉(zhuǎn)賬 則執(zhí)行轉(zhuǎn)賬的邏輯
if( action == N(transfer) )
TOKEN_NAME::apply_currency_transfer( current_message< TOKEN_NAME::transfer >() );
}
}
}
- 可以看到整體上都挺好理解的,只是代碼是c++寫(xiě)的,所以你有可能看著不習(xí)慣
總結(jié)
- 以上就是對(duì)currency合約的整體分析,只代表個(gè)人的理解,可能有不正確的地方歡迎指出
- 大學(xué)的時(shí)候剛開(kāi)始用的就是c,所以搞起c++不是什么難事,但是用c++開(kāi)發(fā)企業(yè)級(jí)的代碼還是需要下點(diǎn)功夫的
- 以太坊上的合約是不可變的也就是你發(fā)布后不能去修改合約代碼
- EOS可以支持合約的重新部署,但是向前兼容還要,不兼容就是一個(gè)大問(wèn)題了
- 智能合約開(kāi)發(fā)和傳統(tǒng)的開(kāi)發(fā)有點(diǎn)區(qū)別,平時(shí)我們開(kāi)發(fā)有bug可以滾動(dòng)發(fā)布去修復(fù),但是智能合約不是兒戲,每次改動(dòng)都可以理解成一次分叉,如果不兼容可能會(huì)導(dǎo)致硬分叉,所以在設(shè)計(jì)之初一定要考慮好 做什么? 怎么做?