Web3SDK¶
标签:Web3SDK
Web3SDK可以支持访问节点、查询节点状态、修改系统设置和发送交易等功能。该版本(2.0)的技术文档只适用Web3SDK 2.0及以上版本(与FISCO BCOS 2.0及以上版本适配),1.2.x版本的技术文档请查看Web3SDK 1.2.x版本技术文档。
2.0+版本主要特性包括:
提供调用FISCO BCOS 2.0 JSON-RPC的Java API
支持预编译(Precompiled)合约管理区块链
支持链上信使协议为联盟链提供安全高效的消息信道
支持使用国密算法发送交易
支持通过国密SSL与节点通信
环境要求¶
重要
Java版本
JDK1.8 或者以上版本,推荐使用OracleJDK。
注意:CentOS的yum仓库的OpenJDK缺少JCE(Java Cryptography Extension),会导致JavaSDK无法正常连接区块链节点。
Java安装
参考Java环境配置
FISCO BCOS区块链环境搭建
网络连通性
检查Web3SDK连接的FISCO BCOS节点`channel_listen_port`是否能telnet通,若telnet不通,需要检查网络连通性和安全策略。
Java应用引入SDK¶
通过gradle或maven引入SDK到java应用
gradle:
compile('org.fisco-bcos:web3sdk:2.6.1')
maven:
<dependency><groupId>org.fisco-bcos</groupId><artifactId>web3sdk</artifactId><version>2.6.1</version></dependency>
由于引入了以太坊的solidity编译器相关jar包,需要在Java应用的gradle配置文件build.gradle中添加以太坊的远程仓库。
repositories{mavenCentral()maven{url"https://dl.bintray.com/ethereum/maven/"}}
注: 如果下载Web3SDK的依赖solcJ-all-0.4.25.jar速度过慢,可以参考这里进行下载。
配置SDK¶
证书配置¶
FISCO BCOS作为联盟链,SDK连接区块链节点时通过SSL进行双向认证。JavaSDK支持SSL与国密SSL两种认证方式。
SSL连接配置¶
国密区块链和非国密区块链环境下,节点与SDK之间均可以建立SSL的连接,将节点所在目录nodes/${ip}/sdk/目录下的ca.crt、sdk.crt和sdk.key文件拷贝到项目的资源目录。(低于2.1版本的FISCO BCOS节点目录下只有node.crt和node.key,需将其重命名为sdk.crt和sdk.key以兼容最新的SDK)
配置文件设置¶
Java应用的配置文件需要做相关配置。值得关注的是,FISCO BCOS 2.0+版本支持多群组功能,SDK需要配置群组的节点信息。将以Spring项目和Spring Boot项目为例,提供配置指引。
Spring项目配置¶
提供Spring项目中关于applicationContext.xml的配置下所示。
<?xml version="1.0" encoding="UTF-8" ?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:p="http://www.springframework.org/schema/p"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"><beanid="encryptType"class="org.fisco.bcos.web3j.crypto.EncryptType"><constructor-argvalue="0"/><!-- 0:standard 1:guomi --></bean><beanid="groupChannelConnectionsConfig"class="org.fisco.bcos.channel.handler.GroupChannelConnectionsConfig"><!-- SSL certificate configuration --><propertyname="caCert"value="ca.crt"/><propertyname="sslCert"value="sdk.crt"/><propertyname="sslKey"value="sdk.key"/><!-- GM SSL certificate configuration --><propertyname="gmCaCert"value="gmca.crt"/><propertyname="gmEnSslCert"value="gmensdk.crt"/><propertyname="gmEnSslKey"value="gmensdk.key"/><propertyname="gmSslCert"value="gmsdk.crt"/><propertyname="gmSslKey"value="gmsdk.key"/><propertyname="allChannelConnections"><list><!-- 每个群组需要配置一个bean,每个群组可以配置多个节点 --><beanid="group1"class="org.fisco.bcos.channel.handler.ChannelConnections"><propertyname="groupId"value="1"/><!-- 群组的groupID --><propertyname="connectionsStr"><list><value>127.0.0.1:20200</value><!-- IP:channel_port --><value>127.0.0.1:20201</value></list></property></bean><beanid="group2"class="org.fisco.bcos.channel.handler.ChannelConnections"><propertyname="groupId"value="2"/><!-- 群组的groupID --><propertyname="connectionsStr"><list><value>127.0.0.1:20202</value><value>127.0.0.1:20203</value></list></property></bean></list></property></bean><beanid="channelService"class="org.fisco.bcos.channel.client.Service"depends-on="groupChannelConnectionsConfig"><propertyname="groupId"value="1"/><!-- 配置连接群组1 --><propertyname="agencyName"value="fisco"/><!-- 配置机构名 --><propertyname="allChannelConnections"ref="groupChannelConnectionsConfig"></property></bean></beans>
applicationContext.xml配置项详细说明:
encryptType: 国密开关(默认为0,关闭)
0: 不使用国密
1: 使用国密
开启国密功能,需要连接的区块链节点是国密节点,搭建国密版FISCO BCOS区块链参考这里)
使用国密SSL,在国密区块链环境基础上,SDK需要打开encryptType开关,然后配置国密SSL证书
groupChannelConnectionsConfig:
配置待连接的群组,可以配置一个或多个群组,每个群组需要配置群组ID
每个群组可以配置一个或多个节点,设置群组节点的配置文件config.ini中
[rpc]部分的channel_listen_ip(若节点小于v2.3.0版本,查看配置项listen_ip)和channel_listen_port。SSL配置项: SDK与节点SSL连接时使用
caCertSL连接根证书路径sslCertSDK证书路径sslKeySDK证书私钥路径
国密SSL配置项: SDK与节点国密SSL连接时使用
gmCaCert国密SSL连接根证书路径gmEnSslCert国密SSL连接加密证书路径gmEnSslKey国密SSL连接加密证书私钥路径gmSslCertSSL连接签名证书路径gmSslKeySSL连接签名证书私钥路径
channelService: 通过指定群组ID配置SDK实际连接的群组,指定的群组ID是groupChannelConnectionsConfig配置中的群组ID。SDK会与群组中配置的节点均建立连接,然后随机选择一个节点发送请求。
备注:刚下载项目时,有些插件可能没有安装,代码会报错。当你第一次在IDEA上使用lombok这个工具包时,请按以下步骤操作:
进入setting->Plugins->Marketplace->选择安装Lombok plugin
进入设置Setting-> Compiler -> Annotation Processors -> 勾选Enable annotation processing。
Spring Boot项目配置¶
提供Spring Boot项目中关于application.yml的配置如下所示。
encrypt-type:# 0:普通, 1:国密encrypt-type:0group-channel-connections-config:caCert:ca.crtsslCert:sdk.crtsslKey:sdk.keyall-channel-connections:-group-id:1#group IDconnections-str:# 若节点小于v2.3.0版本,查看配置项listen_ip:channel_listen_port-127.0.0.1:20200# node channel_listen_ip:channel_listen_port-127.0.0.1:20201-group-id:2connections-str:# 若节点小于v2.3.0版本,查看配置项listen_ip:channel_listen_port-127.0.0.1:20202# node channel_listen_ip:channel_listen_port-127.0.0.1:20203channel-service:group-id:1# sdk实际连接的群组agency-name:fisco# 机构名称
application.yml配置项与applicationContext.xml配置项相对应,详细介绍参考applicationContext.xml配置说明。
使用SDK¶
Spring项目开发指引¶
调用SDK的API(参考Web3SDK API列表设置或查询相关的区块链数据。¶
调用SDK Web3j的API¶
加载配置文件,SDK与区块链节点建立连接,获取web3j对象,根据Web3j对象调用相关API。示例代码如下:
//读取配置文件,SDK与区块链节点建立连接ApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");Serviceservice=context.getBean(Service.class);service.run();ChannelEthereumServicechannelEthereumService=newChannelEthereumService();channelEthereumService.setChannelService(service);//获取Web3j对象Web3jweb3j=Web3j.build(channelEthereumService,service.getGroupId());//通过Web3j对象调用API接口getBlockNumberBigIntegerblockNumber=web3j.getBlockNumber().send().getBlockNumber();System.out.println(blockNumber);
注: SDK处理交易超时时间默认为60秒,即60秒内没有收到交易响应,判断为超时。该值可以通过ChannelEthereumService进行设置,示例如下:
// 设置交易超时时间为100000毫秒,即100秒channelEthereumService.setTimeout(100000);
调用SDK Precompiled的API¶
加载配置文件,SDK与区块链节点建立连接。获取SDK Precompiled Service对象,调用相关的API。示例代码如下:
//读取配置文件,SDK与区块链节点建立连接,获取Web3j对象ApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");Serviceservice=context.getBean(Service.class);service.run();ChannelEthereumServicechannelEthereumService=newChannelEthereumService();channelEthereumService.setChannelService(service);Web3jweb3j=Web3j.build(channelEthereumService,service.getGroupId());StringprivateKey="b83261efa42895c38c6c2364ca878f43e77f3cddbc922bf57d0d48070f79feb6";//指定外部账户私钥,用于交易签名Credentialscredentials=GenCredential.create(privateKey);//获取SystemConfigService对象SystemConfigServicesystemConfigService=newSystemConfigService(web3j,credentials);//通过SystemConfigService对象调用API接口setValueByKeyStringresult=systemConfigService.setValueByKey("tx_count_limit","2000");//通过Web3j对象调用API接口getSystemConfigByKeyStringvalue=web3j.getSystemConfigByKey("tx_count_limit").send().getSystemConfigByKey();System.out.println(value);
创建并使用指定外部账户¶
SDK发送交易需要一个外部账户,下面是随机创建一个外部账户的方法。
//创建普通外部账户EncryptType.encryptType=0;//创建国密外部账户,向国密区块链节点发送交易需要使用国密外部账户// EncryptType.encryptType = 1;Credentialscredentials=GenCredential.create();//账户地址Stringaddress=credentials.getAddress();//账户私钥StringprivateKey=credentials.getEcKeyPair().getPrivateKey().toString(16);//账户公钥StringpublicKey=credentials.getEcKeyPair().getPublicKey().toString(16);
使用指定的外部账户
//通过指定外部账户私钥使用指定的外部账户Credentialscredentials=GenCredential.create(privateKey);
加载账户私钥文件¶
如果通过账户生成脚本get_accounts.sh生成了PEM或PKCS12格式的账户私钥文件(账户生成脚本的用法参考账户管理文档),则可以通过加载PEM或PKCS12账户私钥文件使用账户。加载私钥有两个类:P12Manager和PEMManager,其中,P12Manager用于加载PKCS12格式的私钥文件,PEMManager用于加载PEM格式的私钥文件。
P12Manager用法举例:在applicationContext.xml中配置PKCS12账户的私钥文件路径和密码
<beanid="p12"class="org.fisco.bcos.channel.client.P12Manager"init-method="load"><propertyname="password"value="123456"/><propertyname="p12File"value="classpath:0x0fc3c4bb89bd90299db4c62be0174c4966286c00.p12"/></bean>
开发代码
//加载BeanApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");P12Managerp12=context.getBean(P12Manager.class);//提供密码获取ECKeyPair,密码在生产p12账户文件时指定ECKeyPairp12KeyPair=p12.getECKeyPair(p12.getPassword());//以十六进制串输出私钥和公钥System.out.println("p12 privateKey: "+p12KeyPair.getPrivateKey().toString(16));System.out.println("p12 publicKey: "+p12KeyPair.getPublicKey().toString(16));//生成Web3SDK使用的CredentialsCredentialscredentials=GenCredential.create(p12KeyPair.getPrivateKey().toString(16));System.out.println("p12 Address: "+credentials.getAddress());
PEMManager使用举例
在applicationContext.xml中配置PEM账户的私钥文件路径
<beanid="pem"class="org.fisco.bcos.channel.client.PEMManager"init-method="load"><propertyname="pemFile"value="classpath:0x0fc3c4bb89bd90299db4c62be0174c4966286c00.pem"/></bean>
使用代码加载
//加载BeanApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext-keystore-sample.xml");PEMManagerpem=context.getBean(PEMManager.class);ECKeyPairpemKeyPair=pem.getECKeyPair();//以十六进制串输出私钥和公钥System.out.println("PEM privateKey: "+pemKeyPair.getPrivateKey().toString(16));System.out.println("PEM publicKey: "+pemKeyPair.getPublicKey().toString(16));//生成Web3SDK使用的CredentialsCredentialscredentialsPEM=GenCredential.create(pemKeyPair.getPrivateKey().toString(16));System.out.println("PEM Address: "+credentialsPEM.getAddress());
通过SDK部署并调用合约¶
部署并调用合约¶
SDK的核心功能是部署/加载合约,然后调用合约相关接口,实现相关业务功能。部署合约调用Java合约类的deploy方法,获取合约对象。通过合约对象可以调用getContractAddress方法获取部署合约的地址以及调用该合约的其他方法实现业务功能。如果合约已部署,则通过部署的合约地址可以调用load方法加载合约对象,然后调用该合约的相关方法。
//读取配置文件,SDK与区块链节点建立连接,获取web3j对象ApplicationContextcontext=newClassPathXmlApplicationContext("classpath:applicationContext.xml");Serviceservice=context.getBean(Service.class);service.run();ChannelEthereumServicechannelEthereumService=newChannelEthereumService();channelEthereumService.setChannelService(service);channelEthereumService.setTimeout(10000);Web3jweb3j=Web3j.build(channelEthereumService,service.getGroupId());//准备部署和调用合约的参数BigIntegergasPrice=newBigInteger("300000000");BigIntegergasLimit=newBigInteger("300000000");StringprivateKey="b83261efa42895c38c6c2364ca878f43e77f3cddbc922bf57d0d48070f79feb6";//指定外部账户私钥,用于交易签名Credentialscredentials=GenCredential.create(privateKey);//部署合约YourSmartContractcontract=YourSmartContract.deploy(web3j,credentials,newStaticGasProvider(gasPrice,gasLimit)).send();//根据合约地址加载合约//YourSmartContract contract = YourSmartContract.load(address, web3j, credentials, new StaticGasProvider(gasPrice, gasLimit));//调用合约方法发送交易TransactionReceipttransactionReceipt=contract.someMethod(<param1>,...).send();//查询合约方法查询该合约的数据状态Typeresult=contract.someMethod(<param1>,...).send();
Spring Boot项目开发指引¶
提供spring-boot-starter示例项目供参考。Spring Boot项目开发与Spring项目开发类似,其主要区别在于配置文件方式的差异。该示例项目提供相关的测试案例,具体描述参考示例项目的README文档。
Web3SDK API¶
Web3SDK API主要分为Web3j API和Precompiled Service API。其中Web3j API可以查询区块链相关的状态,发送和查询交易信息;Precompiled Service API可以管理区块链相关配置以及实现特定功能。
Precompiled Service API¶
预编译合约是FISCO BCOS底层通过C++实现的一种高效智能合约。SDK已提供预编译合约对应的Java接口,控制台通过调用这些Java接口实现了相关的操作命令,体验控制台,参考控制台手册。SDK提供Precompiled对应的Service类,分别是分布式控制权限相关的PermissionService,CNS相关的CnsService,系统属性配置相关的SystemConfigService和节点类型配置相关ConsensusService。相关错误码请参考:Precompiled Service API 错误码
PermissionService¶
SDK提供对分布式控制权限的支持,PermissionService可以配置权限信息,其API如下:
public String grantUserTableManager(String tableName, String address): 根据用户表名和外部账户地址设置权限信息。
public String revokeUserTableManager(String tableName, String address): 根据用户表名和外部账户地址去除权限信息。
public List<PermissionInfo> listUserTableManager(String tableName): 根据用户表名查询设置的权限记录列表(每条记录包含外部账户地址和生效块高)。
public String grantDeployAndCreateManager(String address): 增加外部账户地址的部署合约和创建用户表权限。
public String revokeDeployAndCreateManager(String address): 移除外部账户地址的部署合约和创建用户表权限。
public List<PermissionInfo> listDeployAndCreateManager(): 查询拥有部署合约和创建用户表权限的权限记录列表。
public String grantPermissionManager(String address): 增加外部账户地址的管理权限的权限。
public String revokePermissionManager(String address): 移除外部账户地址的管理权限的权限。
public List<PermissionInfo> listPermissionManager(): 查询拥有管理权限的权限记录列表。
public String grantNodeManager(String address): 增加外部账户地址的节点管理权限。
public String revokeNodeManager(String address): 移除外部账户地址的节点管理权限。
public List<PermissionInfo> listNodeManager(): 查询拥有节点管理的权限记录列表。
public String grantCNSManager(String address): 增加外部账户地址的使用CNS权限。
public String revokeCNSManager(String address): 移除外部账户地址的使用CNS权限。
public List<PermissionInfo> listCNSManager(): 查询拥有使用CNS的权限记录列表。
public String grantSysConfigManager(String address): 增加外部账户地址的系统参数管理权限。
public String revokeSysConfigManager(String address): 移除外部账户地址的系统参数管理权限。
public List<PermissionInfo> listSysConfigManager(): 查询拥有系统参数管理的权限记录列表。
CnsService¶
SDK提供对CNS的支持。CnsService可以配置CNS信息,其API如下:
String registerCns(String name, String version, String address, String abi): 根据合约名、合约版本号、合约地址和合约abi注册CNS信息。
String getAddressByContractNameAndVersion(String contractNameAndVersion): 根据合约名和合约版本号(合约名和合约版本号用英文冒号连接)查询合约地址。若缺失合约版本号,默认使用合约最新版本。
List<CnsInfo> queryCnsByName(String name): 根据合约名查询CNS信息。
List<CnsInfo> queryCnsByNameAndVersion(String name, String version): 根据合约名和合约版本号查询CNS信息。
SystemConfigService¶
SDK提供对系统配置的支持。SystemConfigService可以配置系统属性值(目前支持tx_count_limit和tx_gas_limit属性的设置),其API如下:
String setValueByKey(String key, String value): 根据键设置对应的值(查询键对应的值,参考Web3j API中的getSystemConfigByKey接口)。
ConsensusService¶
SDK提供对节点类型配置的支持。ConsensusService可以设置节点类型,其API如下:
String addSealer(String nodeId): 根据节点NodeID设置对应节点为共识节点。
String addObserver(String nodeId): 根据节点NodeID设置对应节点为观察节点。
String removeNode(String nodeId): 根据节点NodeID设置对应节点为游离节点。
CRUDService¶
SDK提供对CRUD(增删改查)操作的支持。CRUDService可以创建表,对表进行增删改查操作,其API如下:
int createTable(Table table): 创建表,提供表对象。表对象需要设置其表名,主键字段名和其他字段名。其中,其他字段名是以英文逗号分隔拼接的字符串。返回创建表的状态值,返回为0则代表创建成功。
int insert(Table table, Entry entry): 插入记录,提供表对象和Entry对象。表对象需要设置表名和主键值;Entry是map对象,提供插入的字段名和字段值。返回插入的记录数。
int update(Table table, Entry entry, Condition condition): 更新记录,提供表对象,Entry对象和Condtion对象。表对象需要设置表名和主键值;Entry是map对象,提供更新的字段名和字段值;Condition对象是条件对象,可以设置更新的匹配条件。返回更新的记录数。
List<Map<String, String>> select(Table table, Condition condition): 查询记录,提供表对象和Condtion对象。表对象需要设置表名和主键值;Condition对象是条件对象,可以设置查询的匹配条件。返回查询的记录。
int remove(Table table, Condition condition): 移除记录,提供表对象和Condtion对象。表对象需要设置表名和主键值;Condition对象是条件对象,可以设置移除的匹配条件。返回移除的记录数。
Table desc(String tableName): 根据表名查询表的信息,主要包含表的主键和其他属性字段。返回表类型,主要包含表的主键字段名和其他属性字段名。
交易解析¶
FISCO BCOS的交易是一段发往区块链系统的请求数据,用于部署合约,调用合约接口,维护合约的生命周期以及管理资产,进行价值交换等。当交易确认后会产生交易回执,交易回执和交易均保存在区块里,用于记录交易执行过程生成的信息,如结果码、日志、消耗的gas量等。用户可以使用交易哈希查询交易回执,判定交易是否完成。
交易回执包含三个关键字段,分别是input, output , logs:
| 字段 | 类型 | 描述 |
|---|---|---|
| input | String | 交易输入的ABI编码十六进制字符串 |
| output | String | 交易返回的ABI编码十六进制字符串 |
| logs | List | event log列表,保存交易的event信息 |
交易解析功能帮助用户解析这三个字段为json数据和java对象。
接口说明¶
代码包路径org.fisco.bcos.web3j.tx.txdecode,使用TransactionDecoderFactory工厂类建立交易解析对象TransactionDecoder,有两种方式:
TransactionDecoderbuildTransactionDecoder(Stringabi,Stringbin);abi:合约的ABI
bin:合约bin,暂无使用,可以直接传入空字符串””
TransactionDecoderbuildTransactionDecoder(StringcontractName);contractName:合约名称,在应用的根目录下创建
solidity目录,将交易相关的合约放在solidity目录,通过指定合约名获取交易解析对象
交易解析对象TransactionDecoder接口列表:
StringdecodeInputReturnJson(Stringinput)解析input,将结果封装为json字符串,json格式
{"data":[{"name":"","type":"","data":} ... ],"function":"","methodID":""}function : 函数签名字符串
methodID :函数选择器
InputAndOutputResultdecodeInputReturnObject(Stringinput)解析input,返回Object对象,InputAndOutputResult结构:
publicclassInputAndOutputResult{privateStringfunction;// 函数签名privateStringmethodID;// methodIDprivateList<ResultEntity>result;// 返回列表}publicclassResultEntity{privateStringname;// 字段名称, 解析output返回时,值为空字符串privateStringtype;// 字段类型privateObjectdata;// 字段值}
StringdecodeOutputReturnJson(Stringinput,Stringoutput)解析output,将结果封装为json字符串,格式同
decodeInputReturnJsonInputAndOutputResultdecodeOutputReturnObject(Stringinput,Stringoutput)解析output,返回java Object对象
StringdecodeEventReturnJson(List<Log>logList)解析event列表,将结果封装为json字符串,json格式
{"event1签名":[[{"name":"","type":"","data":}...]...],"event2签名":[[{"name":"","type":"","data":}...]...]...}Map<String,List<List<ResultEntity>>>decodeEventReturnObject(List<Log>logList)解析event列表,返回java Map对象,key为event签名字符串,
List<ResultEntity>为交易中单个event参数列表,List<List<ResultEntity>>表示单个交易可以包含多个event
TransactionDecoder对input,output和event logs均分别提供返回json字符串和java对象的方法。json字符串方便客户端处理数据,java对象方便服务端处理数据。
示例¶
以TxDecodeSample合约为例说明接口的使用:
pragma solidity^0.4.24;contractTxDecodeSample{eventEvent1(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs);eventEvent2(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs);functionecho(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs)publicconstantreturns(uint256,int256,bool,address,bytes32,string,bytes){Event1(_u,_i,_b,_addr,_bs32,_s,_bs);return(_u,_i,_b,_addr,_bs32,_s,_bs);}functiondo_event(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs)public{Event1(_u,_i,_b,_addr,_bs32,_s,_bs);Event2(_u,_i,_b,_addr,_bs32,_s,_bs);}}
使用buildTransactionDecoder 创建TxDecodeSample合约的解析对象:
// TxDecodeSample合约ABIStringabi="[{\"constant\":false,\"inputs\":[{\"name\":\"_u\",\"type\":\"uint256\"},{\"name\":\"_i\",\"type\":\"int256\"},{\"name\":\"_b\",\"type\":\"bool\"},{\"name\":\"_addr\",\"type\":\"address\"},{\"name\":\"_bs32\",\"type\":\"bytes32\"},{\"name\":\"_s\",\"type\":\"string\"},{\"name\":\"_bs\",\"type\":\"bytes\"}],\"name\":\"do_event\",\"outputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"_u\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"_i\",\"type\":\"int256\"},{\"indexed\":false,\"name\":\"_b\",\"type\":\"bool\"},{\"indexed\":false,\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_bs32\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"_s\",\"type\":\"string\"},{\"indexed\":false,\"name\":\"_bs\",\"type\":\"bytes\"}],\"name\":\"Event1\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"name\":\"_u\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"_i\",\"type\":\"int256\"},{\"indexed\":false,\"name\":\"_b\",\"type\":\"bool\"},{\"indexed\":false,\"name\":\"_addr\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"_bs32\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"_s\",\"type\":\"string\"},{\"indexed\":false,\"name\":\"_bs\",\"type\":\"bytes\"}],\"name\":\"Event2\",\"type\":\"event\"},{\"constant\":true,\"inputs\":[{\"name\":\"_u\",\"type\":\"uint256\"},{\"name\":\"_i\",\"type\":\"int256\"},{\"name\":\"_b\",\"type\":\"bool\"},{\"name\":\"_addr\",\"type\":\"address\"},{\"name\":\"_bs32\",\"type\":\"bytes32\"},{\"name\":\"_s\",\"type\":\"string\"},{\"name\":\"_bs\",\"type\":\"bytes\"}],\"name\":\"echo\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"},{\"name\":\"\",\"type\":\"int256\"},{\"name\":\"\",\"type\":\"bool\"},{\"name\":\"\",\"type\":\"address\"},{\"name\":\"\",\"type\":\"bytes32\"},{\"name\":\"\",\"type\":\"string\"},{\"name\":\"\",\"type\":\"bytes\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"}]";Stringbin="";TransactionDecodertxDecodeSampleDecoder=TransactionDecoderFactory.buildTransactionDecoder(abi,bin);
解析input¶
调用functionecho(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs) 接口,输入参数为[111111-1111111false0x692a70d2e424a56d2c6c27aa97d1a86395877b3aabcdefghiabcdefghiabcdefghiabhjiFISCO-BCOSnice]
// function echo(uint256 _u,int256 _i,bool _b,address _addr,bytes32 _bs32, string _s,bytes _bs)Stringinput="0x406d373b000000000000000000000000000000000000000000000000000000000001b207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef0bb90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000692a70d2e424a56d2c6c27aa97d1a86395877b3a6162636465666768696162636465666768696162636465666768696162686a6900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000021e7aba0e9b1bce5b08fe4b8b8e5ad906c6a6a6b6c3b61646a73666b6c6a6c6b6a6c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d736164666c6a6b6a6b6c6a6b6c00000000000000000000000000000000000000";StringjsonResult=txDecodeSampleDecoder.decodeInputReturnJson(input);InputAndOutputResultobjectResult=txDecodeSampleDecoder.decodeInputReturnObject(input);System.out.println("json => \n"+jsonResult);System.out.println("object => \n"+objectResult);
输出:
json=>{"function":"echo(uint256,int256,bool,address,bytes32,string,bytes)","methodID":"0x406d373b","result":[{"name":"_u","type":"uint256","data":111111},{"name":"_i","type":"int256","data":-1111111},{"name":"_b","type":"bool","data":false},{"name":"_addr","type":"address","data":"0x692a70d2e424a56d2c6c27aa97d1a86395877b3a"},{"name":"_bs32","type":"bytes32","data":"abcdefghiabcdefghiabcdefghiabhji"},{"name":"_s","type":"string","data":"FISCO-BCOS"},{"name":"_bs","type":"bytes","data":"nice"}]}object=>InputAndOutputResult[function=echo(uint256,int256,bool,address,bytes32,string,bytes),methodID=0x406d373b,result=[ResultEntity[name=_u,type=uint256,data=111111],ResultEntity[name=_i,type=int256,data=-1111111],ResultEntity[name=_b,type=bool,data=false],ResultEntity[name=_addr,type=address,data=0x692a70d2e424a56d2c6c27aa97d1a86395877b3a],ResultEntity[name=_bs32,type=bytes32,data=abcdefghiabcdefghiabcdefghiabhji],ResultEntity[name=_s,type=string,data=FISCO-BCOS],ResultEntity[name=_bs,type=bytes,data=nice]]]
解析output¶
调用functionecho(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs) 接口,输入参数为[111111-1111111false0x692a70d2e424a56d2c6c27aa97d1a86395877b3aabcdefghiabcdefghiabcdefghiabhjiFISCO-BCOSnice],echo接口直接将输入返回,因此返回与输入相同
// function echo(uint256 _u,int256 _i,bool _b,address _addr,bytes32 _bs32, string _s,bytes _bs) public constant returns (uint256,int256,bool,address,bytes32,string,bytes)String input = “0x406d373b000000000000000000000000000000000000000000000000000000000001b207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef0bb90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000692a70d2e424a56d2c6c27aa97d1a86395877b3a6162636465666768696162636465666768696162636465666768696162686a6900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000021e7aba0e9b1bce5b08fe4b8b8e5ad906c6a6a6b6c3b61646a73666b6c6a6c6b6a6c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d736164666c6a6b6a6b6c6a6b6c00000000000000000000000000000000000000”;String output = "“0x000000000000000000000000000000000000000000000000000000000001b207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffef0bb90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000692a70d2e424a56d2c6c27aa97d1a86395877b3a6162636465666768696162636465666768696162636465666768696162686a6900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001400000000000000000000000000000000000000000000000000000000000000021e7aba0e9b1bce5b08fe4b8b8e5ad906c6a6a6b6c3b61646a73666b6c6a6c6b6a6c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d736164666c6a6b6a6b6c6a6b6c00000000000000000000000000000000000000";String jsonResult = txDecodeSampleDecoder.decodeOutputReturnJson(input, output);InputAndOutputResult objectResult = txDecodeSampleDecoder.decodeOutputReturnObject(input, output);System.out.println("json => \n" + jsonResult);System.out.println("object => \n" + objectResult);结果:
json=>{"function":"echo(uint256,int256,bool,address,bytes32,string,bytes)","methodID":"0x406d373b","result":[{"name":"","type":"uint256","data":111111},{"name":"","type":"int256","data":-1111111},{"name":"","type":"bool","data":false},{"name":"","type":"address","data":"0x692a70d2e424a56d2c6c27aa97d1a86395877b3a"},{"name":"","type":"bytes32","data":"abcdefghiabcdefghiabcdefghiabhji"},{"name":"","type":"string","data":"FISCO-BCOS"},{"name":"","type":"bytes","data":"nice"}]}object=>InputAndOutputResult[function=echo(uint256,int256,bool,address,bytes32,string,bytes),methodID=0x406d373b,result=[ResultEntity[name=,type=uint256,data=111111],ResultEntity[name=,type=int256,data=-1111111],ResultEntity[name=,type=bool,data=false],ResultEntity[name=,type=address,data=0x692a70d2e424a56d2c6c27aa97d1a86395877b3a],ResultEntity[name=,type=bytes32,data=abcdefghiabcdefghiabcdefghiabhji],ResultEntity[name=,type=string,data=FISCO-BCOS],ResultEntity[name=,type=bytes,data=nice]]]
解析event logs¶
调用functiondo_event(uint256_u,int256_i,bool_b,address_addr,bytes32_bs32,string_s,bytes_bs) 接口,输入参数为[111111-1111111false0x692a70d2e424a56d2c6c27aa97d1a86395877b3aabcdefghiabcdefghiabcdefghiabhjiFISCO-BCOSnice],解析交易中的logs
// transactionReceipt为调用do_event接口的交易回执StringjsonResult=txDecodeSampleDecoder.decodeEventReturnJson(transactionReceipt.getLogs());StringmapResult=txDecodeSampleDecoder.decodeEventReturnJson(transactionReceipt.getLogs());System.out.println("json => \n"+jsonResult);System.out.println("map => \n"+mapResult);
结果:
json=>{"Event1(uint256,int256,bool,address,bytes32,string,bytes)":[[{"name":"_u","type":"uint256","data":111111,"indexed":false},{"name":"_i","type":"int256","data":-1111111,"indexed":false},{"name":"_b","type":"bool","data":false,"indexed":false},{"name":"_addr","type":"address","data":"0x692a70d2e424a56d2c6c27aa97d1a86395877b3a","indexed":false},{"name":"_bs32","type":"bytes32","data":"abcdefghiabcdefghiabcdefghiabhji","indexed":false},{"name":"_s","type":"string","data":"Fisco Bcos","indexed":false},{"name":"_bs","type":"bytes","data":"sadfljkjkljkl","indexed":false}]],"Event2(uint256,int256,bool,address,bytes32,string,bytes)":[[{"name":"_u","type":"uint256","data":111111,"indexed":false},{"name":"_i","type":"int256","data":-1111111,"indexed":false},{"name":"_b","type":"bool","data":false,"indexed":false},{"name":"_addr","type":"address","data":"0x692a70d2e424a56d2c6c27aa97d1a86395877b3a","indexed":false},{"name":"_bs32","type":"bytes32","data":"abcdefghiabcdefghiabcdefghiabhji","indexed":false},{"name":"_s","type":"string","data":"FISCO-BCOS","indexed":false},{"name":"_bs","type":"bytes","data":"nice","indexed":false}]]}map=>{Event1(uint256,int256,bool,address,bytes32,string,bytes)=[[ResultEntity[name=_u,type=uint256,data=111111],ResultEntity[name=_i,type=int256,data=-1111111],ResultEntity[name=_b,type=bool,data=false],ResultEntity[name=_addr,type=address,data=0x692a70d2e424a56d2c6c27aa97d1a86395877b3a],ResultEntity[name=_bs32,type=bytes32,data=abcdefghiabcdefghiabcdefghiabhji],ResultEntity[name=_s,type=string,data=FISCO-BCOS],ResultEntity[name=_bs,type=bytes,data=nice]]],Event2(uint256,int256,bool,address,bytes32,string,bytes)=[[ResultEntity[name=_u,type=uint256,data=111111],ResultEntity[name=_i,type=int256,data=-1111111],ResultEntity[name=_b,type=bool,data=false],ResultEntity[name=_addr,type=address,data=0x692a70d2e424a56d2c6c27aa97d1a86395877b3a],ResultEntity[name=_bs32,type=bytes32,data=abcdefghiabcdefghiabcdefghiabhji],ResultEntity[name=_s,type=string,data=FISCO-BCOS],ResultEntity[name=_bs,type=bytes,data=nices]]]}
合约事件推送¶
功能简介¶
合约事件推送功能提供了合约事件的异步推送机制,客户端向节点发送注册请求,在请求中携带客户端关注的合约事件的参数,节点根据请求参数对请求区块范围的EventLog进行过滤,将结果分次推送给客户端。
交互协议¶
客户端与节点的交互基于Channel协议。交互分为三个阶段:注册请求,节点回复,Event Log数据推送。
注册请求¶
客户端向节点发送Event推送的注册请求:
// request sample:{"fromBlock":"latest","toBlock":"latest","addresses":["0xca5ed56862869c25da0bdf186e634aac6c6361ee"],"topics":["0x91c95f04198617c60eaf2180fbca88fc192db379657df0e412a9f7dd4ebbe95d"],"groupID":"1","filterID":"bb31e4ec086c48e18f21cb994e2e5967"}
filerID:字符串类型,每次请求唯一,标记一次注册任务
groupID:字符串类型,群组ID
fromBlock:整形字符串,初始区块。“latest” 当前块高
toBlock:整形字符串,最终区块。“latest” 处理至当前块高时,继续等待新区块
addresses:字符串或者字符串数组:字符串表示单个合约地址,数组为多个合约地址,数组可以为空
topics:字符串类型或者数组类型:字符串表示单个topic,数组为多个topic,数组可以为空
节点回复¶
节点接受客户端注册请求时,会对请求参数进行校验,将是否成功接受该注册请求结果回复给客户端。
// response sample:{"filterID":"bb31e4ec086c48e18f21cb994e2e5967","result":0}
filterID:字符串类型,每次请求唯一,标记一次注册任务
result:整形,返回结果。0成功,其余为失败状态码
Event Log数据推送¶
节点验证客户端注册请求成功之后,根据客户端请求参数条件,向客户端推送Event的Log数据。
// event log push sample:{"filterID":"bb31e4ec086c48e18f21cb994e2e5967","result":0,"logs":[]}
filterID:字符串类型,每次请求唯一,标记一次注册任务
result:整形 0:
EventLog数据推送 1:推送完成。客户端一次注册请求对应节点的数据推送会有多次(请求区块范围比较大或者等待新的区块),result字段为1时说明节点推送已经结束logs:Log对象数组,result为0时有效
Java SDK教程¶
注册接口¶
Java SDK中org.fisco.bcos.channel.client.Service类提供合约事件的注册接口,用户可以调用registerEventLogFilter向节点发送注册请求,并设置回调函数。
publicvoidregisterEventLogFilter(EventLogUserParamsparams,EventLogPushCallbackcallback);
params注册参数¶
事件回调请求注册的参数:
publicclassEventLogUserParams{privateStringfromBlock;privateStringtoBlock;privateList<String>addresses;privateList<Object>topics;}
callback回调对象¶
publicabstractclassEventLogPushCallback{publicvoidonPushEventLog(intstatus,List<LogResult>logs);}
status回调返回状态:
0 : 正常推送,此时logs为节点推送的Event日志 1 : 推送完成,执行区间的区块都已经处理 -41000 : 参数无效,客户端验证参数错误返回 -41001 : 参数错误,节点验证参数错误返回 -41002 : 群组不存在 -41003 : 请求错误的区块区间 -41004 : 节点推送数据格式错误 -41005 : 请求发送超时 -41006 : 其他错误
logs表示回调的EventLog对象列表,status为0有效
publicclassLogResult{privateList<EventResultEntity>logParams;privateLoglog;}// Log对象publicclassLog{privateStringlogIndex;privateStringtransactionIndex;privateStringtransactionHash;privateStringblockHash;privateStringblockNumber;privateStringaddress;privateStringdata;privateStringtype;privateList<String>topics;}
Loglog:Log对象
List<EventResultEntity>logParams:默认值null,可以在子类中解析Log的data字段,将结果保存入logParams[参考交易解析]
实现回调对象
Java SDK默认实现的回调类ServiceEventLogPushCallback,将status与logs在日志中打印,用户可以通过继承ServiceEventLogPushCallback类,重写onPushEventLog接口,实现自己的回调逻辑处理。
classMyEventLogPushCallBackextendsServiceEventLogPushCallback{@OverridepublicvoidonPushEventLog(intstatus,List<LogResult>logs){// ADD CODE}}
注意:onPushEventLog接口多次回调的logs有重复的可能性,可以根据Log对象中的blockNumber,transactionIndex,logIndex进行去重
topic工具¶
org.fisco.bcos.channel.event.filter.TopicTools提供将各种类型参数转换为对应topic的工具,用户设置EventLogUserParams的topics参数可以使用。
classTopicTools{// int1/uint1~uint1/uint256publicstaticStringintegerToTopic(BigIntegeri)// boolpublicstaticStringboolToTopic(booleanb)// addresspublicstaticStringaddressToTopic(Strings)// stringpublicstaticStringstringToTopic(Strings)// bytespublicstaticStringbytesToTopic(byte[]b)// byte1~byte32publicstaticStringbyteNToTopic(byte[]b)}
Solidity To Java¶
为了简化使用,solidity合约生成对应的Java合约代码时,为每个Event生成两个重载的同名接口,接口命名规则:register + Event名称 +EventLogFilter。
这里以Asset合约的TransferEvent为例说明
contractAsset{eventTransferEvent(int256ret,stringindexedfrom_account,stringindexedto_account,uint256indexedamount)functiontransfer(stringfrom_account,stringto_account,uint256amount)publicreturns(int256){// 结果intresult=0;// 其他逻辑,省略// TransferEvent 保存结果以及接口参数TransferEvent(result,from_account,to_account,amount);}}
将Asset.sol生成对应Java合约文件[将solidity合约生成对应的Java调用文件]
classAsset{// 其他生成代码 省略publicvoidregisterTransferEventEventLogFilter(EventLogPushWithDecodeCallbackcallback);publicvoidregisterTransferEventEventLogFilter(StringfromBlock,StringtoBlock,List<String>otherTopics,EventLogPushWithDecodeCallbackcallback);}
registerTransferEventEventLogFilter¶
这两个接口对org.fisco.bcos.channel.client.Service.registerEventLogFilter进行了封装,调用等价于将registerEventLogFilter的params参数设置为:
EventLogUserParamsparams=newEventLogUserParams();// fromBlock, 无参数设置为“latest”params.setFromBlock(fromBlock);// params.setFromBlock("latest");// toBlock, 无参数设置为“latest”params.setToBlock(toBlock);// params.setToBlock("latest");// addresses,设置为Java合约对象的地址// 当前java合约对象为:Asset assetArrayList<String>addresses=newArrayList<String>();addresses.add(asset.getContractedAddress());params.setAddresses(addresses);// topics, topic0设置为Event接口对应的topicArrayList<Object>topics=newArrayList<>();topics.add(TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)"));// 其他topic设置, 没有则忽略topics.addAll(otherTopics);
可以看出,在关注指定地址特定合约的某个Event,使用生成的Java合约对象中的接口,更加简单方便。
EventLogPushWithDecodeCallback¶
EventLogPushWithDecodeCallback与ServiceEventLogPushCallback相同,是EventLogPushCallback的子类,区别在于:
ServiceEventLogPushCallback回调接口onPushEventLog(intstatus,List<LogResult>logs)LogResult成员logParams为空,用户需要使用Log数据时需要解析数据EventLogPushWithDecodeCallback作为Asset对象的成员,可以根据其保存的ABI成员构造对应Event的解析工具,解析返回的Log数据,解析结果保存在logParams中。
示例¶
这里以Asset合约为例,给出合约事件推送的一些场景供用户参考。
场景1:将链上所有/最新的Event回调至客户端
// 其他初始化逻辑,省略// 参数设置EventLogUserParamsparams=newEventLogUserParams();// 全部Event fromBlock设置为"1"params.setFromBlock("1");// 最新Event fromBlock设置为"latest"// params.setFromBlock("latest");// toBlock设置为"latest",处理至最新区块继续等待新的区块params.setToBlock("latest");// addresses设置为空数组,匹配所有的合约地址params.setAddresses(newArrayList<String>());// topics设置为空数组,匹配所有的Eventparams.setTopics(newArrayList<Object>());// 回调,用户可以替换为自己实现的类的回调对象ServiceEventLogPushCallbackcallback=newServiceEventLogPushCallback();service.registerEventLogFilter(params,callback);
场景2: 将
Asset合约最新的TransferEvent事件回调至客户端
// 其他初始化逻辑,省略// 设置参数EventLogUserParamsparams=newEventLogUserParams();// 从最新区块开始,fromBlock设置为"latest"params.setFromBlock("latest");// toBlock设置为"latest",处理至最新区块继续等待新的区块params.setToBlock("latest");// addresses设置为空数组,匹配所有的合约地址params.setAddresses(newArrayList<String>());// topic0,TransferEvent(int256,string,string,uint256)ArrayList<Object>topics=newArrayList<>();topics.add(TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)"));params.setTopics(topics);// 回调,用户可以替换为自己实现的类的回调对象ServiceEventLogPushCallbackcallback=newServiceEventLogPushCallback();service.registerEventLogFilter(params,callback);
场景3: 将指定地址的
Asset合约最新的TransferEvent事件回调至客户端
合约地址:Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324";
方案1.
// 其他初始化逻辑,省略Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324";// 设置参数EventLogUserParamsparams=newEventLogUserParams();// 从最新区块开始,fromBlock设置为"latest"params.setFromBlock("latest");// toBlock设置为"latest",处理至最新块并继续等待共识出块params.setToBlock("latest");// 合约地址ArrayList<String>addresses=newArrayList<String>();addresses.add(addr);params.setAddresses(addresses);// topic0,匹配 TransferEvent(int256,string,string,uint256) 事件ArrayList<Object>topics=newArrayList<>();topics.add(TopicTools.stringToTopic("TransferEvent(int256,string,uint256)"));params.setTopics(topics);ServiceEventLogPushCallbackcallback=newServiceEventLogPushCallback();service.registerEventLogFilter(params,callback);
方案2.
// 其他初始化逻辑,省略Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324";// 构造Asset合约对象Assetasset=Asset.load(addr,...);EventLogPushWithDecodeCallbackcallback=newEventLogPushWithDecodeCallback();asset.registerTransferEventEventLogFilter(callback);
场景4: 将指定地址的
Asset合约所有TransferEvent事件回调至客户端
合约地址:Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324";
方案1:
// 其他初始化逻辑,省略// 设置参数EventLogUserParamsparams=newEventLogUserParams();// 从最初区块开始,fromBlock设置为"1"params.setFromBlock("1");// toBlock设置为"latest",处理至最新块并继续等待共识出块params.setToBlock("latest");// 设置合约地址ArrayList<String>addresses=newArrayList<String>();addresses.add(addr);params.setAddresses(addresses);// TransferEvent(int256,string,string,uint256) 转换为topicArrayList<Object>topics=newArrayList<>();topics.add(TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)"));params.setTopics(topics);ServiceEventLogPushCallbackcallback=newServiceEventLogPushCallback();service.registerEventLogFilter(params,callback);
方案2.
// 其他初始化逻辑,省略Assetasset=Asset.load(addr,...);// 设置区块范围StringfromBlock="1";StringtoBlock="latest";// 参数topic为空ArrayList<Object>otherTopics=newArrayList<>();EventLogPushWithDecodeCallbackcallback=newEventLogPushWithDecodeCallback();asset.registerTransferEventEventLogFilter(fromBlock,toBlock,otherTopics,callback);
场景5: 将
Asset指定合约指定账户转账的所有事件回调至客户端
合约地址:Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324"
转账账户:StringfromAccount="account"
方案1:
// 其他初始化逻辑,省略Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324";StringfromAccount="account";// 参数EventLogUserParamsparams=newEventLogUserParams();// 从最初区块开始,fromBlock设置为"1"params.setFromBlock("1");// toBlock设置为"latest"params.setToBlock("latest");// 设置合约地址ArrayList<String>addresses=newArrayList<String>();addresses.add(addr);params.setAddresses(addresses);// 设置topicArrayList<Object>topics=newArrayList<>();// TransferEvent(int256,string,string,uint256) 转换为topictopics.add(TopicTools.stringToTopic("TransferEvent(int256,string,string,uint256)"));// 转账账户 fromAccount转换为topictopics.add(TopicTools.stringToTopic(fromAccount));params.setTopics(topics);ServiceEventLogPushCallbackcallback=newServiceEventLogPushCallback();service.registerEventLogFilter(params,callback);
方案2.
// 其他初始化逻辑,省略Stringaddr="0x06922a844c542df030a2a2be8f835892db99f324";StringfromAccount="account";// 加载合约地址,生成Java合约对象Assetasset=Asset.load(addr,...);// 回调函数EventLogPushWithDecodeCallbackcallback=newEventLogPushWithDecodeCallback();// 设置区块范围StringfromBlock="1";StringtoBlock="latest";// 参数topicArrayList<Object>otherTopics=newArrayList<>();// 转账账户 fromAccount转换为topicotherTopics.add(TopicTools.stringToTopic(fromAccount));asset.registerRegisterEventEventLogFilter(fromBlock,toBlock,otherTopics,callback);
附录:JavaSDK启动异常场景¶
Failed to connect to the node. Please check the node status and the console configuration.
比较旧的SDK版本的提示,建议将JavaSDK版本升级至2.2.2或者以上(修改gradle.build或者maven配置文件中web3sdk的版本号),可以获取更准确友好的提示,然后参考下面的错误提示解决问题。Failed to initialize the SSLContext: class path resource [ca.crt] cannot be opened because it does not exist.
无法加载到证书文件,证书文件没有正确拷贝至conf目录,可以参考控制台安装流程,拷贝证书文件至conf目录下。Failed to initialize the SSLContext: Input stream not contain valid certificates.
加载证书文件失败,CentOS系统使用OpenJDK的错误,参考CentOS环境安装JDK章节重新安装OracleJDK。Failed to connect to nodes: [connection timed out: /127.0.0.1:20200]
连接超时,节点的网络不可达,请检查提示的IP是否配置错误,或者,当前JavaSDK运行环境与节点的环境网络确实不通,可以咨询运维人员解决网络不通的问题。Failed to connect to nodes: [Connection refused: /127.0.0.1:20200]
拒绝连接,无法连接对端的端口,可以使用telnet命令检查端口是否连通,可能原因:节点未启动,端口处于未监听状态,启动节点即可。
节点监听
127.0.0.1的网段,监听127.0.0.1网络只能本机的客户端才可以连接,控制台位于不同服务器时无法连接节点,将节点配置文件config.ini中的channel_listen_ip修改为控制台连接节点使用的网段IP,或者将其修改为0.0.0.0。错误的端口配置,配置的端口并不是节点监听的channel端口,修改连接端口为节点
config.ini配置的channel_listen_port的值。
注意:控制台(或者JavaSDK)连接节点时使用Channel端口,并不是RPC端口,Channel端口在节点配置文件中通过channel_listen_ip字段配置,RPC端口通过jsonrpc_listen_port字段配置,注意区分,RPC默认从8545开始分配, Channel端口默认从20200开始分配。
Failed to connect to nodes: [ ssl handshake failed:/127.0.0.1:20233]与节点ssl握手失败,可能原因:
拷贝了错误的证书,检查拷贝的证书是否正确。
端口配置错误,连接其他服务正在监听的端口,检查连接端口是否为节点
channel_listen_port端口。JDK版本问题,推荐使用1.8以及以上的OracleJDK,参考CentOS环境安装JDK章节安装OracleJDK。
Failed to connect to [127.0.0.1:20233, 127.0.0.1:20234, 127.0.0.1:20235] ,groupId: 1 ,caCert: classpath:ca.crt ,sslKey: classpath:sdk.key ,sslCrt: classpath:sdk.crt ,java version: 1.8.0_231.
其他未知的错误,需要查看日志文件分析具体错误。