创建和部署智能合同

为了创建智能合约,我们需要定义要在区块链中存储和管理的实体,如下图的实体模型所示。


以下是 oracle-blockchain-cms-entity.png 的说明
插图 oracle-blockchain-cms-entity.png 的说明

所有这些实体及其关系、管理和与实体交互所需的所有逻辑,以及将它们保留到区块链分类账中的逻辑都在智能合同中定义。

文件夹表示为 NFT 令牌,因此将开发和初始化为 NFT。其他从属实体(文档和属性)是标准实体,是文件夹的子实体,因此将作为标准实体开发,而无需初始化。

一旦智能合约被创建,我们将安装并部署在我们创建的区块链网络中。

创建智能合同

配置 Oracle Blockchain App Builder 后,您可以创建规范文件。可以将规范文件创建为如下代码中所示的简单 YAML 文件。

注意:

您也可以从 GitHub 下载此文件:下载 YAML 文件
#
# Token asset to manage the complete lifecycle of a non-fungible token representing a folder to hold docuements. 
# This specification file will generate an Smartcontract project with a non-fungible token for the folders to be maintained by the users.
#
assets:
    - name: folderNFT
      type: token
      symbol: eDocs
      standard: erc721+
      anatomy: 
        type: nonfungible
        unit: whole
      behavior:
        - indivisible
        - singleton
        - mintable:
        - transferable
        - burnable
        - roles:
            minter_role_name: minter
      properties:
          - name: folderHASH
            type: string
          - name: documents
            type: document[]
      metadata:
          - name: folderID
            type: string
            mandatory: true
            id: true
          - name: folderType
            type: string
            mandatory: true
      methods:
        crud: [create, getById, update, delete]
        others: [getHistoryById, getByRange]
    - name: document
      properties:
        - name: docName
          type: string
          mandatory: true
          id: true
        - name: docURL
          type: string
          mandatory: true
        - name: docHASH
          type: string
          mandatory: true
        - name: docType
          type: string
          mandatory: true
        - name: docProperties
          type: docProperty[]
      methods:
        crud: [create, getById, update, delete]
        others: [getHistoryById, getByRange]
    - name: docProperty
      type: embedded
      properties:
          - name: propName
            type: string
            mandatory: true
          - name: propValue
            type: string
            mandatory: true
      methods:
        crud: [create, getById, update, delete]
        others: [getHistoryById, getByRange]
customMethods:
    - executeQuery
    - "attachDocument(tokenId: string, docName: string, docURL: string, docHASH: string, docType: string, docProps: string[], docVals: string[])" # Attach a document to an existing folder. 
    - "retrieveDocuments(tokenId: string)" # Retrieve Documents of an folder. 
    - "transferFolder(tokenId: string, fromOrgId: string, fromUserId: string, toOrgId: string, toUserId: string)" # Transfer the folder among participants. 
    - "updateFolderHASH(tokenId: string, newHash: string)" # Update HASH folder 
    - "getFolderHASH(tokenId: string)" # Check HASH folder 

在此规范文件中,在定义的第一个实体 (folderNFT ) 中,可以查看用于表示 NFT 令牌的所有节和属性。概述文件中定义的部分:

  • 资产:定义不同资产(标准实体、财务交易、NFT)的位置。在每个资产中,我们可以区分不同的部分,这些部分可能因所表示资产的类型而异。对于 NFT 和 FT,这些是不同的子部分:
    • 类型/符号/标准:指定此标记基于 ERC-721 标准,并为其指定唯一的符号标识符。
    • 解剖学:指定它是一个 NFT,以及它是否细分为较小的分数(现在,“整体”是 NFT 令牌的唯一选项)。
    • 行为:定义令牌是否可以铸造,在这种情况下,可最小化令牌的最大数量是多少。在这里,你还必须说明它是一个不可分割的标记,如果它是每个类的单例,可转移和可燃烧,类似于它的删除(但不会消失,所以它仍然存在,但根本不可用)。也可以在本节中将标记行为限制为特定角色。
    • 元数据:定义在创建标记期间必须设置且将来无法更改的属性类型。因此,它的价值将在令牌的整个生命周期中保持不变。
    • 属性:定义标记的标准属性,这些属性在标记的生命周期内可能会有所不同,例如组成文件夹的文档数组。
  • customMethods:必须定义定制方法列表的位置。对于这些方法,Oracle Blockchain App Builder 将仅生成该方法的签名,而不会对其实施。这些方法的实现是开发人员可以实现的唯一代码。

以下链接介绍了如何根据您的业务需求配置任何类型的实体(NFT、FT 或标准实体):

创建规范文件后,我们可以要求 Oracle Blockchain App Builder 按照以下后续步骤创建项目的支架。

  1. 如果规范文件是在 Oracle Blockchain App Builder 外部创建的,则必须将规范文件导入到 Oracle Blockchain App Builder 中。单击“规格”框架旁边的省略号,然后在弹出窗口中单击导入规格
  2. 选择规范文件,然后单击导入规范
  3. 要基于导入的规范文件创建新的链代码项目,请单击 CHAINCODES 框架右上角的 + 图标。它将打开创建链代码向导。
  4. 创建链代码向导中,必须指定以下详细信息:
    • 名称:项目的名称。
    • 语言:用于创建项目支架的语言。只能在 Typescript 和 GoLang 之间进行选择。由于我们在此解决方案手册中使用 Typescript 来实现自定义方法,因此我们建议选择 Typescript。
    • 规格:选择在前面的步骤中刚创建的规格文件。
    • 位置/域:根据所选语言,系统将提示您定义将用于 Typescript 语言的项目所在位置,或用于 GoLang 语言的域。
    如果所有详细信息都正确,则应在向导中看到一条绿色消息。如果不是这样,请检查在创建项目支架期间生成的输出。
  5. 在 CHAINCODES 部分,我们应该看到新生成的项目。单击项目,导航到项目内的 src 文件夹,其中生成了源代码:
    • src/controller 文件夹中,我们将看到代表智能合同的主类,以及用于管理实体的所有自动生成方法。
    • src/model 文件夹中,我们将看到表示 NFT 实体的类。
  6. 导航到 src/controller 文件夹并选择控制器类,我们将看到 Oracle Blockchain App Builder 自动生成的所有代码,并转到文件末尾,我们将看到定制方法的所有签名,而没有任何实现。
  7. 此时,我们应该创建定制方法的实现。为了简单起见,我们在 GitHub 中提供的文件 CMS_customMethods.ts 中提供了此类方法的所有实现代码。您只需将 Oracle Blockchain App Builder 自动生成的签名替换为引用文件中的内容。
Controller 类(在自定义方法之前的行)包括用于管理 NFT 标记生命周期的所有自动生成的代码。下图描述了此类方法涵盖的不同区域。

oracle-blockchain-nft-token-oracle.zip

此时,链代码可供使用,因此我们可以按照在本地超级账本架构网络上测试您的链代码中的说明在本地部署和测试链代码。

部署智能合同

在本地测试链代码后,通过以下方式将其部署到先前使用 Oracle Blockchain Service Console 创建的实际网络中:

  • 打包链代码项目。
  • 将链代码程序包安装并部署到单个实例(建立者)。
以下部分介绍了执行部署的详细步骤。
  1. 从链代码项目创建可部署程序包。在 Visual Studio 中,单击链代码项目名称顶部的右侧按钮,从弹出菜单中选择程序包选项,然后选择用于保存链代码程序包文件的目录。
  2. 访问 Oracle Blockchain Service Console,将链代码程序包安装并部署到创建者实例中。
  3. 导航到链代码选项卡,然后单击部署新链代码
  4. 选择高级部署选项。
  5. 设置所有值以将链代码程序包安装到创建者实例中,然后单击下一步
    • 程序包标签:指定一个名称,该名称可以帮助您确定在不同的现有通道中安装的软件包。由于可以将同一智能合同的多个版本部署在不同的渠道中,因此最好设置如下软件包标签名称:
      <smartContractName>_<channel>_<version>
      
    • Chaincode Language(链代码语言):根据开发链代码时使用的语言在不同语言中进行选择。
    • 目标对等节点:选择要安装链代码软件包的对等节点。
    • 是否为打包链代码:如果要上载 zip 文件,请取消选中此复选框。选中 tar.gz 文件的复选框。
    • 链代码源:单击上载链代码文件并选择链代码 zip 文件。
  6. 如果安装成功,我们将看到成功消息。下一步是在所选渠道中部署链代码,因此您必须设置与部署阶段相关的所有值,然后单击下一步
    • 渠道:选择要部署智能合同的渠道。
    • 链代码名称:设置将在渠道上部署智能合同的名称。
    • 版本:为此部署分配一个编号,该编号与先前安装的软件包保持一致。这样,您将能够将安装的软件包与部署在不同通道中的链代码相关联。
    • 需要初始:如果在允许用户事务处理之前需要调用链代码的 init 方法,请选中此复选框。
    • 背书策略:在部署期间指定背书策略。在此解决方案手册示例中,我们未使用背书策略。
    • 专用数据收集:根据需要设置专用数据收集。在此解决方案手册示例中,我们不会设置专用数据收集。
如果部署成功,在关闭安装和部署后,您应该看到软件包是如何安装在实例的两个对等节点中,并在其中一个通道中实例化。

初始化智能合同

处理 FT 和 NFT 令牌时,需要执行一组管理操作,然后才能执行业务方法。借助 Oracle Blockchain,所有管理任务都可以作为简单的 REST 调用来执行,大大减少了初始化智能合同所需的工作量。

注意:

在执行以下任何步骤之前,我们必须为被授予智能合同方法访问权限的用户在 REST 代理中创建 enrollmentIDs。注册是调用 REST API 的用户名与由区块链内部管理的账户之间的映射,代币将分配到这些账户,如在 REST 代理节点中创建注册主题中所述。

使用以下 Postman 集合可以轻松初始化智能合同:下载 Postman 集合

在此 Postman 集合的 AdminSteps 文件夹中,有三个请求调用要为智能合同初始化执行。

Postman 集合已准备好使用,但有一组变量需要配置到您自己的环境中(密码、URL 等)。这些变量在 Postman 集合的 Variables(变量)选项卡中设置。下表显示了我们定义的所有变量,并且需要适应您的环境。

变量名称 变量值
bc_founder_provider_url https://org1-w....
bc_timeout 60000
bc_nft_founder_userid1 cmsleg001
bc_nft_founder_userid1_pwd 口令
bc_nft_founder_userid2 cmsfin001
bc_nft_founder_userid2_pwd 口令
bc_nft_founder_userid3 cmsrsk001
bc_nft_founder_userid4_pwd 口令
bc_channel_name 婚礼
bc_chaincode_name 婚礼

查找可从区块链服务控制台访问 REST API 的端点。

  1. 通过 OCI 控制台访问区块链服务控制台。
  2. 转到节点选项卡。它将显示组成此实例的所有节点,在 restproxy 节点中,您将在路由列中看到端点 URL。
    请记住,注册是在实例级别而非网络级别创建的,因此用户的注册只能通过创建者实例的 restproxy URL 进行,因此,如果新实例加入网络,如果允许新用户访问网络,则这些用户需要存在于正确的租户中,并且还应在相应实例的 Restproxy 中创建这些用户的注册。
在正确配置 Postman 集合后,我们可以继续进行智能合约初始化。NFT 智能合约的初始化比 FT 智能合约的初始化要简单得多,我们只需要执行三个步骤:
  1. 智能合同初始化(初始管理用户帐户)。
  2. 为可以拥有 NFT 令牌的用户创建钱包。
  3. 为应具有该权限的用户分配 minter 角色。
以下 API REST 调用对应于提供的 Postman 集合中对 AdminSteps 文件夹的调用。
  1. 初始化链代码 ( Step-0:Init Admin User Account ),指示允许哪些用户帐户执行管理任务。必须正确设置 init 方法的 args:args: Scaped array of user_ids with their org_ids
    
    {
        "chaincode": "{{bc_nft_chaincode_name}}",
        "args": [
            "init",
            "[{\"orgId\":\"org1\",\"userId\":\"cmsleg001\"},{\"orgId\":\"org1\",\"userId\":\"cmsfin001\"},{\"orgId\":\"org1\",\"userId\":\"cmsrsk001\"}]"
        ],
        "timeout": {{bc_timeout}},
        "isInit": true,
        "sync": true
    }
    
  2. 为所有用户创建用户账户,这些用户可以是表示实物资产的 NFT 资产的托管人。可以通过执行 Step-1:Create account Postman 请求来完成。对于我们用例的细节,只有三个用户与属于网络的同一个组织相关。此调用必须执行次数与我们要为其创建帐户的用户相同。在本例中,每个参数有三次:
    • "createAccount", "org1", "cmsleg001", "nonfungible"
    • "createAccount", "org1", "cmsfin001", "nonfungible"
    • "createAccount", "org1", "cmsrsk001", "nonfungible"
    
    {
        "chaincode": "{{bc_nft_chaincode_name}}", //Smartcontract name
        "args": [
            "createAccount", "org1","cmsleg001","nonfungible" //Method, OrgID, UserID, fungible for FT / nonfungible for NFT
        ],
        "timeout": 60000,
        "sync": true
    }
    
  3. 设置允许哪个用户 mint 标记,在本例中,mint a token 表示创建一个新文件夹以存储一组新文档,因此您可以决定三个现有用户(cmsleg001cmsfin001cmsrsk001 )中的哪个用户可以执行这些操作,对于这些用户,则从 Postman 集合执行请求 Step-2:AddRole
    
    {
        "chaincode": "{{bc_nft_chaincode_name}}", //Smartcontract name
        "args": [
            "addRole", //Method name
            "minter","org1","cmsleg001" //Role, OrgId, UserID
            ],
        "timeout": 60000,
        "sync": true
    }