区块链:我们应该建立什么PoC?

眼睛害怕,手痒!

在之前的文章中,我们了解了构建区块链的技术( 我们应该为之构建区块链? )以及可以在其帮助下实现的案例我们应该构建案例 )。 是时候动手了! 为了实施飞行员和PoC(概念验证),我更喜欢使用云,因为 可以从世界任何地方访问它们,通常,您不必花时间进行繁琐的环境安装,因为 有预定义的配置。 因此,让我们做一些简单的事情,例如,在参与者之间转移硬币并适当地称为Citcoin的网络。 为此,我们将使用IBM云和通用区块链Hyperledger Fabric。 首先,让我们看看为什么Hyperledger Fabric被称为通用区块链。

图片

Hyperledger Fabric-通用区块链


一般来说,通用信息系统是:

  • 一组服务器和执行业务逻辑的软件核心;
  • 与系统交互的接口;
  • 设备/人员的注册,认证和授权方式;
  • 存储操作和归档数据的数据库:

图片

可以在网站上阅读Hyperledger Fabric的正式版本,简而言之,Hyperledger Fabric是一个开源平台,可让您构建封闭的区块链并执行以JS和Go编程语言编写的任意智能合约。 让我们详细看一下Hyperledger Fabric的体系结构,并确保它是一个通用系统,仅具有存储和记录数据的细节。 特殊性是,与所有区块链一样,数据仅在参与者达成共识且记录数据后无法谨慎纠正或删除的情况下,才存储在放置在区块链上的块中。

Hyperledger Fabric体系结构


该图显示了Hyperledger Fabric的体系结构:

图片

组织 -组织包含对等组织,即 区块链由于组织的支持而存在。 不同的组织可能是同一渠道的成员。

频道 -将对等方分组的逻辑结构,即 区块链已设置。 Hyperledger Fabric可以同时处理具有不同业务逻辑的多个区块链。

会员服务提供商(MSP)是用于颁发身份和分配角色的CA(证书颁发机构)。 要创建节点,您需要与MSP进行交互。

对等节点 -验证交易,存储区块链,执行智能合约并与应用程序进行交互。 对等方具有颁发MSP的身份(数字证书)。 与所有节点均相等的比特币或以太网络不同,超级账本结构节点中的节点扮演着不同的角色:

  • 对等方可以认可对等方 (EP)并执行智能合约。
  • 提交对等方 (CP)-仅将数据保存在区块链上并更新“世界状态”。
  • 锚点 (AP)-如果多个组织参与了区块链,则锚点将用于它们之间的通信。 每个组织必须有一个或多个锚点对等方。 借助AP,组织中的任何对等方都可以获取有关其他组织中的所有对等方的信息。 八卦协议用于在AP之间同步信息。
  • 领导者对等方 -如果组织有多个对等方,则只有该对等方领导方将从Ordering服务中接收块并将其提供给其他对等方。 领导者可以由组织中的对等方静态设置或动态选择。 八卦协议还用于同步领导者信息。

资产是存储在区块链上的有价值的实体。 更具体地说,这是JSON格式的键值数据。 这些数据记录在区块链中。 他们有一个故事,该故事存储在区块链上,而当前状态则存储在世界状态数据库中。 根据业务任务随机填充数据结构。 没有必填字段,唯一的建议是资产应具有所有者并且是有价值的。

分类帐 -由区块链“ Blockchain”和数据库“ Word state”组成,该数据库存储资产的当前状态。 世界状态使用LevelDB或CouchDB。

智能合约 -使用智能合约,可以实现系统的业务逻辑。 在Hyperledger Fabric中,智能合约称为链码。 借助链码,可以定义资产和资产交易。 用技术语言来说,智能合约是用JS或Go编程语言实现的软件模块。

背书政策 -对于每个链码,您可以指定多少个政策以及希望从谁那里确认交易。 如果未设置该策略,则默认情况下使用:“该交易必须由渠道中任何组织的任何成员确认”。 政策示例:

  • 组织的任何管理员都必须确认交易;
  • 组织的任何成员或客户都必须确认;
  • 必须确认任何同行组织。

订购服务 -将交易打包成块并将同级发送到渠道。 确保将消息传递到网络上的所有对等方。 对于工业系统, Kafka消息代理用于开发和测试Solo

通话流程


图片

  • 该应用程序使用Go,Node.js或Java SDK与Hyperledger Fabric进行交互;
  • 客户端创建一个tx事务并将其发送给背书的对等方。
  • 对等方会验证客户的签名,完成交易,然后将背书签名发送回客户。 链码仅在背书对等体上执行,其执行结果发送到所有对等体。 这种工作算法称为-PBFT(实用拜占庭容错)共识。 它与传统的BFT的不同之处在于,消息是发送的,并非所有参与者都希望进行确认,而是仅从特定的集合中进行确认;
  • 客户端收到与背书策略对应的响应数量后,将交易发送到订购服务;
  • 订购服务构成一个块,并将其发送给所有提交对等方。 订购服务提供块的顺序写入,其中不包括所谓的分类帐分叉( 请参阅“分叉”部分 );
  • 对等方收到一个区块,再次检查背书策略,将该区块写入区块链,并更改“世界状态”数据库中的状态。

即 事实证明,节点之间的角色分离。 这提供了可伸缩性和区块链安全性:

  • 智能合约(链码)执行对等签注。 这样可以确保智能合约的机密性,因为 它不是由所有参与者存储,而是仅存储在认可的同级上。
  • 订购应该很快。 Ordering仅构成一个块并将其发送到固定的一组领导者对等点,从而确保了这一点。
  • 承诺对等方仅存储区块链-可以有很多对等方,它们不需要很多功能和即时工作。

有关Hyperledger Fabric的体系结构解决方案以及为何以这种方式工作的更多信息,请参见此处: 体系结构起源或此处: Hyperledger Fabric:适用于许可区块链的分布式操作系统

因此,Hyperledger Fabric是一个真正的通用系统,您可以使用它:

  • 使用智能合约机制实施任意业务逻辑;
  • 以JSON格式写入和接收来自区块链数据库的数据;
  • 使用证书颁发机构提供并验证对API的访问。

现在我们已经弄清楚了Hyperledger Fabric的细节,让我们最后做一些有用的事情!

扩展区块链


问题陈述


任务是使用以下功能实现Citcoin网络:创建帐户,获取余额,补充帐户,将硬币从一个帐户转移到另一个帐户。 让我们画一个对象模型,我们将在智能合约中进一步实现它。 因此,我们将拥有按名称标识的帐户,其中包含余额和帐户列表。 帐户和帐户列表均以Hyperledger Fabric资产为单位。 因此,它们具有历史和当前状态。 我会尽力画清楚:

图片

上面的数字是当前状态,存储在世界状态数据库中。 在它们下面是显示存储在区块链上的故事的图。 资产的当前状态由交易更改。 资产仅在整体上发生更改,因此,作为交易的结果,将创建一个新的对象,并且资产的当前值成为历史记录。

IBM云


我们在IBM云中创建一个帐户。 要使用区块链平台,您需要将其升级为现收现付。 这个过程可能不会很快,因为 IBM请求其他信息并手动进行验证。 从积极的方面来说,我可以说IBM有很好的培训材料,可让您在他们的云中部署Hyperledger Fabric。 我喜欢以下系列文章和示例:


以下是IBM Blockchain平台的屏幕截图。 这不是创建区块链的指令,而仅仅是任务范围的演示。 因此,出于我们的目的,我们建立了一个组织:

图片

在其中,我们创建以下节点:Orderer CA,Org1 CA,Orderer Peer:

图片

我们开始用户:

图片

创建一个频道并将其称为citcoin:

图片

本质上,Channel是一个区块链,因此它以零块(Genesis块)开头:

图片

编写智能合约


/* * 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)。
  • SendFrom和RefillBalance函数生成演示程序将接收的事件(事件)。
  • 函数实例化-在智能合约实例化期间被调用一次。 实际上,它不是一次被调用,而是每次智能合约的版本被更改时被调用。 因此,用空数组初始化列表是一个坏主意,因为 现在,当更改智能合约的版本时,我们将丢失当前列表。 但是什么都没有,我只是在学习)。
  • 帐户和帐户列表(帐户)是JSON数据结构。 对于数据处理,使用JS。
  • 您可以通过调用getState函数来获取资产的当前值,并使用putState对其进行更新。
  • 创建帐户时,将调用AddAccount函数,其中将比较区块链中的最大帐户数(maxAccounts = 5)。 还有一个门框(已注意到?),这导致帐户数量无限增加。 必须避免此类错误)

接下来,将智能合约上传到Channel并安装它:

图片

我们看一下安装智能合约的交易:

图片

我们查看有关我们频道的详细信息:

图片

结果,我们在IBM云中获得以下区块链网络图。 虚拟服务器上的Amazon云中还运行着一个演示程序(有关详细信息,请参阅下一节):

图片

为Hyperledger Fabric API调用创建GUI


Hyperledger Fabric具有可用于以下目的的API:

  • 创作频道;
  • 对等连接到渠道;
  • 渠道中智能合约的安装和实例化;
  • 交易电话;
  • 在区块链上请求信息。

应用开发


在我们的演示程序中,我们将仅使用API​​来调用交易和请求信息,因为 我们已经使用IBM区块链平台采取了其余步骤。 我们使用标准技术堆栈(Express.js + Vue.js + Node.js)编写GUI。 您可以撰写有关如何开始创建现代Web应用程序的单独文章。 在这里,我将保留我最喜欢的讲座系列的链接: 使用Vue.js和Express.js的Full Stack Web App 。 结果是一个客户服务器应用程序,它具有Google材料设计风格的熟悉的图形界面。 客户端和服务器之间的REST API包含几个调用:

  • HyperledgerDemo / v1 / init-初始化区块链;
  • HyperledgerDemo / v1 /帐户/列表-获取所有帐户的列表
  • HyperledgerDemo / v1 /帐户?名称= Bob和余额= 100-创建一个Bob帐户;
  • HyperledgerDemo / v1 / info?帐户= Bob-获取有关Bob帐户的信息;
  • HyperledgerDemo / v1 / transaction?从= Bob&到= Alice&volume = 2-将两枚硬币从Bob转移到Alice;
  • HyperledgerDemo / v1 /断开连接-关闭与区块链的连接。

我将API描述和示例放在Postman网站,Postman网站是用于测试HTTP API的著名程序。

Amazon Cloud演示应用程序


该应用程序已上传到Amazon,因为 到目前为止,IBM未能升级我的帐户并允许创建虚拟服务器。 Cherry如何附加域名: www.citcoin.info 。 我将服务器保持开启状态,然后关闭它,因为 租金的美分正在下降,交易所未提供citcoin硬币的报价)我在文章中放了该演示的屏幕截图,以便使工作逻辑清晰。 演示应用程序可以:

  • 初始化区块链;
  • 创建一个帐户(但现在不创建新帐户,因为在区块链中已达到智能合约中规定的最大帐户数);
  • 获取帐户列表;
  • 在Alice,Bob和Alex之间转移西币硬币;
  • 接收事件(但是现在无法显示事件,因此,为简单起见,该接口表示不支持事件);
  • 记录操作。

首先,初始化区块链:

图片

接下来,启动您的帐户,不要小看余额:

图片

我们得到所有可用帐户的列表:

图片

我们选择发送者和接收者,我们得到他们的余额。 如果发件人和收件人相同,则将补充其帐户:

图片

在日志中,我们监视事务的执行:

图片

实际上,通过演示程序,仅此而已。 接下来,您可以在区块链中看到我们的交易:

图片

以及一般交易清单:

图片

至此,我们成功完成了PoC的创建,以创建Citcoin网络。 为了使Citcoin成为完整的硬币转移网络,还需要做些什么? 只是一点:

  • 在创建帐户的阶段,实现私钥/公钥的生成。 私钥必须存储在帐户用户中,并在区块链中公开。
  • 进行硬币转账,其中使用公钥而不是姓名来标识用户。
  • 用他的私钥加密从用户到服务器的交易。

结论


我们已经实现了具有以下功能的Citcoin网络:添加帐户,获得余额,补充您的帐户,将硬币从一个帐户转移到另一个帐户。 那么,构建PoC的成本是多少?

  • 我们需要研究一般的区块链,尤其是Hyperledger Fabric。
  • 学习使用IBM或Amazon云;
  • 学习JS编程语言和一些Web框架;
  • 如果某些数据不需要存储在区块链中,而需要存储在单独的数据库中,则学习如何与PostgreSQL集成;
  • 最后但并非最不重要的一点-在现代世界中不了解Linux,无处不在!)

当然,这不是火箭科学,但您必须流汗!

GitHub来源


他将源代码放在GitHub上 。 仓库的简要说明:
目录“ 服务器 ”-Node.js服务器
目录“ 客户端 ”-Node.js客户端
blockchain ”目录(参数值和键当然是不起作用的,仅作为示例提供):

  • 合约-智能合约的来源
  • wallet-使用Hyperledger Fabric API的用户密钥。
  • * .cds-智能合约的编译版本
  • * .json文件-使用Hyperledger Fabric API的示例配置文件

这仅仅是开始!

Source: https://habr.com/ru/post/zh-CN466157/


All Articles