Application

受众:架构师、应用程序和智能合约开发者

An application can interact with a blockchain network by submitting transactions to a ledger or querying ledger content. This topic covers the mechanics of how an application does this; in our scenario, organizations access PaperNet using applications which invoke issue, sell and redeem transactions defined in a commercial paper smart contract. Even though MagnetoCorp’s application to issue a commercial paper is basic, it covers all the major points of understanding.

在这个主题中,我们将涉及: 调用智能合约的应用程序流程 应用程序如何使用钱包和身份 应用程序如何使用网关连接 如何访问特定的网络 如何构造交易请求 如何提交交易 如何处理交易响应

为了帮助您理解,我们将参考超级账本Fabric提供的商业票据示例应用程序。您可以下载它并在本地运行它。它是用JavaScript编写的,但是逻辑是完全独立于语言的,所以您可以很容易地看到发生了什么!(Java和GOLANG也将提供该示例。)

Basic Flow

应用程序使用Fabric SDK与区块链网络进行交互。下面是一个应用程序如何调用商业票据智能合约的简化图:

../_images/develop.diagram.3.pngdevelop.application A PaperNet application invokes the commercial paper smart contract to submit an issue transaction request.

申请提交交易须遵循六个基本步骤:

  • Select an identity from a wallet

  • Connect to a gateway

  • Access the desired network

  • Construct a transaction request for a smart contract

  • Submit the transaction to the network

  • Process the response

您将看到一个典型的应用程序如何使用Fabric SDK执行这六个步骤。您将在issue.js文件中找到应用程序代码。在您的浏览器中查看它,或者在您最喜欢的编辑器中打开它(如果您已经下载了它)。花点时间看看应用程序的整体结构;即使有注释和间距,也只有100行代码!

Wallet

在issue.js的顶部,你会看到两个Fabric类被纳入作用域:

const { FileSystemWallet, Gateway } = require('fabric-network');

您可以阅读node SDK文档中关于fabric-network类的内容,但是现在,让我们看看如何将MagnetoCorp的应用程序连接到PaperNet。应用程序使用Fabric Wallet(钱包)类如下:

const wallet = new FileSystemWallet('../identity/user/isabella/wallet');

查看wallet如何在本地文件系统中定位钱包。从钱包中检索到的身份显然是针对一个名为Isabella的用户的,他正在使用发行应用程序。该钱包拥有一组身份(X.509数字证书),可用于访问PaperNet或任何其他Fabric网络。如果您运行本教程并查看这个目录,您将看到Isabella的身份凭证。

想象一个钱包,里面装着你的身份证、驾照或ATM卡的数字等价物。其中的X.509数字证书将使持有者与某个组织相关联,从而赋予他们在网络通道中的权利。例如,Isabella可能是MagnetoCorp的一名管理员,这可能会给她比其他用户(来自DigiBank的Balaji)更多的特权。此外,智能合约可以在使用交易上下文进行智能合约处理期间检索此身份。

还要注意,钱包里没有任何形式的现金或代币——它们容纳身份。

Gateway

第二个关键类是Fabric网关。最重要的是,网关标识一个或多个peer,这些peer提供对网络的访问——在我们的示例中是PaperNet。查看issue.js如何连接到它的网关:

await gateway.connect(connectionProfile, connectionOptions);

gateway.connect()有两个重要的参数: - connectionProfile:连接概要文件的文件系统位置,它将一组peer标识为到PaperNet的网关 - connectionOptions:一组用于控制issue.js如何与PaperNet交互的选项

  • connectionProfile: the file system location of a connection profile that identifies a set of peers as a gateway to PaperNet

  • connectionOptions: a set of options used to control how issue.js interacts with PaperNet

了解客户端应用程序如何使用网关将自己与可能发生更改的网络拓扑隔离。网关使用连接概要文件和连接选项将交易提议发送到网络中的正确peer节点。

花一些时间检查连接概要文件 ./gateway/connectionProfile.yaml。它使用YAML,使其易于阅读。

加载并转换为JSON对象:

let connectionProfile = yaml.safeLoad(file.readFileSync('./gateway/connectionProfile.yaml', 'utf8'));

现在,我们只对概要文件的 channels: 和peers: 部分感兴趣(我们稍微修改了一些细节,以便更好地解释发生了什么)。

channels:
  papernet:
    peers:
      peer1.magnetocorp.com:
        endorsingPeer: true
        eventSource: true

      peer2.digibank.com:
        endorsingPeer: true
        eventSource: true

peers:
  peer1.magnetocorp.com:
    url: grpcs://localhost:7051
    grpcOptions:
      ssl-target-name-override: peer1.magnetocorp.com
      request-timeout: 120
    tlsCACerts:
      path: certificates/magnetocorp/magnetocorp.com-cert.pem

  peer2.digibank.com:
    url: grpcs://localhost:8051
    grpcOptions:
      ssl-target-name-override: peer1.digibank.com
    tlsCACerts:
      path: certificates/digibank/digibank.com-cert.pem

请参见channel: 如何标识PaperNet: 网络通道,及其两个peer。MagnetoCorp拥有peer1.magenetocorp.com和DigiBank拥有peer2.digibank.com,两者都具有着背书peer的角色。通过peers: key 连接到这些peer,其中包含关于如何连接到它们的详细信息,包括它们各自的网络地址。

连接配置文件包含很多信息——不仅仅是peer——还包括网络通道、网络排序器、组织和CA,所以如果您不了解所有信息,请不要担心!

现在让我们将注意力转向connectionOptions对象:

let connectionOptions = {
  identity: userName,
  wallet: wallet
}

查看它如何指定用于连接到网关的标识、用户名和钱包。这些值是在代码的前部赋予的。

应用程序还可以使用其他连接选项来指示SDK代表它聪明地工作。例如:

let connectionOptions = {
  identity: userName,
  wallet: wallet,
  eventHandlerOptions: {
    commitTimeout: 100,
    strategy: EventStrategies.MSPID_SCOPE_ANYFORTX
  },
}

这里,commitTimeout告诉SDK等待100秒,以得知交易是否被提交。 strategy: EventStrategies.MSPID_SCOPE_ANYFORTX指定SDK可以在一个MagnetoCorp peer确认了交易之后通知应用程序,这与strategy: EventStrategies.NETWORK_SCOPE_ALLFORTX形成了对比,它要求来自MagnetoCorp和DigiBank的所有peer确认这笔交易。

如果您愿意,请阅读更多关于连接选项如何允许应用程序指定面向目标的行为,而不必担心如何实现该行为。

Network channel

在网关的connectionProfile.yaml 中定义的peer提供了issue.js访问PaperNet的权限。因为这些peer可以连接到多个网络通道,所以网关实际上为应用程序提供了对多个网络通道的访问!

查看应用程序如何选择一个特定的通道:

const network = await gateway.getNetwork('PaperNet');

从这一点开始,网络将提供访问PaperNet。此外,如果应用程序想同时访问另一个网络BondNet,则很容易:

const network2 = await gateway.getNetwork('BondNet');

现在我们的应用程序可以访问第二个网络BondNet,与PaperNet一起!

我们可以在这里看到超级账本Fabric的一个强大特性——应用程序可以通过连接到多个网关peer(每个网关peer都加入到多个网络通道),参与到一个网络网络中。根据gateway.connect()中提供的钱包身份,应用程序在不同的通道中具有不同的权限。

Construct request

该应用程序现在可以发行商业票据了。要做到这一点,它将使用CommercialPaperContract,同样,它是简单地访问这个智能合约:

const contract = await network.getContract('papercontract', 'org.papernet.commercialpaper');

注意,应用程序如何提供一个名称——papercontract——和一个显式的合约名称:org.papernet.commercialpaper! 我们将看到如何利用合约名称从包含许多合约的papercontract.js链码文件中选择一个合约。在PaperNet中,papercontract.js以papercontract的名称安装和实例化,如果您感兴趣,请阅读如何安装和实例化包含多个智能合约的链码。

如果我们的应用程序需要同时在PaperNet或BondNet访问另一个合约这将是容易的:

const euroContract = await network.getContract('EuroCommercialPaperContract');

const bondContract = await network2.getContract('BondContract');

在这些示例中,请注意我们没有使用符合条件的合约名称——每个文件只有一个智能合约,getContract()将使用它找到的第一个合约。

回忆一下MagnetoCorp发行第一份商业票据时的交易:

Txn = issue
Issuer = MagnetoCorp
Paper = 00001
Issue time = 31 May 2020 09:00:00 EST
Maturity date = 30 November 2020
Face value = 5M USD

现在让我们把这个交易提交到PaperNet!

Submit transaction

提交交易是对SDK的一个方法调用:

const issueResponse = await contract.submitTransaction('issue', 'MagnetoCorp', '00001', '2020-05-31', '2020-11-30', '5000000');

查看submitTransaction()参数如何匹配交易请求的参数。这些值将传递给智能合约中的issue()方法,并用于创建新的商业票据。回忆它的签名:

async issue(ctx, issuer, paperNumber, issueDateTime, maturityDateTime, faceValue) {...}

智能合约似乎在应用程序发出submitTransaction()之后不久就会收到控制,但事实并非如此。在幕后,SDK使用connectionOptions和connectionProfile详细信息将交易提议发送到网络中的正确peer,在那里可以获得所需的背书。但是应用程序不需要担心这些——它只会发出submitTransaction, SDK会处理所有这些!

注意,submitTransaction API包含一个侦听交易提交的进程。侦听提交是必需的,因为没有提交,您将不知道您的交易是否已成功地排序、验证和提交到账本。

现在让我们将注意力转向应用程序如何处理响应!

Process response

回想一下papercontract.js中的发行交易如何返回商业票据响应:

return paper.toBuffer();

您将注意到一个小问题——在将新文件返回到应用程序之前,需要将其转换为缓冲。注意issue.js如何使用CommercialPaper.fromBuffer()类方法将响应缓冲重新还原为商业票据:

let paper = CommercialPaper.fromBuffer(issueResponse);

这使得票据能够以一种自然的方式在一个描述性的完成信息中使用:

console.log(`${paper.issuer} commercial paper : ${paper.paperNumber} successfully issued for value ${paper.faceValue}`);

查看相同的票据类在应用程序和智能合约中是如何使用的——如果您像这样构造代码,它将真正有助于可读性和重用。

与交易提议一样,应用程序可能会在智能合约完成后不久收到控制,但事实并非如此。在幕后,SDK管理整个共识过程,并根据策略connectionOption通知应用程序何时完成。如果您对SDK的底层功能感兴趣,请阅读详细的交易流程。

就是这样! 在本主题中,通过研究MagnetoCorp的应用程序如何在PaperNet中发行新的商业票据,您已经了解了如何从示例应用程序调用智能合约。现在研究一下关键账本和智能合约数据结构背后的架构主题。