com
└── rcjava
├── client
├── contract
├── exception
├── gm
├── protos
├── sign
├── sync
├── tran
├── util
└── ws
com.rcjava.client
主要用来构造与RepChain交互的客户端,客户端可以用来提交签名交易、获取交易或块数据、订阅出块事件
com.rcjava.contract
主要用来构造与RepChain交互的客户端,客户端可以用来部署升级合约、修改合约状态、调用合约
com.rcjava.exception
自定义的一些异常
com.rcjava.gm
定义了GMProvider
com.rcjava.protos
protoBuf generated messages
com.rcjava.sign
主要是加密相关的工具类,包括签名与Hash等
com.rcjava.tran
用来构建签名交易
com.rcjava.sync
用来同步区块数据
com.rcjava.tran
用来构建签名交易
com.rcjava.util
主要是封装了一些工具类
- CertUtil 证书相关工具类
- GmUtil 国密相关工具类
- KeyUtil key操作相关的工具类
- PemUtil Pem操作相关的工具类
com.rcjava.ws
利用websocket订阅块事件
项目基于MAVEN构建,需搭建好MAVEN环境
jdk1.8+
推荐使用Zulu-Jdk
下载项目
mvn clean install
打包rcjava-core为jar包,并install到本地maven仓库
其他项目就可以通过如下方式使用rcjava-core了
<dependency> <groupId>repchain</groupId> <artifactId>rcjava-core</artifactId> <version>2.1.0</version> </dependency>
step1:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
step2:
<dependency>
<groupId>com.gitee.BTAJL</groupId>
<artifactId>RCJava-core</artifactId>
<version>2.1.0-SNAPSHOT</version>
</dependency>
搭建好RepChain
推荐使用secp256r1
生成密钥对(当然也可以使用其他curves)
// 打印出Jdk可支持的曲线 // --add-exports=java.base/sun.security.util=ALL-UNNAMED public static void printSunCurves() { Collection curves = CurveDB.getSupportedCurves(); curves.forEach(System.out::println); }
注册账户证书
/src/test/java/com/rcjava/did/SignerOperationTest.java
构建签名交易
交易的签名算法根据对应RepChain版本进行设置
RepChain-V1.1.1
及之前版本使用SHA1withECDSA
,之后使用SHA256withECDSA
使用tran/impl下的具体类
InvokeTran
// 标识账户证书
Peer.CertId certId = Peer.CertId.newBuilder()
// 调用者账户号
.setCreditCode("identity-net:121000005l35120456")
// 证书标识,对应于先前注册的证书名
.setCertName("node1")
.build(); // 签名ID
// 这个是给转账交易示范用的,此ID需要与repchain合约部署的一致
Peer.ChaincodeId contractAssetsId = Peer.ChaincodeId.newBuilder()
// 合约名
.setChaincodeName("ContractAssetsTPL")
// 合约版本号
.setVersion(1)
.build();
Transfer transfer = new Transfer("identity-net:121000005l35120456", "identity-net:12110107bi45jh675g", 5);
// 合约方法参数
Peer.ChaincodeInput chaincodeInput = Peer.ChaincodeInput.newBuilder()
// 合约方法
.setFunction("transfer")
// 合约方法实参
.addArgs(JSON.toJSONString(transfer))
.build();
InvokeTran invokeTran = InvokeTran.newBuilder()
.setTxid("1234567890")
.setChaincodeInput(chaincodeInput)
.setCertId(certId)
.setChaincodeId(contractAssetsId)
.setPrivateKey(privateKey)
.setSignAlgorithm("SHA256withECDSA")
.build();
Peer.Transaction transaction = invokeTran.getSignedTran();
// 其他方式:
// Peer.Transaction transaction_2 = invokeTran.getSignedTran(privateKey, "sha256withecdsa");
// Peer.Transaction transaction_4 = RCTranSigner.getSignedTran(invokeTran, privateKey, "sha256withecdsa");
CidStateTran
参考InvokeTran的构建
DeployTran
参考InvokeTran的构建
使用TranCreator构建具体的交易
构建InvokeTran
// 标识账户证书
Peer.CertId certId = Peer.CertId.newBuilder()
.setCreditCode("identity-net:121000005l35120456")
.setCertName("node1")
.build(); // 签名ID
// 这个是给转账交易示范用的,此ID需要与repchain合约部署的一致
Peer.ChaincodeId contractAssetsId = Peer.ChaincodeId.newBuilder()
.setChaincodeName("ContractAssetsTPL")
.setVersion(1)
.build();
Transfer transfer = new Transfer("identity-net:121000005l35120456", "identity-net:12110107bi45jh675g", 5);
// 合约方法参数
Peer.ChaincodeInput chaincodeInput = Peer.ChaincodeInput.newBuilder()
.setFunction("transfer")
.addArgs(JSON.toJSONString(transfer))
.build();
TranCreator tranCreator = TranCreator.newBuilder()
.setPrivateKey(privateKey)
.setSignAlgorithm("SHA256withECDSA")
.build();
String tranId = "UUID";
Peer.Transaction tran = tranCreator.createInvokeTran(tranId, certId, contractAssetsId, "transfer", JSON.toJSONString(transfer), 0, "");
CidStateTran
参考InvokeTran的构建
DeployTran
参考InvokeTran的构建
提交签名交易
使用同步方式
用来提交签名交易的客户端:TranPostClient
public JSONObject postSignedTran(String tranHexString) {/***/} public JSONObject postSignedTran(Transaction tran) {/***/}
使用Hex字符串的方式
TranPostClient tranPostClient = new TranPostClient("localhost:9081");
// -------------------------------
/****构建签名交易 Transaction tran ****/
// -------------------------------
String tranHex = Hex.encodeHexString(tran.toByteArray());
JSONObject res = tranPostClient.postSignedTran(tranHex);
使用字节块提交
TranPostClient tranPostClient = new TranPostClient("localhost:9081");
// -------------------------------
/****构建签名交易 Transaction tran ****/
// -------------------------------
JSONObject res = tranPostClient.postSignedTran(tran);
如果RepChain端开启了https单向或双向认证,则需要使用如下方式构建TranPostClient
SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), new TrustSelfSignedStrategy()) .loadKeyMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), "123".toCharArray()) .build(); // sslContext可根据具体情况来针对性的构建 TranPostClient tranPostClient = new TranPostClient("localhost:9081", sslContext);
使用异步方式
用来异步提交签名交易的客户端:TranPostAsyncClient
public Future<HttpResponse> postSignedTran(String tranHexString) {/***/} public Future<HttpResponse> postSignedTran(Transaction tran) {/***/}
使用Hex字符串的方式
TranPostAsyncClient tranPostClient = new TranPostAsyncClient("localhost:9081");
// -------------------------------
/****构建签名交易 Transaction tran ****/
// -------------------------------
String tranHex = Hex.encodeHexString(tran.toByteArray());
Future<HttpResponse> responseFuture = tranPostClient.postSignedTran(tranHex);
使用字节块提交
TranPostAsyncClient tranPostClient = new TranPostAsyncClient("localhost:9081");
// -------------------------------
/****构建签名交易 Transaction tran ****/
// -------------------------------
Future<HttpResponse> responseFuture = tranPostClient.postSignedTran(tran);
从future中解析数据,默认超时时间20s
HttpResponse httpResponse = responseFuture.get(20, TimeUnit.SECONDS); JSONObject result = TranPostAsyncClient.resolveHttpResponseFuture(responseFuture);
如果RepChain端开启了https单向或双向认证,则需要使用如下方式构建TranPostAsyncClient
SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), new TrustSelfSignedStrategy()) .loadKeyMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), "123".toCharArray()) .build(); // sslContext可根据具体情况来针对性的构建 TranPostAsyncClient tranPostClient = new TranPostAsyncClient("localhost:9081", sslContext);
使用ContractClient
使用ContractClient部署升级合约、修改合约状态、调用合约,可选用具体方法,下面例子中的方法只是其中之一
CertId certId = CertId.newBuilder().setCreditCode("identity-net:121000005l35120456").setCertName("node1").build();
// 这个是给转账交易示范用的,此ID需要与RepChain合约部署的一致
ChaincodeId contractAssetsId = ChaincodeId.newBuilder().setChaincodeName("ContractAssetsTPL").setVersion(1).build();
// privateKey是与certId标识的证书对应的用户私钥
ContractUser user = new ContracUser(certId, privateKey);
ContractClient contractClient = new ContractClient("localhost:9081", contractAssetsId, user);
Transfer transfer = new Transfer("identity-net:121000005l35120456", "identity-net:12110107bi45jh675g", 5);
contractClient.invokeContract("transfer", JSON.toJSONString(transfer));
如果RepChain端开启了https单向或双向认证,则需要使用如下方式构建ContractClient
SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), new TrustSelfSignedStrategy()) .loadKeyMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), "123".toCharArray()) .build(); ...... ContractClient client = new ContractClient(host, contractAssetsId, user, sslContext);
查询交易数据
使用ChainInfoClient构建查询客户端,用来获取链信息的客户端
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
String txid = "1234567890";
// 底层使用Json构建
Transaction tran = chainInfoClient.getTranByTranId(txid);
// 底层直接获取字节块构建
Transaction tran = chainInfoClient.getTranStreamByTranId(txid);
如果RepChain端开启了https单向或双向认证,则需要使用如下方式构建ChaininfoClient
SSLContext sslContext = SSLContexts.custom() .loadTrustMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), new TrustSelfSignedStrategy()) .loadKeyMaterial(new File("jks/jdk13/121000005l35120456.node1.jks"), "123".toCharArray(), "123".toCharArray()) .build(); // sslContext可根据具体情况来针对性的构建 ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081", sslContext);
查询块数据
使用ChainInfoClient构建查询客户端
根据区块高度查询区块数据
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
// 查询链信息
BlockchainInfo blockchainInfo = chainInfoClient.getChainInfo();
// 使用Json构建
Block block = chainInfoClient.getBlockByHeight(5);
// 直接获取字节块构建
Block block = chainInfoClient.getBlockStreamByHeight(5)
// 根据区块高度获取区块头
BlockHeader blockHeader = chainInfoClient.getBlockHeaderByHeight(1)
根据区块hash查询区块数据
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
// 查询链信息
BlockchainInfo blockchainInfo = chainInfoClient.getChainInfo();
// 根据区块HASH获取区块
Block block = chainInfoClient.getBlockByBlockHash(Base64.encodeBase64String(block.getHeader().getHashPresent().toByteArray()));
// 根据区块HASH获取区块头
BlockHeader blockHeader = chainInfoClient.getBlockHeaderByBlockHash(Base64.encodeBase64String(block.getHeader().getHashPresent().toByteArray()));
查询其他数据
使用ChainInfoClient构建查询客户端
查询链中组网的节点数目
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
NodesNum nodeNum = chainInfoClient.getChainInfoNode();
根据交易ID查询交易以及交易所在区块高度
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
// 根据交易ID查询交易以及交易所在区块高度
ChainInfoClient.TranInfoAndHeight tranInfoAndHeight = chainInfoClient.getTranInfoAndHeightByTranId("1234567890");
直接查询状态数据
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
// 查询LevelDB
Object leveldbRes = chainInfoClient.queryDB("identity-net", "ContractAssetsTPL", "", "121000005l35120456");
查询交易入块后的结果
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081");
// 查询交易入块后的结果
TransactionResult tranRes = chainInfoClient.getTranResultByTranId("1234567890")
// 查询交易和交易入块后的结果(state数据)
TranAndTranResult tranAndTranResult = chainInfoClient.getTranAndResultByTranId("1376cbbf-edc1-463b-82af-e643d2257159");
如果RepChain端开启了https单向或双向认证,则需要使用如下方式构建ChainInfoClient
ChainInfoClient chainInfoClient = new ChainInfoClient("localhost:9081", sslContext);
同步块数据
使用sync/SyncService构建同步服务,从指定高度开始同步,一直到最新高度,思路是:定时<拉>(基于http服务)和<推拉>结合(基于ws+http服务)的方式来同步区块,即订阅(基于ws/wss)与拉取(基于http/https)相结合的方案来保证区块同步的实时性,该方案可以防止相应节点ws服务崩溃,使用示例请参考SyncServiceTest
- 使用host、syncInfo、syncListener(实现该接口,可将区块存储到数据库中)初始化
- 初始化之后,就可以启动同步服务
- SyncListener中,如果保存区块出现问题,要throw SyncBlockException
SyncInfo syncInfo = new SyncInfo(locHeight, locBlkHash);
SyncService syncService = SyncService.newBuilder()
.setHost("localhost:9081")
.setSyncInfo(syncInfo)
.setSyncListener("SyncListener实例")
.build();
Thread thread = new Thread(syncService::start);
thread.start();
如果RepChain端开启了https单向或双向认证,则需要使用如下方式构建SyncService
SyncInfo syncInfo = new SyncInfo(locHeight, locBlkHash); SyncService syncService = SyncService.newBuilder() .setHost("localhost:9081") .setSyncInfo(syncInfo) .setSyncListener("SyncListener实例") .setSslContext(sslContext) // 此处需要设置sslContext .build();
订阅块事件
使用client/RSubClient构建客户端,使用观察者模式
- 创建BlockObserver实例
- 创建BlockListener实例(可使用工具类BlockListenerUtil获取-->每个host只有唯一的1个listener)
- 向BlockListener中注册BlockObserver
// 获取block监听,使用 host 获取一个监听,每个 host 对应一个监听
// 也可以自己去实例化
blkListener = BlockListenerUtil.getListener(host);
// event 监听,并回调给具体的实现类
blkListener.registerBlkObserver("BlockObserver实例");
RSubClient rSubClient = new RSubClient(host, blkListener);
// 若RepChain开启了https单向或双向认证
// RSubClient rSubClient = new RSubClient(host, blkListener, sslContext);
rSubClient.connect();
解析区块的StateMap里的数据(指RepChain的proto文件中定义的结构数据,如Signer、Certificate、Operate、Authorize)需要用到rc-proto.jar
(RepChain端proto-scala编译好的class),可从本页面右侧"发行版"找到打包好的对应版本
从release中下载对应版本的
rc-proto.jar
将
rc-proto.jar
安装到本地maven库mvn install:install-file -DgroupId=repchain -DartifactId=rc-proto -Dversion=2.0.0 -Dpackaging=jar -Dfile=rc-proto.jar
在项目中引入依赖
<dependency> <groupId>repchain</groupId> <artifactId>rc-proto</artifactId> <version>2.0.0</version> </dependency>
通过
StateUtil
工具类来解析
国密的引入和使用
国密ssl包需要额外引入,暂时未开源
具体使用示例可参考
src/test/java/com.rcjava.client.gm/TranPostGmClientTest
,src/test/java/com.rcjava.client.gm/ChainInfoGmClientTest
国密ssl
继承src/main/java/com.rcjava.gm.GMProvider
,或手动来安装Provider
Security.insertProviderAt((Provider) Class.forName("org.bouncycastle.jce.provider.BouncyCastleProvider").newInstance(), 1);
Security.insertProviderAt((Provider) Class.forName("org.bouncycastle.jsse.provider.BouncyCastleJsseProvider").newInstance(), 2);
构建国密SSLContext
SSLContext sslContext = SSLContextBuilder.create()
.setProtocol("GMSSLv1.1").setProvider("BCJSSE")
.setKeyStoreType("PKCS12")
.setKeyManagerFactoryAlgorithm("PKIX")
.loadTrustMaterial(new File("pfx/mytruststore.pfx"), "changeme".toCharArray(), new TrustSelfSignedStrategy())
.loadKeyMaterial(new File("pfx/215159697776981712.node1.pfx"), "123".toCharArray(), "123".toCharArray())
.build();
构建交易提交客户端或查询客户端
TranPostClient tranPostClient = new TranPostClient("192.168.2.69:9081", sslContext);
ChainInfoClient chainInfoClient = new ChainInfoClient("192.168.2.69:9081", sslContext);
使用国密构造数字签名交易
与上面构造签名交易类似,只需要将签名算法改为"sm3withsm2"即可
TranCreator tranCreator = TranCreator.newBuilder().setPrivateKey(privateKey).setSignAlgorithm("SM3WITHSM2").build();
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。