第一步、 搭建基础网络
./network.sh up createChannel -c mychannel -s couchdb
第二步、部署通用链码(go版本)
./network.sh deployCC -ccn mycc -ccv 1.0 -ccp ../src/chaincode/common -ccl go
或、部署通用链码(Java版本)
./network.sh deployCC -ccn commoncc -ccv 1.0 -ccp ../src/chaincode-java -ccl java
在部署Java链码时,需要注意:1、如果是Maven开发的,需要将链码打包成jar包,具体的参见源码。2、将链码Jar包拷贝到链码安装目录下的/build/install/链码名称目录下,然后执行上述命令。具体操作请参考demo源码
创建一个Spring Boot Maven工程,并导入fabric-explorer-spring-boot-starter
相关jar包。
<dependency>
<groupId>io.github.ecsoya</groupId>
<artifactId>fabric-explorer-spring-boot-starter</artifactId>
<version>${latest_version}</version>
</dependency>
1- 运行first-network
文件夹中的byfn.sh
脚本,此方法为官方的fabric-samples中的示例,做了少量修改。
./byfn.sh up -a -s couchdb
2- 具体的修改如下: 为了安装通用链码,几处修改如下。
a. 由于将链码放到了`src/chaincode`目录下,所以在`scripts/script.sh`中对链码的位置做了相应的修改。
CC_SRC_PATH="github.com/chaincode/common/"
b. 同样的,将`docker-composite-cli.yaml`中关于链码的映射路径(83行)做了修改。
- ./../src/chaincode/:/opt/gopath/src/github.com/chaincode
c. 修改了`utils.sh`中执行Chaincode的默认的`init`、`invoke`和`query`方法。
1. invoke -> create
2. query -> get
3- 生成Fabric网络配置文件
运行src/main/test
中的NetworkGenerator
文件,在src/main/resource/network
中生成connection-Org1.yml
及connection-Org2.yml
文件。
关于Fabric网络配置文件的相关信息,请参考文档Fabric Network Config。
spring:
mvc:
locale: zh_CN
locale-resolver: fixed
fabric:
chaincode:
identify: mycc
name: Common Chaincode
version: 1.0
channel: mychannel
peers: 2
organizations:
- Org1
- Org2
name: Common Fabric Network
gateway:
wallet:
identify: admin
network:
file: network/connection-Org1.yml
name: example-fabric
# Fabric explorer
explorer:
title: Fabric Explorer
# logo: img/logo.png
copyright: Ecsoya (jin.liu@soyatec.com)
hyperledger-explorer-url: http://www.hyperleder.org
path: /explorer
注意:
first-network
示例中链码默认名称为mycc
,通道默认名称为mychannel
,都没有做修改。network:file
路径为上一步中生成的Fabric网络配置文件的路径。运行DemoApplication,启动Spring Boot服务,然后在浏览器中输入http://localhost:8080/explorer,查看区块链浏览器。
由于此示例中安装了通用链码,所以能够使用IFabricService来创建基于Fabric区块链的CRUD服务。
1- FabricDemoController
实现基本的CRUD
package io.github.ecsoya.demo.controller;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;
import io.github.ecsoya.fabric.FabricPagination;
import io.github.ecsoya.fabric.FabricPaginationQuery;
import io.github.ecsoya.fabric.FabricResponse;
import io.github.ecsoya.fabric.bean.FabricObject;
import io.github.ecsoya.fabric.service.IFabricService;
@RestController
public class FabricDemoController {
@Autowired
private IFabricService fabricService;
@RequestMapping("/")
public ModelAndView index(HttpServletRequest request) {
ModelAndView modelAndView = new ModelAndView("index");
modelAndView.addObject("baseURL", baseUrl(request));
return modelAndView;
}
private String baseUrl(HttpServletRequest request) {
String scheme = request.getScheme();
String serverName = request.getServerName();
int port = request.getServerPort();
String path = request.getContextPath();
return scheme + "://" + serverName + ":" + port + path;
}
@RequestMapping("/list")
public FabricPagination<FabricObject> list(FabricPaginationQuery<FabricObject> query) {
return fabricService.pagination(query);
}
@RequestMapping("/add")
public FabricResponse add(FabricObject object) {
return fabricService.create(object);
}
@RequestMapping("/update")
public FabricResponse update(FabricObject object) {
return fabricService.update(object);
}
@RequestMapping("/remove")
public FabricResponse remove(FabricObject object) {
return fabricService.delete(object.getId(), object.getType());
}
}
2- 关于删除
虽然说区块链是不可篡改的账本,但不是说区块链上的数据就不可删除。其实,Fabric区块链的链码,是支持删除操作的,删除之后便不能再被查询。但是,这里的删除,并不是物理删除,只是给数据打上了一个is_delete的标记,因为所有的操作,都会有对应的交易ID,所以数据即便删除了,仍然在区块链的账本上留下记录。
3- 分页查询
Fabric中的分页查询,是通过bookmark标记来实现的。
在链码的API中,提供了如下的查询方法:
APIstub.GetQueryResultWithPagination(query, pageSize, bookmark);
bookmark是当前查询之后,返回的一个标记,下一次的查询会从这个标记开始。如果是第一次(第一页)查询,可以使用空字符串(”“)代替。
在前段实现时,如果要实现自由切换页码,可以将页码和bookmark缓存到map中进行调用。
以DataTables为例:
//将页码和bookmark缓存
let bookmarkMap = new Map();
let dataTable = $('#dataTable')
.DataTable(
{
"ajax" : function(data, callback) {
let pageSize = data.length; //每页大小
let currentPage = data.start / data.length; //当前页
$.ajax({
url : baseURL + "/list",
data : {
bookmark : bookmarkMap[currentPage], //当前页的bookmark值
pageSize : pageSize,
currentPage : currentPage,
type : $('#type').val()
}
}).then(function success(res) {
bookmarkMap[currentPage + 1] = res.bookmark; //将查询结果中的bookmark值,作为下一页的bookmark
callback(res); // 调用DataTable的callback函数初始化表。
}, function fail(data, status) {
});
},
……