区块链接入开发(Stub)

本章节首先介绍WeCross Stub的设计原理,在此基础上给出区块链接入WeCross的开发流程,用户可以根据教程实现一个区块链的WeCross Stub插件,通过插件接入WeCross

注解

  • Java编程语言

WeCross使用Java实现,区块链需要支持Java版本的SDK,要求开发人员具备Java开发能力。

  • Gradle构建工具

WeCross Java组件使用Gradle构建,假定用户能够使用Gradle。

Stub设计

Stub开发

Stub开发分为两个部分:

  • 系统合约

  • Java插件

系统合约

系统合约包括代理合约(WeCrossProxy)和桥接合约(WeCrossHub),代理合约是WeCross调用该链其它合约的统一入口,桥接合约用于记录跨链调用请求,以配合跨链路由实现合约跨链调用。

代理合约

功能点:

  • 合约调用入口

  • 事务管理

示例:

Solidity版:

Golang版:

以Solidity合约为例,接口列表:

/** 读接口,调用业务合约(不需要脏读,即无事务ID,读事务中资源)
 *
 *  @param _XATransactionID  事务ID
 *  @param _path   目标资源路径
 *  @param _func   调用方法
 *  @param _args   调用参数
 *  @return 调用结果
 */ 
function constantCall(
    string memory _XATransactionID, 
    string memory _path, 
    string memory _func, 
    bytes memory _args
) public returns(bytes memory)


/** 写接口,调用业务合约
 *
 *  @param _uid  交易ID,用于去重
 *  @param _XATransactionID  事务ID
 *  @param _XATransactionSeq  事务序列号
 *  @param _path   目标资源路径
 *  @param _func   调用方法
 *  @param _args   调用参数
 *  @return 调用结果
 */ 
function sendTransaction(
    string memory _uid, 
    string memory _XATransactionID, 
    uint256 _XATransactionSeq, 
    string memory _path, 
    string memory _func, 
    bytes memory _args
) public returns(bytes memory)


/** 开启事务
 *
 *  @param _XATransactionID  事务ID
 *  @param _selfPaths  参与事务的己链资源列表
 *  @param _otherPaths 参与事务的它链资源列表
 *  @return 执行结果
 */ 
function startXATransaction(
    string memory _xaTransactionID, 
    string[] memory _selfPaths, 
    string[] memory _otherPaths
) public returns(string memory)


/** 提交事务
 *
 *  @param _XATransactionID  事务ID
 *  @return 执行结果
 */ 
function commitXATransaction(
    string memory _xaTransactionID
) public returns(string memory)


/** 回滚事务
 *
 *  @param _XATransactionID  事务ID
 *  @return 执行结果
 */ 
function rollbackXATransaction(
  string memory _xaTransactionID
) public returns(string memory)


/** 获取事务列表
 *
 *  @param _index 下标
 *  @param _size  数量
 *  @return 事务列表详情,JSON格式
 */ 
function listXATransactions(
    string memory _index, 
    uint256 _size
) public view returns (string memory)


/** 获取事务详情
 *
 *  @param _xaTransactionID 事务ID
 *  @return 事务详情,JSON格式
 */ 
function getXATransaction(
    string memory _xaTransactionID
) public view returns(string memory)

桥接合约

功能点:

  • 注册跨链调用请求

  • 查询跨链调用回调结果

示例:

Solidity版:

Golang版:

以Solidity合约为例,接口列表:

/** 供业务合约调用,注册跨链调用请求
 *
 *  @param _path     目标链合约的路径
 *  @param _method   调用方法名
 *  @param _args     调用参数列表
 *  @param _callbackPath   回调的合约路径
 *  @param _callbackMethod 回调方法名
 *  @return 跨链请求的唯一ID
 */ 
function interchainInvoke(
        string memory _path, 
        string memory _method, 
        string[] memory _args, 
        string memory _callbackPath, 
        string memory _callbackMethod
) public returns(string memory uid)


/** 供跨链路由调用,获取跨链调用请求
 *
 *  @param _num   获取数量
 *  @return       请求列表,JSON格式
 */ 
function getInterchainRequests(
    uint256 _num
) public view returns(string memory)


/** 供跨链路由调用,更新跨链任务处理进度
 *
 *  @param _index  新下标
 */ 
function updateCurrentRequestIndex(uint256 _index) public


/** 供跨链路由调用,注册回调结果
 *
 *  @param _uid  跨链请求的唯一ID
 *  @param _tid  事务ID
 *  @param _seq  事务序列号
 *  @param _errorCode 回调错误码
 *  @param _errorMsg  回调错误详情
 *  @param _result    回调结果
 */ 
function registerCallbackResult(
    string memory _uid, 
    string memory _tid, 
    string memory _seq, 
    string memory _errorCode, 
    string memory _errorMsg, 
    string[] memory _result
) public


/** 供用户调用,查询回调的调用结果
 *
 *  @param _uid   跨链请求的唯一ID
 *  @return       字符串数组: [事务ID, 事务Seq, 错误码, 错误消息, 回调调用结果的JSON序列化]
 */ 
function selectCallbackResult(
    string memory _uid
) public view returns(string[] memory)

Java插件

Java核心组件

  • StubFactory

    • 组件实例化工厂类

  • Account

    • 区块链账户,用于交易签名

  • Connection

    • 调用区块链SDK接口,与区块链交互

  • Driver

    • 交易、交易回执、区块等与区块链相关数据的编解码

    • 实现Stub的基础接口

    • 调用Connection对象的发送入口与区块链交互

StubFactory
  • 功能描述:

    • 添加@Stub注解,定义插件类型

    • 提供AccountConnectionDriver实例化入口

重要

@Stub定义了插件类型,添加@Stub注解的插件才能被Wecross Router识别加载!

  • 接口定义

public interface StubFactory {
    public Driver newDriver();
    public Connection newConnection(String path);
    public Account newAccount(Map<String, Object> properties);
}
  • 接口列表

    • newDriver

      • 实例化Driver对象

    • newConnection

      • 实例化Connection对象

        • path:配置文件路径,配置文件名称默认stub.toml

    • newAccount

      • 实例化Account对象

        • properties:Account对象实例化的参数

  • FISCO-BCOS StubFactory示例

/** @Stub注解,插件类型: BCOS2.0 */
@Stub("BCOS2.0") 
public class BCOSStubFactory implements StubFactory {
    @Override
    public Driver newDriver() {
        Driver driver = new BCOSDriver();
        /** 其他逻辑 */
        return driver;
    }
    @Override
    public Connection newConnection(String path) {
        Connection connection = new BCOSConnection();
        /** 解析配置文件,初始化 BCOSConnection */
        return connection;
    }
    @Override
    public Account newAccount(Map<String, Object> properties) {
        Account account = new BCOSAccount();
        /** 根据properties参数,初始化 BCOSAccount */
        return account;
    }
}

Account

  • 功能描述

    • 交易签名

    • 链账户签名、验签

  • 接口定义

public interface Account {
    String getName();
    String getType();
    String getIdentity();
    int getKeyID();
    boolean isDefault();
}
  • 接口列表

    • getName

      • 链账户名称,自定义

    • getType

      • 链账户类型,与Stub类型保持一致

    • getIdentify

      • 链账户标记符,通常为链账户公钥

    • isDefault

      • 当前账户是否为默认链账户

    • getKeyID

      • 链账户KeyID

Connection

  • 功能描述

    • 解析配置文件,初始化区块链JavaSDK,参考下面配置文件小节

    • Driver提供统一的发送接口,与区块链进行交互

    • 获取链上的资源列表

  • 接口定义

public interface Connection {
    Response send(Request request);
    List<ResourceInfo> getResources();
}

接口列表

  • getResources

    • 获取区块链上的资源列表

      /** 资源对象 */
      public class ResourceInfo {
          /** 资源名称 */
          private String name;
          /** 资源类型,用户自定义 */
          private String stubType;
          /** 资源属性 */
          private Map<Object, Object> properties = new HashMap<Object, Object>();
      }
      
  • send

    • 发送接口

      • Request request: 请求对象,包括请求类型、请求内容

        public class Request {
            // 请求类型,自定义类型
            private int type;
            // 请求内容,序列化的请求参数
            private byte[] data;
        }
        
      • Response response: 返回对象,包括返回状态、描述信息、返回内容

        public class Response {
            // 返回状态码
            private int errorCode;
            // 描述信息
            private String errorMessage;
            // 返回内容,序列化的返回参数
            private byte[] data;
        }
        

FISCO-BCOS BCOSConnection示例:

// Request type定义,自定义
public class BCOSRequestType {
    // 查询操作
    public static final int CALL = 1000;
    // 发送交易
    public static final int SEND_TRANSACTION = 1001;
    // 获取块高
    public static final int GET_BLOCK_NUMBER = 1002;
    // 获取区块
    public static final int GET_BLOCK_BY_NUMBER = 1003;
    // 获取交易证明
    public static final int GET_TRANSACTION_PROOF = 1004;
}

// Connection定义各个类型消息的处理方式
public class BCOSConnection implements Connection {
    /** 发送入口,区分消息类型,调用区块链RPC接口 */
    @Override
    public Response send(Request request) {
        switch (request.getType()) {
            /** 查询 */
            case BCOSRequestType.CALL:
                /** call请求 */
                break;
            /** 发送交易 */
            case BCOSRequestType.SEND_TRANSACTION:
                /** sendTransaction请求 */
                break;
            /** 获取区块头 */
            case BCOSRequestType.GET_BLOCK_NUMBER:
                /** 获取区块高度请求 */
                break;
            /** 获取块高 */
            case BCOSRequestType.GET_BLOCK_BY_NUMBER:
                /** 获取区块 */
                break;
            /** 获取交易证明 */
            case BCOSRequestType.GET_TRANSACTION_PROOF:
                /** 获取交易证明 */
                break;
        }
    }
}
  • 配置文件 配置文件主要包括区块链JavaSDK初始化需要的参数,也可以包含其他的一些附加信息,由用户自定义。配置默认位于chains/目录,可以配置多个stub,每个stub位于单独的子目录,配置文件名称stub.toml

    # 目录结构, conf/chains/stub名称/
    conf/chains/
            └── bcos # stub名称: bcos
                └── stub.toml # stub.toml配置文件
                # 其他文件列表,比如:证书文件
    

stub.toml解析流程可以参考FISCO-BCOS Stub stub.toml解析

FISCO-BCOS stub.toml示例

[common]    # 通用配置
    name = 'bcos' # 名称,必须项
    type = 'BCOS2.0' # 必须项,插件类型,与插件@Stub注解定义的类型保持一致


[chain]     # FISCO-BCOS 属性
    groupId = 1 # default 1
    chainId = 1 # default 1

[channelService]    # FISCO-BCOS JavaSDK配置
    caCert = 'ca.crt'
    sslCert = 'sdk.crt'
    sslKey = 'sdk.key'
    timeout = 300000  # 超时时间
    connectionsStr = ['127.0.0.1:20200', '127.0.0.1:20201', '127.0.0.1:20202'] # 连接列表

Driver

  • 功能描述

    • 发送交易

    • 状态查询

    • 查询块高

    • 查询区块

    • 获取交易证明

    • 交易、区块编解码

    • 验证交易

    • 查询资源列表

  • 接口定义

public interface Driver {
    interface Callback {
        void onTransactionResponse(
                TransactionException transactionException, TransactionResponse transactionResponse);
    }

    ImmutablePair<Boolean, TransactionRequest> decodeTransactionRequest(Request request);

    List<ResourceInfo> getResources(Connection connection);

    void asyncCall(
            TransactionContext context,
            TransactionRequest request,
            boolean byProxy,
            Connection connection,
            Driver.Callback callback);

    void asyncSendTransaction(
            TransactionContext context,
            TransactionRequest request,
            boolean byProxy,
            Connection connection,
            Driver.Callback callback);

    interface GetBlockNumberCallback {
        void onResponse(Exception e, long blockNumber);
    }

    void asyncGetBlockNumber(Connection connection, GetBlockNumberCallback callback);

    interface GetBlockCallback {
        void onResponse(Exception e, Block block);
    }

    void asyncGetBlock(
            long blockNumber, boolean onlyHeader, Connection connection, GetBlockCallback callback);

    interface GetTransactionCallback {
        void onResponse(Exception e, Transaction transaction);
    }

    void asyncGetTransaction(
            String transactionHash,
            long blockNumber,
            BlockManager blockManager,
            boolean isVerified,
            Connection connection,
            GetTransactionCallback callback);

    interface CustomCommandCallback {
        void onResponse(Exception error, Object response);
    }

    void asyncCustomCommand(
            String command,
            Path path,
            Object[] args,
            Account account,
            BlockManager blockManager,
            Connection connection,
            CustomCommandCallback callback);

    byte[] accountSign(Account account, byte[] message);

    boolean accountVerify(String identity, byte[] signBytes, byte[] message);
}
  • 接口列表:

    • asyncCall

    • asyncSendTransaction 状态查询/发送交易

      • TransactionContext context

        • 请求上下文,交易的上下文,包含交易的附属信息

      • TransactionRequest request

      • boolean byProxy

        • 是否通过代理合约查询状态/发送交易

      • Connection connection

        • 发送请求

      • Driver.Callback callback

        • 回调返回

    • asyncGetBlockNumber 获取区块高度

      • Connection connection

        • 发送请求

      • GetBlockNumberCallback callback

        • 回调返回

    • asyncGetBlock 获取区块

      • long blockNumber

        • 区块高度

      • boolean onlyHeader

        • 是否只获取区块头

      • Connection connection

        • 发送请求

      • GetBlockCallback callback

        • 回调返回

    • asyncGetTransaction 获取交易,并且对交易进行合法性验证

      • String transactionHash

        • 交易hash

      • long blockNumber

        • 区块高度

      • BlockManager blockManager

        • 区块管理对象,用于获取区块信息,可以使用区块头部的状态信息校验交易是否合法

      • boolean isVerified

        • 是否校验交易

      • Connection connection

        • 发送请求

      • GetTransactionCallback callback

        • 回调返回

    • asyncCustomCommand 用户自定义其他接口

      • String command

        • 命令

      • Path path

        • 资源

      • Object[] args

        • 参数列表

      • Account account

        • 账户

      • BlockManager blockManager

        • 区块管理对象

      • Connection connection

        • 发送请求

      • CustomCommandCallback callback

        • 回调返回

    • accountSign 链账户Account对消息进行签名,返回序列化之后的签名对象

      • Account account

        • 签名账户

      • byte[] message

        • 代签名的消息

    • accountVerify 链账户验签

      • String identity

      • byte[] signBytes

        • 签名对象,accountSign的返回值

      • byte[] message

        • 签名的原始消息

开发模板

WeCross提供一个Java模板工程,加快用户开发WeCross Stub的速度,用户仅需要进行少量的修改。

获取:

git clone https://github.com/WeBankBlockchain/WeCross-Stub-Dev-Template.git

目录结构:

WeCross-Stub-Dev-Template
├── README.md
├── build.gradle
└── src
    ├── main
       ├── java
          └── wecross
              └── stub
                  └── demo                  ## Java核心组件,参考上文各个组件的介绍
                      ├── DemoAccount.java      # Account
                      ├── DemoConnection.java   # Connection
                      ├── DemoDriver.java       # Driver
                      └── DemoStubFactory.java  # StubFactory
       └── resources
    └── test
        ├── java
           └── wecross
               └── stub
                   └── demo
                       └── DemoStubTest.java
        └── resources

编译:

cd WeCross-Stub-Dev-Template
bash gradlew build

$ tree -L 1 dist/apps
dist/apps
└── WeCross-Stub-Dev-Template-1.0.0-SNAPSHOT.jar