文字:2993, 预计时间:10分钟
目标:
在本教程中,您将:
创建一个新的使用TypeScript语言编写的智能合约项目
使用标准模板实施基本的智能合约
了解智能合约的作用
摘要:
智能合约包含用代码表示的业务逻辑,用于由单个组织生成事务响应。 该响应由该组织进行数字签名,并包含对一组业务对象的更改。 在Hyperledger Fabric中,智能合约使用包含ledger中所有业务对象作为当前值的状态数据库,来简化事务生成。
每个智能合约可以在每个组织中的一组对等节点上运行。
可以使用IBM Blockchain Platform VS Code扩展来构建和测试智能合约,为了成功完成本教程,您必须安装IBM Blockchain Platform VS Code扩展。
IBM Blockchain Platform VS Code 扩展地址在此: https://marketplace.visualstudio.com/items?itemName=IBMBlockchain.ibm-blockchain-platform
安装IBM Blockchain Platform VS Code 扩展:
在所有操作系统上都需要以下依赖项:
VS Code版本1.40.0或更高版本
VS Code版本可以通过运行命令行查找:
code --version
以下依赖项是可选的:
Node v10(v10.15.3或更高版本)或v12(v12.13.1或更高版本)
可以通过运行以下命令找到Node版本:
node --version
npm v6.x或更高版本
可以通过运行以下命令找到npm版本:
npm --version
注意:
如果使用“ nvm”或“ nodenv”之类的管理器安装Node和npm,则需要设置默认/全局版本,然后重新启动VS Code,以使“先决条件”页面可以检测到该版本。
Go版本v1.12或更高版本,用于开发Go合约
可以通过运行以下命令找到Go版本:
go version
Java v8用于开发Java合约
可以通过运行以下命令找到Java版本:
java -version
Windows的其他要求
如果您使用的是Windows 10 Pro或Enterprise,并且具有更新1607
1组织本地结构功能
该扩展可以使用Docker在您的机器上运行一个简单的预配置本地Hyperledger Fabric网络。默认情况下,此功能已启用,因为我们强烈建议您使用它,但是,如果需要,您可以禁用此功能。
您将需要以下内容:
Docker v17.06.2-ce或更高版本
可以通过运行以下命令找到Docker版本:
docker --version
Windows的其他要求
Docker for Windows配置为使用Linux容器(这是默认设置)
您将需要安装OpenSSL v1.0.2 OpenSSL二进制文件
安装普通版本,而不是标记为“ light”的版本
在64位系统上将Win64版本安装到C:\ OpenSSL-Win64
成功安装IBP VS Code Extension 之后的界面:
一. 创建一个智能合约项目
在IBM Blockchain Platform VS Code扩展中使用Hyperledger Fabric组件和文件时,通常很方便地显示IBM Blockchain Platform侧栏,其中包含智能合约,结构环境,结构网关和结构钱包视图。
您可以通过单击VS Code活动栏中的IBM Blockchain Platform图标来显示侧边栏。但是,请注意,该图标是一个切换按钮:如果在已经显示侧栏的情况下单击它,则侧栏将被隐藏。
现在,我们将创建一个智能合约项目,其中将包含我们智能合约所需的文件。IBM Blockchain Platform将为我们创建一个框架智能合约,稍后我们可以对其进行定制。
将鼠标移到Smart Contracts视图的标题栏上,单击出现的“ ...”,然后选择“创建新项目”。
按Enter接受默认合同类型:
在本教程中,我们将使用TypeScript语言。
单击“ TypeScript”:
在选择了TypeScript 之后, IBP VS Code扩展在后台通过使用Yeoman 来创建一个基于模板(skeleton)的应用,这个应用包含一个单一的业务对象类型(在Fabric中称之为Asset),这个业务对象会作为事务响应(response) 返回。
在这种情况下,资产(Asset)是一组相关对象,按照惯例,它们以大写字母开头, 如同大多数编程语言对于域对象的定义一样。
以后,我们可以根据需要扩展智能合约包,使其具有其他智能合约和资产类型。但是,目前,我们仅接受提供给我们的默认资产类型。
按Enter接受默认资产类型(“ MyAsset”)。
单击浏览以选择项目在文件系统上的目标位置
导航到文件系统上您要用于开发的文件夹。(为方便起见,在屏幕截图中,我们将使用“桌面”文件夹。)
单击“新建文件夹”创建一个用于存储智能合约项目的新文件夹,并将其命名为“ demo-contract”。
选择“添加到工作空间”以告诉IBM Blockchain Platform将项目添加到您的工作空间。
生成智能合约项目最多需要一分钟才能完成。成功完成后,IBM Blockchain Platform侧栏将被隐藏,而Explorer侧栏将被显示。资源管理器侧栏将显示已创建的新项目。
到现在为止, Hyperledger Fabric 的智能合约项目已经创建成功了!
二. 深入理解Fabric智能合约项目
现在,我们将查看已创建的文件以查看其作用。
在资源管理器侧栏中,展开“演示合同”->“ src”。
智能合约包含在“ my-asset-contract.ts”文件中。文件名是根据您先前提供的资产类型生成的。
单击“ my-asset-contract.ts”以将其加载到VS Code编辑器中。
下面来开始通读代码。
文件顶部的import语句使Hyperledger Fabric类可用。
import { Context, Contract, Info, Returns, Transaction } from 'fabric-contract-api';
我们之前选择的是TypeScript 语言, 因此这里通过继承Contract来定义智能合约的类:
export class MyAssetContract extends Contract {
extends子句指出这是使用先前导入的Contract类的Hyperledger Fabric智能合约。
源代码文件的其余部分包含智能合约生成的每种交易类型的实现。
事务
假设我们有一个汽车账本:myAssetExists('CAR001')将返回true或false,这取决于CAR001是否在分类帐中。
查看MyAsset 类中的第一个方法签名:
@Transaction(false)
public async myAssetExists(ctx: Context, myAssetId: string): Promise<boolean> {
任何以@Transaction()装饰器为前缀的方法都表明可以调用此方法来为此智能合约生成事务响应。
我们可以看到myAssetExists()的前缀带有一个更具描述性的装饰器-@Transaction(false)-表示它仅从分类帐中读取。我们将在本教程后面的示例中看到@Transaction(true)方法的示例,该方法将写入总帐。
myAssetExists方法:
@Transaction(false)
@Returns('boolean')
public async myAssetExists(ctx: Context, myAssetId: string): Promise<boolean> {
const buffer = await ctx.stub.getState(myAssetId);
return (!!buffer && buffer.length > 0);
}
myAssetExists()签名的其余部分,主要展示了此智能合约生成交易响应所需要的输入。Context对象是一个特殊参数。智能合约使用它来维护整个合约中的用户数据,稍后我们将看到它如何简化智能合约的编写过程。
开始使用世界状态(world state)
每个方法都使用世界状态来读取业务对象的当前值,并为这些对象生成相应的新值。
上下文对象Context(ctx)使您可以直接访问世界状态:
const buffer = await ctx.stub.getState(myAssetId);
getState方法从世界状态返回与myAssetId描述的键关联的当前值。此方法仅读取业务对象的值,而不更改它。这就是为什么将myAssetExists()方法修饰为@Transaction(false)的原因。
现在看看第二种方法:
@Transaction()
public async createMyAsset(ctx: Context, myAssetId: string, value: string): Promise<void> {
const exists = await this.myAssetExists(ctx, myAssetId);
if (exists) {
throw new Error(`The my asset ${myAssetId} already exists`);
}
const myAsset = new MyAsset();
myAsset.value = value;
const buffer = Buffer.from(JSON.stringify(myAsset));
await ctx.stub.putState(myAssetId, buffer);
}
这个方法以这行结束:
await ctx.stub.putState(myAssetId, buffer);
putState方法使用键myAssetId将业务对象的当前值更改为值缓冲区。这就是为什么createMyAsset方法使用@transaction(true)装饰的原因。
当智能合约执行完成时,交易记录将由Hyperledger Fabric自动生成。如果事务成功提交到分类帐,则它包含已读取的状态和要写入的状态。
重要的是不要将这些事务响应与方法的返回值混淆。它们不是同一件事。
想知道更多有关事务和响应的更多信息,请查看Hyperledger Fabric文档(https://hyperledger-fabric.readthedocs.io/en/latest/ledger/ledger.html)
智能合约的确定性(deterministic)
虽然看起来像,但是putState的作用不是立即的。仅当网络中的每个组织都同意生成的交易响应时,才会更新由myAssetId标识的业务对象。这要求共识过程在整个网络中完成。
作为开发人员,我们通常不需要担心共识。但是,我们确实需要确保我们的智能合约交易具有确定性(deterministic)。也就是说,每个人必须始终为给定的一组交易输入生成相同的交易响应。
这是因为我们的智能合约将由多个组织运行,每个组织都必须生成相同的交易响应。如果不是,则生成的生成交易将作为无效记录在分类帐中,并且世界状态不会更新;该交易不会产生任何影响。
这意味着不建议尝试使用例如随机数,时间戳或其他瞬态值来更新世界状态。
总结
在本教程中,我们生成了第一个智能合约。
我们看到了智能合约如何包含不同的方法,每种方法都可以针对给定的一组输入生成事务响应。交易是使用getState和putState方法生成的,它们提供了键控访问来获取和设置分类账中业务对象的当前值。
为了测试这些事务,我们必须首先将它们部署到Hyperledger Fabric实例;我们将在下一个教程中做到这一点。