Hyperledger Fabric实战(4)—— 编写第一个应用
本文演示Hyperledger Fabric的应用执行过程。本地环境采用Ubuntu14.04(由Vagrant创建 ),Hyperledger Fabric采用1.1.0版本。
在这个示例中,我们将使用一个 fabcar 的应用,来演示与CA交互,并生成登记证书,然后使用这些证书查询和更新账本。
我们主要有三个目的:
1、启动一个开发环境。我们的应用需要一个网络去交互,因此我们需要下载所需要的环境并配置我们需要的组件,然后才可以注册/登记、查询和更新:
2、学习样例中我们应用使用到的智能合约参数。我们的智能合约包含了不同的功能,这些功能运行我们使用不同的方式和账本进行交互。我们将深入并分析这个智能合约,学习我们应用用到的功能。
3、开发应用,查询并更新账本上面的资产。我们将深入应用自身的代码(这个应用使用Nodejs开发),并手动调整一些参数来运行不同种类的查询和更新。
完成这个指南之后,你应该对如何编写应用通过合约与超级账本交互有一个基本的了解。
1、启动你的开发环境
本章节的环境基于上一章节已搭建的环境,如果在上一章节中网络尚未关闭,请使用下面命令删除已有网络和环境:
./byfn.sh -m down |
接下来,我们需要进入到fabcar目录中执行:
cd fabric-samples/fabcar && ls |
可以看到下述文件:
enrollAdmin.js invoke.js package.json query.js registerUser.js startFabric.sh
|
在继续之前,我们仍然需要做一些处理。运行下面的命令,关掉所有docker容器:
docker -rm -f $(docker ps -aq) |
清理所有缓存的网络:
# Press 'y' when prompted by the command
docker network prune
|
最后,如果你曾经执行过这个指南,你需要删除掉 fabcar 智能合约的链码镜像:
docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba
|
安装客户端 & 启动网络
由于我们客户端应用是用Nodejs写的,因此我们需要安装nodejs的依赖。这里将自动下载 fabric-ca-client 和 fabric-clietn 两个库。注意,在执行下面命令之前,请保证你仍在fabcar目录中:
npm install |
[注:在ubuntu14.04环境中,npm可能会报错导致安装一直死循环执行,请ctrl+c强制停止,并使用 npm rebuild 重新执行。通过 npm ls查看所有包只要ok,就可以忽略该错误]
使用 startFabric.sh 脚本启动网络。同时,该脚本还将自动启动一个使用Go语言写的智能合约(智能合约代码后面再讨论):
./startFabric.sh |
当然,你也可以选择使用nodejs写的智能合约,只需要在启动脚本后面加上参数即可,这时,脚本将自动启动一个Nodejs写的智能合约(启动时间可能较久,大约90秒或更久):
./startFabric.sh node |
[注:智能合约支持2种开发语言,Go和Nodejs; 而客户端应用则支持 Go、Nodejs、Java、Python等开发语言。合约的开发语言和应用端无关。在本例中,应用使用 Nodejs开发,智能合约使用Go版本和Nodejs版本均可。下文将对Go的版本进行介绍]
2、应用是如何和网络交互的
作为开发人员,往往最关注的是应用要如何编写——最好是通过代码来讲解。在回答这个问题之前,我们需要先知道,应用是通过一个SDK,来访问Fabric提供的API,从而和账本进行交互。
登记Admin用户
在运行之前,另外创建一个终端,运行下面命令,以便实时地查看CA节点的输出日志:
docker logs -f ca.example.com |
我们使用下面命令登记admin用户,接下来再用admin用户创建一个新用户。仍然使用fabcar目录的终端:
node enrollAdmin.js |
这条命令将创建需要的证书并将其放置到 hfc-key-store 目录中。
注册并登记user1
通过使用刚刚创建的admin的eCert,我们将连接CA服务器并注册、登记一个新的用户。这个新用户—— user1 ——将用来执行后续查询和更新合约的操作。注意 admin 用户是用来注册和登记新的用户的(这个用户作为一个registar的角色)。运行下面命令,注册并登记 user1:
node registerUser.js |
与admin登记相似,这个程序将执行一个CSR,并将输出的key和eCert放置到 hfc-key-store子目录中。现在我们有两个独立的用户。查看下 hfc-key-store子目录:
hfc-key-store/
1a70a50f3d25b4b66a1fe63285de3c9929679fd188a439a9a500a0adb072db81-priv
1a70a50f3d25b4b66a1fe63285de3c9929679fd188a439a9a500a0adb072db81-pub
689abc5f7809c277b7e50c99103a45bdd1f09ffd2a49d1c30a5df7f9ee43f4e5-priv
689abc5f7809c277b7e50c99103a45bdd1f09ffd2a49d1c30a5df7f9ee43f4e5-pub
admin
user1
|
我们可以看到该目录中共有6个文件。两对公钥-私钥,一个admin和user用户文件。进一步查看admin 和user1文件可以看到,这两个文件是json格式的文件,包含了用户名\mspid等信息,而上面的两对公钥-私钥,则分别是这两个用户以签名ID-pri/pub开始的两个证书文件。
查询合约
查询合约的请求方式如下图:
我们应用连接到网络中的一个节点,然后通过智能合约来查询主账本(ledger),然后再依次返回。
首先,我们来运行query.js程序来返回合约中的汽车列表。我们将使用 user1 作为签名实体,在4个程序中,我们使用下面的方式指定了签名用户:
fabric_client.getUserContext('user1',true) |
调用user1所需的证书已经放置在了 hfc-key-store 目录中了,我们将使用这些信息,调用一个 queryAllCars 的方法,这个方法将返回所有的汽车信息:
node query.js |
它将返回下面内容:
Successfully loaded user1 from persistence
Query has completed, checking results
Response is [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},
{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},
{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},
{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},
{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},
{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},
{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},
{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]
|
这里有10辆汽车信息。在我们的定义中,CAR0 - CAR9 分别是它们的键值。
我们来近距离观察下这个程序。使用编辑器 (如 atom)打开 query.js。
在程序的开头部分,我们定义了一些变量,例如通道名称、证书存放位置和网络节点地址。在我们示例应用中,这些变量是写死在程序中的,实际开发时,这些变量需要通过环境来提供:
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;
|
下面这段代码构建了查询请求:
// queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars chaincode function - requires no arguments , ex: args: [''],
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryAllCars',
args: ['']
};
|
当应用执行的时候,它去请求 fabar 的链码,在链码中执行 queryAllCar方法,且没有提供给它任何参数。
现在,我们看一下我们智能合约的代码。在 fabric-sample 目录中,找到 chaincode/fabcar/go 子目录并打开 fabcar.go 文件,这就是我们的链码文件。(当然,你也可以找到对应nodejs版本的文件)
你可以看到我们有下面几个方法可以调用:initLedger, queryCar, queryAllCars, createCar, 以及changeCarOwner。
我们来看一下 queryAllCars 方法,看它是如何和账本交互的:
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {
startKey := "CAR0"
endKey := "CAR999"
resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
|
这里定义了 queryAllCars 的区间。一共1000个汽车,每辆汽车的key都在 CAR0 和 CAR999之间。
下图展示了一个应用如何调用链码中不同的方法。每个方法必须通过链码 shim 接口的API来定义,它允许智能合约和账本进行交互。
我们可以看到 queryAllCars 方法,以及createCar 方法等。
回过来,我们再看query.js 程序,我们将请求中的查询方法从 queryAllCars 修改为 queryCar, 并传递 CAR4 作为参数, 修改后:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR4']
};
|
保存这个程序,并返回到 fabcar 目录。现在再次运行这个程序:
node query.js |
你将会看到以下的信息:
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
|
这个信息就是前面执行queryAllCars时key为CAR4的汽车的信息。
更新合约
我们下面以创建一个汽车为例来演示合约的更新操作:
这是更新的流程图。请求需要先提交、背书,然后才返回给应用。它将被排序并写回到每个成员的账本里面。
我们第一个账本更新将会创建一个新的汽车。我们有一个独立的程序 invoke.js 来完成更新。和查询一样,我们使用编辑器打开这个程序并导航到我们构造请求的代码块部分:
// createCar chaincode function - requires 5 args, ex: args: ['CAR12', 'Honda', 'Accord', 'Black', 'Tom'],
// changeCarOwner chaincode function - requires 2 args , ex: args: ['CAR10', 'Barry'],
// must send the proposal to endorsing peers
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: '',
args: [''],
chainId: 'mychannel',
txId: tx_id
};
|
你可以看到有两个方法提供选择: createCar 或 changeCarOwner. 首先,让我们来创建一个红色的chevy volt并给定它一个所有者Nick。 我们刚才使用到CAR9,现在我们使用CAR10作为这里的主键。修改代码如下:
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'createCar',
args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
chainId: 'mychannel',
txId: tx_id
};
|
保存并运行程序:
node invoke.js |
命令行终端中将会输出ProposalResponse和许可信息。然后,我们最终会得到下面的信息:
The transaction has been committed on peer localhost:7053
|
如果需要确认这条信息被写入,可以返回 query.js 并将 CAR4 修改为 CAR10 。
也就是说,将这里:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR4']
};
|
修改成这样:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR10']
};
|
再次保存,然后查询:
node query.js |
返回的结果应该是这样:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}
|
好了。创建汽车的操作就完成了。
下面演示更新操作。假设Nick想要把他的汽车送给一个叫Dave的人:
我们需要返回到 invoke.js 文件,把方法从 createCar 修改为 changeCarOwner,并设置输入的参数:
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'changeCarOwner',
args: ['CAR10', 'Dave'],
chainId: 'mychannel',
txId: tx_id
};
|
第一个参数 CAR10 指定了需要更改所有者的汽车,第二个参数 Dave定义了新车的主人。
保存并再次执行程序:
node invoke.js |
然后让我们再次查询并确保CAR10车的主人已经更改成Dave了:
node query.js |
将返回如下内容:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Dave"}
|
CAR10的所有者关系已经从Nick更改为 Dave了。
[注意,在实际应用中,链码将会包含一些访问控制逻辑。例如,只有授权的部分用户才可以创建新车,只用汽车的所有者才能将车赠送给别人等]
本文演示Hyperledger Fabric的应用执行过程。本地环境采用Ubuntu14.04(由Vagrant创建 ),Hyperledger Fabric采用1.1.0版本。
在这个示例中,我们将使用一个 fabcar 的应用,来演示与CA交互,并生成登记证书,然后使用这些证书查询和更新账本。
我们主要有三个目的:
1、启动一个开发环境。我们的应用需要一个网络去交互,因此我们需要下载所需要的环境并配置我们需要的组件,然后才可以注册/登记、查询和更新:
2、学习样例中我们应用使用到的智能合约参数。我们的智能合约包含了不同的功能,这些功能运行我们使用不同的方式和账本进行交互。我们将深入并分析这个智能合约,学习我们应用用到的功能。
3、开发应用,查询并更新账本上面的资产。我们将深入应用自身的代码(这个应用使用Nodejs开发),并手动调整一些参数来运行不同种类的查询和更新。
完成这个指南之后,你应该对如何编写应用通过合约与超级账本交互有一个基本的了解。
1、启动你的开发环境
本章节的环境基于上一章节已搭建的环境,如果在上一章节中网络尚未关闭,请使用下面命令删除已有网络和环境:
./byfn.sh -m down |
接下来,我们需要进入到fabcar目录中执行:
cd fabric-samples/fabcar && ls |
可以看到下述文件:
enrollAdmin.js invoke.js package.json query.js registerUser.js startFabric.sh
|
在继续之前,我们仍然需要做一些处理。运行下面的命令,关掉所有docker容器:
docker -rm -f $(docker ps -aq) |
清理所有缓存的网络:
# Press 'y' when prompted by the command
docker network prune
|
最后,如果你曾经执行过这个指南,你需要删除掉 fabcar 智能合约的链码镜像:
docker rmi dev-peer0.org1.example.com-fabcar-1.0-5c906e402ed29f20260ae42283216aa75549c571e2e380f3615826365d8269ba
|
安装客户端 & 启动网络
由于我们客户端应用是用Nodejs写的,因此我们需要安装nodejs的依赖。这里将自动下载 fabric-ca-client 和 fabric-clietn 两个库。注意,在执行下面命令之前,请保证你仍在fabcar目录中:
npm install |
[注:在ubuntu14.04环境中,npm可能会报错导致安装一直死循环执行,请ctrl+c强制停止,并使用 npm rebuild 重新执行。通过 npm ls查看所有包只要ok,就可以忽略该错误]
使用 startFabric.sh 脚本启动网络。同时,该脚本还将自动启动一个使用Go语言写的智能合约(智能合约代码后面再讨论):
./startFabric.sh |
当然,你也可以选择使用nodejs写的智能合约,只需要在启动脚本后面加上参数即可,这时,脚本将自动启动一个Nodejs写的智能合约(启动时间可能较久,大约90秒或更久):
./startFabric.sh node |
[注:智能合约支持2种开发语言,Go和Nodejs; 而客户端应用则支持 Go、Nodejs、Java、Python等开发语言。合约的开发语言和应用端无关。在本例中,应用使用 Nodejs开发,智能合约使用Go版本和Nodejs版本均可。下文将对Go的版本进行介绍]
2、应用是如何和网络交互的
作为开发人员,往往最关注的是应用要如何编写——最好是通过代码来讲解。在回答这个问题之前,我们需要先知道,应用是通过一个SDK,来访问Fabric提供的API,从而和账本进行交互。
登记Admin用户
在运行之前,另外创建一个终端,运行下面命令,以便实时地查看CA节点的输出日志:
docker logs -f ca.example.com |
我们使用下面命令登记admin用户,接下来再用admin用户创建一个新用户。仍然使用fabcar目录的终端:
node enrollAdmin.js |
这条命令将创建需要的证书并将其放置到 hfc-key-store 目录中。
注册并登记user1
通过使用刚刚创建的admin的eCert,我们将连接CA服务器并注册、登记一个新的用户。这个新用户—— user1 ——将用来执行后续查询和更新合约的操作。注意 admin 用户是用来注册和登记新的用户的(这个用户作为一个registar的角色)。运行下面命令,注册并登记 user1:
node registerUser.js |
与admin登记相似,这个程序将执行一个CSR,并将输出的key和eCert放置到 hfc-key-store子目录中。现在我们有两个独立的用户。查看下 hfc-key-store子目录:
hfc-key-store/
1a70a50f3d25b4b66a1fe63285de3c9929679fd188a439a9a500a0adb072db81-priv
1a70a50f3d25b4b66a1fe63285de3c9929679fd188a439a9a500a0adb072db81-pub
689abc5f7809c277b7e50c99103a45bdd1f09ffd2a49d1c30a5df7f9ee43f4e5-priv
689abc5f7809c277b7e50c99103a45bdd1f09ffd2a49d1c30a5df7f9ee43f4e5-pub
admin
user1
|
我们可以看到该目录中共有6个文件。两对公钥-私钥,一个admin和user用户文件。进一步查看admin 和user1文件可以看到,这两个文件是json格式的文件,包含了用户名\mspid等信息,而上面的两对公钥-私钥,则分别是这两个用户以签名ID-pri/pub开始的两个证书文件。
查询合约
查询合约的请求方式如下图:
我们应用连接到网络中的一个节点,然后通过智能合约来查询主账本(ledger),然后再依次返回。
首先,我们来运行query.js程序来返回合约中的汽车列表。我们将使用 user1 作为签名实体,在4个程序中,我们使用下面的方式指定了签名用户:
fabric_client.getUserContext('user1',true) |
调用user1所需的证书已经放置在了 hfc-key-store 目录中了,我们将使用这些信息,调用一个 queryAllCars 的方法,这个方法将返回所有的汽车信息:
node query.js |
它将返回下面内容:
Successfully loaded user1 from persistence
Query has completed, checking results
Response is [{"Key":"CAR0", "Record":{"colour":"blue","make":"Toyota","model":"Prius","owner":"Tomoko"}},
{"Key":"CAR1", "Record":{"colour":"red","make":"Ford","model":"Mustang","owner":"Brad"}},
{"Key":"CAR2", "Record":{"colour":"green","make":"Hyundai","model":"Tucson","owner":"Jin Soo"}},
{"Key":"CAR3", "Record":{"colour":"yellow","make":"Volkswagen","model":"Passat","owner":"Max"}},
{"Key":"CAR4", "Record":{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}},
{"Key":"CAR5", "Record":{"colour":"purple","make":"Peugeot","model":"205","owner":"Michel"}},
{"Key":"CAR6", "Record":{"colour":"white","make":"Chery","model":"S22L","owner":"Aarav"}},
{"Key":"CAR7", "Record":{"colour":"violet","make":"Fiat","model":"Punto","owner":"Pari"}},
{"Key":"CAR8", "Record":{"colour":"indigo","make":"Tata","model":"Nano","owner":"Valeria"}},
{"Key":"CAR9", "Record":{"colour":"brown","make":"Holden","model":"Barina","owner":"Shotaro"}}]
|
这里有10辆汽车信息。在我们的定义中,CAR0 - CAR9 分别是它们的键值。
我们来近距离观察下这个程序。使用编辑器 (如 atom)打开 query.js。
在程序的开头部分,我们定义了一些变量,例如通道名称、证书存放位置和网络节点地址。在我们示例应用中,这些变量是写死在程序中的,实际开发时,这些变量需要通过环境来提供:
var channel = fabric_client.newChannel('mychannel');
var peer = fabric_client.newPeer('grpc://localhost:7051');
channel.addPeer(peer);
var member_user = null;
var store_path = path.join(__dirname, 'hfc-key-store');
console.log('Store path:'+store_path);
var tx_id = null;
|
下面这段代码构建了查询请求:
// queryCar chaincode function - requires 1 argument, ex: args: ['CAR4'],
// queryAllCars chaincode function - requires no arguments , ex: args: [''],
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryAllCars',
args: ['']
};
|
当应用执行的时候,它去请求 fabar 的链码,在链码中执行 queryAllCar方法,且没有提供给它任何参数。
现在,我们看一下我们智能合约的代码。在 fabric-sample 目录中,找到 chaincode/fabcar/go 子目录并打开 fabcar.go 文件,这就是我们的链码文件。(当然,你也可以找到对应nodejs版本的文件)
你可以看到我们有下面几个方法可以调用:initLedger, queryCar, queryAllCars, createCar, 以及changeCarOwner。
我们来看一下 queryAllCars 方法,看它是如何和账本交互的:
func (s *SmartContract) queryAllCars(APIstub shim.ChaincodeStubInterface) sc.Response {
startKey := "CAR0"
endKey := "CAR999"
resultsIterator, err := APIstub.GetStateByRange(startKey, endKey)
|
这里定义了 queryAllCars 的区间。一共1000个汽车,每辆汽车的key都在 CAR0 和 CAR999之间。
下图展示了一个应用如何调用链码中不同的方法。每个方法必须通过链码 shim 接口的API来定义,它允许智能合约和账本进行交互。
我们可以看到 queryAllCars 方法,以及createCar 方法等。
回过来,我们再看query.js 程序,我们将请求中的查询方法从 queryAllCars 修改为 queryCar, 并传递 CAR4 作为参数, 修改后:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR4']
};
|
保存这个程序,并返回到 fabcar 目录。现在再次运行这个程序:
node query.js |
你将会看到以下的信息:
{"colour":"black","make":"Tesla","model":"S","owner":"Adriana"}
|
这个信息就是前面执行queryAllCars时key为CAR4的汽车的信息。
更新合约
我们下面以创建一个汽车为例来演示合约的更新操作:
这是更新的流程图。请求需要先提交、背书,然后才返回给应用。它将被排序并写回到每个成员的账本里面。
我们第一个账本更新将会创建一个新的汽车。我们有一个独立的程序 invoke.js 来完成更新。和查询一样,我们使用编辑器打开这个程序并导航到我们构造请求的代码块部分:
// createCar chaincode function - requires 5 args, ex: args: ['CAR12', 'Honda', 'Accord', 'Black', 'Tom'],
// changeCarOwner chaincode function - requires 2 args , ex: args: ['CAR10', 'Barry'],
// must send the proposal to endorsing peers
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: '',
args: [''],
chainId: 'mychannel',
txId: tx_id
};
|
你可以看到有两个方法提供选择: createCar 或 changeCarOwner. 首先,让我们来创建一个红色的chevy volt并给定它一个所有者Nick。 我们刚才使用到CAR9,现在我们使用CAR10作为这里的主键。修改代码如下:
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'createCar',
args: ['CAR10', 'Chevy', 'Volt', 'Red', 'Nick'],
chainId: 'mychannel',
txId: tx_id
};
|
保存并运行程序:
node invoke.js |
命令行终端中将会输出ProposalResponse和许可信息。然后,我们最终会得到下面的信息:
The transaction has been committed on peer localhost:7053
|
如果需要确认这条信息被写入,可以返回 query.js 并将 CAR4 修改为 CAR10 。
也就是说,将这里:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR4']
};
|
修改成这样:
const request = {
//targets : --- letting this default to the peers assigned to the channel
chaincodeId: 'fabcar',
fcn: 'queryCar',
args: ['CAR10']
};
|
再次保存,然后查询:
node query.js |
返回的结果应该是这样:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Nick"}
|
好了。创建汽车的操作就完成了。
下面演示更新操作。假设Nick想要把他的汽车送给一个叫Dave的人:
我们需要返回到 invoke.js 文件,把方法从 createCar 修改为 changeCarOwner,并设置输入的参数:
var request = {
//targets: let default to the peer assigned to the client
chaincodeId: 'fabcar',
fcn: 'changeCarOwner',
args: ['CAR10', 'Dave'],
chainId: 'mychannel',
txId: tx_id
};
|
第一个参数 CAR10 指定了需要更改所有者的汽车,第二个参数 Dave定义了新车的主人。
保存并再次执行程序:
node invoke.js |
然后让我们再次查询并确保CAR10车的主人已经更改成Dave了:
node query.js |
将返回如下内容:
Response is {"colour":"Red","make":"Chevy","model":"Volt","owner":"Dave"}
|
CAR10的所有者关系已经从Nick更改为 Dave了。
[注意,在实际应用中,链码将会包含一些访问控制逻辑。例如,只有授权的部分用户才可以创建新车,只用汽车的所有者才能将车赠送给别人等]
==更多原创内容分享,请扫码关注公众号: 超级账本开发 ==