/*
* Citcoin smart-contract v1.5 for Hyperledger Fabric
* (c) Alexey Sushkov, 2019
*/
'use strict';
const { Contract } = require('fabric-contract-api');
const maxAccounts = 5;
class CitcoinEvents extends Contract {
async instantiate(ctx) {
console.info('instantiate');
let emptyList = [];
await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(emptyList)));
}
// Get all accounts
async GetAccounts(ctx) {
// Get account list:
let accounts = '{}'
let accountsData = await ctx.stub.getState('accounts');
if (accountsData) {
accounts = JSON.parse(accountsData.toString());
} else {
throw new Error('accounts not found');
}
return accountsData.toString()
}
// add a account object to the blockchain state identifited by their name
async AddAccount(ctx, name, balance) {
// this is account data:
let account = {
name: name,
balance: Number(balance),
type: 'account',
};
// create account:
await ctx.stub.putState(name, Buffer.from(JSON.stringify(account)));
// Add account to list:
let accountsData = await ctx.stub.getState('accounts');
if (accountsData) {
let accounts = JSON.parse(accountsData.toString());
if (accounts.length < maxAccounts)
{
accounts.push(name);
await ctx.stub.putState('accounts', Buffer.from(JSON.stringify(accounts)));
} else {
throw new Error('Max accounts number reached');
}
} else {
throw new Error('accounts not found');
}
// return object
return JSON.stringify(account);
}
// Sends money from Account to Account
async SendFrom(ctx, fromAccount, toAccount, value) {
// get Account from
let fromData = await ctx.stub.getState(fromAccount);
let from;
if (fromData) {
from = JSON.parse(fromData.toString());
if (from.type !== 'account') {
throw new Error('wrong from type');
}
} else {
throw new Error('Accout from not found');
}
// get Account to
let toData = await ctx.stub.getState(toAccount);
let to;
if (toData) {
to = JSON.parse(toData.toString());
if (to.type !== 'account') {
throw new Error('wrong to type');
}
} else {
throw new Error('Accout to not found');
}
// update the balances
if ((from.balance - Number(value)) >= 0 ) {
from.balance -= Number(value);
to.balance += Number(value);
} else {
throw new Error('From Account: not enought balance');
}
await ctx.stub.putState(from.name, Buffer.from(JSON.stringify(from)));
await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
// define and set Event
let Event = {
type: "SendFrom",
from: from.name,
to: to.name,
balanceFrom: from.balance,
balanceTo: to.balance,
value: value
};
await ctx.stub.setEvent('SendFrom', Buffer.from(JSON.stringify(Event)));
// return to object
return JSON.stringify(from);
}
// get the state from key
async GetState(ctx, key) {
let data = await ctx.stub.getState(key);
let jsonData = JSON.parse(data.toString());
return JSON.stringify(jsonData);
}
// GetBalance
async GetBalance(ctx, accountName) {
let data = await ctx.stub.getState(accountName);
let jsonData = JSON.parse(data.toString());
return JSON.stringify(jsonData);
}
// Refill own balance
async RefillBalance(ctx, toAccount, value) {
// get Account to
let toData = await ctx.stub.getState(toAccount);
let to;
if (toData) {
to = JSON.parse(toData.toString());
if (to.type !== 'account') {
throw new Error('wrong to type');
}
} else {
throw new Error('Accout to not found');
}
// update the balance
to.balance += Number(value);
await ctx.stub.putState(to.name, Buffer.from(JSON.stringify(to)));
// define and set Event
let Event = {
type: "RefillBalance",
to: to.name,
balanceTo: to.balance,
value: value
};
await ctx.stub.setEvent('RefillBalance', Buffer.from(JSON.stringify(Event)));
// return to object
return JSON.stringify(from);
}
}
module.exports = CitcoinEvents;
直观上,这里一切都应该很清楚:
演示程序将使用 Hyperledger Fabric API 调用多个函数(AddAccount、GetAccounts、SendFrom、GetBalance、RefillBalance)。
结果,我们得到了 IBM 云中的区块链网络的下图。 该图还显示了在亚马逊云虚拟服务器上运行的演示程序(下一节将详细介绍):
为 Hyperledger Fabric API 调用创建 GUI
Hyperledger Fabric 有一个 API,可用于:
创建频道;
对等通道的连接;
通道中智能合约的安装和实例化;
调用交易;
请求区块链上的信息。
应用开发
在我们的演示程序中,我们将仅使用 API 来调用交易和请求信息,因为我们已经使用 IBM 区块链平台完成了剩余步骤。 我们使用标准技术堆栈编写 GUI:Express.js + Vue.js + Node.js。 您可以撰写一篇单独的文章来介绍如何开始创建现代 Web 应用程序。 在这里我留下我最喜欢的系列讲座的链接: 使用 Vue.js 和 Express.js 的全栈 Web 应用程序。 结果是一个客户端-服务器应用程序,具有熟悉的 Google 材料设计风格的图形界面。 客户端和服务器之间的 REST API 由多个调用组成:
HyperledgerDemo/v1/init - 初始化区块链;
HyperledgerDemo/v1/accounts/list — 获取所有账户的列表;
HyperledgerDemo/v1/account?name=Bob&balance=100 — 创建 Bob 账户;
HyperledgerDemo/v1/info?account=Bob — 获取有关 Bob 帐户的信息;
HyperledgerDemo/v1/transaction?from=Bob&to=Alice&volume=2 — 将两个币从 Bob 转移给 Alice;