家蛙树

武汉珞樱联创Luoying-server和Fabric-REST开源

Zealot
区块链
2018-12-16

武汉珞樱联创Luoying-server和Fabric-REST开源

https://github.com/zealzeng/luoying-server
https://github.com/zealzeng/fabric-rest


Luoying Server Framework

Luoying server是一个轻量级的服务器开发框架,方便开发者快速的定制多种协议的服务器,servlet style的API和简单的容器注入模式让开发者专注于业务实现。

Maven地址

<dependency>
  <groupId>com.whlylc</groupId>
  <artifactId>luoying-server</artifactId>
  <version>0.1.0</version>
</dependency>

HTTP服务器

  • HttpRequest和HttpResponse与Servlet API接近
  • ConcurrentApplicationContext是简单的BeanFactory实现, 方便注入所需组件

参考TestHttpServer.java

    public static void main(String[] args) throws Exception {

        HttpService service = new HttpService() {
            @Override
            public void service(HttpRequest request, HttpResponse response) {
                response.write(String.valueOf(System.currentTimeMillis()));
            }
        };
        //If we need business beans, inject here
        ConcurrentApplicationContext appCtx = new ConcurrentApplicationContext();
        appCtx.addBean("mybatis", new String("mybatisService"));
        service.setApplicationContext(appCtx);

        DefaultHttpServer server = new DefaultHttpServer(8080, service);
        server.startup();
    }

Socket服务器

  • socket通信协议简单使用Netty的LengthFieldBasedFrameDecoder,即长度+消息体
    ```java
    public static void main(String[] args) throws Exception {

      SockService service = new SockService() {
          @Override
          public void service(SockRequest request, SockResponse response) throws Exception {
              System.out.println(request.getRequestBody().toString());
              response.write(String.valueOf(System.currentTimeMillis()));
          }
      };
      //Attach application if it's necessary
      ConcurrentApplicationContext ctx = new ConcurrentApplicationContext();
      ctx.addBean("mybatis", new String("mybatisService"));
      service.setApplicationContext(ctx);
    
    DefaultSockServer server = new DefaultSockServer(9090, service);
    server.startup();
}

## 多端口服务器
```java
    public static void main(String[] args) throws Exception {

        HttpService httpService = new HttpService() {
            @Override
            public void service(HttpRequest request, HttpResponse response) throws Exception {
                response.write(String.valueOf(System.currentTimeMillis()));
            }
        };
        SockService sockService = new SockService() {
            @Override
            public void service(SockRequest request, SockResponse response) throws Exception {
                System.out.println(request.getRequestBody().toString());
                response.write(String.valueOf(System.currentTimeMillis()));
            }
        };

        //Attach application if it's necessary
        ConcurrentApplicationContext ctx = new ConcurrentApplicationContext();
        ctx.addBean("mybatis", new String("mybatisService"));
        sockService.setApplicationContext(ctx);
        httpService.setApplicationContext(ctx);

        DefaultHttpChannelService httpChannelService = new DefaultHttpChannelService(8080, httpService);
        DefaultSockChannelService sockChannelService = new DefaultSockChannelService(9090, sockService);
        ChannelService[] services = new ChannelService[] {httpChannelService, sockChannelService};
        MultiChannelServer server = new MultiChannelServer(services);
        server.startup();

    }

开发计划

想实现的东西不少,当前版本结合Fabric Rest稍稳定后规划。


Luoying Fabric REST

Hyperledger Fabric是当前商用度最高的联盟区块链平台,客户端SDK正式release的有Node.js和Java版本, 非正式发布的SDK有Python, Go和REST. 对于其它开发语言而言原本可以使用REST,只可惜REST一年未更新了。Fabric的更新速度远远超前于SDK. 而Luoying-fabric-rest是类似原REST做法基于Java SDK做包装或代理, 方便其它开发语言通过http协议接入fabric节点和通道, 注册用户,调用链码查询和更新等等,尽量简化接入fabric网络的操作。

Maven地址

<dependency>
  <groupId>com.whlylc</groupId>
  <artifactId>fabric-rest</artifactId>
  <version>1.2.0-rc1</version>
</dependency>

Application Profile模板

参考/doc/app-profile.json

{
  "ca": [
    {
      "name": "ca.example.com",
      "location": "http://192.168.31.168:7054",
      "admin": "admin",
      "password": "adminpw"
    }
  ],
  "users": [
    {
      "name": "user001",
      "orgName": "org1",
      "affiliation":"org1.deparment001"
    }
  ],
  "orderers": [
    {
      "name": "orderer.example.com",
      "location": "grpc://192.168.31.168:7050"
    }
  ],
  "peers": [
    {
      "name": "peer0.org1.example.com",
      "orgName": "org1",
      "location": "grpc://192.168.31.168:7051",
      "eventHub": "grpc://192.168.31.168:7053"
    }
  ],
  "orgs": [
    {
      "orgName": "org1",
      "orgMspId": "Org1MSP",
      "orgDomainName": "org1.example.com",
      "caName": "ca.example.com"
    }
  ],
  "channels": [
    {
      "name": "mychannel",
      "peers": [
        "peer0.org1.example.com"
      ],
      "orderers": [
        "orderer.example.com"
      ]
    }
  ]
}

API文档

参考/doc/fabric-rest-api.docx

Fabric测试网络

参考/fabcar,官方hyperldger fabric 1.2中fabric-sample自带例子, 稍做调整

测试用例

参考FabricRestTest.java

package com.whlylc.fabricrest;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.whlylc.fabricrest.util.FileUtils;
import com.whlylc.fabricrest.util.HttpUtils;
import com.whlylc.fabricrest.vo.FabricUser;
import com.whlylc.fabricrest.vo.Result;
import org.hyperledger.fabric.sdk.security.CryptoSuite;
import org.hyperledger.fabric_ca.sdk.HFCAAffiliation;
import org.hyperledger.fabric_ca.sdk.HFCAClient;

import java.io.File;

/**
 * Created by Zeal on 2018/10/7 0007.
 */
public class FabricRestTest {

    private String appId = "app1";

    private String appSecret = "1234567890";

    private String uri = "http://localhost:8080";

    public void testAppProfile() throws Exception {

        String url = uri + "/appProfile?appId=" + appId + "&appSecret=" + appSecret;
        File dir = new File(System.getProperty("user.dir"));
        File jsonFile = new File(dir, "doc/app-profile.json");
        String json = FileUtils.readFileToString(jsonFile, "UTF-8");
        Result<String> result = HttpUtils.post(url, json);
        System.out.println(result.getResultEntity());
        File targetFile = new File(dir, "doc/app-profile-target.json");
        FileUtils.write(targetFile, result.getResultEntity(), "UTF-8");
    }

    public void testListAffiliation() throws Exception {

        //Fabric CA URL
        HFCAClient caClient = HFCAClient.createNewInstance("http://192.168.31.168:7054", null);
        caClient.setCryptoSuite(CryptoSuite.Factory.getCryptoSuite());
        FabricUser user = new FabricUser();
        //TODO Copy the ca private key and signed cert here to query all affiliations
        user.setPrivateKey("-----BEGIN EC PRIVATE KEY-----\r\nMHcCAQEEIEzAclLGEdj27lO/xAxo951L1/KEcrFUQst9UfyHavZOoAoGCCqGSM49\r\nAwEHoUQDQgAEmdlbG3KArAVGEBDLshNLJs32gVdqdvwVV2WSF0aLC3kiz8LUVHsV\r\nxWSSw3oJDlW//b/51dyl/vnbu6EymT/uOw==\r\n-----END EC PRIVATE KEY-----\r\n");
        user.setSignedCert("-----BEGIN CERTIFICATE-----\nMIICAjCCAaigAwIBAgIUdBr3IbIZ+lXJusXNmyvgE7f4WU8wCgYIKoZIzj0EAwIw\nczELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHDAaBgNVBAMT\nE2NhLm9yZzEuZXhhbXBsZS5jb20wHhcNMTgxMjE1MDczMTAwWhcNMTkxMjE1MDcz\nNjAwWjAhMQ8wDQYDVQQLEwZjbGllbnQxDjAMBgNVBAMTBWFkbWluMFkwEwYHKoZI\nzj0CAQYIKoZIzj0DAQcDQgAEmdlbG3KArAVGEBDLshNLJs32gVdqdvwVV2WSF0aL\nC3kiz8LUVHsVxWSSw3oJDlW//b/51dyl/vnbu6EymT/uO6NsMGowDgYDVR0PAQH/\nBAQDAgeAMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFMXIi7kIFecYeM6EHzBYDILl\n31EfMCsGA1UdIwQkMCKAIEI5qg3NdtruuLoM2nAYUdFFBNMarRst3dusalc2Xkl8\nMAoGCCqGSM49BAMCA0gAMEUCIQDRBUqTiJTiECNe9hEdCChLlfMBThi6K1hS9JGr\nIMsIpAIgZCbdXmW0uEQxjcjytCAYlm9hb7Wzm+ArRFmKAnSOAfs=\n-----END CERTIFICATE-----\n");
        HFCAAffiliation resp = caClient.getHFCAAffiliations(user);
        for (HFCAAffiliation aff : resp.getChildren()) {
            System.out.println(aff.getName() + "==>");
            for (HFCAAffiliation c : aff.getChildren()) {
                System.out.println(c.getName());
            }
        }

    }

    public void testInitLedger() throws Exception {
        JSONObject object = new JSONObject(true);
        object.put("channel", "mychannel");
        object.put("version", "1.0");
        object.put("chaincode", "fabcar");
        object.put("function", "initLedger");
        object.put("user", "user001");
        object.put("organization", "org1");

        String url = uri + "/queryByChaincode?appId=" + appId + "&appSecret=" + appSecret;
        String requestBody = object.toJSONString();
        Result<String> httpResult = HttpUtils.post(url, requestBody);
        System.out.println(httpResult);
    }

    public void queryAllCars() throws Exception {
        JSONObject object = new JSONObject(true);
        object.put("channel", "mychannel");
        object.put("version", "1.0");
        object.put("chaincode", "fabcar");
        object.put("function", "queryAllCars");
        object.put("user", "user001");
        object.put("organization", "org1");

        String url = uri + "/queryByChaincode?appId=" + appId + "&appSecret=" + appSecret;
        String requestBody = object.toJSONString();
        Result<String> httpResult = HttpUtils.post(url, requestBody);
        System.out.println(httpResult);
    }

    public void createCar() throws Exception {
        JSONObject object = new JSONObject(true);
        object.put("channel", "mychannel");
        object.put("version", "1.0");
        object.put("chaincode", "fabcar");
        object.put("function", "createCar");
        JSONArray array = new JSONArray();
        //var car = Car{Make: args[1], Model: args[2], Colour: args[3], Owner: args[4]}
        //Car{Make: "Ford", Model: "Mustang", Colour: "red", Owner: "Brad"},
        array.add("CAR11");
        array.add("Ford");
        array.add("Mustang");
        array.add("gray");
        array.add("Zeal");
        object.put("arguments", array);
        object.put("user", "user001");
        object.put("organization", "org1");

        String url = uri + "/updateByChaincode?appId=" + appId + "&appSecret=" + appSecret;
        String requestBody = object.toJSONString();
        Result<String> httpResult = HttpUtils.post(url, requestBody);
        System.out.println(httpResult);
    }


    public static void main(String[] args) throws Exception {
        FabricRestTest test = new FabricRestTest();
//        test.testAppProfile();
//        test.testListAffiliation();
        //It's already called by start.sh, but we invoke it again
//        test.testInitLedger();
        test.createCar();
        //Result:[resultCode=200, resultMessage=null,resultEntity={"resultCode":0,"resultEntity":[{"Record":{"owner":"Tomoko","colour":"blue","model":"Prius","make":"Toyota"},"Key":"CAR0"},{"Record":{"owner":"Brad","colour":"red","model":"Mustang","make":"Ford"},"Key":"CAR1"},{"Record":{"owner":"Zeal","colour":"gray","model":"Mustang","make":"Ford"},"Key":"CAR11"},{"Record":{"owner":"Jin Soo","colour":"green","model":"Tucson","make":"Hyundai"},"Key":"CAR2"},{"Record":{"owner":"Max","colour":"yellow","model":"Passat","make":"Volkswagen"},"Key":"CAR3"},{"Record":{"owner":"Adriana","colour":"black","model":"S","make":"Tesla"},"Key":"CAR4"},{"Record":{"owner":"Michel","colour":"purple","model":"205","make":"Peugeot"},"Key":"CAR5"},{"Record":{"owner":"Aarav","colour":"white","model":"S22L","make":"Chery"},"Key":"CAR6"},{"Record":{"owner":"Pari","colour":"violet","model":"Punto","make":"Fiat"},"Key":"CAR7"},{"Record":{"owner":"Valeria","colour":"indigo","model":"Nano","make":"Tata"},"Key":"CAR8"},{"Record":{"owner":"Shotaro","colour":"brown","model":"Barina","make":"Holden"},"Key":"CAR9"}],"resultMessage":""}]
        test.queryAllCars();
        //FIXME Change owner

    }

}

开发计划

完整的发布应该类似Tomcat一样的默认打包, 控制脚本等, 消息通知的版本有差异, 对官方Java SDK一些细节也有一些疑惑, 先陆续稳定一版。原则上Fabric REST只做到简单够用即可(毕竟代理了一层,HTTP和无状态也有些限制), 毕竟GRPC现在连JS SDK都有, 其它语言的SDK是时间问题.

t_e278edf93fd644148560ad654c2ec4de.png

点赞 0
0条评论
其他心得
编写过一些链码的人可能会觉得是在操作一个简单的key-value数据库, 就是GetState和PutState去操作键值对,而对复杂些的一对多,多对多等实体关系和数据模型不知怎么设计。我们先从官方的例子入手一起探讨下。 1.简单转账例子 /fabric-samples/chaincode/chaincode_example02/go/chaincode_example02.go 假设链码调用peer chaincode invoke … -c ‘{“Args”:[“invoke”,”a
Zealot · 22天前 
区块链的真实数据依赖于物联网和智能设备,记一次折腾的android无线调试经历。 Android 4.2.2定制版智能硬件, USB口能插鼠标键盘, 但是不能USB调试。供应商两个方案, 要么开壳找到USB OTG排座, USB口自己接线, 但是开壳会导致硬件功能无法使用; 要么手工打包apk安装到硬件慢慢的toast。 摸索出第三条路。 搜索android无线调试, 基本都需要第一次USB调试线, adb tcpip 5555开启android设备端口监听, 之后adb connect ip
武汉珞樱联创Luoying-server和Fabric-REST开源 https://github.com/zealzeng/luoying-server https://github.com/zealzeng/fabric-rest Luoying Server Framework Luoying server是一个轻量级的服务器开发框架,方便开发者快速的定制多种协议的服务器,servlet style的API和简单的容器注入模式让开发者专注于业务实现。 Maven地址
Zealot · 34天前 
Fabric打鸡血了, 1.3正式版发布一个月1.4的RC版出来了,按照惯例在两个月内1.4正式版会发布,我们先预览下新特性。官方文档链接参考 https://hyperledger-fabric.readthedocs.io/en/release-1.4/whatsnew.html 自1.0版本开始, Fabric日渐成熟。1.4版本专注于提高运维和开发的易用性,主要包括两方面。 (1)可维护性和可操作性 1.4大大的改进了日志,服务健康检查和可操作的度量指标, 持续的修复bug和提供系
分享些干货,吸点人气,2018年区块链技术大会的部分PPT放出。建立开源的安全区块链生态系统(PPT下载) http://cbc.dbw.org.cn/documents/%E5%A4%A7%E4%BC%9A%E6%8A%A5%E5%91%8A/%E5%AE%B9%E6%B7%B3%E9%93%AD.pdf企业区块链(PPT下载) http://cbc.dbw.org.cn/documents/%E5%A4%A7%E4%BC%9A%E6%8A%A5%E5%91%8A/%E7%99%BD%E7%