附录

1. 安装问题

1.1 Java部署

CentOS环境安装Java

注意:CentOS下OpenJDK无法正常工作,需要安装OracleJDK下载链接

# 创建新的文件夹,安装Java 8或以上的版本,将下载的jdk放在software目录
# 从Oracle官网(https://www.oracle.com/technetwork/java/javase/downloads/index.html)选择Java 8或以上的版本下载,例如下载jdk-8u201-linux-x64.tar.gz
$ mkdir /software

# 解压jdk
$ tar -zxvf jdk-8u201-linux-x64.tar.gz

# 配置Java环境,编辑/etc/profile文件
$ vim /etc/profile

# 打开以后将下面三句输入到文件里面并保存退出
export JAVA_HOME=/software/jdk-8u201  #这是一个文件目录,非文件
export PATH=$JAVA_HOME/bin:$PATH
export CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar

# 生效profile
$ source /etc/profile

# 查询Java版本,出现的版本是自己下载的版本,则安装成功。
java -version

Ubuntu环境安装Java

  # 安装默认Java版本(Java 8或以上)
  sudo apt install -y default-jdk
  # 查询Java版本
  java -version

1.2 Gradle部署

此处给出简单步骤,供快速查阅。更详细的步骤,请参考官网

(1)从官网下载对应版本的Gradle安装包,并解压到相应目录

mkdir /software/
unzip -d /software/ gradleXXX.zip

(2)配置环境变量

export GRADLE_HOME=/software/gradle-4.9
export PATH=$GRADLE_HOME/bin:$PATH

(3)查看版本

gradle -version

2. 常见问题

  • 1:执行shell脚本报错误”permission denied”或格式错误

    赋权限:chmod + *.sh
    转格式:dos2unix *.sh
    
  • 2:eclipse环境编译源码失败,错误提示如下:

...
/data/temp/WeBASE-Front/src/main/java/com/webank/webase/front/performance/PerformanceService.java:167: error: cannot find symbol
        log.info("begin sync performance");
        ^
  symbol:   variable log
  location: class PerformanceService
Note: /data/temp/WeBASE-Front/src/main/java/com/webank/webase/front/contract/CommonContract.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
Note: Some input files use unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
100 errors

> Task :compileJava FAILED

FAILURE: Build failed with an exception.
...

答:问题是不能编译Lombok注解 ,修改build.gradle文件,将以下代码的注释加上

 //annotationProcessor 'org.projectlombok:lombok:1.18.6'
  • 3:节点运行一段时间后新增了一个群组,前置查不到新群组的信息。

    答:调用 http://{ip}:{port}/WeBASE-Front/1/web3/refresh 方法,即可手动更新。

  • 4:升级1.0.2版本时,数据库报错:

    Caused by: org.h2.jdbc.JdbcSQLException: NULL not allowed for column "TYPE"; SQL statement:
    alter table key_store_info add column type integer not null [23502-197]
            at org.h2.message.DbException.getJdbcSQLException(DbException.java:357) ~[h2-1.4.197.jar:1.4.197]
            at org.h2.message.DbException.get(DbException.java:179) ~[h2-1.4.197.jar:1.4.197]
            at org.h2.message.DbException.get(DbException.java:155) ~[h2-1.4.197.jar:1.4.197]
    

    答:将H2数据库删除(在h2目录下),或者配置新数据库名,在 application.yml 文件中的配置如下:

    spring:
      datasource:
        url: jdbc:h2:file:./h2/webasefront;DB_CLOSE_ON_EXIT=FALSE // 默认H2库为webasefront
    ...
    
  • 5:日志报以下错误信息:

    2019-08-08 17:29:05.505 [pool-11-thread-1] ERROR TaskUtils$LoggingErrorHandler() - Unexpected error occurred in scheduled task.
    org.hyperic.sigar.SigarFileNotFoundException: 没有那个文件或目录
            at org.hyperic.sigar.FileSystemUsage.gather(Native Method) ~[sigar-1.6.4.jar:?]
            at org.hyperic.sigar.FileSystemUsage.fetch(FileSystemUsage.java:30) ~[sigar-1.6.4.jar:?]
            at org.hyperic.sigar.Sigar.getFileSystemUsage(Sigar.java:667) ~[sigar-1.6.4.jar:?]
    

    答:监控目录不存在,需配置节点所在磁盘目录,在 application.yml 文件中的配置如下:

    ...
    constant:  
      monitorDisk: /            // 要监控的磁盘目录,配置节点所在目录(如:/home)
    ...
    
  • 6:启动报错“nested exception is javax.net.ssl.SSLException”:

...
nested exception is javax.net.ssl.SSLException: Failed to initialize the client-side SSLContext: Input stream not contain valid certificates.

答:CentOS的yum仓库的OpenJDK缺少JCE(Java Cryptography Extension),导致Web3SDK/Java-SDK无法正常连接区块链节点,因此在使用CentOS操作系统时,推荐使用OracleJDK

  • 7:启动报错“Processing bcos message timeout”
...
[main] ERROR SpringApplication() - Application startup failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'contractController': Unsatisfied dependency expressed through field 'contractService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'contractService': Unsatisfied dependency expressed through field 'web3jMap'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'web3j' defined in class path resource [com/webank/webase/front/config/Web3Config.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.util.HashMap]: Factory method 'web3j' threw exception; nested exception is java.io.IOException: Processing bcos message timeout
...

答:一些OpenJDK版本缺少相关包,导致节点连接异常。推荐使用OracleJDK

  • 8:启动失败,日志却没有异常
===============================================================================================
Starting Server com.webank.webase.front.Application Port 5002 ................................[Failed]. Please view log file (default path:./log/).
Because port 5002 not up in 20 seconds.Script finally killed the process.
===============================================================================================

答:确认机器是否满足硬件要求。机器性能过低会导致服务端口一定时间内没起来,脚本会自动杀掉进程。可以尝试手动修改dist目录下的start.sh脚本,将启动等待时间设置久一点(默认600,单位:秒),然后启动。

...
startWaitTime=600
...
  • 9:启动报错SSLContext: null

答:确保conf/目录下包含sdk证书; 若使用的是v1.5.0以前的版本,则需要保证ca.crt, node.crt, node.key;其中node.crt, node.key为sdk.crt, sdk.key复制并重命名得到;若使用v1.5.0及以上版本,则需要复制链的sdk目录下的所有文件(ca.crt, sdk.crt, sdk.key及gm文件夹)到前置服务的conf目录

3. 使用说明

测试用户管理

3.1. 导入私钥

支持txt文件和pem文件导入测试用户的私钥信息

导入.txt私钥内容格式示例:

{
  "address":"0x06f81c8e1cb59b5df2cdeb87a212d17fba79aad7",
  "publicKey":"0x4b1041710a4427dc1c0d542c8f0fd312d92b0d03a989f512d1f8d3cafb851967f3592df0035e01fa63b2626165d0f5cffab15792161aa0360b8dfba2f3a7cf59",
  "privateKey":"71f1479d9051e8d6b141a3b3ef9c01a7756da823a0af280c6bf62d18ee0cc978", // 十六进制
  "userName":"111",
  "type":0  // type为0,不可修改
}

其中用户类型为0代表用户为WeBASE-Front的本地私钥用户,导入的私钥均为该类型;

导入.pem私钥内容示例:

-----BEGIN PRIVATE KEY-----
MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgC8TbvFSMA9y3CghFt51/
XmExewlioX99veYHOV7dTvOhRANCAASZtMhCTcaedNP+H7iljbTIqXOFM6qm5aVs
fM/yuDBK2MRfFbfnOYVTNKyOSnmkY+xBfCR8Q86wcsQm9NZpkmFK
-----END PRIVATE KEY-----

其中pem文件开头的-----BEGIN PRIVATE KEY-----\n和结尾的\n-----END PRIVATE KEY-----\n格式不可更改,否则将读取pem文件失败

3.2. 导出私钥

目前仅支持导出测试用户的txt格式私钥

Java中如何使用导出的私钥

以上文中的私钥加载:

基于javasdk的私钥加载:

@Test
public void testCrypto() {
    // 1-国密,0-ECDSA
    CryptoSuite cryptoSuite = new CryptoSuite(1);
    CryptoKeyPair keyPair = cryptoSuite.createKeyPair("e843a542a7a8240f9c9e418b9517c2c8f4dc041a11a44e614a3b026c3588c188");
    System.out.println("privateKey: " + keyPair.getHexPrivateKey());
    System.out.println("address: " + keyPair.getAddress());
    System.out.println("publicKey: " + keyPair.getHexPublicKey());
}

基于web3sdk的私钥加载:

@Test
public void loadPrivateKeyTest() {
  CryptoSuite
  String privateKey = "71f1479d9051e8d6b141a3b3ef9c01a7756da823a0af280c6bf62d18ee0cc978";
  Credentials credentials = GenCredential.create(privateKey);
  // private key 实例
  BigInteger privateKeyInstance = credentials.getEcKeyPair().getPrivateKey();
  System.out.println(Numeric.toHexStringNoPrefix(privateKeyInstance));
  // public key 实例
  BigInteger publicKeyInstance = credentials.getEcKeyPair().getPublicKey();
  System.out.println(Numeric.toHexString(publicKeyInstance));
  // address 地址
  String address = credentials.getAddress();
  System.out.println(address);
}

访问h2数据库

WeBASE-Front采用 JPA + H2数据库 的方式保存数据

  • 源码查看各个数据表的内容:需要通过查看WeBASE-Front源码的各个包中带有@Entity注解的entity实体类;如,查看私钥数据表KeyStoreInfo则查看该文件com.webank.webase.front.keystore.entity.KeyStoreInfo.java
  • 通过H2控制台连接H2数据库

../../_images/h2_console.png

  • 同机H2访问:可以通过浏览器打开localhost:5002/WeBASE-Front/console,以默认配置为例填入连接参数
    • JDBC URL应填入file:../h2/webasefront;,与前置服务的application.yml中配置的spring.datasource.url对应
    • 若未设置用户名与密码,则默认用户名为sa,密码为空
  • 服务端H2访问:
    • 修改前置服务的application.yml中的spring.h2.console.settings.web-allow-others设为true,允许远端访问H2控制台
    • 重启前置服务
    • 访问{ip}:{port}/WeBASE-Front/console,参数填入方法同上

使用swagger

节点前置搭配了swagger,可用于直接调试接口,通过访问 {ip}:5002/WeBASE-Front/swagger-ui.html 即可访问前置的swagger页面

../../_images/swagger1.png

在swagger页面中选中一个接口后,点击“Try it out”既可以开始调用了,输入框将提示入参的格式

../../_images/swagger_tool_api.png

两阶段交易

在v1.5.2后,WeBASE-Front中丰富了组装交易的接口,包括了本地签名组装交易接口/trans/convertRawTxStr/local和通过WeBASE-Sign组装交易接口/trans/convertRawTxStr/withSign,下面以本地签名举例(接口的具体入参可参考对应接口文档)。

本地签名组装交易需要填入的参数包含合约地址、函数名及函数入参、群组ID和WeBASE-Front的私钥用户地址等,如下所示

{
    "user":"0x2db346f9d24324a4b0eac7fb7f3379a2422704db",
    "contractName":"HelloWorld",
    "contractAddress":"dasdfav23rf213vbcdvadf3bcdf2fc23rqde",
    "funcName":"set",
    "contractAbi":[{"constant":true,"inputs":[],"name":"getVersion","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"getStorageCell","outputs":[{"name":"","type":"string"},{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"n","type":"string"}],"name":"setVersion","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"storageHash","type":"string"},{"name":"storageInfo","type":"string"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"}],
    "funcParam":["Hi,Welcome!"],
    "groupId" :"1",
    "useCns": false
}

调用组装交易接口后,接口将返回签名后的交易体编码值(在测试接口时,可以通过节点前置的Swagger直接发起请求):

0xf9012da001071041dddc1b3c553b48c0fbefecc07f3812f5ce4004d47708f1c3342844db018405f5e10082029d94e10441d9179cf0424aae808b51bc85dcbbfe144780b8643590b49f000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000033333330000000000000000000000000000000000000000000000000000000000010180b84083bb5313e3dd7825b8b3e32d73aa8aedf9f9a8fcf435e5c37edfe4645c1af4211c12e1368024336a576f26ed624407da0b94e0bc5760514543c0b7a38fa03a7da0972843d0879ffdbdae733e8707896a532e5e1a3c7262cb84db657dd34f09111ba0786106465fe0fd2383588693cafef8934df62b188c6bb5a74eb6b9f23adaba32

通过已签名交易发送接口/trans/signed-transaction将交易编码值发到链上,接口将返回交易回执,可根据交易回执的status判断交易是否成功

**注:**若发起的是查询交易,除了上述接口的两阶段调用方法之外,还可以使用合约函数的编码值接口/trans/encodeFunction获取encodedFunction值。 根据入参要求调用已编码查询交易发送接口/trans/query-transaction,即可发送查询交易,接口将返回查询的返回值。

4. 支持链上事件订阅和通知

在某些业务场景中,应用层需要实时获取链上的事件,如出块事件、合约Event事件等。应用层通过WeBASE连接节点后,由于无法和节点直接建立长连接,难以实时获取链上的消息。

支持通过消息队列(Message Queue)来获取WeBASE-Front(v1.2.3+)的链上事件的消息推送

目前支持出块事件与智能合约Event事件的事件Push通知,大致流程为:

  1. WeBASE-Front连接到MQ-Server(目前支持RabbitMQ-Server);
  2. WeBASE-Front接收节点的事件Push后,如出块通知,WeBASE-Front将出块消息发送到消息队列中;
  3. 区块链应用连接MQ-Server,获取消息队列中待消费的消息,即可获得事件通知;

下面介绍如何搭建RabbitMQ的消息队列服务与WeBASE-Front的配置方法

5. 配置文件解析

    1. 配置文件解析
参数 默认值 描述
server.port 5002 当前服务端口
server.context-path /WeBASE-Front 当前服务访问目录
server.connection-timeout 30000 服务连接超时时间ms
server.tomcat.max-threads 200 tomcat最大线程数
server.tomcat.max-connections 10000 tomcat最大连接数
sdk.useSmSsl false SSL连接是否使用国密SM
sdk.certPath conf SDK证书所在目录(相对目录或绝对路径)
sdk.peers ['127.0.0.1:20200','127.0.0.1:20201'] RPC节点的IP:PORT列表
sdk.threadPoolSize 50 SDK线程池的线程数
logging.config classpath:log4j2.xml logging配置文件的位置
constant.keyServer 127.0.0.1:5004 webase-sign服务的IP:Port(单个)
constant.transMaxWait 30 交易最大等待时间(s)
constant.aesKey EfdsW23D23d3df43 webase服务的aes秘钥
constant.http_read_timeOut 100000 访问服务的读取超时(ms)
constant.http_connect_timeOut 100000 访问服务的连接超时(ms)
constant.eventRegisterTaskFixedDelay 5000 事件推送注册的频率(ms)
constant.syncEventMapTaskFixedDelay 60000 事件推送内存的同步频率(ms)
constant.syncStatLogTime 5000 节点日志监控频率(ms)
constant.syncStatLogCountLimit 10000 节点日志监控数据最大值
constant.statLogEnabled false 是否启用节点日志监控
constant.eventCallbackWait 4 获取event log的超时时间(秒)