介绍
JDChain 是专为企业应用设计的区块链框架系统,适用多种通用业务场景,秉承简单易用、灵活高效的设计理念,满足企业积木化按需定制,让企业快速接入区块链世界,重塑商业未来。本例我们采用WEB管理工具进行布署,平台为CentOS 7.6
基本概念
系统结构
交易执行过程
SDK整体介绍
概念体系
下载
http://ledger.jd.com/developer.html
peer-1.5.0.RELEASE
gateway-1.5.0.RELEASE
peer-1.4.2.RELEASE
gateway-1.4.2.RELEASE
kvdb-1.1.2.RELEASE
用winscp上传到服务器/home/jdchain/
下
安装
1、解压缩
unzip jdchain-peer-1.5.0.RELEASE.zip -d /home/jdchain/peer1
unzip jdchain-gateway-1.5.0.RELEASE.zip -d /home/jdchain/gateway
2、将peer复制4份,分别为,并配置权限
cp /home/jdchain/peer1 /home/jdchain/peer2 -rf
cp /home/jdchain/peer1 /home/jdchain/peer3 -rf
cp /home/jdchain/peer1 /home/jdchain/peer4 -rf
chmod 775 /home/jdchain/peer1/bin/*.sh
chmod 775 /home/jdchain/peer2/bin/*.sh
chmod 775 /home/jdchain/peer3/bin/*.sh
chmod 775 /home/jdchain/peer4/bin/*.sh
3、修改节点端口为10011
~10014
vim /home/jdchain/peer1/bin/manager-startup.sh
vim /home/jdchain/peer2/bin/manager-startup.sh
vim /home/jdchain/peer3/bin/manager-startup.sh
vim /home/jdchain/peer4/bin/manager-startup.sh
4、修改peer
启动端口为10021
~10024
vim /home/jdchain/peer1/bin/peer-startup.sh
vim /home/jdchain/peer2/bin/peer-startup.sh
vim /home/jdchain/peer3/bin/peer-startup.sh
vim /home/jdchain/peer4/bin/peer-startup.sh
5、开放防火墙,启动管理工具
firewall-cmd --add-port=10011-10014/tcp
firewall-cmd --add-port=10021-10024/tcp
firewall-cmd --add-port=10550/tcp
sh /home/jdchain/peer1/bin/manager-startup.sh
sh /home/jdchain/peer2/bin/manager-startup.sh
sh /home/jdchain/peer3/bin/manager-startup.sh
sh /home/jdchain/peer4/bin/manager-startup.sh
6、依次打开各节点WEB界面
http://192.168.10.8:10011/
http://192.168.10.8:10012/
http://192.168.10.8:10013/
http://192.168.10.8:10014/
7、生成公私钥
公私钥管理
->生成公私钥
名称
jcpeer1
密码
1
随机数
(1)peer1
生成公钥为:
7VeRHRFgPzVG6azVoXNuft6EpdGPw8WCT32wQfZ9acutytCV
私钥为:
177gjxqZf53RWh8Vc3eoYURCtLqkf5XKvzEx5czmzyBfDVVjFre9d2JERDLGCMbG1QsQ7fK
(2)peer2
生成公钥为:
7VeR7Fb5dXJS3udx7PVwzbUErczaWH6HQmDoQRjye5LKx39U
私钥为:
177gjxRHSC7u7NVc3wD9HYtnwjF5DHdfY425hZY2HhrgGUn11tZDq5NnH2ftDPN4sziV1f1
(3)peer3
生成公钥为:
7VeRCSJqmm56ZfPFtV1EuAZzmamXfcRhwL93f5D2P3GYY6yP
私钥为:
177gjzsWQPeuFCsk16WhZPoBgpTQQRvWipCiN2VBGAwyo9jNotPM5nSu4fMozsCw5dUCfbD
(4)peer4
生成公钥为:
7VeRE4kN4HNeTpk5X1mZVNuFnPeNbZ8oEx7cW6h9RY8Ya13L
私钥为:
177gk1goAukdVgJCX4KV7RJmD3N1Te2WvcQ8ki4jFvQLd2Mi6R6LtKKpW3ihxnRwyoz92HF
8、生成帐本
我们选择peer1
为协调方,其它为参与方,回到http://192.168.10.8:10011/
账本
-> 初始化账本
初始化账本操作类型:协调方
账本邀请码: 45421406
帐本名称:jdchain.ledger
共识协议:Bftsmart
密码算法:默认
参与方数量:4
共识节点信息:端口范围规划在10110-10140 (注意,不能设置为连续端口)
peer节点名称:a0.com - a3.com
初始化共识地址:端口范围规划在10041-10044 (注意,不能设置为连续端口)
数据库名称:rocksdb1-rocksdb4
暂时不要保存,切换到参与方节点,填写好信息暂不保存
账本邀请码: 45421406 (必须使用协调方的邀请码)
协调方地址:127.0.0.1:10011
四个节点全部填写完成后,先保存协调方节点,然后快速保存参与方节点。
此时页面下方会显示所有参与方信息
快速点击四个节点的开始
按钮进行账本创建
静待初始化完成
9、启动节点
点击各节点 -> 账本 -> 查看账本 中的 “启动节点”按钮
10、安装网关
chmod 775 /home/jdchain/gateway/bin/*.sh
vim cd /home/jdchain/gateway/config/gateway.conf
查询peer1的公钥、私钥和解密密钥,写到下面的配置项
more /home/jdchain/peer1/config/keys/*
#网关的HTTP服务端口;
http.port=10550
#共识节点的服务地址(与该网关节点连接的Peer节点的IP地址);
peer.host=127.0.0.1
#共识节点的服务端口(与该网关节点连接的Peer节点的端口,即在Peer节点的peer-startup.sh中定义的端口);
peer.port=10011
#默认公钥的内容(Base58编码数据);
keys.default.pubkey=7VeRHvvx9E7FvnPRJko33YZvBgLrfP2dAhM5KA4vBkkhqQRU
#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
keys.default.privkey=177gjzf6e4g21xs7pJSGS4ScMrCRfnHVq63Rtu8T9FryQwiW667bXcW33Xr48w73gZWWnm5
#默认私钥的解码密码;
keys.default.privkey-password=8EjkXVSTxMFjCvNNsTo8RBMDEVQmk7gYkW4SCDuvdsBG
启动网关后,即可打开区块链浏览器查看相关信息。
sh /home/jdchain/gateway/bin/startup.sh
通过SDK操作
1、打开IDEA,新建一个Maven Quickstart项目
2、修改pom.xml
引入区块链SDK
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>jdchainconsole</artifactId>
<version>1.0-SNAPSHOT</version>
<name>jdchainconsole</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
<framework.version>1.5.0.RELEASE</framework.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>sdk-client</artifactId>
<version>${framework.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>sdk-rpc</artifactId>
<version>${framework.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>crypto-classic</artifactId>
<version>${framework.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>crypto-sm</artifactId>
<version>${framework.version}</version>
</dependency>
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>ledger-model</artifactId>
<version>${framework.version}</version>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
3、COPY 示例代码到程序中,测试建立数据账本、向账本中写入值等SDK
package org.example;
import com.jd.blockchain.contract.ContractEventContext;
import com.jd.blockchain.contract.EventProcessingAware;
import com.jd.blockchain.contract.LedgerContext;
import com.jd.blockchain.crypto.*;
import com.jd.blockchain.ledger.*;
import com.jd.blockchain.sdk.BlockchainService;
import com.jd.blockchain.sdk.client.GatewayServiceFactory;
import utils.Bytes;
import utils.codec.Base58Utils;
/**
* Hello world!
*
*/
public class App
{
// 账本Hash
protected static HashDigest ledger;
protected static BlockchainService blockchainService;
protected static BlockchainKeypair adminKey;
public static void main( String[] args )
{
// 初始配置交易签名用户信息
PubKey pubKey = KeyGenUtils.decodePubKey("7VeR8q2oLa5dBgcSKi8AZ8ojpyDcVPP3pnaa4BCC7Kom5xhC");
PrivKey privKey = KeyGenUtils.decodePrivKey("177gjvw6ePEW5GhThsQFUNXwu16MkaPEh8TPiEXjmNMj25UJUycaaZCYQ8rkucmYTqrJQ6H", "8EjkXVSTxMFjCvNNsTo8RBMDEVQmk7gYkW4SCDuvdsBG");
adminKey = new BlockchainKeypair(pubKey, privKey);
// 读取网关配置
String gatewayHost = "192.168.10.8";
Integer gatewayPort = 10550;
blockchainService = GatewayServiceFactory.connect(gatewayHost, gatewayPort, false, adminKey).getBlockchainService();
String ledgerHash = "j5tos1z5oFXagUdJUuYjjjYC3rXYWn2kiDs4F4qVGBLz4N";
ledger = Crypto.resolveAsHashDigest(Base58Utils.decode(ledgerHash));
testSetKV();
System.out.println( "Hello World!4444" );
}
public void getTransactionByContentHash(HashDigest sampleHash) {
LedgerTransaction tx = blockchainService.getTransactionByContentHash(ledger, sampleHash);
}
// 数据上链
public static void testSetKV() {
// 新建交易
TransactionTemplate txTemp = blockchainService.newTransaction(ledger);
// 请正确填写数据账户地址
// expVersion是针对此key的插入更新操作次数严格递增,初始为-1,再次运行本测试用例请修改该值,否则服务端将报版本冲突异常。
txTemp.dataAccount(Bytes.fromBase58("LdeNve1fHLWCfRCm9J77pbWGDnR56xskKT3Mz"))
.setText("key1", "文本值", -1)
.setInt64("key2", 1024, -1)
.setJSON("key3", "{\"Name\":\"JSON值\"}", -1)
.setBytes("key4", Bytes.fromInt(2048), -1);
// 交易准备
PreparedTransaction ptx = txTemp.prepare();
// 交易签名
ptx.sign(adminKey);
// 提交交易
TransactionResponse response = ptx.commit();
if (response.isSuccess()) {
System.out.println(String.format("height=%d, ###OK#, contentHash=%s, executionState=%s",
response.getBlockHeight(),
response.getContentHash(), response.getExecutionState().toString()));
} else {
System.out.println(String.format("height=%d, ###exception#, contentHash=%s, executionState=%s",
response.getBlockHeight(),
response.getContentHash(), response.getExecutionState().toString()));
}
}
// 注册数据帐户
public static void testRegisterDataAccount() {
// 新建交易
TransactionTemplate txTemp = blockchainService.newTransaction(ledger);
// 生成数据账户
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate();
System.out.println("数据账户地址:" + dataAccount.getAddress());
// 注册数据账户
txTemp.dataAccounts().register(dataAccount.getIdentity());
// 交易准备
PreparedTransaction ptx = txTemp.prepare();
// 交易签名
ptx.sign(adminKey);
System.out.println( "Hello World!2222" );
// 提交交易
TransactionResponse response = ptx.commit();
if (response.isSuccess()) {
System.out.println(String.format("height=%d, ###OK#, contentHash=%s, executionState=%s",
response.getBlockHeight(),
response.getContentHash(), response.getExecutionState().toString()));
} else {
System.out.println(String.format("height=%d, ###exception#, contentHash=%s, executionState=%s",
response.getBlockHeight(),
response.getContentHash(), response.getExecutionState().toString()));
}
}
public static void testRegisterUserAndConfigRole() {
// 新建交易
TransactionTemplate txTemp = blockchainService.newTransaction(ledger);
// 生成用户
BlockchainKeypair user = BlockchainKeyGenerator.getInstance().generate();
System.out.println("用户地址:" + user.getAddress());
// 注册用户
txTemp.users().register(user.getIdentity());
// 创建角色 MANAGER
txTemp.security().roles().configure("MANAGER")
.enable(LedgerPermission.WRITE_DATA_ACCOUNT)
.enable(TransactionPermission.DIRECT_OPERATION);
// 设置用户角色权限
txTemp.security().authorziations().forUser(user.getAddress()).authorize("MANAGER");
System.out.println("完成11111111111");
// 交易主恩贝
PreparedTransaction ptx = txTemp.prepare();
System.out.println("222222");
// 交易签名
ptx.sign(adminKey);
System.out.println("333333");
// 提交交易
TransactionResponse response = ptx.commit();
if (response.isSuccess()) {
System.out.println(String.format("height=%d, ###OK#, contentHash=%s, executionState=%s",
response.getBlockHeight(),
response.getContentHash(), response.getExecutionState().toString()));
} else {
System.out.println(String.format("height=%d, ###exception#, contentHash=%s, executionState=%s",
response.getBlockHeight(),
response.getContentHash(), response.getExecutionState().toString()));
}
}
public static boolean registerUser() {
// 新建交易
TransactionTemplate txTemp = blockchainService.newTransaction(ledger);
// 生成用户
BlockchainKeypair user = BlockchainKeyGenerator.getInstance().generate();
System.out.println("用户地址:" + user.getAddress());
// 注册用户
txTemp.users().register(user.getIdentity());
// 交易准备
PreparedTransaction ptx = txTemp.prepare();
// 交易签名
ptx.sign(adminKey);
// 提交交易
TransactionResponse response = ptx.commit();
return response.isSuccess();
}
public static void createRole() {
// 新建交易
TransactionTemplate txTemp = blockchainService.newTransaction(ledger);
// 创建角色 MANAGER ,并设置可以写数据账户,能执行交易
txTemp.security().roles().configure("MANAGER")
.enable(LedgerPermission.WRITE_DATA_ACCOUNT)
.enable(TransactionPermission.DIRECT_OPERATION);
// 交易准备
PreparedTransaction ptx = txTemp.prepare();
// 交易签名
ptx.sign(adminKey);
// 提交交易
TransactionResponse response = ptx.commit();
System.out.println("完成");
}
public static void configUserRole() {
// 新建交易
TransactionTemplate txTemp = blockchainService.newTransaction(ledger);
// 给用户设置 MANAGER 角色权限
txTemp.security().authorziations().forUser(Bytes.fromBase58("LdeNv7JLbcQidY98y2QYXK1dGmi589zjFrf4v")).authorize("MANAGER");
// 交易准备
PreparedTransaction ptx = txTemp.prepare();
// 交易签名
ptx.sign(adminKey);
// 提交交易
TransactionResponse response = ptx.commit();
System.out.println("完成2");
}
}
FAQ
查看账本的时候报错:java.lang.NoClassDefFoundError: sun/jvmstat/monitor/MonitoredHost
如果采用OracleJDK,需要把JDK中的tools.jar拷贝到各peer节点下的libs中。 OpenJDK不存在此问题。
此操作需要重启管理工具
sh /home/jdchain/peer1/bin/manager-shutdown.sh
sh /home/jdchain/peer2/bin/manager-shutdown.sh
sh /home/jdchain/peer3/bin/manager-shutdown.sh
sh /home/jdchain/peer4/bin/manager-shutdown.sh
sh /home/jdchain/peer1/bin/manager-startup.sh
sh /home/jdchain/peer2/bin/manager-startup.sh
sh /home/jdchain/peer3/bin/manager-startup.sh
sh /home/jdchain/peer4/bin/manager-startup.sh
账本初始化时间过长
要以尽量短的时间内点击“开始”按钮,否则需要清理配置后重新安装
SDK调用无响应
因为节点间通讯我们配置了一个共识服务端口,但服务会开启多一个端口去通讯,故不能把单机多节点的端口设置连续值。(例如上面我们间隔了10进行设置)
sh /home/jdchain/peer1/bin/peer-shutdown.sh
sh /home/jdchain/peer2/bin/peer-shutdown.sh
sh /home/jdchain/peer3/bin/peer-shutdown.sh
sh /home/jdchain/peer4/bin/peer-shutdown.sh
sh /home/jdchain/peer1/bin/peer-startup.sh
sh /home/jdchain/peer2/bin/peer-startup.sh
sh /home/jdchain/peer3/bin/peer-startup.sh
sh /home/jdchain/peer4/bin/peer-startup.sh