一 下载IDE
mac版 https://gxb-package.oss-cn-hangzhou.aliyuncs.com/gxchain-alpha-0.0.1.dmg
windows版 https://gxb-package.oss-cn-hangzhou.aliyuncs.com/gxchain-alpha%20Setup%200.0.1.exe
二 设计原理
1 合约代码: 暂时⽀持C++,后续会⽀持更多语⾔
2 编译⼯具:llvm + binaryen, 将C++代码编译成wasm代码
3 智能合约执⾏环境: WebAssembly作为底层执⾏VM,VM调⽤封装的API读取
外部状态、读写外部存储。 wasm-jit 地址:
三 合约帐户设计
1 帐户分2种:普通帐户和合约帐户
2 合约帐户,由普通帐户通过部署合约的⽅式创建,合约帐户⽆私钥,资产权限
由合约代码控制
3 合约帐户对象,code字段存储合约代码, abi存储合约代码和abi
4 合约代码不⽀持更新
四 合约的持久化存储
1 定义合约的数据存储格式; 定义线性存储空间,每个元素是⼀个struct类型
2 witness_node运⾏时,合约的持久化存储,放内存;程序退出时,写⼊磁盘;
后期可以考虑优化内存(磁盘+缓存)。
3 封装读写持久化存储的API, 提供读、写、检索⽅法的接⼝供合约调⽤
4 合约只能写⾃⼰的存储空间,可以读其它合约的存储空间,但不可直接写; 同
⼀合约内不同帐户的存储空间,由⽤户合约代码控制权限
5 合约内可以根据区块号获取区块,读取外部状态
五 基本接口
1 部署合约 create_contract
2 调用合约 call_contract
六 手续费计算
1 根据cpu_usage使⽤量和存储使⽤量,计算实际⼿续费
2 每笔交易,设置max_transaction_cpu_usage 单个交易的CPU上限,全局参数
(单个交易的cpu usage上限和单个区块的cpu usage上限)
3 限制合约执⾏deadline, 节点接收交易/打包区块时,验证deadline;验证区
块/replay区块时,不验证deadline
<4 创建合约⼿续费 = KB消息体⼤⼩ * 基准⼿续费
<5 调⽤合约⼿续费 = CPU使⽤量 + 存储使⽤量 + KB消息体⼤⼩ * 基准⼿续费
七 体验总结
image.png1 语法和EOS类似,
<1 继承合约类
<2 定义action
<3 定义table
2合约不支持同名合约部署。
3 部署过的合约不支持更新,这个看仁看智。我是比较喜欢可更新。
4 体验比eos开发好一点,自带IDE。内置了编译服务器地址配置,还有区块链节点。不需要搭建本地环境即可开发,编译,部署,执行。
5 js开发调用这块,有https://github.com/gxchain/gxbjs库。不过当前没有网页钱包插件(如以太的metamask,eos的scatter)。只有和布洛克城(公信宝手机端应用)绑定开发。做独立网页游戏基本不可能。
6 合约创建的token和公信宝钱包创建的UIA(用户创建资产)是两个东西,不能直接互换。感觉公信宝承认合约创建的token可上交易所,可以普及的更广。毕竟创建UIA要几千几百个GXS,发个合约token最多1个GXS。
八 参考
https://github.com/gxchain/Technical-Documents/blob/master/gxchain_contract_start.md
九 代码示例
#include <graphenelib/asset.h>
#include <graphenelib/contract.hpp>
#include <graphenelib/contract_asset.hpp>
#include <graphenelib/dispatcher.hpp>
#include <graphenelib/global.h>
#include <graphenelib/multi_index.hpp>
#include <graphenelib/system.h>
#include <vector>
using namespace graphene;
class bank : public contract
{
public:
bank(uint64_t account_id)
: contract(account_id)
, accounts(_self, _self)
{
}
// @abi action
// @abi payable
void deposit()
{
int64_t asset_amount = get_action_asset_amount();
uint64_t asset_id = get_action_asset_id();
contract_asset amount{asset_amount, asset_id};
uint64_t owner = get_trx_sender();
auto it = accounts.find(owner);
if (it == accounts.end()) {
accounts.emplace(owner, [&](auto &o) {
o.owner = owner;
o.balances.emplace_back(amount);
});
} else {
uint16_t asset_index = std::distance(it->balances.begin(),
find_if(it->balances.begin(), it->balances.end(), [&](const auto &a) { return a.asset_id == asset_id; }));
if (asset_index < it->balances.size()) {
accounts.modify(it, 0, [&](auto &o) { o.balances[asset_index] += amount; });
} else {
accounts.modify(it, 0, [&](auto &o) { o.balances.emplace_back(amount); });
}
}
}
// @abi action
void withdraw(std::string to_account, contract_asset amount)
{
int64_t account_id = get_account_id(to_account.c_str(), to_account.size());
graphene_assert(account_id >= 0, "invalid account_name to_account");
uint64_t owner = get_trx_sender();
auto it = accounts.find(owner);
graphene_assert(it != accounts.end(), "owner has no asset");
int asset_index = 0;
for (auto asset_it = it->balances.begin(); asset_it != it->balances.end(); ++asset_it) {
if ((amount.asset_id) == asset_it->asset_id) {
graphene_assert(asset_it->amount >= amount.amount, "balance not enough");
if (asset_it->amount == amount.amount) {
accounts.modify(it, 0, [&](auto &o) {
o.balances.erase(asset_it);
});
if (it->balances.size() == 0) {
accounts.erase(it);
}
} else {
accounts.modify(it, 0, [&](auto &o) {
o.balances[asset_index] -= amount;
});
}
break;
}
asset_index++;
}
withdraw_asset(_self, account_id, amount.asset_id, amount.amount);
}
private:
//@abi table account i64
struct account {
uint64_t owner;
std::vector<contract_asset> balances;
uint64_t primary_key() const { return owner; }
GRAPHENE_SERIALIZE(account, (owner)(balances))
};
typedef graphene::multi_index<N(account), account> account_index;
account_index accounts;
};
GRAPHENE_ABI(bank, (deposit)(withdraw))
网友评论