# Conflicts: # source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/ClientResolveUtil.java # source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitCommand.java # source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.javatags/1.1.0
@@ -0,0 +1,11 @@ | |||||
language: java | |||||
jdk: | |||||
- openjdk8 | |||||
before_install: | |||||
- cd ./tools | |||||
- mvn install:install-file -Dfile=core-0.1.4.jar -DgroupId=com.yahoo.ycsb -DartifactId=core -Dversion=0.1.4 -Dpackaging=jar -DgeneratePom=true -DcreateChecksum=true | |||||
- cd ../source | |||||
script: | |||||
- mvn clean package |
@@ -1,626 +1,120 @@ | |||||
[TOC] | [TOC] | ||||
#JD区块链 | #JD区块链 | ||||
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) | [![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) | ||||
[![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.jd.blockchain/sdk-pack/badge.svg)](https://maven-badges.herokuapp.com/maven-central/com.jd.blockchain/sdk-pack/) | |||||
[![Build Status](https://travis-ci.com/blockchain-jd-com/jdchain.svg?branch=master)](https://travis-ci.org/blockchain-jd-com/jdchain) | |||||
------------------------------------------------------------------------ | |||||
### 版本修订历史 | |||||
<table> | |||||
<tr> | |||||
<th>版本号</th> | |||||
<th>作 者</th> | |||||
<th>修改日期</th> | |||||
<th>备 注</th> | |||||
</tr> | |||||
<tr> | |||||
<td>0.0.6</td> | |||||
<td>黄海泉</td> | |||||
<td>2017-11-10</td> | |||||
<td> | |||||
定义JD区块链项目的目标与关键能力;<br> | |||||
定义JD区块链的核心对象模型;<br> | |||||
定义“账户”的生成算法和“区块/交易/操作/账户”的关键属性;<br> | |||||
描述了编程接口的示例代码; | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td>0.0.7</td> | |||||
<td>黄海泉</td> | |||||
<td>2017-11-17</td> | |||||
<td> | |||||
丰富了对“节点共识”、“节点分区”两项关键能力的详细描述; | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td>0.0.8</td> | |||||
<td>黄海泉</td> | |||||
<td>2018-07-17</td> | |||||
<td> | |||||
增加部署图;增加智能合约开发的示例; | |||||
</td> | |||||
</tr> | |||||
</table> | |||||
------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ||||
## 一、概述 | |||||
JD区块链项目的目标是提供一个面向广泛的应用场景、满足企业核心需求的灵活和易用的区块链系统。 | |||||
以下是 JD 区块链用以满足企业核心需求的关键能力,也是显著区别于其它区块链的重要特征: | |||||
- 快速共识 | |||||
- 节点分区 | |||||
- 并行多账本 | |||||
- 大数据伸缩存储 | |||||
- 条件检索 | |||||
- 面向对象的合约代码编程模型 | |||||
- 节点快速部署 | |||||
- 多终端灵活接入 | |||||
- 分布式自治的账户权限管理模型 | |||||
JD区块链对于关键能力的定义是建立在深入理解和抽象各种多样化需求的基础上的。 | |||||
- 快速共识(Efficient Consensus) | |||||
我们认为,“快速”不仅仅体现在“用更短时间”达成共识,还要体现在交易(Transaction)要得到可靠地执行。 | |||||
需要在算法和实现层面做出保障,确保所有提交的合法的交易会被系统在一个“确定性”和“足够短”的时间之内被严格地执行,系统不主动作出随机性地丢弃(不包括系统故障因素)。注:POW类算法产生的链分叉处理是一种系统随机性地丢弃交易的行为。 | |||||
从使用者的视角来看,这种能力就是区块链系统的“可靠性”,这对于企业、金融场景而言尤其重要。 | |||||
- 节点分区(Peer Partition) | |||||
“分区”是一种分布式系统架构原则,通过将大范围目标按照某种相似特征分隔为一个个小范围目标,分别进行更高效地处理,这能从整体上提升整个系统的处理能力。 | |||||
区块链系统也是一种分布式系统,沿用“分区”的思想这点来自以往的系统架构实践的经验,是有可能让区块链系统获得可媲美现有系统的处理能力。这种能力将可以无障碍地把区块链在应用于广泛的企业场景中。 | |||||
在此,我们所说的“节点分区(Peer Partition)” 是共识过程中的物理通讯层面的分区,在共识过程中只有相关的物理节点才会建立通讯链路并复制和存储共识的状态数据。在一个区块链系统中,可以从节点全集中选择一个或多个节点子集,分别组成一个或多个节点分区(Peer Partition) | |||||
- 并行多账本 | |||||
账本(Ledger) | |||||
- 大数据伸缩存储 | |||||
- 条件检索 | |||||
- 面向对象的合约代码编程模型 | |||||
- 节点快速部署 | |||||
- 多终端灵活接入 | |||||
- 分布式自治的账户权限管理模型 | |||||
## 二、对象模型 | |||||
JD区块链的核心对象包括: | |||||
- 账本(Ledger) | |||||
一份账本(Ledger)实质上是由两部分组成: | |||||
- 一组以“账户(Account)”表示的状态数据; | |||||
- 以“区块的链条(Block-chain)”表示的状态数据的变更历史; | |||||
JD区块链的“账本(Ledger)”是一个最顶层的管理数据的逻辑单元。在一个区块链节点构成的共识网络中,可以维护多套并行的“账本(Ledger)”。 | |||||
- 账户(Account) | |||||
- 在JD区块链中,账户(Account)被设计为包含“身份(Identity)”、“权限(Privilege)”、“状态(State)”、“控制规则(Control Rule)” 这4种属性的对象。 | |||||
- 其中,“身份(Identity)”、“权限(Privilege)”这两种属性是一个面向应用的系统的基本功能; | |||||
- “状态(State)”、“控制规则(Control Rule)” 这两种属性这是一个具备“图灵完备性”的系统的基本属性,使系统可以处理任意多样化的任务; | |||||
- 在这里,“身份(Identity)”是一个抽象表述,其形式上就是一个“区块链地址(Address)”和相应的“非对称秘钥钥对(KeyPair)”/证书。 简单来说,一个“账户(Account)”就是一个区块链地址、公私钥对以及一套的权限配置. | |||||
- 一个“账户(Account)”的“状态(State)”、“控制规则(Control Rule)” 这2种属性则是可选的,这提供了不同于其它区块链的账户/合约的一种新的使用方式,即一种数据和逻辑分离的应用设计思路(这在传统的web应用编程中被称为“贫血模型”)。同时,也意味着一个特定“账户”中的数据状态是可以跨账户/合约代码进行共享访问的。 | |||||
- 从应用的视角来看,对“账户”的使用方式可以有几种模式: | |||||
- 用于表示业务角色/用户: | |||||
- 用于表示业务数据:仅有“状态(State)”没有“控制规则(Control Rule)”的账户,可称为数据账户,就有些类似于关系数据库中的“表(Table)”的作用,不同业务类别的数据则使用不同的账户来管理。 | |||||
- 用于表示业务逻辑:仅有“控制规则(Control Rule)”没有“状态(State)”的账户,即所谓的合约账户,可表示某种用于处理数据的通用逻辑,可被授权访问特定的数据账户。 | |||||
- 区块(Block) | |||||
在概念上,与通常所说的区块的概念是一致的。 | |||||
在实现上,一个区块的主要内容是包含了某一时刻提交的所有交易以及交易执行之后的状态数据的快照的hash,而不存储具体的交易操作和状态数据。 | |||||
- 交易(Transaction) | |||||
在概念上,与通常所说的Transaction的概念是一致的,表示一组需要原子执行的操作。 | |||||
- 操作(Operation) | |||||
操作是针对“账户(Account)”的“写”指令,包括以下几类: | |||||
- 注册账户 | |||||
- 更新账户的状态数据 | |||||
- 更新账户的合约代码定义 | |||||
- 调用账户的合约代码 | |||||
- 合约代码(Contract Code) | |||||
合约代码是一段用于对“账户(Account)”的状态数据执行操作的代码程序。 | |||||
## 三、部署模型 | |||||
1. 总体部署 | |||||
![deployment architecture](docs/images/deployment.png) | |||||
2. 系统组件 | |||||
- 共识节点 | |||||
- 复制节点 | |||||
- SDK | |||||
- 网关 | |||||
- 终端 | |||||
3. 配置和管理 | |||||
## 四、账本结构 | |||||
### 1. 账户生成算法 | |||||
- 公私钥对 | |||||
- 算法:默认ED25519 ,支持 SM2、CA; | |||||
- 公钥存储格式:版本 + 算法标识 + 公私钥原始内容 + 校验码 | |||||
- 字符编码方式:Base64 | |||||
- 地址 | |||||
- 算法 | |||||
- 给定公钥 P (或由私钥 R 算出公钥 P) | |||||
- 中间值 H1 = SHA256( P ) | |||||
- 中间值 H2 = RIPEMD-160( H1 ) | |||||
- 中间值 X = 版本 + 公钥算法标识 + H2 | |||||
- 校验和 C = 前4字节( SHA256( SHA256( X )) ) | |||||
- 地址 Address = Base58( X + C ) | |||||
### 2. 区块 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>BlockHash</td> | |||||
<td>当前区块 hash</td> | |||||
<td>对区块中除此之外的其它所有属性一起进行哈希运算生成</td> | |||||
</tr> | |||||
<tr> | |||||
<td>BlockVersion </td> | |||||
<td>区块版本</td> | |||||
<td>表示区块-交易的属性结构的版本号;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>PreviousBlockHash</td> | |||||
<td>上一区块 hash</td> | |||||
<td></td> | |||||
</tr> | |||||
<tr> | |||||
<td>BlockNumber</td> | |||||
<td>区块高度</td> | |||||
<td>区块高度是一个区块在链中的序号;<br>创始区块的高度为 0,每个新区块的高度依次递增;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>AccountHash</td> | |||||
<td>账户树hash</td> | |||||
<td>账户的 Merkle Tree 根的 hash</td> | |||||
</tr> | |||||
<tr> | |||||
<td>AccountCount</td> | |||||
<td>账户数量</td> | |||||
<td>区块生成时账本中的全部账户的总数</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxTreeHash</td> | |||||
<td>交易树 hash</td> | |||||
<td>本区块的交易集合的 Merkle Tree 根的 hash</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxCount</td> | |||||
<td>区块交易数量</td> | |||||
<td>当前区块包含的交易的数量;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxTotalCount</td> | |||||
<td>账本交易总数</td> | |||||
<td>截止到当前区块为止当前账本的所有交易的总数量;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CloseTime</td> | |||||
<td>区块关闭时间</td> | |||||
<td>生成当前区块时的区块链节点的网络时间;</td> | |||||
</tr> | |||||
</table> | |||||
### 3. 交易 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Hash</td> | |||||
<td>当前交易 hash</td> | |||||
<td>对交易中除此之外的其它所有属性一起进行哈希运算生成</td> | |||||
</tr> | |||||
<tr> | |||||
<td>LedgerNumber</td> | |||||
<td>区块高度</td> | |||||
<td>交易被包含的区块高度</td> | |||||
</tr> | |||||
<tr> | |||||
<td>BlobHash</td> | |||||
<td>交易数据块hash</td> | |||||
<td>交易的数据块是交易的原始数据,包含客户端提交的交易的全部操作及其参数; | |||||
<br>交易的参与者需要使用私钥对交易数据块进行签名;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Operations</td> | |||||
<td>操作列表</td> | |||||
<td>交易的操作列表;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Sponsor</td> | |||||
<td>交易发起人</td> | |||||
<td>交易发起人的账户地址;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>SequenceNumber</td> | |||||
<td>交易序号</td> | |||||
<td>交易序号记录了一个特定的发起人的交易的顺序号,等同于该发起人历史上发起的交易的总数;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Signatures</td> | |||||
<td>签名列表</td> | |||||
<td>由交易发起人和其它参与者对交易数据块的签名的列表;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Result</td> | |||||
<td>交易结果</td> | |||||
<td>0 - 表示执行成功;非零表示执行失败;<br>注:最终的账本只包含成功的交易;</td> | |||||
</tr> | |||||
</table> | |||||
### 4. 操作 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>OpType</td> | |||||
<td>操作类型</td> | |||||
<td> | |||||
一级操作类型包括:注册账户、配置权限、写入键值数据、写入对象数据、定义合约代码、调用合约代码;<br> | |||||
“键值数据写入”操作的子操作类型包括:填入键值、移除键、数值增加、数值减少;<br> | |||||
“对象数据写入”操作的自操作类型包括:插入对象、更新对象、移除对象; | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Args</td> | |||||
<td>参数列表</td> | |||||
<td>与操作类型相对应的参数列表;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>SubOps</td> | |||||
<td>子操作列表</td> | |||||
<td>“子操作”是“操作”的递归定义,由“操作类型”来标识;</td> | |||||
</tr> | |||||
</table> | |||||
### 5. 账户 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Address</td> | |||||
<td>地址</td> | |||||
<td>账户的唯一标识</td> | |||||
</tr> | |||||
<tr> | |||||
<td>RegNumber</td> | |||||
<td>注册号</td> | |||||
<td>账户被注册到区块链的区块高度;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxSquenceNumber</td> | |||||
<td>交易序列号</td> | |||||
<td>由账户发起的交易的序列号,初始为 0,账户每发起一个交易则增加1;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>ModelVersion</td> | |||||
<td>账户模型版本</td> | |||||
<td>表示构成一个账户结构的属性模型的程序版本号;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Version</td> | |||||
<td>账户版本</td> | |||||
<td>初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;<br>注:交易序号的改变不会导致账户版本的增加;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>PrivilegeHash</td> | |||||
<td>权限 hash</td> | |||||
<td>权限树的根hash;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>PrivilegeVersion</td> | |||||
<td>权限版本</td> | |||||
<td>初始为 0, 每次对权限的变更都导致版本号加 1;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>StateType</td> | |||||
<td>状态类型</td> | |||||
<td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>StateVersion</td> | |||||
<td>状态版本</td> | |||||
<td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>StateHash</td> | |||||
<td>状态哈希</td> | |||||
<td>数据状态的 merkle tree 的根hash;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CodeHash</td> | |||||
<td>合约代码哈希</td> | |||||
<td>由“账户地址+合约代码版本号+合约代码内容”生成的哈希;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CodeVersion</td> | |||||
<td>代码版本</td> | |||||
<td>初始为 0,每次对代码的变更都使版本加 1 ;</td> | |||||
</tr> | |||||
</table> | |||||
## 五、编程接口 | |||||
### 1. 服务连接 | |||||
```java | |||||
//创建服务代理 | |||||
public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(); | |||||
final String GATEWAY_IP = "127.0.0.1"; | |||||
final int GATEWAY_PORT = 80; | |||||
final boolean SECURE = false; | |||||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | |||||
CLIENT_CERT); | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
``` | |||||
## 一、项目介绍 | |||||
JD Chain 的目标是实现一个面向企业应用场景的通用区块链框架系统,能够作为企业级基础设施,为业务创新提供高效、灵活和安全的解决方案。 | |||||
### 2. 用户注册 | |||||
## 二、部署模型 | |||||
JD Chain 主要部署组件包括以下几种: | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义注册账号的 TX; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||||
CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||||
BlockchainKeyPair user = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||||
txTemp.users().register(user.getIdentity()); | |||||
- 共识节点 | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
共识节点即参与共识的节点,这是系统的核心组件,承担了运行共识协议、管理账本数据、运行智能合约的职责。 | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
``` | |||||
一个区块链网络由多个共识节点组成,共识节点的数量范围由选择的共识协议决定。 | |||||
共识节点和账本是两个不同的概念,共识节点是个物理上的概念,账本是个逻辑上的概念。JD Chain 是一个多账本区块链系统,一个共识节点上可以装载运行多个账本。账本是数据维度的独立管理单元。共识节点和账本的关系,就像关系数据库系统中,数据库服务器和数据库实例的关系。 | |||||
### 3. 数据账户注册 | |||||
共识节点通常都部署在参与方的内部网络中,通过由网络管理员指定的安全的网络出口与其它的共识节点建立通讯连接。 | |||||
共识节点在形态上是服务器中的一个处理进程,背后需要连接一个本地或者内网的NoSQL数据库系统作为账本的存储。当前版本,共识节点目前是单进程的,未来版本将实现多进程以及多服务器集群模式。 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义注册账号的 TX; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||||
CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||||
BlockchainKeyPair dataAccount = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
``` | |||||
- 网关节点 | |||||
### 4. 写入数据 | |||||
网关节点是负责终端接入的节点,负责终端连接、协议转换、交易准入、本地密码运算、密钥管理等职责。 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
HashDigest ledgerHash = getLedgerHash(); | |||||
// 在本地定义注册账号的 TX; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
// -------------------------------------- | |||||
// 将商品信息写入到指定的账户中; | |||||
// 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; | |||||
String commodityDataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||||
Commodity commodity1 = new Commodity(); | |||||
txTemp.dataAccount(commodityDataAccount).set("ASSET_CODE", commodity1.getCode().getBytes(), -1); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
``` | |||||
网关节点是一种轻量节点,需要绑定一个特定参与方的密钥对,连接到一个或多个共识节点。 | |||||
### 5. 查询数据 | |||||
网关节点向共识节点的连接是需要通过认证的,绑定的参与方的密钥对必须事先已经注册到区块链账本中,且得到接入授权。 | |||||
> 注:详细的查询可参考模块sdk-samples中SDK_GateWay_Query_Test_相关测试用例 | |||||
- 终端 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 查询区块信息; | |||||
// 区块高度; | |||||
long ledgerNumber = service.getLedger(LEDGER_HASH).getLatestBlockHeight(); | |||||
// 最新区块; | |||||
LedgerBlock latestBlock = service.getBlock(LEDGER_HASH, ledgerNumber); | |||||
// 区块中的交易的数量; | |||||
long txCount = service.getTransactionCount(LEDGER_HASH, latestBlock.getHash()); | |||||
// 获取交易列表; | |||||
LedgerTransaction[] txList = service.getTransactions(LEDGER_HASH, ledgerNumber, 0, 100); | |||||
// 遍历交易列表 | |||||
for (LedgerTransaction ledgerTransaction : txList) { | |||||
TransactionContent txContent = ledgerTransaction.getTransactionContent(); | |||||
Operation[] operations = txContent.getOperations(); | |||||
if (operations != null && operations.length > 0) { | |||||
for (Operation operation : operations) { | |||||
operation = ClientOperationUtil.read(operation); | |||||
// 操作类型:数据账户注册操作 | |||||
if (operation instanceof DataAccountRegisterOperation) { | |||||
DataAccountRegisterOperation daro = (DataAccountRegisterOperation) operation; | |||||
BlockchainIdentity blockchainIdentity = daro.getAccountID(); | |||||
} | |||||
// 操作类型:用户注册操作 | |||||
else if (operation instanceof UserRegisterOperation) { | |||||
UserRegisterOperation uro = (UserRegisterOperation) operation; | |||||
BlockchainIdentity blockchainIdentity = uro.getUserID(); | |||||
} | |||||
// 操作类型:账本注册操作 | |||||
else if (operation instanceof LedgerInitOperation) { | |||||
LedgerInitOperation ledgerInitOperation = (LedgerInitOperation)operation; | |||||
LedgerInitSetting ledgerInitSetting = ledgerInitOperation.getInitSetting(); | |||||
ParticipantNode[] participantNodes = ledgerInitSetting.getConsensusParticipants(); | |||||
} | |||||
// 操作类型:合约发布操作 | |||||
else if (operation instanceof ContractCodeDeployOperation) { | |||||
ContractCodeDeployOperation ccdo = (ContractCodeDeployOperation) operation; | |||||
BlockchainIdentity blockchainIdentity = ccdo.getContractID(); | |||||
} | |||||
// 操作类型:合约执行操作 | |||||
else if (operation instanceof ContractEventSendOperation) { | |||||
ContractEventSendOperation ceso = (ContractEventSendOperation) operation; | |||||
} | |||||
// 操作类型:KV存储操作 | |||||
else if (operation instanceof DataAccountKVSetOperation) { | |||||
DataAccountKVSetOperation.KVWriteEntry[] kvWriteEntries = | |||||
((DataAccountKVSetOperation) operation).getWriteSet(); | |||||
if (kvWriteEntries != null && kvWriteEntries.length > 0) { | |||||
for (DataAccountKVSetOperation.KVWriteEntry kvWriteEntry : kvWriteEntries) { | |||||
BytesValue bytesValue = kvWriteEntry.getValue(); | |||||
DataType dataType = bytesValue.getType(); | |||||
Object showVal = ClientOperationUtil.readValueByBytesValue(bytesValue); | |||||
System.out.println("writeSet.key=" + kvWriteEntry.getKey()); | |||||
System.out.println("writeSet.value=" + showVal); | |||||
System.out.println("writeSet.type=" + dataType); | |||||
System.out.println("writeSet.version=" + kvWriteEntry.getExpectedVersion()); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// 根据交易的 hash 获得交易;注:客户端生成 PrepareTransaction 时得到交易hash; | |||||
HashDigest txHash = txList[0].getTransactionContent().getHash(); | |||||
Transaction tx = service.getTransactionByContentHash(LEDGER_HASH, txHash); | |||||
// 获取数据; | |||||
String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||||
String[] objKeys = new String[] { "x001", "x002" }; | |||||
KVDataEntry[] kvData = service.getDataEntries(LEDGER_HASH, commerceAccount, objKeys); | |||||
long payloadVersion = kvData[0].getVersion(); | |||||
// 获取数据账户下所有的KV列表 | |||||
KVDataEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||||
if (kvData != null && kvData.length > 0) { | |||||
for (KVDataEntry kvDatum : kvData) { | |||||
System.out.println("kvData.key=" + kvDatum.getKey()); | |||||
System.out.println("kvData.version=" + kvDatum.getVersion()); | |||||
System.out.println("kvData.type=" + kvDatum.getType()); | |||||
System.out.println("kvData.value=" + kvDatum.getValue()); | |||||
} | |||||
} | |||||
``` | |||||
终端泛指可以提交交易的客户端,典型来说,包括人、自动化设备、链外的信息系统等。 | |||||
终端只能通过网关节点来提交交易。终端提交的交易需要用体现该终端身份的私钥来签署,产生一份电子签名。随后当交易提交给网关节点时,网关节点需要在把交易提交到共识节点之前,对交易请求以网关节点绑定的私钥追加一项“节点签名”。 | |||||
### 6. 合约发布 | |||||
- 备份节点 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义TX模板 | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
仅对账本数据提供备份,但不参与交易共识的节点。(注:目前版本中尚未实现,将在后续版本中提供) | |||||
// 合约内容读取 | |||||
byte[] contractBytes = FileUtils.readBytes(new File(CONTRACT_FILE)); | |||||
// 生成用户 | |||||
BlockchainIdentityData blockchainIdentity = new BlockchainIdentityData(getSponsorKey().getPubKey()); | |||||
![](docs/images/deployment.jpg) | |||||
// 发布合约 | |||||
txTemp.contracts().deploy(blockchainIdentity, contractBytes); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
## 三、编译源代码 | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
1. 安装 Maven 环境 | |||||
prepTx.sign(keyPair); | |||||
JD Chain 当前版本以 Java 语言开发,需要安装配置 JVM 和 Maven,JDK 版本不低于1.8 。(没有特殊要求,请按标准方法安装,此处不赘述) | |||||
2. 安装 Git 工具 | |||||
为了能够执行 git clone 命令获取代码仓库。 (没有特殊要求,请按标准方法安装,此处不赘述) | |||||
3. 工程代码 | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
JD Chain 源代码包括 3 个代码仓库 | |||||
assertTrue(transactionResponse.isSuccess()); | |||||
- jdchain | |||||
- 这是当前仓库,也是核心仓库,包含了共识节点、网关节点、SDK等一切部署组件。依赖于 explorer 和 bftsmart 这两个仓库先进行编译安装; | |||||
- explorer | |||||
- 这是区块链浏览器的前端Web页面的工程,需要编译成静态资源包,由网关节点集成到一起部署。 | |||||
- 地址:git@github.com:blockchain-jd-com/explorer.git | |||||
// 打印合约地址 | |||||
System.out.println(blockchainIdentity.getAddress().toBase58()); | |||||
- bftsmart | |||||
- 这是bftsmart共识协议的工程,需要先编译安装到本地 maven 仓库; | |||||
``` | |||||
### 7. 合约执行 | |||||
4. 命令操作 | |||||
```java | |||||
- 编译安装 explorer 到本地 maven 仓库; | |||||
```sh | |||||
$ git clone git@github.com:blockchain-jd-com/explorer.git explorer | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
$ cd explorer | |||||
// 在本地定义TX模板 | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
$ git checkout master | |||||
// 合约地址 | |||||
String contractAddressBase58 = ""; | |||||
// 使用接口方式调用合约 | |||||
TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||||
// 使用decode方式调用合约内部方法(create方法) | |||||
// 返回GenericValueHolder可通过get方法获取结果,但get方法需要在commit调用后执行 | |||||
GenericValueHolder<String> result = ContractReturnValue.decode(transferContract.create(address, account, money)); | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(adminKey); | |||||
TransactionResponse transactionResponse = ptx.commit(); | |||||
$ mvn clean install | |||||
String cotractExecResult = result.get(); | |||||
// TransactionResponse也提供了可供查询结果的接口 | |||||
OperationResult[] operationResults = transactionResponse.getOperationResults(); | |||||
``` | |||||
- 编译安装 bftsmart 到本地 maven 仓库; | |||||
- 需要手动先安装一个第三方包,位于仓库根目录下 lib/core-0.1.4.jar | |||||
```sh | |||||
$ git clone git@github.com:blockchain-jd-com/bftsmart.git bftsmart | |||||
$ cd bftsmart | |||||
$ git checkout master | |||||
// 通过OperationResult获取结果 | |||||
for (int i = 0; i < operationResults.length; i++) { | |||||
OperationResult opResult = operationResults[i]; | |||||
System.out.printf("Operation[%s].result = %s \r\n", | |||||
opResult.getIndex(), BytesValueEncoding.decode(opResult.getResult())); | |||||
} | |||||
$ mvn install:install-file -Dfile=lib/core-0.1.4.jar -DgroupId=com.yahoo.ycsb -DartifactId=core -Dversion=0.1.4 -Dpackaging=jar | |||||
$ mvn clean install | |||||
``` | |||||
- 编译 jdchain 工程; | |||||
- 当编译完成后,共识节点的安装包位于 "仓库根目录"/source/deployment/deployment-peer/target/jdchain-peer-1.0.1.RELEASE.zip | |||||
- 当编译完成后,网关节点的安装包位于 "仓库根目录"/source/deployment/deployment-gateway/target/jdchain-gateway-1.0.1.RELEASE.zip | |||||
``` | |||||
```sh | |||||
$ git clone git@github.com:blockchain-jd-com/jdchain.git jdchain | |||||
$ cd jdchain/source | |||||
$ git checkout master | |||||
$ mvn clean package | |||||
``` |
@@ -0,0 +1,630 @@ | |||||
[TOC] | |||||
#JD区块链 | |||||
[![License](https://img.shields.io/badge/license-Apache%202-4EB1BA.svg)](https://www.apache.org/licenses/LICENSE-2.0.html) | |||||
------------------------------------------------------------------------ | |||||
### 版本修订历史 | |||||
<table> | |||||
<tr> | |||||
<th>版本号</th> | |||||
<th>作 者</th> | |||||
<th>修改日期</th> | |||||
<th>备 注</th> | |||||
</tr> | |||||
<tr> | |||||
<td>0.0.6</td> | |||||
<td>黄海泉</td> | |||||
<td>2017-11-10</td> | |||||
<td> | |||||
定义JD区块链项目的目标与关键能力;<br> | |||||
定义JD区块链的核心对象模型;<br> | |||||
定义“账户”的生成算法和“区块/交易/操作/账户”的关键属性;<br> | |||||
描述了编程接口的示例代码; | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td>0.0.7</td> | |||||
<td>黄海泉</td> | |||||
<td>2017-11-17</td> | |||||
<td> | |||||
丰富了对“节点共识”、“节点分区”两项关键能力的详细描述; | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td>0.0.8</td> | |||||
<td>黄海泉</td> | |||||
<td>2018-07-17</td> | |||||
<td> | |||||
增加部署图;增加智能合约开发的示例; | |||||
</td> | |||||
</tr> | |||||
</table> | |||||
------------------------------------------------------------------------ | |||||
## 一、概述 | |||||
JD Chain 的目标是实现一个面向企业应用场景的通用区块链框架系统,能够作为企业级基础设施,为业务创新提供高效、灵活和安全的解决方案。 | |||||
区块链本质上是一种新的分布式架构,以密码学和分布式技术为核心,旨在实现无需借助“第三方” 就能在多个业务方之间进行安全、可信、直接的信息和价值交换。从点对点的信息和价值交换的角度看,区块链发挥了“协议”的作用。 | |||||
以下是 JD 区块链用以满足企业核心需求的关键能力,也是显著区别于其它区块链的重要特征: | |||||
- 快速共识 | |||||
- 节点分区 | |||||
- 并行多账本 | |||||
- 大数据伸缩存储 | |||||
- 条件检索 | |||||
- 面向对象的合约代码编程模型 | |||||
- 节点快速部署 | |||||
- 多终端灵活接入 | |||||
- 分布式自治的账户权限管理模型 | |||||
JD区块链对于关键能力的定义是建立在深入理解和抽象各种多样化需求的基础上的。 | |||||
- 快速共识(Efficient Consensus) | |||||
我们认为,“快速”不仅仅体现在“用更短时间”达成共识,还要体现在交易(Transaction)要得到可靠地执行。 | |||||
需要在算法和实现层面做出保障,确保所有提交的合法的交易会被系统在一个“确定性”和“足够短”的时间之内被严格地执行,系统不主动作出随机性地丢弃(不包括系统故障因素)。注:POW类算法产生的链分叉处理是一种系统随机性地丢弃交易的行为。 | |||||
从使用者的视角来看,这种能力就是区块链系统的“可靠性”,这对于企业、金融场景而言尤其重要。 | |||||
- 节点分区(Peer Partition) | |||||
“分区”是一种分布式系统架构原则,通过将大范围目标按照某种相似特征分隔为一个个小范围目标,分别进行更高效地处理,这能从整体上提升整个系统的处理能力。 | |||||
区块链系统也是一种分布式系统,沿用“分区”的思想这点来自以往的系统架构实践的经验,是有可能让区块链系统获得可媲美现有系统的处理能力。这种能力将可以无障碍地把区块链在应用于广泛的企业场景中。 | |||||
在此,我们所说的“节点分区(Peer Partition)” 是共识过程中的物理通讯层面的分区,在共识过程中只有相关的物理节点才会建立通讯链路并复制和存储共识的状态数据。在一个区块链系统中,可以从节点全集中选择一个或多个节点子集,分别组成一个或多个节点分区(Peer Partition) | |||||
- 并行多账本 | |||||
账本(Ledger) | |||||
- 大数据伸缩存储 | |||||
- 条件检索 | |||||
- 面向对象的合约代码编程模型 | |||||
- 节点快速部署 | |||||
- 多终端灵活接入 | |||||
- 分布式自治的账户权限管理模型 | |||||
## 二、对象模型 | |||||
JD区块链的核心对象包括: | |||||
- 账本(Ledger) | |||||
一份账本(Ledger)实质上是由两部分组成: | |||||
- 一组以“账户(Account)”表示的状态数据; | |||||
- 以“区块的链条(Block-chain)”表示的状态数据的变更历史; | |||||
JD区块链的“账本(Ledger)”是一个最顶层的管理数据的逻辑单元。在一个区块链节点构成的共识网络中,可以维护多套并行的“账本(Ledger)”。 | |||||
- 账户(Account) | |||||
- 在JD区块链中,账户(Account)被设计为包含“身份(Identity)”、“权限(Privilege)”、“状态(State)”、“控制规则(Control Rule)” 这4种属性的对象。 | |||||
- 其中,“身份(Identity)”、“权限(Privilege)”这两种属性是一个面向应用的系统的基本功能; | |||||
- “状态(State)”、“控制规则(Control Rule)” 这两种属性这是一个具备“图灵完备性”的系统的基本属性,使系统可以处理任意多样化的任务; | |||||
- 在这里,“身份(Identity)”是一个抽象表述,其形式上就是一个“区块链地址(Address)”和相应的“非对称秘钥钥对(KeyPair)”/证书。 简单来说,一个“账户(Account)”就是一个区块链地址、公私钥对以及一套的权限配置. | |||||
- 一个“账户(Account)”的“状态(State)”、“控制规则(Control Rule)” 这2种属性则是可选的,这提供了不同于其它区块链的账户/合约的一种新的使用方式,即一种数据和逻辑分离的应用设计思路(这在传统的web应用编程中被称为“贫血模型”)。同时,也意味着一个特定“账户”中的数据状态是可以跨账户/合约代码进行共享访问的。 | |||||
- 从应用的视角来看,对“账户”的使用方式可以有几种模式: | |||||
- 用于表示业务角色/用户: | |||||
- 用于表示业务数据:仅有“状态(State)”没有“控制规则(Control Rule)”的账户,可称为数据账户,就有些类似于关系数据库中的“表(Table)”的作用,不同业务类别的数据则使用不同的账户来管理。 | |||||
- 用于表示业务逻辑:仅有“控制规则(Control Rule)”没有“状态(State)”的账户,即所谓的合约账户,可表示某种用于处理数据的通用逻辑,可被授权访问特定的数据账户。 | |||||
- 区块(Block) | |||||
在概念上,与通常所说的区块的概念是一致的。 | |||||
在实现上,一个区块的主要内容是包含了某一时刻提交的所有交易以及交易执行之后的状态数据的快照的hash,而不存储具体的交易操作和状态数据。 | |||||
- 交易(Transaction) | |||||
在概念上,与通常所说的Transaction的概念是一致的,表示一组需要原子执行的操作。 | |||||
- 操作(Operation) | |||||
操作是针对“账户(Account)”的“写”指令,包括以下几类: | |||||
- 注册账户 | |||||
- 更新账户的状态数据 | |||||
- 更新账户的合约代码定义 | |||||
- 调用账户的合约代码 | |||||
- 合约代码(Contract Code) | |||||
合约代码是一段用于对“账户(Account)”的状态数据执行操作的代码程序。 | |||||
## 三、部署模型 | |||||
1. 总体部署 | |||||
![deployment architecture](docs/images/deployment.png) | |||||
2. 系统组件 | |||||
- 共识节点 | |||||
- 复制节点 | |||||
- SDK | |||||
- 网关 | |||||
- 终端 | |||||
3. 配置和管理 | |||||
## 四、账本结构 | |||||
### 1. 账户生成算法 | |||||
- 公私钥对 | |||||
- 算法:默认ED25519 ,支持 SM2、CA; | |||||
- 公钥存储格式:版本 + 算法标识 + 公私钥原始内容 + 校验码 | |||||
- 字符编码方式:Base64 | |||||
- 地址 | |||||
- 算法 | |||||
- 给定公钥 P (或由私钥 R 算出公钥 P) | |||||
- 中间值 H1 = SHA256( P ) | |||||
- 中间值 H2 = RIPEMD-160( H1 ) | |||||
- 中间值 X = 版本 + 公钥算法标识 + H2 | |||||
- 校验和 C = 前4字节( SHA256( SHA256( X )) ) | |||||
- 地址 Address = Base58( X + C ) | |||||
### 2. 区块 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>BlockHash</td> | |||||
<td>当前区块 hash</td> | |||||
<td>对区块中除此之外的其它所有属性一起进行哈希运算生成</td> | |||||
</tr> | |||||
<tr> | |||||
<td>BlockVersion </td> | |||||
<td>区块版本</td> | |||||
<td>表示区块-交易的属性结构的版本号;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>PreviousBlockHash</td> | |||||
<td>上一区块 hash</td> | |||||
<td></td> | |||||
</tr> | |||||
<tr> | |||||
<td>BlockNumber</td> | |||||
<td>区块高度</td> | |||||
<td>区块高度是一个区块在链中的序号;<br>创始区块的高度为 0,每个新区块的高度依次递增;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>AccountHash</td> | |||||
<td>账户树hash</td> | |||||
<td>账户的 Merkle Tree 根的 hash</td> | |||||
</tr> | |||||
<tr> | |||||
<td>AccountCount</td> | |||||
<td>账户数量</td> | |||||
<td>区块生成时账本中的全部账户的总数</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxTreeHash</td> | |||||
<td>交易树 hash</td> | |||||
<td>本区块的交易集合的 Merkle Tree 根的 hash</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxCount</td> | |||||
<td>区块交易数量</td> | |||||
<td>当前区块包含的交易的数量;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxTotalCount</td> | |||||
<td>账本交易总数</td> | |||||
<td>截止到当前区块为止当前账本的所有交易的总数量;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CloseTime</td> | |||||
<td>区块关闭时间</td> | |||||
<td>生成当前区块时的区块链节点的网络时间;</td> | |||||
</tr> | |||||
</table> | |||||
### 3. 交易 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Hash</td> | |||||
<td>当前交易 hash</td> | |||||
<td>对交易中除此之外的其它所有属性一起进行哈希运算生成</td> | |||||
</tr> | |||||
<tr> | |||||
<td>LedgerNumber</td> | |||||
<td>区块高度</td> | |||||
<td>交易被包含的区块高度</td> | |||||
</tr> | |||||
<tr> | |||||
<td>BlobHash</td> | |||||
<td>交易数据块hash</td> | |||||
<td>交易的数据块是交易的原始数据,包含客户端提交的交易的全部操作及其参数; | |||||
<br>交易的参与者需要使用私钥对交易数据块进行签名;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Operations</td> | |||||
<td>操作列表</td> | |||||
<td>交易的操作列表;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Sponsor</td> | |||||
<td>交易发起人</td> | |||||
<td>交易发起人的账户地址;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>SequenceNumber</td> | |||||
<td>交易序号</td> | |||||
<td>交易序号记录了一个特定的发起人的交易的顺序号,等同于该发起人历史上发起的交易的总数;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Signatures</td> | |||||
<td>签名列表</td> | |||||
<td>由交易发起人和其它参与者对交易数据块的签名的列表;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Result</td> | |||||
<td>交易结果</td> | |||||
<td>0 - 表示执行成功;非零表示执行失败;<br>注:最终的账本只包含成功的交易;</td> | |||||
</tr> | |||||
</table> | |||||
### 4. 操作 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>OpType</td> | |||||
<td>操作类型</td> | |||||
<td> | |||||
一级操作类型包括:注册账户、配置权限、写入键值数据、写入对象数据、定义合约代码、调用合约代码;<br> | |||||
“键值数据写入”操作的子操作类型包括:填入键值、移除键、数值增加、数值减少;<br> | |||||
“对象数据写入”操作的自操作类型包括:插入对象、更新对象、移除对象; | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Args</td> | |||||
<td>参数列表</td> | |||||
<td>与操作类型相对应的参数列表;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>SubOps</td> | |||||
<td>子操作列表</td> | |||||
<td>“子操作”是“操作”的递归定义,由“操作类型”来标识;</td> | |||||
</tr> | |||||
</table> | |||||
### 5. 账户 | |||||
<table> | |||||
<tr> | |||||
<th>属性</th> | |||||
<th>名称</th> | |||||
<th>说明</th> | |||||
</tr> | |||||
<tr> | |||||
<td>Address</td> | |||||
<td>地址</td> | |||||
<td>账户的唯一标识</td> | |||||
</tr> | |||||
<tr> | |||||
<td>RegNumber</td> | |||||
<td>注册号</td> | |||||
<td>账户被注册到区块链的区块高度;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>TxSquenceNumber</td> | |||||
<td>交易序列号</td> | |||||
<td>由账户发起的交易的序列号,初始为 0,账户每发起一个交易则增加1;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>ModelVersion</td> | |||||
<td>账户模型版本</td> | |||||
<td>表示构成一个账户结构的属性模型的程序版本号;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>Version</td> | |||||
<td>账户版本</td> | |||||
<td>初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;<br>注:交易序号的改变不会导致账户版本的增加;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>PrivilegeHash</td> | |||||
<td>权限 hash</td> | |||||
<td>权限树的根hash;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>PrivilegeVersion</td> | |||||
<td>权限版本</td> | |||||
<td>初始为 0, 每次对权限的变更都导致版本号加 1;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>StateType</td> | |||||
<td>状态类型</td> | |||||
<td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>StateVersion</td> | |||||
<td>状态版本</td> | |||||
<td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>StateHash</td> | |||||
<td>状态哈希</td> | |||||
<td>数据状态的 merkle tree 的根hash;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CodeHash</td> | |||||
<td>合约代码哈希</td> | |||||
<td>由“账户地址+合约代码版本号+合约代码内容”生成的哈希;</td> | |||||
</tr> | |||||
<tr> | |||||
<td>CodeVersion</td> | |||||
<td>代码版本</td> | |||||
<td>初始为 0,每次对代码的变更都使版本加 1 ;</td> | |||||
</tr> | |||||
</table> | |||||
## 五、编程接口 | |||||
### 1. 服务连接 | |||||
```java | |||||
//创建服务代理 | |||||
public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(); | |||||
final String GATEWAY_IP = "127.0.0.1"; | |||||
final int GATEWAY_PORT = 80; | |||||
final boolean SECURE = false; | |||||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | |||||
CLIENT_CERT); | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
``` | |||||
### 2. 用户注册 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义注册账号的 TX; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||||
CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||||
BlockchainKeyPair user = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||||
txTemp.users().register(user.getIdentity()); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
``` | |||||
### 3. 数据账户注册 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义注册账号的 TX; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||||
CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||||
BlockchainKeyPair dataAccount = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
``` | |||||
### 4. 写入数据 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
HashDigest ledgerHash = getLedgerHash(); | |||||
// 在本地定义注册账号的 TX; | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
// -------------------------------------- | |||||
// 将商品信息写入到指定的账户中; | |||||
// 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; | |||||
String commodityDataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||||
Commodity commodity1 = new Commodity(); | |||||
txTemp.dataAccount(commodityDataAccount).set("ASSET_CODE", commodity1.getCode().getBytes(), -1); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
prepTx.commit(); | |||||
``` | |||||
### 5. 查询数据 | |||||
> 注:详细的查询可参考模块sdk-samples中SDK_GateWay_Query_Test_相关测试用例 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 查询区块信息; | |||||
// 区块高度; | |||||
long ledgerNumber = service.getLedger(LEDGER_HASH).getLatestBlockHeight(); | |||||
// 最新区块; | |||||
LedgerBlock latestBlock = service.getBlock(LEDGER_HASH, ledgerNumber); | |||||
// 区块中的交易的数量; | |||||
long txCount = service.getTransactionCount(LEDGER_HASH, latestBlock.getHash()); | |||||
// 获取交易列表; | |||||
LedgerTransaction[] txList = service.getTransactions(LEDGER_HASH, ledgerNumber, 0, 100); | |||||
// 遍历交易列表 | |||||
for (LedgerTransaction ledgerTransaction : txList) { | |||||
TransactionContent txContent = ledgerTransaction.getTransactionContent(); | |||||
Operation[] operations = txContent.getOperations(); | |||||
if (operations != null && operations.length > 0) { | |||||
for (Operation operation : operations) { | |||||
operation = ClientOperationUtil.read(operation); | |||||
// 操作类型:数据账户注册操作 | |||||
if (operation instanceof DataAccountRegisterOperation) { | |||||
DataAccountRegisterOperation daro = (DataAccountRegisterOperation) operation; | |||||
BlockchainIdentity blockchainIdentity = daro.getAccountID(); | |||||
} | |||||
// 操作类型:用户注册操作 | |||||
else if (operation instanceof UserRegisterOperation) { | |||||
UserRegisterOperation uro = (UserRegisterOperation) operation; | |||||
BlockchainIdentity blockchainIdentity = uro.getUserID(); | |||||
} | |||||
// 操作类型:账本注册操作 | |||||
else if (operation instanceof LedgerInitOperation) { | |||||
LedgerInitOperation ledgerInitOperation = (LedgerInitOperation)operation; | |||||
LedgerInitSetting ledgerInitSetting = ledgerInitOperation.getInitSetting(); | |||||
ParticipantNode[] participantNodes = ledgerInitSetting.getConsensusParticipants(); | |||||
} | |||||
// 操作类型:合约发布操作 | |||||
else if (operation instanceof ContractCodeDeployOperation) { | |||||
ContractCodeDeployOperation ccdo = (ContractCodeDeployOperation) operation; | |||||
BlockchainIdentity blockchainIdentity = ccdo.getContractID(); | |||||
} | |||||
// 操作类型:合约执行操作 | |||||
else if (operation instanceof ContractEventSendOperation) { | |||||
ContractEventSendOperation ceso = (ContractEventSendOperation) operation; | |||||
} | |||||
// 操作类型:KV存储操作 | |||||
else if (operation instanceof DataAccountKVSetOperation) { | |||||
DataAccountKVSetOperation.KVWriteEntry[] kvWriteEntries = | |||||
((DataAccountKVSetOperation) operation).getWriteSet(); | |||||
if (kvWriteEntries != null && kvWriteEntries.length > 0) { | |||||
for (DataAccountKVSetOperation.KVWriteEntry kvWriteEntry : kvWriteEntries) { | |||||
BytesValue bytesValue = kvWriteEntry.getValue(); | |||||
DataType dataType = bytesValue.getType(); | |||||
Object showVal = ClientOperationUtil.readValueByBytesValue(bytesValue); | |||||
System.out.println("writeSet.key=" + kvWriteEntry.getKey()); | |||||
System.out.println("writeSet.value=" + showVal); | |||||
System.out.println("writeSet.type=" + dataType); | |||||
System.out.println("writeSet.version=" + kvWriteEntry.getExpectedVersion()); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
// 根据交易的 hash 获得交易;注:客户端生成 PrepareTransaction 时得到交易hash; | |||||
HashDigest txHash = txList[0].getTransactionContent().getHash(); | |||||
Transaction tx = service.getTransactionByContentHash(LEDGER_HASH, txHash); | |||||
// 获取数据; | |||||
String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||||
String[] objKeys = new String[] { "x001", "x002" }; | |||||
KVDataEntry[] kvData = service.getDataEntries(LEDGER_HASH, commerceAccount, objKeys); | |||||
long payloadVersion = kvData[0].getVersion(); | |||||
// 获取数据账户下所有的KV列表 | |||||
KVDataEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||||
if (kvData != null && kvData.length > 0) { | |||||
for (KVDataEntry kvDatum : kvData) { | |||||
System.out.println("kvData.key=" + kvDatum.getKey()); | |||||
System.out.println("kvData.version=" + kvDatum.getVersion()); | |||||
System.out.println("kvData.type=" + kvDatum.getType()); | |||||
System.out.println("kvData.value=" + kvDatum.getValue()); | |||||
} | |||||
} | |||||
``` | |||||
### 6. 合约发布 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义TX模板 | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
// 合约内容读取 | |||||
byte[] contractBytes = FileUtils.readBytes(new File(CONTRACT_FILE)); | |||||
// 生成用户 | |||||
BlockchainIdentityData blockchainIdentity = new BlockchainIdentityData(getSponsorKey().getPubKey()); | |||||
// 发布合约 | |||||
txTemp.contracts().deploy(blockchainIdentity, contractBytes); | |||||
// TX 准备就绪; | |||||
PreparedTransaction prepTx = txTemp.prepare(); | |||||
// 使用私钥进行签名; | |||||
CryptoKeyPair keyPair = getSponsorKey(); | |||||
prepTx.sign(keyPair); | |||||
// 提交交易; | |||||
TransactionResponse transactionResponse = prepTx.commit(); | |||||
assertTrue(transactionResponse.isSuccess()); | |||||
// 打印合约地址 | |||||
System.out.println(blockchainIdentity.getAddress().toBase58()); | |||||
``` | |||||
### 7. 合约执行 | |||||
```java | |||||
// 创建服务代理; | |||||
BlockchainService service = serviceFactory.getBlockchainService(); | |||||
// 在本地定义TX模板 | |||||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||||
// 合约地址 | |||||
String contractAddressBase58 = ""; | |||||
// 使用接口方式调用合约 | |||||
TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||||
// 使用decode方式调用合约内部方法(create方法) | |||||
// 返回GenericValueHolder可通过get方法获取结果,但get方法需要在commit调用后执行 | |||||
GenericValueHolder<String> result = ContractReturnValue.decode(transferContract.create(address, account, money)); | |||||
PreparedTransaction ptx = txTpl.prepare(); | |||||
ptx.sign(adminKey); | |||||
TransactionResponse transactionResponse = ptx.commit(); | |||||
String cotractExecResult = result.get(); | |||||
// TransactionResponse也提供了可供查询结果的接口 | |||||
OperationResult[] operationResults = transactionResponse.getOperationResults(); | |||||
// 通过OperationResult获取结果 | |||||
for (int i = 0; i < operationResults.length; i++) { | |||||
OperationResult opResult = operationResults[i]; | |||||
System.out.printf("Operation[%s].result = %s \r\n", | |||||
opResult.getIndex(), BytesValueEncoding.decode(opResult.getResult())); | |||||
} | |||||
``` |
@@ -168,4 +168,5 @@ public interface DataCodes { | |||||
public static final int CONSENSUS_MSGQUEUE_BLOCK_SETTINGS = CONSENSUS_MSGQUEUE | 0x05; | public static final int CONSENSUS_MSGQUEUE_BLOCK_SETTINGS = CONSENSUS_MSGQUEUE | 0x05; | ||||
} | } |
@@ -0,0 +1,19 @@ | |||||
package com.jd.blockchain.consts; | |||||
import java.util.TimeZone; | |||||
public class Global { | |||||
public static final String DEFAULT_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss.SSSZ"; | |||||
public static final String DEFAULT_TIME_ZONE = "GMT+08:00"; | |||||
static { | |||||
initialize(); | |||||
} | |||||
public static void initialize() { | |||||
TimeZone.setDefault(TimeZone.getTimeZone(DEFAULT_TIME_ZONE)); | |||||
} | |||||
} |
@@ -1,74 +0,0 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> | |||||
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出 --> | |||||
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数 --> | |||||
<configuration status="WARN" monitorInterval="60"> | |||||
<!--先定义所有的appender --> | |||||
<appenders> | |||||
<!--这个输出控制台的配置 --> | |||||
<console name="Console" target="SYSTEM_OUT"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> | |||||
<ThresholdFilter level="error" onMatch="ACCEPT" | |||||
onMismatch="DENY" /> | |||||
<!--输出日志的格式 --> | |||||
<PatternLayout | |||||
pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" /> | |||||
</console> | |||||
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用 --> | |||||
<File name="log" fileName="../logs/test.log" append="false"> | |||||
<PatternLayout | |||||
pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n" /> | |||||
</File> | |||||
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档 --> | |||||
<RollingFile name="PeerRollingInfo" | |||||
fileName="../logs/peer.out.info.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/peer.out.info-%d{yyyy-MM-dd}-%i.log"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch) --> | |||||
<ThresholdFilter level="info" onMatch="ACCEPT" | |||||
onMismatch="DENY" /> | |||||
<PatternLayout | |||||
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" /> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy /> | |||||
<SizeBasedTriggeringPolicy size="100 MB" /> | |||||
</Policies> | |||||
</RollingFile> | |||||
<RollingFile name="PeerRollingWarn" | |||||
fileName="../logs/peer.out.warn.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/peer.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="warn" onMatch="ACCEPT" | |||||
onMismatch="DENY" /> | |||||
<PatternLayout | |||||
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" /> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy /> | |||||
<SizeBasedTriggeringPolicy size="100 MB" /> | |||||
</Policies> | |||||
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> | |||||
<DefaultRolloverStrategy max="20" /> | |||||
</RollingFile> | |||||
<RollingFile name="PeerRollingError" | |||||
fileName="../logs/peer.out.error.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/peer.out.error-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="error" onMatch="ACCEPT" | |||||
onMismatch="DENY" /> | |||||
<PatternLayout | |||||
pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n" /> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy /> | |||||
<SizeBasedTriggeringPolicy size="100 MB" /> | |||||
</Policies> | |||||
</RollingFile> | |||||
</appenders> | |||||
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效 --> | |||||
<loggers> | |||||
<!--过滤掉spring的一些DEBUG信息 --> | |||||
<logger name="org.springframework" level="INFO"></logger> | |||||
<root level="all"> | |||||
<appender-ref ref="Console" /> | |||||
<appender-ref ref="PeerRollingInfo" /> | |||||
<appender-ref ref="PeerRollingWarn" /> | |||||
<appender-ref ref="PeerRollingError" /> | |||||
</root> | |||||
</loggers> | |||||
</configuration> |
@@ -0,0 +1,20 @@ | |||||
package com.jd.blockchain; | |||||
import static org.junit.Assert.assertTrue; | |||||
import org.junit.Test; | |||||
/** | |||||
* Unit test for simple App. | |||||
*/ | |||||
public class AppTest | |||||
{ | |||||
/** | |||||
* Rigorous Test :-) | |||||
*/ | |||||
@Test | |||||
public void shouldAnswerWithTrue() | |||||
{ | |||||
assertTrue( true ); | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.jd.blockchain; | |||||
import static org.junit.Assert.assertTrue; | |||||
import org.junit.Test; | |||||
/** | |||||
* Unit test for simple App. | |||||
*/ | |||||
public class AppTest | |||||
{ | |||||
/** | |||||
* Rigorous Test :-) | |||||
*/ | |||||
@Test | |||||
public void shouldAnswerWithTrue() | |||||
{ | |||||
assertTrue( true ); | |||||
} | |||||
} |
@@ -5,5 +5,5 @@ GATEWAY=$(ls $HOME/lib | grep deployment-gateway-) | |||||
if [ ! -n "$GATEWAY" ]; then | if [ ! -n "$GATEWAY" ]; then | ||||
echo "GateWay Is Null !!!" | echo "GateWay Is Null !!!" | ||||
else | else | ||||
nohup java -jar -server $HOME/lib/$GATEWAY -c $HOME/config/gateway.conf $* > gw.out 2>&1 & | |||||
nohup java -jar -server -Dgateway.log=$HOME $HOME/lib/$GATEWAY -c $HOME/config/gateway.conf $* >$HOME/bin/gw.out 2>&1 & | |||||
fi | fi |
@@ -25,7 +25,11 @@ | |||||
<artifactId>runtime-modular-booter</artifactId> | <artifactId>runtime-modular-booter</artifactId> | ||||
<version>${project.version}</version> | <version>${project.version}</version> | ||||
</dependency> | </dependency> | ||||
<!--<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-booter</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency>--> | |||||
<dependency> | <dependency> | ||||
<groupId>com.jd.blockchain</groupId> | <groupId>com.jd.blockchain</groupId> | ||||
<artifactId>storage-composite</artifactId> | <artifactId>storage-composite</artifactId> | ||||
@@ -55,6 +55,16 @@ | |||||
<exclude>com.jd.blockchain:deployment-peer</exclude> | <exclude>com.jd.blockchain:deployment-peer</exclude> | ||||
</excludes> | </excludes> | ||||
</dependencySet> | </dependencySet> | ||||
<!--<dependencySet> | |||||
<unpack>false</unpack> | |||||
<useProjectArtifact>true</useProjectArtifact> | |||||
<outputDirectory>ext</outputDirectory> | |||||
<includes> | |||||
<include>com.jd.blockchain:ump-booter</include> | |||||
</includes> | |||||
</dependencySet>--> | |||||
</dependencySets> | </dependencySets> | ||||
<moduleSets> | <moduleSets> | ||||
@@ -78,7 +78,7 @@ Peer打包程序解压完后的安装包结构如下: | |||||
其中目录说明如下: | 其中目录说明如下: | ||||
+ **bin** :相关命令操作目录; | + **bin** :相关命令操作目录; | ||||
+ **config** :对应命令的配置目录,keys路径默认可能不存在,会在执行相关脚本时自动创建; | |||||
+ **config** :对应命令的配置目录,keys路径解压时不存在,会在执行keygen.sh脚本时自动创建;ledger-binding.conf文件解压时不存在,会在成功执行ledger-init.sh脚本后生成; | |||||
+ **docs** :相关文档保存目录; | + **docs** :相关文档保存目录; | ||||
+ **libs** :项目运行依赖第三方及非system依赖包保存路径; | + **libs** :项目运行依赖第三方及非system依赖包保存路径; | ||||
+ **system** :项目运行系统包保存路径; | + **system** :项目运行系统包保存路径; | ||||
@@ -0,0 +1,9 @@ | |||||
#!/bin/bash | |||||
HOME=$(cd `dirname $0`;cd ../; pwd) | |||||
UMP=$(ls $HOME/ext | grep ump-booter-) | |||||
if [ ! -n "UMP" ]; then | |||||
echo "Unified Management Platform Is Null !!!" | |||||
else | |||||
nohup java -jar -server -Djump.log=$HOME $HOME/ext/$UMP -p 8000 $* >$HOME/bin/jump.out 2>&1 & | |||||
fi |
@@ -0,0 +1,16 @@ | |||||
#!/bin/bash | |||||
#启动Home路径 | |||||
BOOT_HOME=$(cd `dirname $0`;cd ../; pwd) | |||||
#获取进程PID | |||||
PID=`ps -ef | grep $BOOT_HOME/ext/ump-booter | grep -v grep | awk '{print $2}'` | |||||
#通过Kill命令将进程杀死 | |||||
if [ -z "$PID" ]; then | |||||
echo "Unable to find UMP PID. stop aborted." | |||||
else | |||||
echo "Start to kill PID = $PID ..." | |||||
kill -9 $PID | |||||
echo "Unified Management Platform has been stopped ..." | |||||
fi |
@@ -1,9 +1,9 @@ | |||||
#!/bin/bash | #!/bin/bash | ||||
HOME=$(cd `dirname $0`;cd ../; pwd) | HOME=$(cd `dirname $0`;cd ../; pwd) | ||||
boot_file=$(ls ../libs | grep tools-initializer-booter-) | |||||
boot_file=$(ls $HOME/libs | grep tools-initializer-booter-) | |||||
if [ ! -n "$boot_file" ]; then | if [ ! -n "$boot_file" ]; then | ||||
echo "tools-initializer-booter is null" | echo "tools-initializer-booter is null" | ||||
else | else | ||||
java -jar $HOME/libs/$boot_file -l $HOME/config/init/local.conf -i $HOME/config/init/ledger.init $* | |||||
fi | |||||
java -jar -server -Dinit.log=$HOME $HOME/libs/$boot_file -l $HOME/config/init/local.conf -i $HOME/config/init/ledger.init $* | |||||
fi |
@@ -5,5 +5,5 @@ PEER=$(ls $HOME/system | grep deployment-peer-) | |||||
if [ ! -n "$PEER" ]; then | if [ ! -n "$PEER" ]; then | ||||
echo "Peer Is Null !!!" | echo "Peer Is Null !!!" | ||||
else | else | ||||
nohup java -jar -server -Xmx2g -Xms2g $HOME/system/$PEER -home=$HOME -c $HOME/config/ledger-binding.conf -p 7080 $* & | |||||
nohup java -jar -server -Xmx2g -Xms2g -Dpeer.log=$HOME $HOME/system/$PEER -home=$HOME -c $HOME/config/ledger-binding.conf -p 7080 $* >$HOME/bin/peer.out 2>&1 & | |||||
fi | fi |
@@ -13,12 +13,12 @@ | |||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | ||||
</console> | </console> | ||||
<!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> | <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> | ||||
<File name="log" fileName="../logs/test.log" append="false"> | |||||
<File name="log" fileName="${sys:gateway.log}/logs/gateway.temp.log" append="false"> | |||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | ||||
</File> | </File> | ||||
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> | <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> | ||||
<RollingFile name="GatewayRollingInfo" fileName="../logs/gateway.out.info.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/gateway.out.info-%d{yyyy-MM-dd}-%i.log"> | |||||
<RollingFile name="GatewayRollingInfo" fileName="${sys:gateway.log}/logs/gateway.info.log" | |||||
filePattern="${sys:gateway.log}/logs/$${date:yyyy-MM}/gateway.info-%d{yyyy-MM-dd}-%i.log"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | ||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> | <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> | ||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | ||||
@@ -27,8 +27,8 @@ | |||||
<SizeBasedTriggeringPolicy size="100 MB"/> | <SizeBasedTriggeringPolicy size="100 MB"/> | ||||
</Policies> | </Policies> | ||||
</RollingFile> | </RollingFile> | ||||
<RollingFile name="GatewayRollingWarn" fileName="../logs/gateway.out.warn.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/gateway.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||||
<RollingFile name="GatewayRollingWarn" fileName="${sys:gateway.log}/logs/gateway.warn.log" | |||||
filePattern="${sys:gateway.log}/logs/$${date:yyyy-MM}/gateway.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> | <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> | ||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | ||||
<Policies> | <Policies> | ||||
@@ -38,8 +38,8 @@ | |||||
<!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> | <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> | ||||
<DefaultRolloverStrategy max="20"/> | <DefaultRolloverStrategy max="20"/> | ||||
</RollingFile> | </RollingFile> | ||||
<RollingFile name="GatewayRollingError" fileName="../logs/gateway.out.error.log" | |||||
filePattern="../logs/$${date:yyyy-MM}/gateway.out.error-%d{yyyy-MM-dd}-%i.log"> | |||||
<RollingFile name="GatewayRollingError" fileName="${sys:gateway.log}/logs/gateway.error.log" | |||||
filePattern="${sys:gateway.log}/logs/$${date:yyyy-MM}/gateway.error-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | ||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | ||||
<Policies> | <Policies> | ||||
@@ -51,7 +51,7 @@ | |||||
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> | <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> | ||||
<loggers> | <loggers> | ||||
<!--过滤掉spring的一些DEBUG信息--> | <!--过滤掉spring的一些DEBUG信息--> | ||||
<logger name="org.springframework" level="INFO"></logger> | |||||
<logger name="org.springframework" level="INFO"/> | |||||
<root level="all"> | <root level="all"> | ||||
<appender-ref ref="Console"/> | <appender-ref ref="Console"/> | ||||
<appender-ref ref="GatewayRollingInfo"/> | <appender-ref ref="GatewayRollingInfo"/> | ||||
@@ -79,6 +79,8 @@ public class KVDataObject implements KVDataEntry { | |||||
return BytesUtils.toLong(bytesValue.getValue().toBytes()); | return BytesUtils.toLong(bytesValue.getValue().toBytes()); | ||||
case JSON: | case JSON: | ||||
return bytesValue.getValue().toUTF8String(); | return bytesValue.getValue().toUTF8String(); | ||||
case XML: | |||||
return bytesValue.getValue().toUTF8String(); | |||||
default: | default: | ||||
throw new IllegalStateException("Unsupported value type[" + getType() + "] to resolve!"); | throw new IllegalStateException("Unsupported value type[" + getType() + "] to resolve!"); | ||||
@@ -0,0 +1,18 @@ | |||||
<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> | |||||
<!--<modules> | |||||
<module>ump-booter</module> | |||||
<module>ump-web</module> | |||||
<module>ump-service</module> | |||||
<module>ump-model</module> | |||||
</modules>--> | |||||
<parent> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>jdchain-root</artifactId> | |||||
<version>1.1.0-SNAPSHOT</version> | |||||
</parent> | |||||
<artifactId>manager</artifactId> | |||||
<packaging>pom</packaging> | |||||
</project> |
@@ -0,0 +1,132 @@ | |||||
<?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"> | |||||
<parent> | |||||
<artifactId>manager</artifactId> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<version>1.1.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>ump-booter</artifactId> | |||||
<name>ump-booter</name> | |||||
<properties> | |||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
<maven.compiler.source>1.8</maven.compiler.source> | |||||
<maven.compiler.target>1.8</maven.compiler.target> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
<exclusions> | |||||
<exclusion> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-logging</artifactId> | |||||
</exclusion> | |||||
</exclusions> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-web</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-service</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-model</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-explorer</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-security</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-configuration-processor</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-devtools</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
<build> | |||||
<plugins> | |||||
<plugin> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-maven-plugin</artifactId> | |||||
</plugin> | |||||
<plugin> | |||||
<groupId>org.apache.maven.plugins</groupId> | |||||
<artifactId>maven-assembly-plugin</artifactId> | |||||
<executions> | |||||
<execution> | |||||
<id>make-assembly</id> | |||||
<phase>package</phase> | |||||
<goals> | |||||
<goal>single</goal> | |||||
</goals> | |||||
<configuration> | |||||
<finalName>jump</finalName> | |||||
<descriptors> | |||||
<descriptor>src/main/resources/assembly.xml</descriptor> | |||||
</descriptors> | |||||
</configuration> | |||||
</execution> | |||||
</executions> | |||||
</plugin> | |||||
</plugins> | |||||
<resources> | |||||
<resource> | |||||
<directory>${project.basedir}/libs</directory> | |||||
<targetPath>BOOT-INF/lib/</targetPath> | |||||
<includes> | |||||
<include>*.jar</include> | |||||
</includes> | |||||
</resource> | |||||
<resource> | |||||
<directory>${project.basedir}/src/main/resources</directory> | |||||
<targetPath>BOOT-INF/classes/</targetPath> | |||||
<includes> | |||||
<include>log4j2-jump.xml</include> | |||||
<include>*.txt</include> | |||||
<include>*.properties</include> | |||||
</includes> | |||||
</resource> | |||||
</resources> | |||||
</build> | |||||
</project> |
@@ -0,0 +1,128 @@ | |||||
package com.jd.blockchain.ump; | |||||
import org.springframework.boot.SpringApplication; | |||||
import java.io.File; | |||||
import java.io.InputStream; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Properties; | |||||
public class UmpBooter { | |||||
private static final String ARG_PORT = "-p"; | |||||
private static final String ARG_HOST = "-h"; | |||||
private static final String CONFIG = "BOOT-INF" + File.separator + "classes" + File.separator + "config.properties"; | |||||
private static final String CONFIG_PROP_HOST = "server.host"; | |||||
private static final String CONFIG_PROP_HOST_DEFAULT = "0.0.0.0"; | |||||
private static final String CONFIG_PROP_PORT = "server.port"; | |||||
private static final String CONFIG_PROP_PORT_DEFAULT = "8080"; | |||||
private static final String CONFIG_PROP_DB_URL = "db.url"; | |||||
private static final String CONFIG_PROP_DB_URL_DEFAULT = "rocksdb://#project#/jumpdb"; | |||||
public static void main(String[] args) { | |||||
startServer(server(args)); | |||||
System.out.println("Unified Management Platform Server Start SUCCESS !!!"); | |||||
} | |||||
private static void startServer(Server server) { | |||||
List<String> argList = new ArrayList<>(); | |||||
argList.add(String.format("--server.address=%s", server.host)); | |||||
argList.add(String.format("--server.port=%s", server.port)); | |||||
argList.add(String.format("--db.url=%s", server.dbUrl)); | |||||
String[] args = argList.toArray(new String[argList.size()]); | |||||
// 启动服务器; | |||||
SpringApplication.run(UmpConfiguration.class, args); | |||||
} | |||||
private static Server server(String[] args) { | |||||
Server defaultServer = serverFromConfig(); | |||||
if (args == null || args.length == 0) { | |||||
return defaultServer; | |||||
} | |||||
String host = null; | |||||
int port = 0; | |||||
// 读取参数列表 | |||||
for (int i = 0; i < args.length; i++) { | |||||
String arg = args[i]; | |||||
if (arg.equals(ARG_HOST)) { | |||||
host = args[i + 1]; | |||||
} else if (arg.equals(ARG_PORT)) { | |||||
port = Integer.parseInt(args[i + 1]); | |||||
} | |||||
} | |||||
// 参数列表中的数据不完整,则剩余部分数据从配置文件中获取 | |||||
if (host == null) { | |||||
host = defaultServer.host; | |||||
} | |||||
if (port == 0) { | |||||
port = defaultServer.port; | |||||
} | |||||
return new Server(host, port, defaultServer.dbUrl); | |||||
} | |||||
private static Server serverFromConfig() { | |||||
try { | |||||
InputStream inputStream = UmpBooter.class.getResourceAsStream(File.separator + CONFIG); | |||||
if (inputStream == null) { | |||||
System.err.println("InputStream is NULL !!!"); | |||||
} | |||||
Properties props = new Properties(); | |||||
props.load(inputStream); | |||||
String host = props.getProperty(CONFIG_PROP_HOST, CONFIG_PROP_HOST_DEFAULT); | |||||
int port = Integer.parseInt( | |||||
props.getProperty(CONFIG_PROP_PORT, CONFIG_PROP_PORT_DEFAULT)); | |||||
String dbUrl = props.getProperty(CONFIG_PROP_DB_URL, CONFIG_PROP_DB_URL_DEFAULT); | |||||
return new Server(host, port, dbUrl); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
private static class Server { | |||||
private String host; | |||||
private int port; | |||||
private String dbUrl; | |||||
public Server(String host, int port, String dbUrl) { | |||||
this.host = host; | |||||
this.port = port; | |||||
this.dbUrl = dbUrl; | |||||
} | |||||
public String getHost() { | |||||
return host; | |||||
} | |||||
public void setHost(String host) { | |||||
this.host = host; | |||||
} | |||||
public int getPort() { | |||||
return port; | |||||
} | |||||
public void setPort(int port) { | |||||
this.port = port; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,12 @@ | |||||
package com.jd.blockchain.ump; | |||||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||||
@EnableConfigurationProperties | |||||
@SpringBootApplication(exclude = { | |||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class} | |||||
) | |||||
public class UmpConfiguration { | |||||
} |
@@ -0,0 +1,5 @@ | |||||
server.tomcat.uri-encoding=utf-8 | |||||
spring.mvc.favicon.enabled=false | |||||
logging.config=classpath:log4j2-jump.xml |
@@ -0,0 +1,27 @@ | |||||
<?xml version='1.0' encoding='UTF-8'?> | |||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" | |||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 | |||||
http://maven.apache.org/xsd/assembly-1.1.0.xsd"> | |||||
<id>${project.version}</id> | |||||
<formats> | |||||
<format>zip</format> | |||||
</formats> | |||||
<includeBaseDirectory>false</includeBaseDirectory> | |||||
<fileSets> | |||||
<fileSet> | |||||
<directory>src/main/resources/scripts</directory> | |||||
<outputDirectory>bin</outputDirectory> | |||||
</fileSet> | |||||
</fileSets> | |||||
<dependencySets> | |||||
<dependencySet> | |||||
<unpack>false</unpack> | |||||
<useProjectArtifact>true</useProjectArtifact> | |||||
<outputDirectory>ext</outputDirectory> | |||||
<includes> | |||||
<include>com.jd.blockchain:ump-booter</include> | |||||
</includes> | |||||
</dependencySet> | |||||
</dependencySets> | |||||
</assembly> |
@@ -0,0 +1,13 @@ | |||||
▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄ | |||||
▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░▌ ▐░▌ | |||||
▀▀▀▀▀█░█▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌ ▐░▌ | |||||
▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ | |||||
▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ | |||||
▐░▌ ▐░▌ ▐░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ | |||||
▐░▌ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ | |||||
▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ | |||||
▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌ ▐░▐░▌ | |||||
▐░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░░▌ | |||||
▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀ | |||||
@@ -0,0 +1,8 @@ | |||||
# Tomcat启动的HOST,默认为0.0.0.0 | |||||
server.host=0.0.0.0 | |||||
# Tomcat启动监听端口号 | |||||
server.port=8080 | |||||
# 本地数据库存储位置 | |||||
db.url=rocksdb://#project#/jumpdb |
@@ -0,0 +1,46 @@ | |||||
<?xml version="1.0" encoding="UTF-8"?> | |||||
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL --> | |||||
<!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> | |||||
<!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> | |||||
<configuration status="WARN" monitorInterval="60"> | |||||
<!--先定义所有的appender--> | |||||
<appenders> | |||||
<!--这个输出控制台的配置--> | |||||
<console name="Console" target="SYSTEM_OUT"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | |||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<!--输出日志的格式--> | |||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | |||||
</console> | |||||
<!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> | |||||
<RollingFile name="UmpRollingInfo" fileName="${sys:jump.log}/logs/ump.info.log" | |||||
filePattern="${sys:jump.log}/logs/$${date:yyyy-MM}/ump.info-%d{yyyy-MM-dd}-%i.log"> | |||||
<!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | |||||
<ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy/> | |||||
<SizeBasedTriggeringPolicy size="100 MB"/> | |||||
</Policies> | |||||
</RollingFile> | |||||
<RollingFile name="UmpRollingError" fileName="${sys:jump.log}/logs/ump.error.log" | |||||
filePattern="${sys:jump.log}/logs/$${date:yyyy-MM}/ump.error-%d{yyyy-MM-dd}-%i.log"> | |||||
<ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||||
<PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||||
<Policies> | |||||
<TimeBasedTriggeringPolicy/> | |||||
<SizeBasedTriggeringPolicy size="100 MB"/> | |||||
</Policies> | |||||
</RollingFile> | |||||
</appenders> | |||||
<!--然后定义logger,只有定义了logger并引入的appender,appender才会生效--> | |||||
<loggers> | |||||
<!--过滤掉spring的一些DEBUG信息--> | |||||
<logger name="org.springframework" level="INFO"></logger> | |||||
<root level="all"> | |||||
<appender-ref ref="Console"/> | |||||
<appender-ref ref="UmpRollingInfo"/> | |||||
<appender-ref ref="UmpRollingError"/> | |||||
</root> | |||||
</loggers> | |||||
</configuration> |
@@ -0,0 +1,9 @@ | |||||
#!/bin/bash | |||||
HOME=$(cd `dirname $0`;cd ../; pwd) | |||||
UMP=$(ls $HOME/ext | grep ump-booter-) | |||||
if [ ! -n "UMP" ]; then | |||||
echo "Unified Management Platform Is Null !!!" | |||||
else | |||||
nohup java -jar -server -Djump.log=$HOME $HOME/ext/$UMP $* >$HOME/bin/jump.out 2>&1 & | |||||
fi |
@@ -0,0 +1,16 @@ | |||||
#!/bin/bash | |||||
#启动Home路径 | |||||
BOOT_HOME=$(cd `dirname $0`;cd ../; pwd) | |||||
#获取进程PID | |||||
PID=`ps -ef | grep $BOOT_HOME/ext/ump-booter | grep -v grep | awk '{print $2}'` | |||||
#通过Kill命令将进程杀死 | |||||
if [ -z "$PID" ]; then | |||||
echo "Unable to find UMP PID. stop aborted." | |||||
else | |||||
echo "Start to kill PID = $PID ..." | |||||
kill -9 $PID | |||||
echo "Unified Management Platform has been stopped ..." | |||||
fi |
@@ -0,0 +1,81 @@ | |||||
<?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"> | |||||
<parent> | |||||
<artifactId>manager</artifactId> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<version>1.1.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>ump-model</artifactId> | |||||
<name>ump-model</name> | |||||
<properties> | |||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
<maven.compiler.source>1.8</maven.compiler.source> | |||||
<maven.compiler.target>1.8</maven.compiler.target> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
<exclusions> | |||||
<exclusion> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-logging</artifactId> | |||||
</exclusion> | |||||
</exclusions> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-security</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-configuration-processor</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-devtools</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba</groupId> | |||||
<artifactId>fastjson</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.reflections</groupId> | |||||
<artifactId>reflections</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.rocksdb</groupId> | |||||
<artifactId>rocksdbjni</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>commons-io</groupId> | |||||
<artifactId>commons-io</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,16 @@ | |||||
package com.jd.blockchain.ump.dao; | |||||
public interface DBConnection { | |||||
String dbSchema(); | |||||
DBConnection initDbUrl(String dbUrl); | |||||
void put(String key, String value); | |||||
void put(String key, Object value, Class<?> type); | |||||
String get(String key); | |||||
boolean exist(String dbUrl); | |||||
} |
@@ -0,0 +1,58 @@ | |||||
package com.jd.blockchain.ump.dao; | |||||
import org.reflections.Reflections; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
public class DBConnectionProvider { | |||||
private static final Map<String, DBConnection> dbConnections = new ConcurrentHashMap<>(); | |||||
static { | |||||
init(); | |||||
} | |||||
public static DBConnection dbConnection(String dbUrl) { | |||||
String dbSchema = dbSchema(dbUrl); | |||||
if (!dbConnections.containsKey(dbSchema)) { | |||||
throw new IllegalStateException( | |||||
String.format("Can not find DBConnection by {%s} !", dbUrl)); | |||||
} | |||||
DBConnection dbConnection = dbConnections.get(dbSchema); | |||||
return dbConnection.initDbUrl(dbUrl); | |||||
} | |||||
private static String dbSchema(String dbUrl) { | |||||
// rocksdb:///home/xxx -> rocksdb | |||||
return dbUrl.split("://")[0]; | |||||
} | |||||
private static void init() { | |||||
// 初始化所有实现类 | |||||
Reflections reflections = new Reflections("com.jd.blockchain.ump.dao"); | |||||
Set<Class<? extends DBConnection>> dbConnectionSet = | |||||
reflections.getSubTypesOf(DBConnection.class); | |||||
for (Class<? extends DBConnection> clazz : dbConnectionSet) { | |||||
if (!clazz.isInterface() && !clazz.equals(UmpDaoHandler.class)) { | |||||
try { | |||||
// 根据class生成对象 | |||||
DBConnection dbConnection = clazz.newInstance(); | |||||
String dbSchema = dbConnection.dbSchema(); | |||||
if (dbSchema != null && dbSchema.length() > 0) { | |||||
dbConnections.put(dbSchema, dbConnection); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,47 @@ | |||||
package com.jd.blockchain.ump.dao; | |||||
import com.alibaba.fastjson.JSON; | |||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
public class MemoryDBConnection implements DBConnection { | |||||
private static final String MEMORY_SCHEMA = "memory"; | |||||
private final Map<String, String> memory = new ConcurrentHashMap<>(); | |||||
@Override | |||||
public String dbSchema() { | |||||
return MEMORY_SCHEMA; | |||||
} | |||||
@Override | |||||
public DBConnection initDbUrl(String dbUrl) { | |||||
return this; | |||||
} | |||||
@Override | |||||
public void put(String key, String value) { | |||||
memory.put(key, value); | |||||
} | |||||
@Override | |||||
public void put(String key, Object value, Class<?> type) { | |||||
String json = JSON.toJSONString(value); | |||||
put(key, json); | |||||
} | |||||
@Override | |||||
public String get(String key) { | |||||
return memory.get(key); | |||||
} | |||||
@Override | |||||
public boolean exist(String dbUrl) { | |||||
if (dbUrl.startsWith(MEMORY_SCHEMA)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,145 @@ | |||||
package com.jd.blockchain.ump.dao; | |||||
import com.alibaba.fastjson.JSON; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.rocksdb.*; | |||||
import org.rocksdb.util.SizeUnit; | |||||
import java.io.File; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import static java.nio.charset.StandardCharsets.UTF_8; | |||||
public class RocksDBConnection implements DBConnection { | |||||
public static final String SCHEMA = "rocksdb"; | |||||
public static final String PROTOCOL_SPLIT = "://"; | |||||
public static final String ROCKSDB_PROTOCOL = SCHEMA + PROTOCOL_SPLIT; | |||||
static { | |||||
RocksDB.loadLibrary(); | |||||
} | |||||
private RocksDB rocksDB; | |||||
@Override | |||||
public String dbSchema() { | |||||
return SCHEMA; | |||||
} | |||||
@Override | |||||
public DBConnection initDbUrl(String dbUrl) { | |||||
if (!dbUrl.startsWith(dbSchema())) { | |||||
throw new IllegalStateException(String.format("Unsupport DBConnection by URL {%s} !!!", dbUrl)); | |||||
} | |||||
String dbSavePath = dbUrl.split(PROTOCOL_SPLIT)[1]; | |||||
initDBConnection(dbSavePath); | |||||
return this; | |||||
} | |||||
@Override | |||||
public void put(String key, String value) { | |||||
if (this.rocksDB == null) { | |||||
throw new IllegalStateException("Rocksdb is NULL, Please initDbUrl first !!!"); | |||||
} | |||||
try { | |||||
this.rocksDB.put(key.getBytes(UTF_8), value.getBytes(UTF_8)); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
@Override | |||||
public void put(String key, Object value, Class<?> type) { | |||||
// 使用JSON序列化 | |||||
String json = JSON.toJSONString(value); | |||||
put(key, json); | |||||
} | |||||
@Override | |||||
public String get(String key) { | |||||
try { | |||||
byte[] value = this.rocksDB.get(key.getBytes(UTF_8)); | |||||
if (value != null && value.length > 0) { | |||||
return new String(value, UTF_8); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public boolean exist(String dbUrl) { | |||||
// 首先该dbUrl是Rocksdb | |||||
if (dbUrl.startsWith(ROCKSDB_PROTOCOL)) { | |||||
// 判断File是否存在,并且是文件夹 | |||||
File dbPath = new File(dbUrl.substring(ROCKSDB_PROTOCOL.length())); | |||||
if (dbPath.exists() && dbPath.isDirectory()) { | |||||
return true; | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
private void initDBConnection(String dbUrl) { | |||||
try { | |||||
File dbPath = new File(dbUrl); | |||||
File dbParentPath = dbPath.getParentFile(); | |||||
if (!dbParentPath.exists()) { | |||||
FileUtils.forceMkdir(dbParentPath); | |||||
} | |||||
this.rocksDB = RocksDB.open(initOptions(), dbUrl); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e.getMessage(), e); | |||||
} | |||||
} | |||||
private Options initOptions() { | |||||
final Filter bloomFilter = new BloomFilter(32); | |||||
final BlockBasedTableConfig tableOptions = new BlockBasedTableConfig() | |||||
.setFilter(bloomFilter) | |||||
.setBlockSize(4 * SizeUnit.KB) | |||||
.setBlockSizeDeviation(10) | |||||
.setBlockCacheSize(64 * SizeUnit.GB) | |||||
.setNoBlockCache(false) | |||||
.setCacheIndexAndFilterBlocks(true) | |||||
.setBlockRestartInterval(16) | |||||
; | |||||
final List<CompressionType> compressionLevels = new ArrayList<>(); | |||||
compressionLevels.add(CompressionType.NO_COMPRESSION); // 0-1 | |||||
compressionLevels.add(CompressionType.SNAPPY_COMPRESSION); // 1-2 | |||||
compressionLevels.add(CompressionType.SNAPPY_COMPRESSION); // 2-3 | |||||
compressionLevels.add(CompressionType.SNAPPY_COMPRESSION); // 3-4 | |||||
compressionLevels.add(CompressionType.SNAPPY_COMPRESSION); // 4-5 | |||||
compressionLevels.add(CompressionType.SNAPPY_COMPRESSION); // 5-6 | |||||
compressionLevels.add(CompressionType.SNAPPY_COMPRESSION); // 6-7 | |||||
Options options = new Options() | |||||
.setAllowConcurrentMemtableWrite(true) | |||||
.setEnableWriteThreadAdaptiveYield(true) | |||||
.setCreateIfMissing(true) | |||||
.setMaxWriteBufferNumber(3) | |||||
.setTableFormatConfig(tableOptions) | |||||
.setMaxBackgroundCompactions(10) | |||||
.setMaxBackgroundFlushes(4) | |||||
.setBloomLocality(10) | |||||
.setMinWriteBufferNumberToMerge(4) | |||||
.setCompressionPerLevel(compressionLevels) | |||||
.setNumLevels(7) | |||||
.setCompressionType(CompressionType.SNAPPY_COMPRESSION) | |||||
.setCompactionStyle(CompactionStyle.UNIVERSAL) | |||||
.setMemTableConfig(new SkipListMemTableConfig()) | |||||
; | |||||
return options; | |||||
} | |||||
public static void main(String[] args) { | |||||
String path = "rocksdb:///zhangsan/lisi"; | |||||
System.out.println(path.substring(ROCKSDB_PROTOCOL.length())); | |||||
} | |||||
} |
@@ -0,0 +1,4 @@ | |||||
package com.jd.blockchain.ump.dao; | |||||
public interface UmpDao { | |||||
} |
@@ -0,0 +1,115 @@ | |||||
package com.jd.blockchain.ump.dao; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import org.springframework.boot.CommandLineRunner; | |||||
import org.springframework.stereotype.Repository; | |||||
import java.io.File; | |||||
@Repository | |||||
public class UmpDaoHandler implements UmpDao, CommandLineRunner, DBConnection { | |||||
public final String PROJECT_FLAG = "#project#"; | |||||
private static final String PROTOCOL_FILE = "file:"; | |||||
private static final String INNER_FILE_SEPARATOR = "!"; | |||||
private static final String PROTOCOL_SEPARATOR = "://"; | |||||
private DBConnection dbConnection; | |||||
@Override | |||||
public void run(String... args) { | |||||
String dbUrl = RocksDBConnection.SCHEMA + PROTOCOL_SEPARATOR + | |||||
PROJECT_FLAG + File.separator + UmpConstant.DB_NAME; | |||||
if (args != null && args.length > 0) { | |||||
for (String arg : args) { | |||||
if (arg.startsWith("--db.url")) { | |||||
dbUrl = arg.split("=")[1]; | |||||
} | |||||
} | |||||
} | |||||
dbConnection = DBConnectionProvider.dbConnection(realPath(dbUrl)); | |||||
initProjectPath(); | |||||
} | |||||
private void initProjectPath() { | |||||
UmpConstant.PROJECT_PATH = projectPath(); | |||||
System.out.printf("Init Project Path = %s \r\n", UmpConstant.PROJECT_PATH); | |||||
} | |||||
@Override | |||||
public String dbSchema() { | |||||
return null; | |||||
} | |||||
@Override | |||||
public DBConnection initDbUrl(String dbUrl) { | |||||
return dbConnection; | |||||
} | |||||
@Override | |||||
public void put(String key, String value) { | |||||
dbConnection.put(key, value); | |||||
} | |||||
@Override | |||||
public void put(String key, Object value, Class<?> type) { | |||||
dbConnection.put(key, value, type); | |||||
} | |||||
@Override | |||||
public String get(String key) { | |||||
return dbConnection.get(key); | |||||
} | |||||
@Override | |||||
public boolean exist(String dbUrl) { | |||||
try { | |||||
return dbConnection.exist(dbUrl); | |||||
} catch (Exception e) { | |||||
// 不关心异常 | |||||
System.err.println(e); | |||||
return false; | |||||
} | |||||
} | |||||
private String realPath(String dbUrl) { | |||||
if (dbUrl.contains(PROJECT_FLAG)) { | |||||
// 获取当前jar包路径 | |||||
try { | |||||
String projectPath = projectPath(); | |||||
return dbUrl.replaceAll(PROJECT_FLAG, projectPath); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
return dbUrl; | |||||
} | |||||
private String projectPath() { | |||||
File jarDirectory = new File(jarRootPath()); | |||||
return jarDirectory.getParentFile().getParentFile().getPath(); | |||||
} | |||||
private String jarRootPath() { | |||||
// 获取Jar包所在路径 | |||||
String jarRootPath = UmpDaoHandler.class.getProtectionDomain().getCodeSource().getLocation().getPath(); | |||||
// 处理打包到SpringBoot后路径问题:file: | |||||
if (jarRootPath.startsWith(PROTOCOL_FILE)) { | |||||
jarRootPath = jarRootPath.substring(PROTOCOL_FILE.length()); | |||||
} | |||||
// 处理打包到SpringBoot后内部分隔符问题:! | |||||
if (jarRootPath.contains(INNER_FILE_SEPARATOR)) { | |||||
jarRootPath = jarRootPath.substring(0, jarRootPath.indexOf(INNER_FILE_SEPARATOR)); | |||||
} | |||||
return jarRootPath; | |||||
} | |||||
} |
@@ -0,0 +1,47 @@ | |||||
package com.jd.blockchain.ump.model; | |||||
public class MasterAddr { | |||||
private String ipAddr; | |||||
private int port; | |||||
public MasterAddr() { | |||||
} | |||||
public MasterAddr(String ipAddr, int port) { | |||||
this.ipAddr = ipAddr; | |||||
this.port = port; | |||||
} | |||||
public String getIpAddr() { | |||||
return ipAddr; | |||||
} | |||||
public void setIpAddr(String ipAddr) { | |||||
this.ipAddr = ipAddr; | |||||
} | |||||
public int getPort() { | |||||
return port; | |||||
} | |||||
public void setPort(int port) { | |||||
this.port = port; | |||||
} | |||||
public static MasterAddr newInstance(String ipAddr, int port) { | |||||
return new MasterAddr(ipAddr, port); | |||||
} | |||||
public String toHttpUrl() { | |||||
return "http://" + ipAddr + ":" + port; | |||||
} | |||||
public boolean legal() { | |||||
if (this.ipAddr == null || this.ipAddr.length() == 0 || this.port == 0) { | |||||
return false; | |||||
} | |||||
return true; | |||||
} | |||||
} |
@@ -0,0 +1,88 @@ | |||||
package com.jd.blockchain.ump.model; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class PartiNode { | |||||
private int id; | |||||
private String name; | |||||
private String pubKey; | |||||
private String initHost; | |||||
private int initPort; | |||||
private boolean isSecure; | |||||
public List<String> toConfigChars() { | |||||
List<String> configCharList = new ArrayList<>(); | |||||
configCharList.add(formatConfig(UmpConstant.PARTINODE_NAME_FORMAT, name)); | |||||
configCharList.add(formatConfig(UmpConstant.PARTINODE_PUBKEY_FORMAT, pubKey)); | |||||
configCharList.add(formatConfig(UmpConstant.PARTINODE_INIT_HOST_FORMAT, initHost)); | |||||
configCharList.add(formatConfig(UmpConstant.PARTINODE_INIT_PORT_FORMAT, initPort)); | |||||
configCharList.add(formatConfig(UmpConstant.PARTINODE_INIT_SECURE_FORMAT, isSecure)); | |||||
return configCharList; | |||||
} | |||||
private String formatConfig(String formatter, Object value) { | |||||
return String.format(formatter, id) + "=" + value; | |||||
} | |||||
public int getId() { | |||||
return id; | |||||
} | |||||
public void setId(int id) { | |||||
this.id = id; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public void setPubKey(String pubKey) { | |||||
this.pubKey = pubKey; | |||||
} | |||||
public String getInitHost() { | |||||
return initHost; | |||||
} | |||||
public void setInitHost(String initHost) { | |||||
this.initHost = initHost; | |||||
} | |||||
public int getInitPort() { | |||||
return initPort; | |||||
} | |||||
public void setInitPort(int initPort) { | |||||
this.initPort = initPort; | |||||
} | |||||
public boolean isSecure() { | |||||
return isSecure; | |||||
} | |||||
public void setSecure(boolean secure) { | |||||
isSecure = secure; | |||||
} | |||||
} |
@@ -0,0 +1,217 @@ | |||||
package com.jd.blockchain.ump.model; | |||||
import com.jd.blockchain.ump.model.config.LedgerInitConfig; | |||||
import com.jd.blockchain.ump.model.config.MasterConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerSharedConfig; | |||||
import com.jd.blockchain.ump.model.state.LedgerMasterInstall; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.concurrent.CountDownLatch; | |||||
import java.util.concurrent.TimeUnit; | |||||
import java.util.concurrent.locks.Condition; | |||||
import java.util.concurrent.locks.Lock; | |||||
import java.util.concurrent.locks.ReentrantLock; | |||||
public class PeerSharedConfigs { | |||||
/** | |||||
* 默认的一次邀请码最长等待时间,单位分钟,默认30分钟 | |||||
*/ | |||||
private static final int MAX_WAIT_MINUTE = 30; | |||||
private CountDownLatch latch = null; | |||||
private Lock lock = new ReentrantLock(); | |||||
private Lock waitLock = new ReentrantLock(); | |||||
private Condition sizeCondition = waitLock.newCondition(); | |||||
private List<PeerLocalConfig> sharedConfigs = new ArrayList<>(); | |||||
private int waitNodeSize; | |||||
private String consensusProvider; | |||||
private String sharedKey; | |||||
private String ledgerName; | |||||
private LedgerInitConfig ledgerInitConfig; | |||||
public synchronized PeerSharedConfigs addConfig(PeerLocalConfig sharedConfig) { | |||||
// 判断内容是否存在重复 | |||||
for (PeerSharedConfig innerSharedConfig : sharedConfigs) { | |||||
if (innerSharedConfig.getName().equals(sharedConfig.getName()) | |||||
|| innerSharedConfig.getPubKey().equals(sharedConfig.getPubKey())) { | |||||
return null; | |||||
} | |||||
} | |||||
if (sharedConfig.getMasterConfig().isMaster()) { | |||||
initDataByMaster(sharedConfig); | |||||
} | |||||
sharedConfigs.add(sharedConfig); | |||||
if (latch != null) { | |||||
// 不管是Master还是普通用户都需要-1 | |||||
latch.countDown(); | |||||
} | |||||
return this; | |||||
} | |||||
/** | |||||
* 由Master节点传入的信息对数据进行初始化 | |||||
* | |||||
* @param sharedConfig | |||||
*/ | |||||
private void initDataByMaster(PeerLocalConfig sharedConfig) { | |||||
MasterConfig masterConfig = sharedConfig.getMasterConfig(); | |||||
// master需要对数据进行组织 | |||||
if (latch == null) { | |||||
latch = new CountDownLatch(masterConfig.getNodeSize() - sharedConfigs.size()); | |||||
} | |||||
if (consensusProvider == null) { | |||||
consensusProvider = sharedConfig.getConsensusProvider(); | |||||
} | |||||
if (sharedKey == null) { | |||||
sharedKey = sharedConfig.getSharedKey(); | |||||
} | |||||
if (ledgerName == null) { | |||||
ledgerName = masterConfig.getLedgerName(); | |||||
} | |||||
waitNodeSize = masterConfig.getNodeSize(); | |||||
} | |||||
/** | |||||
* 线程等待 | |||||
* 一直处于等待状态(30分钟),直到有线程调用single方法 | |||||
* | |||||
*/ | |||||
public void await() { | |||||
waitLock.lock(); | |||||
try { | |||||
sizeCondition.await(MAX_WAIT_MINUTE, TimeUnit.MINUTES); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} finally { | |||||
waitLock.unlock(); | |||||
} | |||||
} | |||||
/** | |||||
* 通知其他线程等待状态结束 | |||||
* | |||||
*/ | |||||
public void single() { | |||||
waitLock.lock(); | |||||
try { | |||||
sizeCondition.signalAll(); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} finally { | |||||
waitLock.unlock(); | |||||
} | |||||
} | |||||
/** | |||||
* Master线程调用,等待数据满足后通知其他线程 | |||||
* | |||||
*/ | |||||
public void waitAndNotify() { | |||||
if (this.latch == null) { | |||||
throw new IllegalStateException("Please init MasterConfig first !!!"); | |||||
} | |||||
try { | |||||
latch.await(MAX_WAIT_MINUTE, TimeUnit.MINUTES); | |||||
single(); // 通知其他线程释放 | |||||
} catch (Exception e) { | |||||
if (sharedConfigs.size() >= waitNodeSize) { | |||||
// 成功 | |||||
single(); | |||||
} | |||||
} | |||||
} | |||||
public synchronized LedgerInitConfig ledgerInitConfig(String seed, String createTime) { | |||||
if (ledgerInitConfig != null) { | |||||
return ledgerInitConfig; | |||||
} | |||||
// 处理该ledgerInitConfig | |||||
ledgerInitConfig = new LedgerInitConfig(seed, ledgerName, createTime, consensusProvider, waitNodeSize); | |||||
// 添加参与方 | |||||
for (int i = 0; i < sharedConfigs.size(); i++) { | |||||
PeerLocalConfig sharedConfig = sharedConfigs.get(i); | |||||
ledgerInitConfig.addPartiNode(sharedConfig.toPartiNode(i)); | |||||
} | |||||
return ledgerInitConfig; | |||||
} | |||||
public String getConsensusProvider() { | |||||
return consensusProvider; | |||||
} | |||||
public void setConsensusProvider(String consensusProvider) { | |||||
this.consensusProvider = consensusProvider; | |||||
} | |||||
public String getSharedKey() { | |||||
return sharedKey; | |||||
} | |||||
public void setSharedKey(String sharedKey) { | |||||
this.sharedKey = sharedKey; | |||||
} | |||||
public Lock getLock() { | |||||
return lock; | |||||
} | |||||
public String getLedgerName() { | |||||
return ledgerName; | |||||
} | |||||
public void setLedgerName(String ledgerName) { | |||||
this.ledgerName = ledgerName; | |||||
} | |||||
public List<PeerLocalConfig> getSharedConfigs() { | |||||
return sharedConfigs; | |||||
} | |||||
public void setSharedConfigs(List<PeerLocalConfig> sharedConfigs) { | |||||
this.sharedConfigs = sharedConfigs; | |||||
} | |||||
public LedgerInitConfig getLedgerInitConfig() { | |||||
return ledgerInitConfig; | |||||
} | |||||
public void setLedgerInitConfig(LedgerInitConfig ledgerInitConfig) { | |||||
this.ledgerInitConfig = ledgerInitConfig; | |||||
} | |||||
public LedgerMasterInstall toLedgerMasterInstall() { | |||||
// String ledgerKey, String sharedKey, int totalNodeSize | |||||
LedgerMasterInstall masterInstall = new LedgerMasterInstall( | |||||
ledgerInitConfig.ledgerKey(), sharedConfigs.size()) | |||||
.initCreateTime(ledgerInitConfig.getCreateTime()); | |||||
for (PeerLocalConfig sharedConfig : sharedConfigs) { | |||||
masterInstall.add(sharedConfig.toPeerInstall()); | |||||
} | |||||
return masterInstall; | |||||
} | |||||
} |
@@ -0,0 +1,104 @@ | |||||
package com.jd.blockchain.ump.model; | |||||
import java.io.File; | |||||
public class UmpConstant { | |||||
public static String PROJECT_PATH = ""; | |||||
public static final String DB_NAME = "jumpdb"; | |||||
public static final String URL_SEPARATOR = "/"; | |||||
public static final String URL_MASTER = "/master"; | |||||
public static final String URL_PEER = "/peer"; | |||||
public static final String PRIVATE_KEY_SUFFIX = ".priv"; | |||||
public static final String PUBLIC_KEY_SUFFIX = ".priv"; | |||||
public static final String PWD_SUFFIX = ".pwd"; | |||||
public static final String REQUEST_SHARED_URL = URL_MASTER + URL_SEPARATOR + "share"; | |||||
public static final String REQUEST_STATE_URL = URL_MASTER + URL_SEPARATOR + "receive"; | |||||
public static final String PARTINODE_COUNT = "cons_parti.count"; | |||||
public static final String PARTINODE_FORMAT = "cons_parti.%s"; | |||||
public static final String PARTINODE_NAME_FORMAT = PARTINODE_FORMAT + ".name"; | |||||
public static final String PARTINODE_PUBKEY_FORMAT = PARTINODE_FORMAT + ".pubkey"; | |||||
public static final String PARTINODE_INIT_FORMAT = PARTINODE_FORMAT + ".initializer"; | |||||
public static final String PARTINODE_INIT_HOST_FORMAT = PARTINODE_INIT_FORMAT + ".host"; | |||||
public static final String PARTINODE_INIT_PORT_FORMAT = PARTINODE_INIT_FORMAT + ".port"; | |||||
public static final String PARTINODE_INIT_SECURE_FORMAT = PARTINODE_INIT_FORMAT + ".secure"; | |||||
public static final String LEDGER_PREFIX = "ledger"; | |||||
public static final String LEDGER_SEED_PREFIX = LEDGER_PREFIX + ".seed"; | |||||
public static final String LEDGER_NAME_PREFIX = LEDGER_PREFIX + ".name"; | |||||
public static final String CREATE_TIME_PREFIX = "created-time"; | |||||
public static final String CONSENSUS_PREFIX = "consensus"; | |||||
public static final String CONSENSUS_PROVIDER_PREFIX = CONSENSUS_PREFIX + ".service-provider"; | |||||
public static final String CONSENSUS_CONF_PREFIX = CONSENSUS_PREFIX + ".conf"; | |||||
public static final String CRYPTO_PREFIX = "crypto"; | |||||
public static final String CRYPTO_PROVIDERS_PREFIX = CRYPTO_PREFIX + ".service-providers"; | |||||
public static final String LOCAL_PREFIX = "local"; | |||||
public static final String LOCAL_PARTI_PREFIX = LOCAL_PREFIX + ".parti"; | |||||
public static final String LOCAL_PARTI_ID_PREFIX = LOCAL_PARTI_PREFIX + ".id"; | |||||
public static final String LOCAL_PARTI_PUBKEY_PREFIX = LOCAL_PARTI_PREFIX + ".pubkey"; | |||||
public static final String LOCAL_PARTI_PRIVKEY_PREFIX = LOCAL_PARTI_PREFIX + ".privkey"; | |||||
public static final String LOCAL_PARTI_PWD_PREFIX = LOCAL_PARTI_PREFIX + ".pwd"; | |||||
public static final String LEDGER_BINDING_OUT_PREFIX = LEDGER_PREFIX + ".binding.out"; | |||||
public static final String LEDGER_DB_URI_PREFIX = LEDGER_PREFIX + ".db.uri"; | |||||
public static final String LEDGER_DB_PWD_PREFIX = LEDGER_PREFIX + ".db.pwd"; | |||||
public static final String CMD_LEDGER_INIT = "/bin/bash %s -monitor"; | |||||
public static final String CMD_START_UP_FORMAT = "/bin/bash %s"; | |||||
public static final String PATH_BIN = File.separator + "bin"; | |||||
public static final String PATH_LEDGER_INIT_BIN = PATH_BIN + File.separator + "ledger-init.sh"; | |||||
public static final String PATH_STARTUP_BIN = PATH_BIN + File.separator + "startup.sh"; | |||||
public static final String PATH_LIBS = File.separator + "libs"; | |||||
public static final String PATH_SYSTEM = File.separator + "system"; | |||||
public static final String PATH_CONFIG = File.separator + "config"; | |||||
public static final String PATH_CONFIG_KEYS = PATH_CONFIG + File.separator + "keys"; | |||||
public static final String PATH_LEDGER_BINDING_CONFIG = PATH_CONFIG + File.separator + "ledger-binding.conf"; | |||||
public static final String PATH_CONFIG_INIT = PATH_CONFIG + File.separator + "init"; | |||||
public static final String PATH_LOCAL_CONFIG = PATH_CONFIG_INIT + File.separator + "local.conf"; | |||||
public static final String PATH_LEDGER_INIT_CONFIG = PATH_CONFIG_INIT + File.separator + "ledger.init"; | |||||
} |
@@ -0,0 +1,50 @@ | |||||
package com.jd.blockchain.ump.model; | |||||
import com.jd.blockchain.ump.model.state.InstallSchedule; | |||||
import java.util.concurrent.BlockingQueue; | |||||
import java.util.concurrent.LinkedBlockingQueue; | |||||
public class UmpQueue { | |||||
private final BlockingQueue<InstallScheduleRequest> QUEUE_INSTALL_SCHEDULE = new LinkedBlockingQueue<>(); | |||||
public void put(InstallSchedule installSchedule, MasterAddr masterAddr) throws InterruptedException { | |||||
QUEUE_INSTALL_SCHEDULE.put(new InstallScheduleRequest(installSchedule, masterAddr)); | |||||
} | |||||
public InstallScheduleRequest take() throws InterruptedException { | |||||
return QUEUE_INSTALL_SCHEDULE.take(); | |||||
} | |||||
public static class InstallScheduleRequest { | |||||
private InstallSchedule installSchedule; | |||||
private MasterAddr masterAddr; | |||||
public InstallScheduleRequest() { | |||||
} | |||||
public InstallScheduleRequest(InstallSchedule installSchedule, MasterAddr masterAddr) { | |||||
this.installSchedule = installSchedule; | |||||
this.masterAddr = masterAddr; | |||||
} | |||||
public InstallSchedule getInstallSchedule() { | |||||
return installSchedule; | |||||
} | |||||
public void setInstallSchedule(InstallSchedule installSchedule) { | |||||
this.installSchedule = installSchedule; | |||||
} | |||||
public MasterAddr getMasterAddr() { | |||||
return masterAddr; | |||||
} | |||||
public void setMasterAddr(MasterAddr masterAddr) { | |||||
this.masterAddr = masterAddr; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,24 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
public class ConsensusConfig { | |||||
private String confPath; | |||||
private byte[] content; | |||||
public String getConfPath() { | |||||
return confPath; | |||||
} | |||||
public void setConfPath(String confPath) { | |||||
this.confPath = confPath; | |||||
} | |||||
public byte[] getContent() { | |||||
return content; | |||||
} | |||||
public void setContent(byte[] content) { | |||||
this.content = content; | |||||
} | |||||
} |
@@ -0,0 +1,46 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
public class LedgerConfig { | |||||
private LedgerInitConfig initConfig; | |||||
/** | |||||
* 共识文件配置信息,Base58格式 | |||||
*/ | |||||
private String consensusConfig; | |||||
public LedgerConfig() { | |||||
} | |||||
/** | |||||
* 包装一下,使用JSON处理过程 | |||||
* | |||||
* @param ledgerConfig | |||||
*/ | |||||
public LedgerConfig(LedgerConfig ledgerConfig) { | |||||
this.consensusConfig = ledgerConfig.getConsensusConfig(); | |||||
} | |||||
public LedgerConfig(LedgerInitConfig initConfig, String consensusConfig) { | |||||
this.initConfig = initConfig; | |||||
this.consensusConfig = consensusConfig; | |||||
} | |||||
public LedgerInitConfig getInitConfig() { | |||||
return initConfig; | |||||
} | |||||
public void setInitConfig(LedgerInitConfig initConfig) { | |||||
this.initConfig = initConfig; | |||||
} | |||||
public String getConsensusConfig() { | |||||
return consensusConfig; | |||||
} | |||||
public void setConsensusConfig(String consensusConfig) { | |||||
this.consensusConfig = consensusConfig; | |||||
} | |||||
} |
@@ -0,0 +1,92 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
public class LedgerIdentification { | |||||
private String ledgerKey; | |||||
private String ledgerAndNodeKey; | |||||
private MasterAddr masterAddr; | |||||
private int nodeId; | |||||
private PeerLocalConfig localConfig; | |||||
private LedgerInitConfig initConfig; | |||||
public LedgerIdentification() { | |||||
} | |||||
public LedgerIdentification(int nodeId, PeerLocalConfig localConfig, MasterAddr masterAddr, String ledgerAndNodeKey, LedgerInitConfig initConfig) { | |||||
this.nodeId = nodeId; | |||||
this.localConfig = localConfig; | |||||
this.masterAddr = masterAddr; | |||||
this.ledgerKey = initConfig.ledgerKey(); | |||||
this.ledgerAndNodeKey = ledgerAndNodeKey; | |||||
this.initConfig = initConfig; | |||||
init(); | |||||
} | |||||
private void init() { | |||||
// 初始化部分配置信息 | |||||
MasterConfig masterConfig = localConfig.getMasterConfig(); | |||||
// 设置账本名称 | |||||
if (masterConfig.getLedgerName() == null || masterConfig.getLedgerName().length() == 0) { | |||||
masterConfig.setLedgerName(initConfig.getName()); | |||||
} | |||||
// 设置NodeSize | |||||
if (masterConfig.getNodeSize() == 0) { | |||||
masterConfig.setNodeSize(initConfig.getNodeSize()); | |||||
} | |||||
} | |||||
public String getLedgerKey() { | |||||
return ledgerKey; | |||||
} | |||||
public void setLedgerKey(String ledgerKey) { | |||||
this.ledgerKey = ledgerKey; | |||||
} | |||||
public String getLedgerAndNodeKey() { | |||||
return ledgerAndNodeKey; | |||||
} | |||||
public void setLedgerAndNodeKey(String ledgerAndNodeKey) { | |||||
this.ledgerAndNodeKey = ledgerAndNodeKey; | |||||
} | |||||
public MasterAddr getMasterAddr() { | |||||
return masterAddr; | |||||
} | |||||
public void setMasterAddr(MasterAddr masterAddr) { | |||||
this.masterAddr = masterAddr; | |||||
} | |||||
public int getNodeId() { | |||||
return nodeId; | |||||
} | |||||
public void setNodeId(int nodeId) { | |||||
this.nodeId = nodeId; | |||||
} | |||||
public PeerLocalConfig getLocalConfig() { | |||||
return localConfig; | |||||
} | |||||
public void setLocalConfig(PeerLocalConfig localConfig) { | |||||
this.localConfig = localConfig; | |||||
} | |||||
public LedgerInitConfig getInitConfig() { | |||||
return initConfig; | |||||
} | |||||
public void setInitConfig(LedgerInitConfig initConfig) { | |||||
this.initConfig = initConfig; | |||||
} | |||||
} |
@@ -0,0 +1,141 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
import com.jd.blockchain.ump.model.PartiNode; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class LedgerInitConfig { | |||||
private String seed; | |||||
private String name; | |||||
private String createTime; | |||||
private String consensusProvider; | |||||
private int nodeSize; | |||||
private String cryptoProviders = | |||||
"com.jd.blockchain.crypto.service.classic.ClassicCryptoService, " + | |||||
"com.jd.blockchain.crypto.service.sm.SMCryptoService"; | |||||
private List<PartiNode> partiNodes = new ArrayList<>(); | |||||
public LedgerInitConfig() { | |||||
} | |||||
public LedgerInitConfig(String seed, String name, String createTime, String consensusProvider, int nodeSize) { | |||||
this.seed = seed; | |||||
this.name = name; | |||||
this.createTime = createTime; | |||||
this.consensusProvider = consensusProvider; | |||||
this.nodeSize = nodeSize; | |||||
} | |||||
public List<String> toConfigChars(String consensusConf) { | |||||
List<String> configChars = new ArrayList<>(); | |||||
configChars.add(toConfigChars(UmpConstant.LEDGER_SEED_PREFIX, seed)); | |||||
configChars.add(toConfigChars(UmpConstant.LEDGER_NAME_PREFIX, name)); | |||||
configChars.add(toConfigChars(UmpConstant.CREATE_TIME_PREFIX, createTime)); | |||||
configChars.add(toConfigChars(UmpConstant.CONSENSUS_PROVIDER_PREFIX, consensusProvider)); | |||||
configChars.add(toConfigChars(UmpConstant.CONSENSUS_CONF_PREFIX, consensusConf)); | |||||
configChars.add(toConfigChars(UmpConstant.CRYPTO_PROVIDERS_PREFIX, cryptoProviders)); | |||||
configChars.add(toConfigChars(UmpConstant.PARTINODE_COUNT, partiNodes.size())); | |||||
for (PartiNode partiNode : partiNodes) { | |||||
configChars.addAll(partiNode.toConfigChars()); | |||||
} | |||||
return configChars; | |||||
} | |||||
public String ledgerKey() { | |||||
return seed + "-" + name; | |||||
} | |||||
public int nodeId(String pubKey) { | |||||
for (int i = 0; i < partiNodes.size(); i++) { | |||||
PartiNode partiNode = partiNodes.get(i); | |||||
if (partiNode.getPubKey().equals(pubKey)) { | |||||
return i; | |||||
} | |||||
} | |||||
throw new IllegalStateException(String.format("Can not find PubKey = %s !", pubKey)); | |||||
} | |||||
private String toConfigChars(String prefix, Object value) { | |||||
return prefix + "=" + value; | |||||
} | |||||
public String getSeed() { | |||||
return seed; | |||||
} | |||||
public void setSeed(String seed) { | |||||
this.seed = seed; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getCreateTime() { | |||||
return createTime; | |||||
} | |||||
public void setCreateTime(String createTime) { | |||||
this.createTime = createTime; | |||||
} | |||||
public String getConsensusProvider() { | |||||
return consensusProvider; | |||||
} | |||||
public void setConsensusProvider(String consensusProvider) { | |||||
this.consensusProvider = consensusProvider; | |||||
} | |||||
public int getNodeSize() { | |||||
return nodeSize; | |||||
} | |||||
public void setNodeSize(int nodeSize) { | |||||
this.nodeSize = nodeSize; | |||||
} | |||||
public String getCryptoProviders() { | |||||
return cryptoProviders; | |||||
} | |||||
public void setCryptoProviders(String cryptoProviders) { | |||||
this.cryptoProviders = cryptoProviders; | |||||
} | |||||
public List<PartiNode> getPartiNodes() { | |||||
return partiNodes; | |||||
} | |||||
public void setPartiNodes(List<PartiNode> partiNodes) { | |||||
this.partiNodes = partiNodes; | |||||
} | |||||
public void addPartiNode(PartiNode partiNode) { | |||||
this.partiNodes.add(partiNode); | |||||
} | |||||
} |
@@ -0,0 +1,89 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
public class MasterConfig { | |||||
private String masterAddr; | |||||
private int masterPort; | |||||
private String ledgerName; | |||||
private int nodeSize; | |||||
private boolean isMaster = false; | |||||
public MasterConfig() { | |||||
} | |||||
public String getMasterAddr() { | |||||
return masterAddr; | |||||
} | |||||
public void setMasterAddr(String masterAddr) { | |||||
this.masterAddr = masterAddr; | |||||
} | |||||
public int getMasterPort() { | |||||
return masterPort; | |||||
} | |||||
public void setMasterPort(int masterPort) { | |||||
this.masterPort = masterPort; | |||||
} | |||||
public String getLedgerName() { | |||||
return ledgerName; | |||||
} | |||||
public void setLedgerName(String ledgerName) { | |||||
this.ledgerName = ledgerName; | |||||
} | |||||
public int getNodeSize() { | |||||
return nodeSize; | |||||
} | |||||
public void setNodeSize(int nodeSize) { | |||||
this.nodeSize = nodeSize; | |||||
} | |||||
public boolean isMaster() { | |||||
return isMaster; | |||||
} | |||||
public void setMaster(boolean master) { | |||||
isMaster = master; | |||||
} | |||||
public MasterConfig buildIsMaster(boolean isMaster) { | |||||
setMaster(isMaster); | |||||
return this; | |||||
} | |||||
public MasterConfig buildNodeSize(int nodeSize) { | |||||
setNodeSize(nodeSize); | |||||
return this; | |||||
} | |||||
public MasterConfig buildLedgerName(String ledgerName) { | |||||
setLedgerName(ledgerName); | |||||
return this; | |||||
} | |||||
public MasterConfig buildMasterAddr(String masterAddr) { | |||||
setMasterAddr(masterAddr); | |||||
return this; | |||||
} | |||||
public MasterConfig buildMasterPort(int masterPort) { | |||||
setMasterPort(masterPort); | |||||
return this; | |||||
} | |||||
public MasterAddr toMasterAddr() { | |||||
return MasterAddr.newInstance(masterAddr, masterPort); | |||||
} | |||||
} |
@@ -0,0 +1,160 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import com.jd.blockchain.ump.model.PartiNode; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import com.jd.blockchain.ump.model.state.LedgerMasterInstall; | |||||
import com.jd.blockchain.ump.model.state.LedgerPeerInstall; | |||||
import java.io.File; | |||||
/** | |||||
* Peer本地配置信息 | |||||
*/ | |||||
public class PeerLocalConfig extends PeerSharedConfig { | |||||
private String peerPath; | |||||
private String consensusConf = "bftsmart.config"; // 默认为bftsmart配置 | |||||
private String privKey; | |||||
private String encodePwd; | |||||
private String dbName; | |||||
private MasterConfig masterConfig; | |||||
public String bindingOutPath() { | |||||
return peerPath + UmpConstant.PATH_CONFIG; | |||||
} | |||||
public String localConfPath() { | |||||
return peerPath + UmpConstant.PATH_LOCAL_CONFIG; | |||||
} | |||||
public String ledgerInitConfPath() { | |||||
return peerPath + UmpConstant.PATH_LEDGER_INIT_CONFIG; | |||||
} | |||||
public String consensusConfPath() { | |||||
return peerPath + UmpConstant.PATH_CONFIG_INIT + File.separator + consensusConf; | |||||
} | |||||
public String libsDirectory() { | |||||
return peerPath + UmpConstant.PATH_LIBS; | |||||
} | |||||
public String getPeerPath() { | |||||
return peerPath; | |||||
} | |||||
public void setPeerPath(String peerPath) { | |||||
this.peerPath = peerPath; | |||||
} | |||||
public String getConsensusConf() { | |||||
return consensusConf; | |||||
} | |||||
public void setConsensusConf(String consensusConf) { | |||||
this.consensusConf = consensusConf; | |||||
} | |||||
public String getPrivKey() { | |||||
return privKey; | |||||
} | |||||
public void setPrivKey(String privKey) { | |||||
this.privKey = privKey; | |||||
} | |||||
public String getEncodePwd() { | |||||
return encodePwd; | |||||
} | |||||
public void setEncodePwd(String encodePwd) { | |||||
this.encodePwd = encodePwd; | |||||
} | |||||
public String getDbName() { | |||||
return dbName; | |||||
} | |||||
public void setDbName(String dbName) { | |||||
this.dbName = dbName; | |||||
} | |||||
public MasterConfig getMasterConfig() { | |||||
return masterConfig; | |||||
} | |||||
public void setMasterConfig(MasterConfig masterConfig) { | |||||
this.masterConfig = masterConfig; | |||||
} | |||||
public synchronized PartiNode toPartiNode(int nodeId) { | |||||
if (this.partiNode != null) { | |||||
return partiNode; | |||||
} | |||||
partiNode = new PartiNode(); | |||||
partiNode.setId(nodeId); | |||||
partiNode.setName(name); | |||||
partiNode.setInitHost(initAddr); | |||||
partiNode.setInitPort(initPort); | |||||
partiNode.setPubKey(pubKey); | |||||
partiNode.setSecure(false); | |||||
return partiNode; | |||||
} | |||||
public LedgerPeerInstall toLedgerPeerInstall(int totalNodeSize) { | |||||
return new LedgerPeerInstall(name, sharedKey, peerPath, totalNodeSize); | |||||
} | |||||
public LedgerMasterInstall.PeerInstall toPeerInstall() { | |||||
return new LedgerMasterInstall.PeerInstall(name, pubKey, initAddr, initPort, consensusNode, consensusProvider); | |||||
} | |||||
public void verify() { | |||||
// 主要校验dbName地址是否存在 | |||||
String dbPath = peerPath + File.separator + dbName; | |||||
File dbDir = new File(dbPath); | |||||
if (dbDir.exists()) { | |||||
throw new IllegalStateException(String.format("DB name = %s, path = %s is exist !!!", dbName, dbPath)); | |||||
} | |||||
// 其他配置信息是否正确 | |||||
if (masterConfig == null) { | |||||
// Master不能为空 | |||||
throw new IllegalStateException("Master Config can not be NULL !!!"); | |||||
} | |||||
if (masterConfig.isMaster()) { | |||||
// 账本名字及NodeSize不能为空 | |||||
if (masterConfig.getLedgerName() == null || masterConfig.getLedgerName().length() == 0) { | |||||
throw new IllegalStateException("Master 's LedgerName can not be empty !!!"); | |||||
} | |||||
if (masterConfig.getNodeSize() == 0) { | |||||
throw new IllegalStateException("Master 's NodeSize can not be Zero !!!"); | |||||
} | |||||
} else { | |||||
// 普通Peer需要检查Master的IP地址及端口 | |||||
if (masterConfig.getMasterAddr() == null || masterConfig.getMasterAddr().length() == 0) { | |||||
throw new IllegalStateException("Master 's IP Address can not be empty !!!"); | |||||
} | |||||
if (masterConfig.getMasterPort() == 0) { | |||||
throw new IllegalStateException("Master 's Port must be Set !!!"); | |||||
} | |||||
} | |||||
} | |||||
public boolean master() { | |||||
return masterConfig.isMaster(); | |||||
} | |||||
public MasterAddr masterAddr() { | |||||
return masterConfig.toMasterAddr(); | |||||
} | |||||
} |
@@ -0,0 +1,86 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
import com.jd.blockchain.ump.model.PartiNode; | |||||
public class PeerSharedConfig { | |||||
public static final String DB_ROCKSDB_SUFFIX = "rocksdb_"; | |||||
protected String sharedKey; | |||||
protected String name; | |||||
protected String initAddr; | |||||
protected String pubKey; | |||||
protected int initPort; | |||||
protected String consensusNode; | |||||
protected String consensusProvider = "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"; | |||||
protected PartiNode partiNode; | |||||
public String getSharedKey() { | |||||
return sharedKey; | |||||
} | |||||
public void setSharedKey(String sharedKey) { | |||||
this.sharedKey = sharedKey; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getInitAddr() { | |||||
return initAddr; | |||||
} | |||||
public void setInitAddr(String initAddr) { | |||||
this.initAddr = initAddr; | |||||
} | |||||
public String getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public void setPubKey(String pubKey) { | |||||
this.pubKey = pubKey; | |||||
} | |||||
public int getInitPort() { | |||||
return initPort; | |||||
} | |||||
public void setInitPort(int initPort) { | |||||
this.initPort = initPort; | |||||
} | |||||
public String addr() { | |||||
return initAddr + "-" + initPort; | |||||
} | |||||
public String getConsensusNode() { | |||||
return consensusNode; | |||||
} | |||||
public void setConsensusNode(String consensusNode) { | |||||
this.consensusNode = consensusNode; | |||||
} | |||||
public String getConsensusProvider() { | |||||
return consensusProvider; | |||||
} | |||||
public void setConsensusProvider(String consensusProvider) { | |||||
this.consensusProvider = consensusProvider; | |||||
} | |||||
} |
@@ -0,0 +1,180 @@ | |||||
package com.jd.blockchain.ump.model.config; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
/** | |||||
* | |||||
*/ | |||||
public class PeerSharedConfigVv { | |||||
private String sharedKey; | |||||
private String name; | |||||
private int userId; | |||||
private String pubKey; | |||||
private String initAddr; | |||||
private int initPort; | |||||
private String consensusNode; | |||||
private String peerPath = UmpConstant.PROJECT_PATH; | |||||
private String dbName; | |||||
private int nodeSize; | |||||
private String masterAddr; | |||||
private int masterPort; | |||||
private String ledgerName; | |||||
public String getSharedKey() { | |||||
return sharedKey; | |||||
} | |||||
public void setSharedKey(String sharedKey) { | |||||
this.sharedKey = sharedKey; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public int getUserId() { | |||||
return userId; | |||||
} | |||||
public void setUserId(int userId) { | |||||
this.userId = userId; | |||||
} | |||||
public String getInitAddr() { | |||||
return initAddr; | |||||
} | |||||
public String getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public void setPubKey(String pubKey) { | |||||
this.pubKey = pubKey; | |||||
} | |||||
public void setInitAddr(String initAddr) { | |||||
this.initAddr = initAddr; | |||||
} | |||||
public int getInitPort() { | |||||
return initPort; | |||||
} | |||||
public void setInitPort(int initPort) { | |||||
this.initPort = initPort; | |||||
} | |||||
public String getConsensusNode() { | |||||
return consensusNode; | |||||
} | |||||
public void setConsensusNode(String consensusNode) { | |||||
this.consensusNode = consensusNode; | |||||
} | |||||
public String getPeerPath() { | |||||
return peerPath; | |||||
} | |||||
public void setPeerPath(String peerPath) { | |||||
this.peerPath = peerPath; | |||||
} | |||||
public String getDbName() { | |||||
return dbName; | |||||
} | |||||
public void setDbName(String dbName) { | |||||
this.dbName = dbName; | |||||
} | |||||
public int getNodeSize() { | |||||
return nodeSize; | |||||
} | |||||
public void setNodeSize(Integer nodeSize) { | |||||
this.nodeSize = nodeSize; | |||||
} | |||||
public String getMasterAddr() { | |||||
return masterAddr; | |||||
} | |||||
public void setMasterAddr(String masterAddr) { | |||||
this.masterAddr = masterAddr; | |||||
} | |||||
public int getMasterPort() { | |||||
return masterPort; | |||||
} | |||||
public void setMasterPort(Integer masterPort) { | |||||
this.masterPort = masterPort; | |||||
} | |||||
public String getLedgerName() { | |||||
return ledgerName; | |||||
} | |||||
public void setLedgerName(String ledgerName) { | |||||
this.ledgerName = ledgerName; | |||||
} | |||||
public PeerLocalConfig toPeerLocalConfig(UserKeys userKeys) { | |||||
PeerLocalConfig localConfig = new PeerLocalConfig(); | |||||
localConfig.setSharedKey(sharedKey); | |||||
localConfig.setName(name); | |||||
localConfig.setInitAddr(initAddr); | |||||
localConfig.setInitPort(initPort); | |||||
localConfig.setConsensusNode(consensusNode); | |||||
localConfig.setPubKey(userKeys.getPubKey()); | |||||
localConfig.setPrivKey(userKeys.getPrivKey()); | |||||
localConfig.setEncodePwd(userKeys.getEncodePwd()); | |||||
localConfig.setPeerPath(peerPath); | |||||
localConfig.setDbName(dbName); | |||||
MasterConfig masterConfig = new MasterConfig(); | |||||
if (master()) { | |||||
masterConfig.buildIsMaster(true) | |||||
.buildLedgerName(ledgerName) | |||||
.buildNodeSize(nodeSize); | |||||
} else { | |||||
masterConfig.buildIsMaster(false) | |||||
.buildMasterAddr(masterAddr) | |||||
.buildMasterPort(masterPort); | |||||
} | |||||
localConfig.setMasterConfig(masterConfig); | |||||
return localConfig; | |||||
} | |||||
private boolean master() { | |||||
if (masterAddr == null || masterAddr.length() == 0) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public class InstallProcess { | |||||
private String content; | |||||
public InstallProcess() { | |||||
} | |||||
public InstallProcess(String content) { | |||||
this.content = content; | |||||
} | |||||
public String getContent() { | |||||
return content; | |||||
} | |||||
public void setContent(String content) { | |||||
this.content = content; | |||||
} | |||||
} |
@@ -0,0 +1,54 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public class InstallSchedule { | |||||
private String ledgerKey; | |||||
private String ledgerAndNodeKey; | |||||
private InstallProcess process; | |||||
private ScheduleState state; | |||||
public InstallSchedule() { | |||||
} | |||||
public InstallSchedule(String ledgerKey, String ledgerAndNodeKey, InstallProcess process, ScheduleState state) { | |||||
this.ledgerKey = ledgerKey; | |||||
this.ledgerAndNodeKey = ledgerAndNodeKey; | |||||
this.process = process; | |||||
this.state = state; | |||||
} | |||||
public String getLedgerKey() { | |||||
return ledgerKey; | |||||
} | |||||
public void setLedgerKey(String ledgerKey) { | |||||
this.ledgerKey = ledgerKey; | |||||
} | |||||
public String getLedgerAndNodeKey() { | |||||
return ledgerAndNodeKey; | |||||
} | |||||
public void setLedgerAndNodeKey(String ledgerAndNodeKey) { | |||||
this.ledgerAndNodeKey = ledgerAndNodeKey; | |||||
} | |||||
public InstallProcess getProcess() { | |||||
return process; | |||||
} | |||||
public void setProcess(InstallProcess process) { | |||||
this.process = process; | |||||
} | |||||
public ScheduleState getState() { | |||||
return state; | |||||
} | |||||
public void setState(ScheduleState state) { | |||||
this.state = state; | |||||
} | |||||
} |
@@ -0,0 +1,33 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
import java.util.Set; | |||||
public class LedgerBindingConf { | |||||
private Set<String> ledgerHashs; | |||||
private long lastTime; | |||||
public LedgerBindingConf() { | |||||
} | |||||
public LedgerBindingConf(long lastTime) { | |||||
this.lastTime = lastTime; | |||||
} | |||||
public Set<String> getLedgerHashs() { | |||||
return ledgerHashs; | |||||
} | |||||
public void setLedgerHashs(Set<String> ledgerHashs) { | |||||
this.ledgerHashs = ledgerHashs; | |||||
} | |||||
public long getLastTime() { | |||||
return lastTime; | |||||
} | |||||
public void setLastTime(long lastTime) { | |||||
this.lastTime = lastTime; | |||||
} | |||||
} |
@@ -0,0 +1,101 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public class LedgerInited { | |||||
private String ledgerHash; | |||||
private String ledgerName; | |||||
private String partiName; | |||||
private String partiAddress; | |||||
private String dbUri; | |||||
private StartupState startupState = StartupState.UNKNOWN; | |||||
public LedgerInited() { | |||||
} | |||||
public LedgerInited(String ledgerHash) { | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public String getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
public void setLedgerHash(String ledgerHash) { | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public String getLedgerName() { | |||||
return ledgerName; | |||||
} | |||||
public void setLedgerName(String ledgerName) { | |||||
this.ledgerName = ledgerName; | |||||
} | |||||
public String getPartiName() { | |||||
return partiName; | |||||
} | |||||
public void setPartiName(String partiName) { | |||||
this.partiName = partiName; | |||||
} | |||||
public String getPartiAddress() { | |||||
return partiAddress; | |||||
} | |||||
public void setPartiAddress(String partiAddress) { | |||||
this.partiAddress = partiAddress; | |||||
} | |||||
public String getDbUri() { | |||||
return dbUri; | |||||
} | |||||
public void setDbUri(String dbUri) { | |||||
this.dbUri = dbUri; | |||||
} | |||||
public StartupState getStartupState() { | |||||
return startupState; | |||||
} | |||||
public void setStartupState(StartupState startupState) { | |||||
this.startupState = startupState; | |||||
} | |||||
public LedgerInited buildLedgerHash(String ledgerHash) { | |||||
setLedgerHash(ledgerHash); | |||||
return this; | |||||
} | |||||
public LedgerInited buildLedgerName(String ledgerName) { | |||||
setLedgerName(ledgerName); | |||||
return this; | |||||
} | |||||
public LedgerInited buildPartiName(String partiName) { | |||||
setPartiName(partiName); | |||||
return this; | |||||
} | |||||
public LedgerInited buildPartiAddress(String partiAddress) { | |||||
setPartiAddress(partiAddress); | |||||
return this; | |||||
} | |||||
public LedgerInited buildDbUri(String dbUri) { | |||||
setDbUri(dbUri); | |||||
return this; | |||||
} | |||||
public LedgerInited buildStartupState(StartupState startupState) { | |||||
setStartupState(startupState); | |||||
return this; | |||||
} | |||||
} |
@@ -0,0 +1,156 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.ArrayList; | |||||
import java.util.Date; | |||||
import java.util.List; | |||||
public class LedgerMasterInstall { | |||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |||||
private String ledgerKey; | |||||
private int totalNodeSize; | |||||
private String createTime; | |||||
private List<PeerInstall> peerInstalls = new ArrayList<>(); | |||||
public LedgerMasterInstall() { | |||||
} | |||||
public LedgerMasterInstall(String ledgerKey, int totalNodeSize) { | |||||
this.ledgerKey = ledgerKey; | |||||
this.totalNodeSize = totalNodeSize; | |||||
} | |||||
public LedgerMasterInstall initCreateTime(String createTime) { | |||||
this.createTime = createTime; | |||||
return this; | |||||
} | |||||
public LedgerMasterInstall initCreateTime(Date date) { | |||||
this.createTime = SDF.format(date); | |||||
return this; | |||||
} | |||||
public LedgerMasterInstall add(PeerInstall peerInstall) { | |||||
peerInstalls.add(peerInstall); | |||||
return this; | |||||
} | |||||
public LedgerMasterInstall add(String name, String pubKey, String ipAddr, int initPort, | |||||
String consensusNode, String consensusProvider) { | |||||
PeerInstall peerInstall = new PeerInstall( | |||||
name, pubKey, ipAddr, initPort, consensusNode, consensusProvider); | |||||
return add(peerInstall); | |||||
} | |||||
public String getLedgerKey() { | |||||
return ledgerKey; | |||||
} | |||||
public void setLedgerKey(String ledgerKey) { | |||||
this.ledgerKey = ledgerKey; | |||||
} | |||||
public int getTotalNodeSize() { | |||||
return totalNodeSize; | |||||
} | |||||
public void setTotalNodeSize(int totalNodeSize) { | |||||
this.totalNodeSize = totalNodeSize; | |||||
} | |||||
public String getCreateTime() { | |||||
return createTime; | |||||
} | |||||
public void setCreateTime(String createTime) { | |||||
this.createTime = createTime; | |||||
} | |||||
public List<PeerInstall> getPeerInstalls() { | |||||
return peerInstalls; | |||||
} | |||||
public void setPeerInstalls(List<PeerInstall> peerInstalls) { | |||||
this.peerInstalls = peerInstalls; | |||||
} | |||||
public static class PeerInstall { | |||||
private String name; | |||||
private String pubKey; | |||||
private String ipAddr; | |||||
private int initPort; | |||||
private String consensusNode; | |||||
private String consensusProvider; | |||||
public PeerInstall() { | |||||
} | |||||
public PeerInstall(String name, String pubKey, String ipAddr, int initPort, String consensusNode, String consensusProvider) { | |||||
this.name = name; | |||||
this.pubKey = pubKey; | |||||
this.ipAddr = ipAddr; | |||||
this.initPort = initPort; | |||||
this.consensusNode = consensusNode; | |||||
this.consensusProvider = consensusProvider; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public void setPubKey(String pubKey) { | |||||
this.pubKey = pubKey; | |||||
} | |||||
public String getIpAddr() { | |||||
return ipAddr; | |||||
} | |||||
public void setIpAddr(String ipAddr) { | |||||
this.ipAddr = ipAddr; | |||||
} | |||||
public int getInitPort() { | |||||
return initPort; | |||||
} | |||||
public void setInitPort(int initPort) { | |||||
this.initPort = initPort; | |||||
} | |||||
public String getConsensusNode() { | |||||
return consensusNode; | |||||
} | |||||
public void setConsensusNode(String consensusNode) { | |||||
this.consensusNode = consensusNode; | |||||
} | |||||
public String getConsensusProvider() { | |||||
return consensusProvider; | |||||
} | |||||
public void setConsensusProvider(String consensusProvider) { | |||||
this.consensusProvider = consensusProvider; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,42 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public class LedgerPeerInited { | |||||
private String ledgerHash; | |||||
private LedgerPeerInstall peerInstall; | |||||
private StartupState startupState; | |||||
public LedgerPeerInited() { | |||||
} | |||||
public LedgerPeerInited(String ledgerHash, LedgerPeerInstall peerInstall) { | |||||
this.ledgerHash = ledgerHash; | |||||
this.peerInstall = peerInstall; | |||||
} | |||||
public String getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
public void setLedgerHash(String ledgerHash) { | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public LedgerPeerInstall getPeerInstall() { | |||||
return peerInstall; | |||||
} | |||||
public void setPeerInstall(LedgerPeerInstall peerInstall) { | |||||
this.peerInstall = peerInstall; | |||||
} | |||||
public StartupState getStartupState() { | |||||
return startupState; | |||||
} | |||||
public void setStartupState(StartupState startupState) { | |||||
this.startupState = startupState; | |||||
} | |||||
} |
@@ -0,0 +1,117 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.Date; | |||||
public class LedgerPeerInstall { | |||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); | |||||
private String ledgerKey; | |||||
private String ledgerAndNodeKey; | |||||
private String nodeName; | |||||
private String sharedKey; | |||||
private String peerPath; | |||||
private int totalNodeSize; | |||||
private MasterAddr masterAddr; | |||||
private String createTime; | |||||
public LedgerPeerInstall() { | |||||
} | |||||
public LedgerPeerInstall(String nodeName, String sharedKey, String peerPath, int totalNodeSize) { | |||||
this.nodeName = nodeName; | |||||
this.sharedKey = sharedKey; | |||||
this.peerPath = peerPath; | |||||
this.totalNodeSize = totalNodeSize; | |||||
} | |||||
public LedgerPeerInstall initKey(String ledgerKey, String ledgerAndNodeKey) { | |||||
this.ledgerKey = ledgerKey; | |||||
this.ledgerAndNodeKey = ledgerAndNodeKey; | |||||
return this; | |||||
} | |||||
public LedgerPeerInstall initMasterAddr(MasterAddr masterAddr) { | |||||
this.masterAddr = masterAddr; | |||||
return this; | |||||
} | |||||
public LedgerPeerInstall initCreateTime(Date date) { | |||||
createTime = SDF.format(date); | |||||
return this; | |||||
} | |||||
public String getLedgerKey() { | |||||
return ledgerKey; | |||||
} | |||||
public void setLedgerKey(String ledgerKey) { | |||||
this.ledgerKey = ledgerKey; | |||||
} | |||||
public String getLedgerAndNodeKey() { | |||||
return ledgerAndNodeKey; | |||||
} | |||||
public void setLedgerAndNodeKey(String ledgerAndNodeKey) { | |||||
this.ledgerAndNodeKey = ledgerAndNodeKey; | |||||
} | |||||
public String getNodeName() { | |||||
return nodeName; | |||||
} | |||||
public void setNodeName(String nodeName) { | |||||
this.nodeName = nodeName; | |||||
} | |||||
public String getSharedKey() { | |||||
return sharedKey; | |||||
} | |||||
public void setSharedKey(String sharedKey) { | |||||
this.sharedKey = sharedKey; | |||||
} | |||||
public String getPeerPath() { | |||||
return peerPath; | |||||
} | |||||
public void setPeerPath(String peerPath) { | |||||
this.peerPath = peerPath; | |||||
} | |||||
public int getTotalNodeSize() { | |||||
return totalNodeSize; | |||||
} | |||||
public void setTotalNodeSize(int totalNodeSize) { | |||||
this.totalNodeSize = totalNodeSize; | |||||
} | |||||
public String getCreateTime() { | |||||
return createTime; | |||||
} | |||||
public void setCreateTime(String createTime) { | |||||
this.createTime = createTime; | |||||
} | |||||
public MasterAddr getMasterAddr() { | |||||
return masterAddr; | |||||
} | |||||
public void setMasterAddr(MasterAddr masterAddr) { | |||||
this.masterAddr = masterAddr; | |||||
} | |||||
} |
@@ -0,0 +1,32 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public class PeerInstallSchedule { | |||||
private InstallProcess process; | |||||
private ScheduleState state; | |||||
public PeerInstallSchedule() { | |||||
} | |||||
public PeerInstallSchedule(InstallProcess process, ScheduleState state) { | |||||
this.process = process; | |||||
this.state = state; | |||||
} | |||||
public InstallProcess getProcess() { | |||||
return process; | |||||
} | |||||
public void setProcess(InstallProcess process) { | |||||
this.process = process; | |||||
} | |||||
public ScheduleState getState() { | |||||
return state; | |||||
} | |||||
public void setState(ScheduleState state) { | |||||
this.state = state; | |||||
} | |||||
} |
@@ -0,0 +1,61 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
import com.jd.blockchain.ump.model.config.LedgerIdentification; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class PeerInstallSchedules { | |||||
private String ledgerHash; | |||||
private LedgerIdentification identification; | |||||
private List<PeerInstallSchedule> installSchedules = new ArrayList<>(); | |||||
public PeerInstallSchedules() { | |||||
} | |||||
public PeerInstallSchedules(LedgerIdentification identification) { | |||||
this.identification = identification; | |||||
} | |||||
public PeerInstallSchedules(LedgerIdentification identification, String ledgerHash) { | |||||
this.identification = identification; | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public PeerInstallSchedules addInstallSchedule(PeerInstallSchedule installSchedule) { | |||||
this.installSchedules.add(installSchedule); | |||||
return this; | |||||
} | |||||
public PeerInstallSchedules initLedgerHash(String ledgerHash) { | |||||
setLedgerHash(ledgerHash); | |||||
return this; | |||||
} | |||||
public String getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
public void setLedgerHash(String ledgerHash) { | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public LedgerIdentification getIdentification() { | |||||
return identification; | |||||
} | |||||
public void setIdentification(LedgerIdentification identification) { | |||||
this.identification = identification; | |||||
} | |||||
public List<PeerInstallSchedule> getInstallSchedules() { | |||||
return installSchedules; | |||||
} | |||||
public void setInstallSchedules(List<PeerInstallSchedule> installSchedules) { | |||||
this.installSchedules = installSchedules; | |||||
} | |||||
} |
@@ -0,0 +1,40 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
public class PeerStartupSchedules { | |||||
private String peerPath; | |||||
private List<PeerInstallSchedule> installSchedules = new ArrayList<>(); | |||||
public PeerStartupSchedules() { | |||||
} | |||||
public PeerStartupSchedules(String peerPath) { | |||||
this.peerPath = peerPath; | |||||
} | |||||
public PeerStartupSchedules addInstallSchedule(PeerInstallSchedule installSchedule) { | |||||
this.installSchedules.add(installSchedule); | |||||
return this; | |||||
} | |||||
public List<PeerInstallSchedule> getInstallSchedules() { | |||||
return installSchedules; | |||||
} | |||||
public void setInstallSchedules(List<PeerInstallSchedule> installSchedules) { | |||||
this.installSchedules = installSchedules; | |||||
} | |||||
public String getPeerPath() { | |||||
return peerPath; | |||||
} | |||||
public void setPeerPath(String peerPath) { | |||||
this.peerPath = peerPath; | |||||
} | |||||
} |
@@ -0,0 +1,43 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public enum ScheduleState { | |||||
/** | |||||
* 加载内容,包括获取各种数据列表 | |||||
*/ | |||||
LOAD, | |||||
LOAD_SUCCESS, // 加载成功 | |||||
LOAD_FAIL, // 加载失败 | |||||
/** | |||||
* 将获取的数据写入文件 | |||||
* | |||||
*/ | |||||
WRITE, | |||||
WRITE_SUCCESS, // 写入文件成功 | |||||
WRITE_FAIL, // 写入文件失败 | |||||
/** | |||||
* Ledger_INIT:账本初始化过程 | |||||
* 主要是调用SHELL | |||||
* | |||||
*/ | |||||
INIT, | |||||
INIT_SUCCESS, // 账本初始化成功 | |||||
INIT_FAIL, // 账本初始化失败 | |||||
/** | |||||
* 无须启动PEER,等待PEER自动更新账本信息 | |||||
*/ | |||||
NO_STARTUP, | |||||
/** | |||||
* 启动Peer节点 | |||||
*/ | |||||
STARTUP_START, | |||||
STARTUP_OVER, | |||||
STARTUP_SUCCESS, // Peer节点启动成功 | |||||
STARTUP_FAIL, // Peer节点启动失败 | |||||
; | |||||
} |
@@ -0,0 +1,48 @@ | |||||
package com.jd.blockchain.ump.model.state; | |||||
public enum StartupState { | |||||
/** | |||||
* UNEXIST | |||||
* 不存在,描述该账本Hash曾经创建,但目前在LedgerBinding.conf文件中不存在 | |||||
* 此状态不支持任何其他操作 | |||||
*/ | |||||
UNEXIST, | |||||
/** | |||||
* UNLOAD | |||||
* 账本存在,但未加载 | |||||
* 此状态可以启动,不能停止 | |||||
*/ | |||||
UNLOAD, | |||||
/** | |||||
* LOADING | |||||
* 账本加载中,说明程序已经启动,但尚未加载该程序 | |||||
* 此状态不可以启动,不建议停止 | |||||
*/ | |||||
LOADING, | |||||
/** | |||||
* LOADED | |||||
* 账本已加载 | |||||
* 此状态不可以启动,后续可以支持停止操作 | |||||
*/ | |||||
LOADED, | |||||
/** | |||||
* UNKNOWN | |||||
* 未知,常见于命令检测执行错误或程序启动,但账本尚未加载完成 | |||||
* 此状态不支持任何其他操作 | |||||
*/ | |||||
UNKNOWN, | |||||
/** | |||||
* DB_UNEXIST | |||||
* 该账本对应的数据库不存在 | |||||
* 此状态不支持任何其他操作 | |||||
*/ | |||||
DB_UNEXIST, | |||||
; | |||||
} |
@@ -0,0 +1,34 @@ | |||||
package com.jd.blockchain.ump.model.user; | |||||
public class UserKeyBuilder { | |||||
private String name; | |||||
private String seed; | |||||
private String pwd; | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getSeed() { | |||||
return seed; | |||||
} | |||||
public void setSeed(String seed) { | |||||
this.seed = seed; | |||||
} | |||||
public String getPwd() { | |||||
return pwd; | |||||
} | |||||
public void setPwd(String pwd) { | |||||
this.pwd = pwd; | |||||
} | |||||
} |
@@ -0,0 +1,68 @@ | |||||
package com.jd.blockchain.ump.model.user; | |||||
public class UserKeys { | |||||
private int id; | |||||
private String name; | |||||
private String privKey; | |||||
private String pubKey; | |||||
private String encodePwd; | |||||
public UserKeys() { | |||||
} | |||||
public UserKeys(String name, String privKey, String pubKey, String encodePwd) { | |||||
this.name = name; | |||||
this.privKey = privKey; | |||||
this.pubKey = pubKey; | |||||
this.encodePwd = encodePwd; | |||||
} | |||||
public int getId() { | |||||
return id; | |||||
} | |||||
public void setId(int id) { | |||||
this.id = id; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getPrivKey() { | |||||
return privKey; | |||||
} | |||||
public void setPrivKey(String privKey) { | |||||
this.privKey = privKey; | |||||
} | |||||
public String getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public void setPubKey(String pubKey) { | |||||
this.pubKey = pubKey; | |||||
} | |||||
public String getEncodePwd() { | |||||
return encodePwd; | |||||
} | |||||
public void setEncodePwd(String encodePwd) { | |||||
this.encodePwd = encodePwd; | |||||
} | |||||
public UserKeysVv toUserKeysVv() { | |||||
return new UserKeysVv(id, name, privKey, pubKey); | |||||
} | |||||
} |
@@ -0,0 +1,69 @@ | |||||
package com.jd.blockchain.ump.model.user; | |||||
public class UserKeysVv { | |||||
public static final int PRIVKEY_HEADER_LENGTH = 4; | |||||
public static final int PRIVKEY_TAIL_LENGTH = 8; | |||||
public static final String PRIVKEY_HIDE_CONTENT = "******"; | |||||
private int id; | |||||
private String name; | |||||
private String privKey; | |||||
private String pubKey; | |||||
public UserKeysVv() { | |||||
} | |||||
public UserKeysVv(int id, String name, String privKey, String pubKey) { | |||||
this.id = id; | |||||
this.name = name; | |||||
this.privKey = encodePrivKey(privKey); | |||||
this.pubKey = pubKey; | |||||
} | |||||
public int getId() { | |||||
return id; | |||||
} | |||||
public void setId(int id) { | |||||
this.id = id; | |||||
} | |||||
public String getName() { | |||||
return name; | |||||
} | |||||
public void setName(String name) { | |||||
this.name = name; | |||||
} | |||||
public String getPrivKey() { | |||||
return privKey; | |||||
} | |||||
public void setPrivKey(String privKey) { | |||||
this.privKey = privKey; | |||||
} | |||||
public String getPubKey() { | |||||
return pubKey; | |||||
} | |||||
public void setPubKey(String pubKey) { | |||||
this.pubKey = pubKey; | |||||
} | |||||
private String encodePrivKey(final String privKey) { | |||||
if (privKey != null && privKey.length() > (PRIVKEY_HEADER_LENGTH + PRIVKEY_TAIL_LENGTH)) { | |||||
return privKey.substring(0, PRIVKEY_HEADER_LENGTH) + | |||||
PRIVKEY_HIDE_CONTENT + | |||||
privKey.substring(privKey.length() - PRIVKEY_TAIL_LENGTH); | |||||
} | |||||
return privKey; | |||||
} | |||||
} |
@@ -0,0 +1,20 @@ | |||||
package com.jd.blockchain.ump.model.web; | |||||
/** | |||||
* 错误代码; | |||||
*/ | |||||
public enum ErrorCode { | |||||
UNEXPECTED(5000), | |||||
; | |||||
private int value; | |||||
ErrorCode(int value) { | |||||
this.value = value; | |||||
} | |||||
public int getValue() { | |||||
return value; | |||||
} | |||||
} |
@@ -0,0 +1,97 @@ | |||||
package com.jd.blockchain.ump.model.web; | |||||
public class WebResponse<T> { | |||||
private boolean success; | |||||
private T data; | |||||
private ErrorMessage error; | |||||
private WebResponse(){ | |||||
} | |||||
public boolean isSuccess() { | |||||
return success; | |||||
} | |||||
public void setSuccess(boolean success) { | |||||
this.success = success; | |||||
} | |||||
public T getData() { | |||||
return data; | |||||
} | |||||
public void setData(T data) { | |||||
this.data = data; | |||||
} | |||||
public ErrorMessage getError() { | |||||
return error; | |||||
} | |||||
public void setError(ErrorMessage error) { | |||||
this.error = error; | |||||
} | |||||
public static WebResponse createSuccessResult(Object data){ | |||||
WebResponse responseResult = new WebResponse(); | |||||
responseResult.setSuccess(true); | |||||
responseResult.setData(data); | |||||
return responseResult; | |||||
} | |||||
public static WebResponse createFailureResult(int code, String message){ | |||||
ErrorMessage errorMessage = new ErrorMessage(code, message); | |||||
return createFailureResult(errorMessage); | |||||
} | |||||
public static WebResponse createFailureResult(ErrorMessage errorMessage){ | |||||
WebResponse responseResult = new WebResponse(); | |||||
responseResult.setSuccess(false); | |||||
responseResult.setError(errorMessage); | |||||
return responseResult; | |||||
} | |||||
/** | |||||
* 错误消息实体 | |||||
* | |||||
* @author liuxrb | |||||
* | |||||
*/ | |||||
public static class ErrorMessage { | |||||
private int errorCode; | |||||
private String errorMessage; | |||||
public ErrorMessage() { | |||||
} | |||||
public ErrorMessage(int errorCode, String errorMessage) { | |||||
this.errorCode = errorCode; | |||||
this.errorMessage = errorMessage; | |||||
} | |||||
public int getErrorCode() { | |||||
return errorCode; | |||||
} | |||||
public void setErrorCode(int errorCode) { | |||||
this.errorCode = errorCode; | |||||
} | |||||
public String getErrorMessage() { | |||||
return errorMessage; | |||||
} | |||||
public void setErrorMessage(String errorMessage) { | |||||
this.errorMessage = errorMessage; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,119 @@ | |||||
<?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"> | |||||
<parent> | |||||
<artifactId>manager</artifactId> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<version>1.1.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>ump-service</artifactId> | |||||
<name>ump-service</name> | |||||
<properties> | |||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
<maven.compiler.source>1.8</maven.compiler.source> | |||||
<maven.compiler.target>1.8</maven.compiler.target> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
<exclusions> | |||||
<exclusion> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-logging</artifactId> | |||||
</exclusion> | |||||
</exclusions> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-configuration-processor</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-devtools</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>crypto-classic</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>crypto-sm</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>tools-keygen</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.alibaba</groupId> | |||||
<artifactId>fastjson</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>commons-io</groupId> | |||||
<artifactId>commons-io</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>commons-codec</groupId> | |||||
<artifactId>commons-codec</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-model</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.reflections</groupId> | |||||
<artifactId>reflections</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.google.guava</groupId> | |||||
<artifactId>guava</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.apache.httpcomponents</groupId> | |||||
<artifactId>httpclient</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.sun</groupId> | |||||
<artifactId>tools</artifactId> | |||||
<version>1.8</version> | |||||
<scope>system</scope> | |||||
<systemPath>${project.basedir}/../ump-booter/libs/tools.jar</systemPath> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,31 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.model.state.LedgerBindingConf; | |||||
import com.jd.blockchain.ump.model.state.LedgerInited; | |||||
import java.util.List; | |||||
public interface LedgerService { | |||||
String randomSeed(); | |||||
String currentCreateTime(); | |||||
String ledgerInitCommand(String peerPath); | |||||
String peerStartCommand(String peerPath); | |||||
LedgerBindingConf allLedgerHashs(String peerPath); | |||||
LedgerBindingConf allLedgerHashs(long lastTime, String peerPath); | |||||
List<LedgerInited> allLedgerIniteds(String peerPath); | |||||
boolean dbExist(String peerPath, String ledgerHash); | |||||
String peerVerifyKey(String peerPath); | |||||
void save(String ledgerAndNodeKey, String ledgerHash); | |||||
String readLedgerHash(String ledgerAndNodeKey); | |||||
} |
@@ -0,0 +1,310 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.dao.DBConnection; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import com.jd.blockchain.ump.model.state.LedgerBindingConf; | |||||
import com.jd.blockchain.ump.model.state.LedgerInited; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import java.io.File; | |||||
import java.io.FileInputStream; | |||||
import java.io.InputStream; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.*; | |||||
@Service | |||||
public class LedgerServiceHandler implements LedgerService { | |||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSZ"); | |||||
private static final String LEDGER_HASHS_FLAG = "ledger.bindings"; | |||||
private static final String LEDGER_NAME_FORMAT = "binding.%s.name"; | |||||
private static final String LEDGER_PARTI_ADDRESS_FORMAT = "binding.%s.parti.address"; | |||||
private static final String LEDGER_PARTI_NAME_FORMAT = "binding.%s.parti.name"; | |||||
private static final String LEDGER_DB_FORMAT = "binding.%s.db.uri"; | |||||
private static final String FILE_PEER_FLAG = "deployment-peer"; | |||||
private static final String JAR_SUFFIX = "jar"; | |||||
private static final int SEED_BYTES_LENGTH = 32; | |||||
private static final int NAME_BYTES_LENGTH = 8; | |||||
private static final int SEED_PART_LENGTH = 8; | |||||
private static final Random LEDGER_RANDOM = new Random(); | |||||
@Autowired | |||||
private UmpStateService umpStateService; | |||||
@Autowired | |||||
private DBConnection dbConnection; | |||||
@Override | |||||
public String randomSeed() { | |||||
byte[] seedBytes = new byte[SEED_BYTES_LENGTH]; | |||||
LEDGER_RANDOM.nextBytes(seedBytes); | |||||
char[] seedChars = Hex.encodeHex(seedBytes); | |||||
StringBuilder sBuilder = new StringBuilder(); | |||||
for (int i = 0; i < seedChars.length; i++) { | |||||
if (i != 0 && i % SEED_PART_LENGTH == 0) { | |||||
sBuilder.append("-"); | |||||
} | |||||
sBuilder.append(seedChars[i]); | |||||
} | |||||
return sBuilder.toString(); | |||||
} | |||||
@Override | |||||
public String currentCreateTime() { | |||||
return SDF.format(new Date()); | |||||
} | |||||
@Override | |||||
public String ledgerInitCommand(String peerPath) { | |||||
return String.format(UmpConstant.CMD_LEDGER_INIT, | |||||
peerPath + UmpConstant.PATH_LEDGER_INIT_BIN); | |||||
} | |||||
@Override | |||||
public String peerStartCommand(String peerPath) { | |||||
return String.format(UmpConstant.CMD_START_UP_FORMAT, | |||||
peerPath + UmpConstant.PATH_STARTUP_BIN); | |||||
} | |||||
@Override | |||||
public LedgerBindingConf allLedgerHashs(String peerPath) { | |||||
return allLedgerHashs(0L, peerPath); | |||||
} | |||||
@Override | |||||
public LedgerBindingConf allLedgerHashs(long lastTime, String peerPath) { | |||||
// 读取LedgerBingConf文件,假设该文件不存在则返回空值 | |||||
Set<String> allLedgerHashs = new HashSet<>(); | |||||
PropAndTime propAndTime = loadLedgerBindingConf(lastTime, peerPath); | |||||
Properties props = propAndTime.getProp(); | |||||
if (props != null) { | |||||
String ledgerHashChars = props.getProperty(LEDGER_HASHS_FLAG); | |||||
if (ledgerHashChars != null && ledgerHashChars.length() > 0) { | |||||
String[] ledgerHashArray = ledgerHashChars.split(","); | |||||
if (ledgerHashArray.length > 0) { | |||||
for (String ledgerHash : ledgerHashArray) { | |||||
allLedgerHashs.add(ledgerHash.trim()); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
LedgerBindingConf ledgerBindingConf = new LedgerBindingConf(propAndTime.getLastTime()); | |||||
ledgerBindingConf.setLedgerHashs(allLedgerHashs); | |||||
return ledgerBindingConf; | |||||
} | |||||
@Override | |||||
public List<LedgerInited> allLedgerIniteds(String peerPath) { | |||||
List<LedgerInited> ledgerIniteds = new ArrayList<>(); | |||||
PropAndTime propAndTime = loadLedgerBindingConf(0L, peerPath); | |||||
Properties props = propAndTime.getProp(); | |||||
if (props != null) { | |||||
String ledgerHashChars = props.getProperty(LEDGER_HASHS_FLAG); | |||||
Set<String> ledgerHashSet = new HashSet<>(); | |||||
if (ledgerHashChars != null && ledgerHashChars.length() > 0) { | |||||
String[] ledgerHashArray = ledgerHashChars.split(","); | |||||
if (ledgerHashArray.length > 0) { | |||||
for (String ledgerHash : ledgerHashArray) { | |||||
ledgerHashSet.add(ledgerHash.trim()); | |||||
} | |||||
} | |||||
} | |||||
// 根据Hash值,遍历Prop | |||||
for (String hash : ledgerHashSet) { | |||||
LedgerInited ledgerInited = new LedgerInited(hash); | |||||
String ledgerName = props.getProperty(String.format(LEDGER_NAME_FORMAT, hash)); | |||||
String partiAddress = props.getProperty(String.format(LEDGER_PARTI_ADDRESS_FORMAT, hash)); | |||||
String partiName = props.getProperty(String.format(LEDGER_PARTI_NAME_FORMAT, hash)); | |||||
String dbUri = props.getProperty(String.format(LEDGER_DB_FORMAT, hash)); | |||||
ledgerIniteds.add( | |||||
ledgerInited | |||||
.buildLedgerName(ledgerName) | |||||
.buildPartiAddress(partiAddress) | |||||
.buildPartiName(partiName) | |||||
.buildDbUri(dbUri)); | |||||
} | |||||
} | |||||
return ledgerIniteds; | |||||
} | |||||
@Override | |||||
public synchronized boolean dbExist(String peerPath, String ledgerHash) { | |||||
// 检查该账本对应的数据库是否存在 | |||||
PropAndTime propAndTime = loadLedgerBindingConf(0L, peerPath); | |||||
// binding.j5faRYSqSqSRmSVgdmPsgq7Hzd1yP7yAGPWkTihekWms94.db.uri=rocksdb:///Users/shaozhuguang/Documents/ideaProjects/jdchain-patch/source/test/test-integration/rocks.db/rocksdb4.db | |||||
Properties props = propAndTime.getProp(); | |||||
if (props != null) { | |||||
String dbKey = String.format(LEDGER_DB_FORMAT, ledgerHash); | |||||
String dbUri = props.getProperty(dbKey); | |||||
if (dbUri != null && dbUri.length() > 0) { | |||||
return dbConnection.exist(dbUri); | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
@Override | |||||
public String peerVerifyKey(String peerPath) { | |||||
// 从libs中读取对应的Peer.jar的文件名称,配合全路径 | |||||
File libsDirectory = new File(peerPath + UmpConstant.PATH_SYSTEM); | |||||
Collection<File> jars = FileUtils.listFiles(libsDirectory, new String[]{JAR_SUFFIX}, false); | |||||
String peerVerifyKey = null; | |||||
if (!jars.isEmpty()) { | |||||
for (File jar : jars) { | |||||
String jarName = jar.getName(); | |||||
if (jarName.startsWith(FILE_PEER_FLAG)) { | |||||
peerVerifyKey = jar.getPath(); | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return peerVerifyKey; | |||||
} | |||||
@Override | |||||
public void save(String ledgerAndNodeKey, String ledgerHash) { | |||||
// 保存LedgerAndNodeKey与账本关系 | |||||
umpStateService.saveLedgerHash(ledgerAndNodeKey, ledgerHash); | |||||
} | |||||
@Override | |||||
public String readLedgerHash(String ledgerAndNodeKey) { | |||||
return umpStateService.readLedgerHash(ledgerAndNodeKey); | |||||
} | |||||
private PropAndTime loadLedgerBindingConf(long lastTime, String peerPath) { | |||||
File ledgerBindingConf = new File(peerPath + UmpConstant.PATH_LEDGER_BINDING_CONFIG); | |||||
PropAndTime propAndTime = new PropAndTime(lastTime); | |||||
// 说明被修改过 | |||||
if (ledgerBindingConf.exists() && ledgerBindingConf.lastModified() > lastTime) { | |||||
propAndTime.lastTime = ledgerBindingConf.lastModified(); | |||||
try (InputStream inputStream = new FileInputStream(ledgerBindingConf)) { | |||||
Properties props = new Properties(); | |||||
props.load(inputStream); | |||||
propAndTime.prop = props; | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
return propAndTime; | |||||
} | |||||
private static class PropAndTime { | |||||
private Properties prop; | |||||
private long lastTime; | |||||
public PropAndTime() { | |||||
} | |||||
public PropAndTime(long lastTime) { | |||||
this.lastTime = lastTime; | |||||
} | |||||
public Properties getProp() { | |||||
return prop; | |||||
} | |||||
public void setProp(Properties prop) { | |||||
this.prop = prop; | |||||
} | |||||
public long getLastTime() { | |||||
return lastTime; | |||||
} | |||||
public void setLastTime(long lastTime) { | |||||
this.lastTime = lastTime; | |||||
} | |||||
} | |||||
// private Properties loadLedgerBindingConf(String peerPath) { | |||||
// | |||||
// File ledgerBindingConf = new File(peerPath + UmpConstant.PATH_LEDGER_BINDING_CONFIG); | |||||
// | |||||
// if (ledgerBindingConf.exists()) { | |||||
// | |||||
// try (InputStream inputStream = new FileInputStream(ledgerBindingConf)) { | |||||
// | |||||
// Properties props = new Properties(); | |||||
// | |||||
// props.load(inputStream); | |||||
// | |||||
// return props; | |||||
// | |||||
// } catch (Exception e) { | |||||
// throw new IllegalStateException(e); | |||||
// } | |||||
// } | |||||
// | |||||
// return null; | |||||
// } | |||||
} |
@@ -0,0 +1,36 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import com.jd.blockchain.ump.model.PeerSharedConfigs; | |||||
import com.jd.blockchain.ump.model.config.*; | |||||
import com.jd.blockchain.ump.model.state.PeerInstallSchedules; | |||||
import com.jd.blockchain.ump.model.state.PeerStartupSchedules; | |||||
public interface UmpService { | |||||
PeerSharedConfigs loadPeerSharedConfigs(PeerLocalConfig sharedConfig); | |||||
LedgerConfig response(PeerSharedConfigs peerSharedConfigs, PeerLocalConfig localConfig); | |||||
String save(MasterAddr masterAddr, LedgerConfig ledgerConfig, PeerLocalConfig localConfig); | |||||
String ledgerAndNodeKey(LedgerConfig ledgerConfig, PeerSharedConfig sharedConfig); | |||||
PeerInstallSchedules install(LedgerIdentification identification, PeerLocalConfig localConfig, String ledgerAndNodeKey); | |||||
PeerInstallSchedules install(String ledgerAndNodeKey); | |||||
PeerInstallSchedules init(String ledgerAndNodeKey); | |||||
PeerInstallSchedules init(LedgerIdentification identification, PeerLocalConfig localConfig, String ledgerAndNodeKey); | |||||
// PeerInstallSchedules startup(String ledgerAndNodeKey); | |||||
PeerStartupSchedules startup(); | |||||
boolean stop(String ledgerAndNodeKey); | |||||
boolean stop(); | |||||
} |
@@ -0,0 +1,925 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.dao.DBConnection; | |||||
import com.jd.blockchain.ump.dao.RocksDBConnection; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import com.jd.blockchain.ump.model.PeerSharedConfigs; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import com.jd.blockchain.ump.model.config.*; | |||||
import com.jd.blockchain.ump.model.state.*; | |||||
import com.jd.blockchain.ump.service.consensus.ConsensusService; | |||||
import com.jd.blockchain.ump.util.Base58Utils; | |||||
import com.jd.blockchain.ump.util.CommandUtils; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.*; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
import java.util.concurrent.locks.Lock; | |||||
@Service | |||||
public class UmpServiceHandler implements UmpService { | |||||
private final Logger LOGGER = LoggerFactory.getLogger(getClass()); | |||||
private static final String SUCCESS = "SUCCESS"; | |||||
private static final String ROCKSDB_PROTOCOL = RocksDBConnection.ROCKSDB_PROTOCOL; | |||||
private static final int DB_SUFFIX_LENGTH = 4; | |||||
private static final Random DB_RANDOM = new Random(); | |||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyyMMddHHmmssSSS");//yyyy-MM-dd HH:mm:ss为目标的样式 | |||||
private final Map<String, LedgerConfig> ledgerConfigs = new ConcurrentHashMap<>(); | |||||
private final Map<String, MasterConfig> masterConfigs = new ConcurrentHashMap<>(); | |||||
private final Map<String, PeerSharedConfigs> peerShareds = new ConcurrentHashMap<>(); | |||||
private final Map<String, LedgerConfig> ledgerConfigMemory = new ConcurrentHashMap<>(); | |||||
@Autowired | |||||
private ConsensusService consensusService; | |||||
@Autowired | |||||
private LedgerService ledgerService; | |||||
@Autowired | |||||
private DBConnection dbConnection; | |||||
@Autowired | |||||
private UmpStateService umpStateService; | |||||
@Override | |||||
public synchronized PeerSharedConfigs loadPeerSharedConfigs(PeerLocalConfig sharedConfig) { | |||||
String sharedKey = sharedConfig.getSharedKey(); | |||||
PeerSharedConfigs peerSharedConfigs = peerShareds.get(sharedKey); | |||||
if (peerSharedConfigs == null) { | |||||
peerSharedConfigs = new PeerSharedConfigs(); | |||||
peerShareds.put(sharedKey, peerSharedConfigs); | |||||
} | |||||
return peerSharedConfigs.addConfig(sharedConfig); | |||||
} | |||||
@Override | |||||
public LedgerConfig response(PeerSharedConfigs sharedConfigs, PeerLocalConfig localConfig) { | |||||
try { | |||||
// 对于Master和Peer处理方式不同 | |||||
if (localConfig.getMasterConfig().isMaster()) { | |||||
// Master节点需要等待完成后通知其他线程 | |||||
sharedConfigs.waitAndNotify(); | |||||
} else { | |||||
// 等待Master节点通知 | |||||
sharedConfigs.await(); | |||||
} | |||||
// 此处需要防止并发 | |||||
final String sharedKey = sharedConfigs.getSharedKey(); | |||||
LedgerConfig savedLedgerConfig = ledgerConfigMemory.get(sharedKey); | |||||
if (savedLedgerConfig != null) { | |||||
return savedLedgerConfig; | |||||
} | |||||
// 获取当前对象锁(所有节点请求使用同一个对象) | |||||
final Lock lock = sharedConfigs.getLock(); | |||||
lock.lock(); | |||||
try { | |||||
// 执行到此表示获取到锁,此时需要判断是否有数据 | |||||
// Double Check !!! | |||||
savedLedgerConfig = ledgerConfigMemory.get(sharedKey); | |||||
if (savedLedgerConfig != null) { | |||||
return savedLedgerConfig; | |||||
} | |||||
// 校验 | |||||
verify(sharedConfigs); | |||||
// 所有数据到达之后生成返回的应答 | |||||
LedgerInitConfig initConfig = sharedConfigs.ledgerInitConfig( | |||||
ledgerService.randomSeed(), ledgerService.currentCreateTime()); | |||||
// 生成共识文件 | |||||
String consensusConfig = consensusService.initConsensusConf( | |||||
sharedConfigs.getConsensusProvider(), sharedConfigs.getSharedConfigs()); | |||||
LedgerConfig ledgerConfig = new LedgerConfig(initConfig, consensusConfig); | |||||
// 将本次LedgerKey信息写入数据库 | |||||
String ledgerKey = initConfig.ledgerKey(); | |||||
dbConnection.put(ledgerKey, ledgerConfig, LedgerConfig.class); | |||||
// 将节点的Key信息写入数据库 | |||||
umpStateService.save(ledgerKey, sharedConfigKeys(ledgerKey, sharedConfigs)); | |||||
// 将本地生成数据的信息写入数据库 | |||||
LedgerMasterInstall masterInstall = sharedConfigs.toLedgerMasterInstall(); | |||||
umpStateService.save(masterInstall); | |||||
// 将数据放入内存 | |||||
ledgerConfigMemory.put(sharedKey, ledgerConfig); | |||||
return ledgerConfig; | |||||
} finally { | |||||
lock.unlock(); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
@Override | |||||
public String save(MasterAddr masterAddr, LedgerConfig ledgerConfig, PeerLocalConfig localConfig) { | |||||
String ledgerAndNodeKey = ledgerAndNodeKey(ledgerConfig, localConfig); | |||||
ledgerConfigs.put(ledgerAndNodeKey, ledgerConfig); | |||||
// 保存本次需要发送的Master地址 | |||||
masterConfigs.put(ledgerAndNodeKey, localConfig.getMasterConfig()); | |||||
// 保存所有的信息至本地 | |||||
umpStateService.save(ledgerAndNodeKey, localConfig); | |||||
// 保存当前同步信息至数据库 | |||||
LedgerPeerInstall peerInstall = localConfig.toLedgerPeerInstall(ledgerConfig.getInitConfig().getNodeSize()); | |||||
// init相关配置信息 | |||||
peerInstall | |||||
.initKey(ledgerConfig.getInitConfig().ledgerKey(), ledgerAndNodeKey) | |||||
.initCreateTime(new Date()) | |||||
.initMasterAddr(masterAddr); | |||||
// 写入数据库 | |||||
umpStateService.save(peerInstall); | |||||
return ledgerAndNodeKey; | |||||
} | |||||
@Override | |||||
public String ledgerAndNodeKey(LedgerConfig ledgerConfig, PeerSharedConfig sharedConfig) { | |||||
return ledgerAndNodeKey(ledgerConfig.getInitConfig().ledgerKey(), sharedConfig); | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules install(LedgerIdentification identification, PeerLocalConfig localConfig, String ledgerAndNodeKey) { | |||||
// 初始化Peer节点数据 | |||||
PeerInstallSchedules installSchedules = init(identification, localConfig, ledgerAndNodeKey); | |||||
// Peer节点启动 | |||||
peerStart(localConfig.getPeerPath(), installSchedules); | |||||
return installSchedules; | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules install(String ledgerAndNodeKey) { | |||||
PeerLocalConfig localConfig = umpStateService.readConfig(ledgerAndNodeKey); | |||||
if (localConfig != null) { | |||||
// 获取LedgerIdentification | |||||
LedgerIdentification identification = umpStateService.readIdentification(ledgerAndNodeKey); | |||||
return install(identification, localConfig, ledgerAndNodeKey); | |||||
} | |||||
throw new IllegalStateException("Can not find LocalConfig from DataBase !!!"); | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules init(LedgerIdentification identification, PeerLocalConfig localConfig, String ledgerAndNodeKey) { | |||||
PeerInstallSchedules installSchedules = new PeerInstallSchedules(identification); | |||||
MasterAddr masterAddr = loadMaster(localConfig); | |||||
LedgerConfig ledgerConfig = ledgerConfigs.get(ledgerAndNodeKey); | |||||
if (ledgerConfig == null || ledgerConfig.getInitConfig() == null) { | |||||
saveInstallSchedule(installSchedules, masterAddr, "", ledgerAndNodeKey, | |||||
String.format("Ledger Key = [%s] can not find Ledger-Config !!!", ledgerAndNodeKey), | |||||
ScheduleState.LOAD_FAIL); | |||||
throw new IllegalStateException(String.format("Ledger Key = [%s] can not find Ledger-Config !!!", ledgerAndNodeKey)); | |||||
} | |||||
LedgerInitConfig initConfig = ledgerConfig.getInitConfig(); | |||||
String ledgerKey = initConfig.ledgerKey(); | |||||
List<String> localConfContents, ledgerInitContents; | |||||
try { | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Find LedgerConfig from Memory for Key [%s] -> %s", ledgerAndNodeKey, SUCCESS), | |||||
ScheduleState.LOAD); | |||||
// 首先获取当前节点的ID | |||||
int nodeId = initConfig.nodeId(localConfig.getPubKey()); | |||||
// 生成local.conf文件内容 | |||||
localConfContents = localConfContents(localConfig, nodeId); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Init Local.Conf's Content -> %s", SUCCESS), | |||||
ScheduleState.LOAD); | |||||
// 生成LedgerInit内容 | |||||
ledgerInitContents = initConfig.toConfigChars(localConfig.consensusConfPath()); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Init Ledger.Init's Content -> %s", SUCCESS), | |||||
ScheduleState.LOAD); | |||||
} catch (Exception e) { | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
"Load Config's Content !!!", | |||||
ScheduleState.LOAD_FAIL); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
"Load Config's Content !!!", | |||||
ScheduleState.LOAD_SUCCESS); | |||||
try { | |||||
// 将该文件内容写入Local.Conf | |||||
forceWrite(localConfContents, new File(localConfig.localConfPath())); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Write And Backup File local.conf -> %s", SUCCESS), | |||||
ScheduleState.WRITE); | |||||
// 将文件内容写入Ledger-Init | |||||
forceWrite(ledgerInitContents, new File(localConfig.ledgerInitConfPath())); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Write And Backup File ledger.init -> %s", SUCCESS), | |||||
ScheduleState.WRITE); | |||||
// 将共识内容写入文件,例如bftsmart.conf | |||||
String consensusFileName = writeConsensusContent(ledgerConfig.getConsensusConfig(), | |||||
new File(localConfig.consensusConfPath())); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Write And Backup Consensus File %s -> %s", consensusFileName, SUCCESS), | |||||
ScheduleState.WRITE); | |||||
} catch (Exception e) { | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
"Write Config's Content to Config File !!!", | |||||
ScheduleState.WRITE_FAIL); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
"Write Config's Content to Config File !!!", | |||||
ScheduleState.WRITE_SUCCESS); | |||||
// 账本初始化 | |||||
String ledgerHash = ledgerInit(localConfig.getPeerPath(), installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey); | |||||
// 设置账本Hash | |||||
installSchedules.setLedgerHash(ledgerHash); | |||||
return installSchedules; | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules init(String ledgerAndNodeKey) { | |||||
PeerLocalConfig localConfig = umpStateService.readConfig(ledgerAndNodeKey); | |||||
if (localConfig != null) { | |||||
// 获取LedgerIdentification | |||||
LedgerIdentification identification = umpStateService.readIdentification(ledgerAndNodeKey); | |||||
return init(identification, localConfig, ledgerAndNodeKey); | |||||
} | |||||
throw new IllegalStateException("Can not find LocalConfig from DataBase !!!"); | |||||
} | |||||
// @Override | |||||
// public PeerInstallSchedules startup(String ledgerAndNodeKey) { | |||||
// | |||||
// PeerLocalConfig localConfig = umpStateService.readConfig(ledgerAndNodeKey); | |||||
// | |||||
// if (localConfig != null) { | |||||
// | |||||
// PeerInstallSchedules installSchedules = umpStateService.loadState(ledgerAndNodeKey); | |||||
// | |||||
// // Peer节点启动 | |||||
// return peerStart(localConfig.getPeerPath(), installSchedules); | |||||
// | |||||
// } | |||||
// throw new IllegalStateException("Can not find LocalConfig from DataBase !!!"); | |||||
// } | |||||
@Override | |||||
public PeerStartupSchedules startup() { | |||||
PeerStartupSchedules startupSchedules = new PeerStartupSchedules(UmpConstant.PROJECT_PATH); | |||||
return peerStart(startupSchedules); | |||||
} | |||||
@Override | |||||
public boolean stop(String ledgerAndNodeKey) { | |||||
PeerLocalConfig localConfig = umpStateService.readConfig(ledgerAndNodeKey); | |||||
if (localConfig != null) { | |||||
// Peer节点停止 | |||||
return peerStop(localConfig.getPeerPath()); | |||||
} | |||||
throw new IllegalStateException("Can not find LocalConfig from DataBase !!!"); | |||||
} | |||||
@Override | |||||
public boolean stop() { | |||||
return peerStop(UmpConstant.PROJECT_PATH); | |||||
} | |||||
private MasterAddr loadMaster(PeerLocalConfig localConfig) { | |||||
// 开始安装之后则可以将内存中的数据释放 | |||||
String sharedKey = localConfig.getSharedKey(); | |||||
if (sharedKey != null) { | |||||
ledgerConfigMemory.remove(sharedKey); | |||||
} | |||||
if (localConfig.master()) { | |||||
return null; | |||||
} | |||||
return localConfig.masterAddr(); | |||||
} | |||||
private List<String> sharedConfigKeys(String ledgerKey, PeerSharedConfigs sharedConfigs) { | |||||
List<String> sharedConfigKeys = new ArrayList<>(); | |||||
List<PeerLocalConfig> pscs = sharedConfigs.getSharedConfigs(); | |||||
for(PeerSharedConfig psc : pscs) { | |||||
sharedConfigKeys.add(ledgerAndNodeKey(ledgerKey, psc)); | |||||
} | |||||
return sharedConfigKeys; | |||||
} | |||||
private String ledgerAndNodeKey(String ledgerKey, PeerSharedConfig sharedConfig) { | |||||
return ledgerKey + "-" + sharedConfig.getName(); | |||||
} | |||||
private String ledgerInit(String peerPath, PeerInstallSchedules installSchedules, MasterAddr masterAddr, String ledgerKey, String ledgerAndNodeKey) { | |||||
String newLedgerHash = ""; | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
"Steps to start processing LedgerInit !!!", | |||||
ScheduleState.INIT); | |||||
// 获取当前已经存在的Ledger列表 | |||||
LedgerBindingConf ledgerBindingConf = ledgerService.allLedgerHashs(peerPath); | |||||
Set<String> currentLedgerHashs = ledgerBindingConf.getLedgerHashs(); | |||||
long lastTime = ledgerBindingConf.getLastTime(); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Find History Ledger's Size = %s", currentLedgerHashs.size()), | |||||
ScheduleState.INIT); | |||||
String ledgerInitCommand = ledgerService.ledgerInitCommand(peerPath); | |||||
try { | |||||
LOGGER.info("Execute Ledger-Init's Shell {}", ledgerInitCommand); | |||||
Process ledgerInitProcess; | |||||
try { | |||||
// 调用ledgerInit初始化脚本 | |||||
ledgerInitProcess = CommandUtils.execute(CommandUtils.toCommandList(ledgerInitCommand)); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Execute LedgerInit's Command -> %s", SUCCESS), | |||||
ScheduleState.INIT); | |||||
} catch (Exception e) { | |||||
LOGGER.error("Execute Ledger-Init's Shell !!!", e); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
int maxSize = 512; | |||||
boolean isInitSuccess = false; | |||||
int checkIndex = 1; | |||||
while (maxSize > 0) { | |||||
// 时延 | |||||
Thread.sleep(6000); | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("%s Check LedgerInit's Status ...... ", checkIndex++), | |||||
ScheduleState.INIT); | |||||
// 检查账本是否增加 | |||||
CurrentLedger currentLedger = checkNewLedger(lastTime, peerPath, currentLedgerHashs); | |||||
lastTime = currentLedger.getLastTime(); | |||||
newLedgerHash = currentLedger.getLedgerHash(); | |||||
if (newLedgerHash != null && newLedgerHash.length() > 0) { | |||||
isInitSuccess = true; | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Find New Ledger = %s", newLedgerHash), | |||||
ScheduleState.INIT); | |||||
break; | |||||
} | |||||
maxSize --; | |||||
} | |||||
// 完成后,不管是否处理完,都将命令停止 | |||||
// 为防止其他应用仍在访问,延时6秒停止 | |||||
try { | |||||
Thread.sleep(6000); | |||||
ledgerInitProcess = ledgerInitProcess.destroyForcibly(); | |||||
if (ledgerInitProcess.isAlive()) { | |||||
// 再尝试一次 | |||||
ledgerInitProcess.destroyForcibly(); | |||||
} | |||||
} catch (Exception e) { | |||||
// 暂时打印日志 | |||||
LOGGER.error("Stop Ledger Init Command !!!", e); | |||||
} | |||||
// 再次判断是否初始化账本成功 | |||||
if (newLedgerHash == null) { | |||||
CurrentLedger currentLedger = checkNewLedger(lastTime, peerPath, currentLedgerHashs); | |||||
newLedgerHash = currentLedger.getLedgerHash(); | |||||
if (newLedgerHash != null && newLedgerHash.length() > 0) { | |||||
isInitSuccess = true; | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Final Find New Ledger = %s", newLedgerHash), | |||||
ScheduleState.INIT); | |||||
} | |||||
} | |||||
if (!isInitSuccess) { | |||||
// 失败则抛出异常 | |||||
throw new IllegalStateException("Can Not Find New Ledger !!!"); | |||||
} | |||||
} catch (Exception e) { | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
"Execute Ledger-Init Command Fail !!!", | |||||
ScheduleState.INIT_FAIL); | |||||
LOGGER.error("Execute Ledger-Init Command Fail !!!", e); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
String.format("Steps to processing LedgerInit -> %s", SUCCESS), | |||||
ScheduleState.INIT_SUCCESS); | |||||
// 将账本Hash写入数据库 | |||||
ledgerService.save(ledgerAndNodeKey, newLedgerHash); | |||||
return newLedgerHash; | |||||
} | |||||
private CurrentLedger checkNewLedger(long lastTime, String peerPath, Set<String> currentLedgerHashs) { | |||||
// 再次判断是否初始化账本成功 | |||||
LedgerBindingConf ledgerBindingConf = ledgerService.allLedgerHashs(lastTime, peerPath); | |||||
Set<String> newLedgerHashs = ledgerBindingConf.getLedgerHashs(); | |||||
CurrentLedger currentLedger = new CurrentLedger(ledgerBindingConf.getLastTime()); | |||||
if (newLedgerHashs.size() > currentLedgerHashs.size()) { | |||||
// 获取其新安装的LedgerHash | |||||
for (String ledgerHash : newLedgerHashs) { | |||||
if (!currentLedgerHashs.contains(ledgerHash)) { | |||||
// 新获取的LedgerHash为当前值 | |||||
currentLedger.ledgerHash = ledgerHash; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
return currentLedger; | |||||
} | |||||
private PeerInstallSchedules peerStart(String peerPath, PeerInstallSchedules installSchedules) { | |||||
saveInstallSchedule(installSchedules, | |||||
"Steps to start processing PeerNodeStart !!!", | |||||
ScheduleState.STARTUP_START); | |||||
// 启动Peer | |||||
// 说明初始化成功 | |||||
// 判断是否需要启动Peer | |||||
String peerVerify = ledgerService.peerVerifyKey(peerPath); | |||||
try { | |||||
if (!CommandUtils.isActive(peerVerify)) { | |||||
// 不存在,则需要再启动 | |||||
String peerStartCmd = ledgerService.peerStartCommand(peerPath); | |||||
LOGGER.info("Execute Peer-Startup's Shell {}", peerStartCmd); | |||||
if (!CommandUtils.executeAndVerify(CommandUtils.toCommandList(peerStartCmd), peerVerify)) { | |||||
// Peer节点启动失败 | |||||
throw new IllegalStateException("Peer Node Start UP Fail !!!"); | |||||
} | |||||
saveInstallSchedule(installSchedules, | |||||
String.format("Peer's process %s start -> %s", peerVerify, SUCCESS), | |||||
ScheduleState.STARTUP_SUCCESS); | |||||
} else { | |||||
// 命令已经存在 | |||||
saveInstallSchedule(installSchedules, | |||||
String.format("Peer's process is exist -> %s", peerVerify), | |||||
ScheduleState.NO_STARTUP); | |||||
} | |||||
} catch (Exception e) { | |||||
saveInstallSchedule(installSchedules, | |||||
e.getMessage(), | |||||
ScheduleState.STARTUP_FAIL); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
saveInstallSchedule(installSchedules, | |||||
"Steps to start processing PeerNodeStart over !!!", | |||||
ScheduleState.STARTUP_OVER); | |||||
return installSchedules; | |||||
} | |||||
private PeerStartupSchedules peerStart(PeerStartupSchedules startupSchedules) { | |||||
String peerPath = startupSchedules.getPeerPath(); | |||||
saveStartupSchedules(startupSchedules, | |||||
"Steps to start processing PeerNodeStart !!!", | |||||
ScheduleState.STARTUP_START); | |||||
// 启动Peer | |||||
// 说明初始化成功 | |||||
// 判断是否需要启动Peer | |||||
String peerVerify = ledgerService.peerVerifyKey(peerPath); | |||||
try { | |||||
if (!CommandUtils.isActive(peerVerify)) { | |||||
// 不存在,则需要再启动 | |||||
String peerStartCmd = ledgerService.peerStartCommand(peerPath); | |||||
LOGGER.info("Execute Peer-Startup's Shell {}", peerStartCmd); | |||||
if (!CommandUtils.executeAndVerify(CommandUtils.toCommandList(peerStartCmd), peerVerify)) { | |||||
// Peer节点启动失败 | |||||
throw new IllegalStateException("Peer Node Start UP Fail !!!"); | |||||
} | |||||
saveStartupSchedules(startupSchedules, | |||||
String.format("Peer's process %s start -> %s", peerVerify, SUCCESS), | |||||
ScheduleState.STARTUP_SUCCESS); | |||||
} else { | |||||
// 命令已经存在 | |||||
saveStartupSchedules(startupSchedules, | |||||
String.format("Peer's process is exist -> %s", peerVerify), | |||||
ScheduleState.NO_STARTUP); | |||||
} | |||||
} catch (Exception e) { | |||||
saveStartupSchedules(startupSchedules, | |||||
e.getMessage(), | |||||
ScheduleState.STARTUP_FAIL); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
saveStartupSchedules(startupSchedules, | |||||
"Steps to start processing PeerNodeStart over !!!", | |||||
ScheduleState.STARTUP_OVER); | |||||
return startupSchedules; | |||||
} | |||||
// private PeerInstallSchedules peerStart(String peerPath, PeerInstallSchedules installSchedules) { | |||||
// | |||||
// MasterAddr masterAddr = installSchedules.getIdentification().getMasterAddr(); | |||||
// | |||||
// String ledgerKey = installSchedules.getIdentification().getLedgerKey(); | |||||
// | |||||
// String ledgerAndNodeKey = installSchedules.getIdentification().getLedgerAndNodeKey(); | |||||
// | |||||
// saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
// "Steps to start processing PeerNodeStart !!!", | |||||
// ScheduleState.STARTUP_START); | |||||
// // 启动Peer | |||||
// // 说明初始化成功 | |||||
// // 判断是否需要启动Peer | |||||
// String peerVerify = ledgerService.peerVerifyKey(peerPath); | |||||
// | |||||
// try { | |||||
// if (!CommandUtils.isActive(peerVerify)) { | |||||
// // 不存在,则需要再启动 | |||||
// String peerStartCmd = ledgerService.peerStartCommand(peerPath); | |||||
// | |||||
// LOGGER.info("Execute Peer-Startup's Shell {}", peerStartCmd); | |||||
// | |||||
// if (!CommandUtils.executeAndVerify(CommandUtils.toCommandList(peerStartCmd), peerVerify)) { | |||||
// // Peer节点启动失败 | |||||
// throw new IllegalStateException("Peer Node Start UP Fail !!!"); | |||||
// } | |||||
// saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
// String.format("Peer's process %s start -> %s", peerVerify, SUCCESS), | |||||
// ScheduleState.STARTUP_SUCCESS); | |||||
// } else { | |||||
// // 命令已经存在 | |||||
// saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
// String.format("Peer's process is exist -> %s", peerVerify), | |||||
// ScheduleState.NO_STARTUP); | |||||
// } | |||||
// } catch (Exception e) { | |||||
// saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
// e.getMessage(), | |||||
// ScheduleState.STARTUP_FAIL); | |||||
// throw new IllegalStateException(e); | |||||
// } | |||||
// | |||||
// saveInstallSchedule(installSchedules, masterAddr, ledgerKey, ledgerAndNodeKey, | |||||
// "Steps to start processing PeerNodeStart over !!!", | |||||
// ScheduleState.STARTUP_OVER); | |||||
// | |||||
// return installSchedules; | |||||
// } | |||||
private boolean peerStop(String peerPath) { | |||||
// 判断是否需要停止Peer | |||||
String peerVerify = ledgerService.peerVerifyKey(peerPath); | |||||
try { | |||||
if (CommandUtils.isActive(peerVerify)) { | |||||
LOGGER.info("We need stop peer {}", peerVerify); | |||||
// 需要停止Peer节点 | |||||
CommandUtils.killVm(peerVerify); | |||||
// 最多循环5次进行判断 | |||||
int maxSize = 5; | |||||
while (maxSize > 0) { | |||||
try { | |||||
Thread.sleep(3000); | |||||
if (!CommandUtils.isActive(peerVerify)) { | |||||
return true; | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error("Check Peer Stop State !!!", e); | |||||
} finally { | |||||
maxSize--; | |||||
} | |||||
} | |||||
} else { | |||||
LOGGER.info("We do not need stop peer {}", peerVerify); | |||||
return false; | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error("Stop Peer Node", e); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
return false; | |||||
} | |||||
private String writeConsensusContent(String consensusContent, File consensusFile) throws IOException { | |||||
// 将字符串转换为字节数组 | |||||
byte[] consensusBytes = Base58Utils.decode(consensusContent); | |||||
forceWrite(consensusBytes, consensusFile); | |||||
return consensusFile.getName(); | |||||
} | |||||
private void forceWrite(List<String> lines, File file) throws IOException { | |||||
if (file.exists()) { | |||||
FileUtils.moveFile(file, new File(file.getPath() + "_bak_" + currentDate())); | |||||
} | |||||
FileUtils.writeLines(file, StandardCharsets.UTF_8.toString(), lines); | |||||
} | |||||
private void forceWrite(byte[] content, File file) throws IOException { | |||||
if (file.exists()) { | |||||
FileUtils.moveFile(file, new File(file.getPath() + "_bak_" + currentDate())); | |||||
} | |||||
FileUtils.writeByteArrayToFile(file, content); | |||||
} | |||||
private void verify(PeerSharedConfigs peerSharedConfigs) { | |||||
// 校验其中内容 | |||||
List<PeerLocalConfig> sharedConfigs = peerSharedConfigs.getSharedConfigs(); | |||||
// 首先保证其中的数据一致性 | |||||
// 1、name不能重复; | |||||
// 2、pubKey不能重复; | |||||
// 3、ipAddr + initPort不能重复; | |||||
Set<String> nameSet = new HashSet<>(), | |||||
pubKeySet = new HashSet<>(), | |||||
addrSet = new HashSet<>(); | |||||
for (PeerSharedConfig sharedConfig : sharedConfigs) { | |||||
String name = sharedConfig.getName(), | |||||
pubKey = sharedConfig.getPubKey(), | |||||
addr = sharedConfig.addr(); | |||||
if (nameSet.contains(name)) { | |||||
throw new IllegalStateException(String.format("Name [%s] is Conflict !!!", name)); | |||||
} else { | |||||
nameSet.add(name); | |||||
} | |||||
if (pubKeySet.contains(pubKey)) { | |||||
throw new IllegalStateException(String.format("PubKey [%s] is Conflict !!!", pubKey)); | |||||
} else { | |||||
pubKeySet.add(pubKey); | |||||
} | |||||
if (addrSet.contains(addr)) { | |||||
throw new IllegalStateException(String.format("Address [%s] is Conflict !!!", addr)); | |||||
} else { | |||||
addrSet.add(addr); | |||||
} | |||||
} | |||||
} | |||||
private void saveInstallSchedule(PeerInstallSchedules installSchedules, MasterAddr masterAddr, String ledgerKey, String ledgerAndNodeKey, String content, ScheduleState state) { | |||||
// 日志打印相关内容 | |||||
LOGGER.info(content); | |||||
// 生成InstallSchedule对象 | |||||
InstallSchedule schedule = installSchedule(ledgerKey, ledgerAndNodeKey, content, state); | |||||
// 加入反馈列表 | |||||
installSchedules.addInstallSchedule( | |||||
new PeerInstallSchedule(new InstallProcess(content), state)); | |||||
// 将InstallSchedule写入数据库 | |||||
umpStateService.save(schedule, masterAddr); | |||||
} | |||||
private void saveInstallSchedule(PeerInstallSchedules installSchedules, String content, ScheduleState state) { | |||||
// 日志打印相关内容 | |||||
LOGGER.info(content); | |||||
// 加入反馈列表 | |||||
installSchedules.addInstallSchedule( | |||||
new PeerInstallSchedule(new InstallProcess(content), state)); | |||||
} | |||||
private void saveStartupSchedules(PeerStartupSchedules startupSchedules, String content, ScheduleState state) { | |||||
// 日志打印相关内容 | |||||
LOGGER.info(content); | |||||
// 加入反馈列表 | |||||
startupSchedules.addInstallSchedule( | |||||
new PeerInstallSchedule(new InstallProcess(content), state)); | |||||
} | |||||
private InstallSchedule installSchedule(String ledgerKey, String ledgerAndNodeKey, String content, ScheduleState state) { | |||||
InstallProcess process = new InstallProcess(content); | |||||
return new InstallSchedule(ledgerKey, ledgerAndNodeKey, process, state); | |||||
} | |||||
private List<String> localConfContents(PeerLocalConfig localConfig, int nodeId) { | |||||
/** | |||||
* #当前参与方的 id,与ledger.init文件中cons_parti.id一致,默认从0开始 | |||||
* local.parti.id=0 | |||||
* | |||||
* #当前参与方的公钥 | |||||
* local.parti.pubkey= | |||||
* | |||||
* #当前参与方的私钥(密文编码) | |||||
* local.parti.privkey= | |||||
* | |||||
* #当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入 | |||||
* local.parti.pwd= | |||||
* | |||||
* #账本初始化完成后生成的"账本绑定配置文件"的输出目录 | |||||
* #推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准 | |||||
* ledger.binding.out=../ | |||||
* | |||||
* #账本数据库的连接字符 | |||||
* #rocksdb数据库连接格式:rocksdb://{path},例如:rocksdb:///export/App08/peer/rocks.db/rocksdb0.db | |||||
* #redis数据库连接格式:redis://{ip}:{prot}/{db},例如:redis://127.0.0.1:6379/0 | |||||
* ledger.db.uri= | |||||
* | |||||
* #账本数据库的连接口令 | |||||
* ledger.db.pwd= | |||||
*/ | |||||
List<String> localContents = new ArrayList<>(); | |||||
localContents.add(valueToConfig(UmpConstant.LOCAL_PARTI_ID_PREFIX, nodeId)); | |||||
localContents.add(valueToConfig(UmpConstant.LOCAL_PARTI_PUBKEY_PREFIX, localConfig.getPubKey())); | |||||
localContents.add(valueToConfig(UmpConstant.LOCAL_PARTI_PRIVKEY_PREFIX, localConfig.getPrivKey())); | |||||
localContents.add(valueToConfig(UmpConstant.LOCAL_PARTI_PWD_PREFIX, localConfig.getEncodePwd())); | |||||
localContents.add(valueToConfig(UmpConstant.LEDGER_BINDING_OUT_PREFIX, localConfig.bindingOutPath())); | |||||
localContents.add(valueToConfig(UmpConstant.LEDGER_DB_URI_PREFIX, dbUri(localConfig.getDbName(), localConfig.getPeerPath()))); | |||||
localContents.add(valueToConfig(UmpConstant.LEDGER_DB_PWD_PREFIX, "")); | |||||
return localContents; | |||||
} | |||||
private String valueToConfig(String prefix, Object value) { | |||||
return prefix + "=" + value; | |||||
} | |||||
private String currentDate() { | |||||
return SDF.format(new Date()); | |||||
} | |||||
private String dbUri(final String dbName, final String peerPath) { | |||||
String dbDirectoryPath = peerPath + File.separator + dbName; | |||||
String dbUri = ROCKSDB_PROTOCOL + dbDirectoryPath; | |||||
File dbDirectory = new File(dbDirectoryPath); | |||||
if (!dbDirectory.exists()) { | |||||
return dbUri; | |||||
} | |||||
throw new IllegalStateException(String.format("DB name = %s, path = %s is Exist !!!", dbName, dbDirectoryPath)); | |||||
} | |||||
private static class CurrentLedger { | |||||
private String ledgerHash; | |||||
private long lastTime; | |||||
public CurrentLedger() { | |||||
} | |||||
public CurrentLedger(long lastTime) { | |||||
this.lastTime = lastTime; | |||||
} | |||||
public String getLedgerHash() { | |||||
return ledgerHash; | |||||
} | |||||
public void setLedgerHash(String ledgerHash) { | |||||
this.ledgerHash = ledgerHash; | |||||
} | |||||
public long getLastTime() { | |||||
return lastTime; | |||||
} | |||||
public void setLastTime(long lastTime) { | |||||
this.lastTime = lastTime; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerSharedConfigVv; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.ump.model.user.UserKeysVv; | |||||
public interface UmpSimulateService { | |||||
UserKeysVv userKeysVv(int nodeId); | |||||
UserKeys userKeys(int nodeId); | |||||
PeerLocalConfig nodePeerLocalConfig(int nodeId, boolean isMaster); | |||||
PeerSharedConfigVv peerSharedConfigVv(int nodeId, boolean isMaster); | |||||
} |
@@ -0,0 +1,134 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerSharedConfigVv; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.ump.model.user.UserKeysVv; | |||||
import com.jd.blockchain.ump.service.consensus.providers.BftsmartConsensusProvider; | |||||
import org.apache.commons.codec.binary.Hex; | |||||
import org.springframework.stereotype.Service; | |||||
import java.util.Random; | |||||
@Service | |||||
public class UmpSimulateServiceHandler implements UmpSimulateService { | |||||
private static final Random RANDOM_ROCKSDB = new Random(); | |||||
private static final String SHARED_KEY = "JDChain"; | |||||
private static final int TOTAL_SIZE = 4; | |||||
private static final String LOCALHOST = "127.0.0.1"; | |||||
private static final String CONSENSUS_PROVIDER = BftsmartConsensusProvider.BFTSMART_PROVIDER; | |||||
private static final String CONSENSUS_CONF = BftsmartConsensusProvider.BFTSMART_CONFIG_FILE; | |||||
private static final int INIT_PORT_START = 9000; | |||||
private static final String[] PUBKEYS = new String[]{ | |||||
"3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", | |||||
"3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX", | |||||
"3snPdw7i7PZi6TStiyc6mzjprnNhgs2atSGNS8wPYzhbKaUWGFJt7x", | |||||
"3snPdw7i7PifPuRX7fu3jBjsb3rJRfDe9GtbDfvFJaJ4V4hHXQfhwk"}; | |||||
private static final String[] PRIVKEYS = new String[]{ | |||||
"177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", | |||||
"177gju9p5zrNdHJVEQnEEKF4ZjDDYmAXyfG84V5RPGVc5xFfmtwnHA7j51nyNLUFffzz5UT", | |||||
"177gjtwLgmSx5v1hFb46ijh7L9kdbKUpJYqdKVf9afiEmAuLgo8Rck9yu5UuUcHknWJuWaF", | |||||
"177gk1pudweTq5zgJTh8y3ENCTwtSFsKyX7YnpuKPo7rKgCkCBXVXh5z2syaTCPEMbuWRns"}; | |||||
private static final String ENCODE_PWD = "DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY"; | |||||
private static final String BINDING_OUT = "../"; | |||||
private static final String[] DB_URIS = new String[]{ | |||||
"rocksdb:///Users/shaozhuguang/Documents/simulate/peer0/rocksdb", | |||||
"rocksdb:///Users/shaozhuguang/Documents/simulate/peer1/rocksdb", | |||||
"rocksdb:///Users/shaozhuguang/Documents/simulate/peer2/rocksdb", | |||||
"rocksdb:///Users/shaozhuguang/Documents/simulate/peer3/rocksdb"}; | |||||
private static final String DB_PWD = ""; | |||||
private static final String DB_NAME = "rocksdb_"; | |||||
private static final String[] PEER_PATHS = new String[]{ | |||||
"/Users/shaozhuguang/Documents/simulate/peer0", | |||||
"/Users/shaozhuguang/Documents/simulate/peer1", | |||||
"/Users/shaozhuguang/Documents/simulate/peer2", | |||||
"/Users/shaozhuguang/Documents/simulate/peer3"}; | |||||
private static final String[] CONSENSUS_NODES = new String[]{ | |||||
"127.0.0.1:6000", | |||||
"127.0.0.1:6010", | |||||
"127.0.0.1:6020", | |||||
"127.0.0.1:6030"}; | |||||
@Override | |||||
public UserKeysVv userKeysVv(int nodeId) { | |||||
UserKeys userKeys = userKeys(nodeId); | |||||
return userKeys.toUserKeysVv(); | |||||
} | |||||
@Override | |||||
public UserKeys userKeys(int nodeId) { | |||||
return new UserKeys("Peer-" + nodeId, PRIVKEYS[nodeId], PUBKEYS[nodeId], ENCODE_PWD); | |||||
} | |||||
@Override | |||||
public PeerLocalConfig nodePeerLocalConfig(int nodeId, boolean isMaster) { | |||||
UserKeys userKeys = userKeys(nodeId); | |||||
return peerSharedConfigVv(nodeId, isMaster).toPeerLocalConfig(userKeys); | |||||
} | |||||
@Override | |||||
public PeerSharedConfigVv peerSharedConfigVv(int nodeId, boolean isMaster) { | |||||
PeerSharedConfigVv sharedConfigVv = new PeerSharedConfigVv(); | |||||
sharedConfigVv.setSharedKey(SHARED_KEY); | |||||
sharedConfigVv.setName(SHARED_KEY + "-" + nodeId); | |||||
sharedConfigVv.setInitAddr(LOCALHOST); | |||||
sharedConfigVv.setInitPort(INIT_PORT_START + nodeId * 10); | |||||
sharedConfigVv.setConsensusNode(CONSENSUS_NODES[nodeId]); | |||||
sharedConfigVv.setPubKey(PUBKEYS[nodeId]); | |||||
sharedConfigVv.setUserId(nodeId); | |||||
sharedConfigVv.setPeerPath(PEER_PATHS[nodeId]); | |||||
sharedConfigVv.setDbName(dbName()); | |||||
if (isMaster) { | |||||
sharedConfigVv.setLedgerName(ledgerName()); | |||||
sharedConfigVv.setNodeSize(TOTAL_SIZE); | |||||
} else { | |||||
sharedConfigVv.setMasterAddr(LOCALHOST); | |||||
sharedConfigVv.setMasterPort(8080); | |||||
} | |||||
return sharedConfigVv; | |||||
} | |||||
private String ledgerName() { | |||||
byte[] nameBytes = new byte[4]; | |||||
RANDOM_ROCKSDB.nextBytes(nameBytes); | |||||
return Hex.encodeHexString(nameBytes); | |||||
} | |||||
private String dbName() { | |||||
byte[] nameBytes = new byte[4]; | |||||
RANDOM_ROCKSDB.nextBytes(nameBytes); | |||||
return DB_NAME + Hex.encodeHexString(nameBytes); | |||||
} | |||||
} |
@@ -0,0 +1,62 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import com.jd.blockchain.ump.model.config.LedgerIdentification; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.state.*; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.ump.model.user.UserKeysVv; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
public interface UmpStateService { | |||||
void save(String ledgerAndNodeKey, PeerLocalConfig localConfig); | |||||
void save(String ledgerKey, List<String> sharedConfigKeys); | |||||
void save(InstallSchedule installSchedule, MasterAddr masterAddr); | |||||
void save(UserKeys userKeys); | |||||
void save(LedgerPeerInstall peerInstall); | |||||
void save(LedgerMasterInstall masterInstall); | |||||
void save(LedgerIdentification identification); | |||||
void saveLedgerHash(String ledgerAndNodeKey, String ledgerHash); | |||||
List<UserKeys> readUserKeysList(); | |||||
List<UserKeysVv> readUserKeysVvList(); | |||||
UserKeys readUserKeys(int id); | |||||
PeerLocalConfig readConfig(String ledgerAndNodeKey); | |||||
PeerInstallSchedules loadState(String ledgerAndNodeKey); | |||||
PeerInstallSchedules loadInitState(String ledgerAndNodeKey); | |||||
PeerInstallSchedules readState(String ledgerAndNodeKey); | |||||
PeerInstallSchedules readInitState(String ledgerAndNodeKey); | |||||
Map<String, List<InstallSchedule>> readStates(String ledgerKey); | |||||
LedgerIdentification readIdentification(String ledgerAndNodeKey); | |||||
List<LedgerPeerInstall> readLedgerPeerInstalls(); | |||||
List<LedgerMasterInstall> readLedgerMasterInstalls(); | |||||
List<LedgerPeerInited> readLedgerPeerIniteds(); | |||||
List<LedgerPeerInited> readLedgerPeerIniteds(String search); | |||||
List<LedgerInited> readLedgerIniteds(String search); | |||||
String readLedgerHash(String ledgerAndNodeKey); | |||||
} |
@@ -0,0 +1,880 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.JSONArray; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import com.jd.blockchain.ump.dao.DBConnection; | |||||
import com.jd.blockchain.ump.model.*; | |||||
import com.jd.blockchain.ump.model.config.LedgerIdentification; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.state.*; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.ump.model.user.UserKeysVv; | |||||
import com.jd.blockchain.ump.util.CommandUtils; | |||||
import com.jd.blockchain.ump.util.HttpJsonClientUtils; | |||||
import org.apache.commons.io.FileUtils; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import java.io.Closeable; | |||||
import java.io.File; | |||||
import java.io.IOException; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.text.SimpleDateFormat; | |||||
import java.util.*; | |||||
import java.util.concurrent.ExecutorService; | |||||
import java.util.concurrent.Executors; | |||||
@Service | |||||
public class UmpStateServiceHandler implements UmpStateService, Closeable { | |||||
private final Logger LOGGER = LoggerFactory.getLogger(getClass()); | |||||
private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyyMM-ddHHmmss"); | |||||
private static final String PEER_IDENTIFICATION_FORMAT = "PEER_IDENTIFICATION_INDEX_%s"; | |||||
private static final String PEER_INSTALL_MAX_KEY = "PEER_INSTALL_MAX_INDEX"; | |||||
private static final String PEER_INSTALL_KEY_FORMAT = "PEER_INSTALL_INDEX_%s"; | |||||
private static final String MASTER_INSTALL_MAX_KEY = "MASTER_INSTALL_MAX_INDEX"; | |||||
private static final String MASTER_INSTALL_KEY_FORMAT = "MASTER_INSTALL_INDEX_%s"; | |||||
private static final String USERS_KEY_MAX_KEY = "USERS_KEY_MAX_INDEX"; | |||||
private static final String USERS_KEY_FORMAT = "USERS_%s_REGISTER"; | |||||
private static final String MAX_SIZE_KEY_SUFFIX = "_MAX_SIZE_KEY"; | |||||
private static final String LEDGER_HASH_KEY_SUFFIX = "_LEDGER_HASH_KEY"; | |||||
private static final String LEDGER_NODE_KEY_CONFIG_SUFFIX = "_LEDGER_NODE_CONFIG_KEY"; | |||||
private static final String LEDGER_NODE_KEY_SUFFIX = "_LEDGER_NODE_KEY"; | |||||
private static final String CURRENT_INDEX_KEY_SUFFIX_FORMAT = "_%s_INDEX_KEY"; | |||||
private static final String PORT_ARG = "-p"; | |||||
private static final String LOCALHOST = "127.0.0.1"; | |||||
private ExecutorService singleHttpThread = Executors.newSingleThreadExecutor(); | |||||
@Autowired | |||||
private DBConnection dbConnection; | |||||
@Autowired | |||||
private LedgerService ledgerService; | |||||
@Override | |||||
public synchronized void save(String ledgerAndNodeKey, PeerLocalConfig localConfig) { | |||||
String ledgerAndNodeConfigKey = ledgerAndNodeConfigKey(ledgerAndNodeKey); | |||||
dbConnection.put(ledgerAndNodeConfigKey, JSON.toJSONString(localConfig)); | |||||
} | |||||
@Override | |||||
public synchronized void save(String ledgerKey, List<String> sharedConfigKeys) { | |||||
String ledgerAllNodeKey = ledgerAllNodeKey(ledgerKey); | |||||
StringBuilder sBuilder = new StringBuilder(); | |||||
for (String sharedConfigKey : sharedConfigKeys) { | |||||
if (sBuilder.length() > 0) { | |||||
sBuilder.append(";"); | |||||
} | |||||
sBuilder.append(sharedConfigKey); | |||||
} | |||||
dbConnection.put(ledgerAllNodeKey, sBuilder.toString()); | |||||
} | |||||
@Override | |||||
public synchronized void save(InstallSchedule installSchedule, MasterAddr masterAddr) { | |||||
try { | |||||
String ledgerAndNodeKey = installSchedule.getLedgerAndNodeKey(); | |||||
// 不使用队列,直接将其写入数据库 | |||||
// 需要查询目前该Key对应的最大值是多少 | |||||
String maxKey = ledgerAndNodeMaxKey(ledgerAndNodeKey); | |||||
String maxIdChars = dbConnection.get(maxKey); | |||||
int maxId = 0; | |||||
if (maxIdChars != null && maxIdChars.length() > 0) { | |||||
maxId = Integer.parseInt(maxIdChars) + 1; | |||||
} | |||||
String newKey = ledgerAndNodeCurrentNewKey(ledgerAndNodeKey, maxId); | |||||
// 内容写入数据库 | |||||
dbConnection.put(newKey, installSchedule, InstallSchedule.class); | |||||
// 更新最大值 | |||||
dbConnection.put(maxKey, String.valueOf(maxId)); | |||||
if (masterAddr != null && masterAddr.legal()) { | |||||
singleHttpThread.execute(() -> { | |||||
try { | |||||
// 发送HTTP请求 | |||||
HttpJsonClientUtils.httpPost(masterAddr, UmpConstant.REQUEST_STATE_URL, installSchedule, String.class, false); | |||||
} catch (Exception e) { | |||||
// 暂不关注是否发送成功 | |||||
LOGGER.error(e.toString()); | |||||
} | |||||
}); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
@Override | |||||
public synchronized void save(UserKeys userKeys) { | |||||
int maxIndex = maxIndex(USERS_KEY_MAX_KEY); | |||||
String userKey = usersKey(maxIndex); | |||||
// 重置userId | |||||
userKeys.setId(maxIndex); | |||||
// 将用户信息写入数据库 | |||||
dbConnection.put(userKey, JSON.toJSONString(userKeys)); | |||||
// 更新最大值 | |||||
dbConnection.put(USERS_KEY_MAX_KEY, String.valueOf(maxIndex)); | |||||
try { | |||||
// 将其放入文件中 | |||||
String keysDirPath = UmpConstant.PROJECT_PATH + UmpConstant.PATH_CONFIG_KEYS; | |||||
File keysDir = new File(keysDirPath); | |||||
if (!keysDir.exists()) { | |||||
// 创建文件夹 | |||||
keysDir.mkdir(); | |||||
} | |||||
saveKeys2Files(keysDirPath, userKeys); | |||||
} catch (Exception e) { | |||||
LOGGER.error("Save Keys To File !", e); | |||||
} | |||||
} | |||||
@Override | |||||
public synchronized void save(LedgerPeerInstall peerInstall) { | |||||
int maxIndex = maxIndex(PEER_INSTALL_MAX_KEY); | |||||
// 将用户信息写入数据库 | |||||
dbConnection.put(peerInstallKey(maxIndex), JSON.toJSONString(peerInstall)); | |||||
// 更新最大值 | |||||
dbConnection.put(PEER_INSTALL_MAX_KEY, String.valueOf(maxIndex)); | |||||
} | |||||
@Override | |||||
public synchronized void save(LedgerMasterInstall masterInstall) { | |||||
int maxIndex = maxIndex(MASTER_INSTALL_MAX_KEY); | |||||
// 将用户信息写入数据库 | |||||
dbConnection.put(masterInstallKey(maxIndex), JSON.toJSONString(masterInstall)); | |||||
// 更新最大值 | |||||
dbConnection.put(MASTER_INSTALL_MAX_KEY, String.valueOf(maxIndex)); | |||||
} | |||||
@Override | |||||
public synchronized void save(LedgerIdentification identification) { | |||||
String ledgerAndNodeKey = identification.getLedgerAndNodeKey(); | |||||
String idKey = String.format(PEER_IDENTIFICATION_FORMAT, ledgerAndNodeKey); | |||||
dbConnection.put(idKey, JSON.toJSONString(identification)); | |||||
} | |||||
@Override | |||||
public void saveLedgerHash(String ledgerAndNodeKey, String ledgerHash) { | |||||
String ledgerHashKey = ledgerAndNodeHashKey(ledgerAndNodeKey); | |||||
dbConnection.put(ledgerHashKey, ledgerHash); | |||||
} | |||||
@Override | |||||
public List<UserKeys> readUserKeysList() { | |||||
List<UserKeys> userKeysList = new ArrayList<>(); | |||||
String maxIndexChars = dbConnection.get(USERS_KEY_MAX_KEY); | |||||
if (maxIndexChars != null && maxIndexChars.length() > 0) { | |||||
int maxIndex = Integer.parseInt(maxIndexChars); | |||||
for (int i = 0; i <= maxIndex; i++) { | |||||
try { | |||||
String json = dbConnection.get(usersKey(i)); | |||||
if (json != null && json.length() > 0) { | |||||
userKeysList.add(JSON.parseObject(json, UserKeys.class)); | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error(e.toString()); | |||||
} | |||||
} | |||||
} | |||||
return userKeysList; | |||||
} | |||||
@Override | |||||
public List<UserKeysVv> readUserKeysVvList() { | |||||
List<UserKeysVv> userKeysVvList = new ArrayList<>(); | |||||
List<UserKeys> userKeysList = readUserKeysList(); | |||||
if (!userKeysList.isEmpty()) { | |||||
for (UserKeys userKeys : userKeysList) { | |||||
userKeysVvList.add(userKeys.toUserKeysVv()); | |||||
} | |||||
} | |||||
return userKeysVvList; | |||||
} | |||||
@Override | |||||
public UserKeys readUserKeys(int id) { | |||||
String userKey = usersKey(id); | |||||
String userKeysJson = dbConnection.get(userKey); | |||||
if (userKeysJson != null && userKeysJson.length() > 0) { | |||||
return JSON.parseObject(userKeysJson, UserKeys.class); | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public PeerLocalConfig readConfig(String ledgerAndNodeKey) { | |||||
String json = dbConnection.get(ledgerAndNodeConfigKey(ledgerAndNodeKey)); | |||||
if (json != null && json.length() > 0) { | |||||
return JSON.parseObject(json, PeerLocalConfig.class); | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules loadState(String ledgerAndNodeKey) { | |||||
PeerInstallSchedules installSchedules = loadInitState(ledgerAndNodeKey); | |||||
String ledgerHash = ledgerService.readLedgerHash(ledgerAndNodeKey); | |||||
if (ledgerHash == null || ledgerHash.length() == 0) { | |||||
throw new IllegalStateException("Can not find LedgerHash from DataBase !!!"); | |||||
} | |||||
return installSchedules.initLedgerHash(ledgerHash); | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules loadInitState(String ledgerAndNodeKey) { | |||||
// 获取LedgerIdentification | |||||
LedgerIdentification identification = readIdentification(ledgerAndNodeKey); | |||||
if (identification == null) { | |||||
throw new IllegalStateException("Can not find LedgerIdentification from DataBase !!!"); | |||||
} | |||||
return new PeerInstallSchedules(identification); | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules readState(final String ledgerAndNodeKey) { | |||||
PeerInstallSchedules installSchedules = loadState(ledgerAndNodeKey); | |||||
loadInstallSchedules(installSchedules, ledgerAndNodeKey); | |||||
return installSchedules; | |||||
} | |||||
@Override | |||||
public PeerInstallSchedules readInitState(String ledgerAndNodeKey) { | |||||
PeerInstallSchedules installSchedules = loadInitState(ledgerAndNodeKey); | |||||
loadInstallSchedules(installSchedules, ledgerAndNodeKey); | |||||
return installSchedules; | |||||
} | |||||
@Override | |||||
public Map<String, List<InstallSchedule>> readStates(String ledgerKey) { | |||||
String ledgerAllNodeKey = ledgerAllNodeKey(ledgerKey); | |||||
String ledgerAllNodeValues = dbConnection.get(ledgerAllNodeKey); | |||||
String[] ledgerAndNodeKeys = ledgerAllNodeValues.split(";"); | |||||
Map<String, List<InstallSchedule>> allInstallSchedules = new HashMap<>(); | |||||
// 不存在就返回空值 | |||||
if (ledgerAndNodeKeys.length > 0) { | |||||
for (String ledgerAndNodeKey : ledgerAndNodeKeys) { | |||||
// 获取每个LedgerAndNodeKey数据 | |||||
List<InstallSchedule> installSchedules = readInstallSchedules(ledgerAndNodeKey); | |||||
if (installSchedules != null) { | |||||
allInstallSchedules.put(ledgerAndNodeKey, installSchedules); | |||||
} | |||||
} | |||||
} | |||||
return allInstallSchedules; | |||||
} | |||||
@Override | |||||
public LedgerIdentification readIdentification(String ledgerAndNodeKey) { | |||||
String idKey = String.format(PEER_IDENTIFICATION_FORMAT, ledgerAndNodeKey); | |||||
String identificationJson = dbConnection.get(idKey); | |||||
if (identificationJson != null && identificationJson.length() > 0) { | |||||
return JSON.parseObject(identificationJson, LedgerIdentification.class); | |||||
} | |||||
return null; | |||||
} | |||||
@Override | |||||
public List<LedgerPeerInstall> readLedgerPeerInstalls() { | |||||
List<LedgerPeerInstall> peerInstallList = new ArrayList<>(); | |||||
String maxIndexChars = dbConnection.get(PEER_INSTALL_MAX_KEY); | |||||
if (maxIndexChars != null && maxIndexChars.length() > 0) { | |||||
int maxIndex = Integer.parseInt(maxIndexChars); | |||||
for (int i = 1; i <= maxIndex; i++) { | |||||
try { | |||||
String json = dbConnection.get(peerInstallKey(i)); | |||||
if (json != null && json.length() > 0) { | |||||
peerInstallList.add(JSON.parseObject(json, LedgerPeerInstall.class)); | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error(e.toString()); | |||||
} | |||||
} | |||||
} | |||||
return peerInstallList; | |||||
} | |||||
@Override | |||||
public List<LedgerMasterInstall> readLedgerMasterInstalls() { | |||||
List<LedgerMasterInstall> masterInstalls = new ArrayList<>(); | |||||
String maxIndexChars = dbConnection.get(PEER_INSTALL_MAX_KEY); | |||||
if (maxIndexChars != null && maxIndexChars.length() > 0) { | |||||
int maxIndex = Integer.parseInt(maxIndexChars); | |||||
for (int i = 1; i <= maxIndex; i++) { | |||||
try { | |||||
String json = dbConnection.get(masterInstallKey(i)); | |||||
if (json != null && json.length() > 0) { | |||||
masterInstalls.add(JSON.parseObject(json, LedgerMasterInstall.class)); | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error(e.toString()); | |||||
} | |||||
} | |||||
} | |||||
return masterInstalls; | |||||
} | |||||
@Override | |||||
public List<LedgerPeerInited> readLedgerPeerIniteds() { | |||||
List<LedgerPeerInited> peerIniteds = new ArrayList<>(); | |||||
List<LedgerPeerInstall> peerInstalls = readLedgerPeerInstalls(); | |||||
if (!peerInstalls.isEmpty()) { | |||||
LOGGER.info("Read LedgerPeerInstalls, Size = {}", peerInstalls.size()); | |||||
for (LedgerPeerInstall peerInstall : peerInstalls) { | |||||
String ledgerAndNodeKey = peerInstall.getLedgerAndNodeKey(); | |||||
// 数据库中读取存放的LedgerHash | |||||
String ledgerHash = readLedgerHash(ledgerAndNodeKey); | |||||
if (ledgerHash == null || ledgerHash.length() == 0) { | |||||
continue; | |||||
} | |||||
LedgerPeerInited peerInited = new LedgerPeerInited(ledgerHash, peerInstall); | |||||
// 检测账本中的Hash是否真正存在 | |||||
StartupState startupState = StartupState.UNKNOWN; | |||||
try { | |||||
startupState = startupState(ledgerHash, peerInstall); | |||||
} catch (Exception e) { | |||||
LOGGER.error("Check Ledger Hash Exist !!!", e); | |||||
} | |||||
// 设置账本状态 | |||||
peerInited.setStartupState(startupState); | |||||
// 添加到集合 | |||||
peerIniteds.add(peerInited); | |||||
} | |||||
} else { | |||||
LOGGER.error("Read LedgerPeerInstalls is Empty !!!"); | |||||
} | |||||
return peerIniteds; | |||||
} | |||||
@Override | |||||
public List<LedgerPeerInited> readLedgerPeerIniteds(String search) { | |||||
List<LedgerPeerInited> initedList = readLedgerPeerIniteds(); | |||||
if (search != null && search.length() > 0 && !initedList.isEmpty()) { | |||||
List<LedgerPeerInited> filterInitedList = new ArrayList<>(); | |||||
for (LedgerPeerInited peerInited : initedList) { | |||||
if (isMatch(peerInited, search)) { | |||||
filterInitedList.add(peerInited); | |||||
} | |||||
} | |||||
return filterInitedList; | |||||
} | |||||
return initedList; | |||||
} | |||||
@Override | |||||
public List<LedgerInited> readLedgerIniteds(String search) { | |||||
List<LedgerInited> ledgerInitedsFromConf = loadAllLedgerIniteds(UmpConstant.PROJECT_PATH); | |||||
if (!ledgerInitedsFromConf.isEmpty()) { | |||||
List<LedgerInited> ledgerIniteds = new ArrayList<>(); | |||||
for (LedgerInited ledgerInited : ledgerInitedsFromConf) { | |||||
if (isMatch(ledgerInited, search)) { | |||||
ledgerIniteds.add(ledgerInited); | |||||
} | |||||
} | |||||
return ledgerIniteds; | |||||
} | |||||
return ledgerInitedsFromConf; | |||||
} | |||||
@Override | |||||
public String readLedgerHash(String ledgerAndNodeKey) { | |||||
String ledgerHashKey = ledgerAndNodeHashKey(ledgerAndNodeKey); | |||||
return dbConnection.get(ledgerHashKey); | |||||
} | |||||
@Override | |||||
public void close() throws IOException { | |||||
// writeRunner.close(); | |||||
} | |||||
private boolean isMatch(LedgerInited ledgerInited, String search) { | |||||
if (search == null || search.length() == 0) { | |||||
return true; | |||||
} | |||||
String ledgerHash = ledgerInited.getLedgerHash(); | |||||
String ledgerName = ledgerInited.getLedgerName(); | |||||
String partiName = ledgerInited.getPartiName(); | |||||
String partiAddress = ledgerInited.getPartiAddress(); | |||||
String dbUri = ledgerInited.getDbUri(); | |||||
StartupState startupState = ledgerInited.getStartupState(); | |||||
if ( | |||||
ledgerHash.contains(search) || | |||||
startupState.toString().equals(search) || | |||||
ledgerName.contains(search) || | |||||
partiName.contains(search) || | |||||
partiAddress.contains(search) || | |||||
dbUri.contains(search) | |||||
) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
private boolean isMatch(LedgerPeerInited peerInited, String search) { | |||||
if (search == null || search.length() == 0) { | |||||
return true; | |||||
} | |||||
String ledgerHash = peerInited.getLedgerHash(); | |||||
StartupState startupState = peerInited.getStartupState(); | |||||
LedgerPeerInstall peerInstall = peerInited.getPeerInstall(); | |||||
if (ledgerHash.contains(search) || | |||||
startupState.toString().equals(search) || | |||||
peerInstall.getNodeName().contains(search) || | |||||
peerInstall.getCreateTime().contains(search) | |||||
) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
private void loadInstallSchedules(PeerInstallSchedules installSchedules, String ledgerAndNodeKey) { | |||||
List<InstallSchedule> schedules = readInstallSchedules(ledgerAndNodeKey); | |||||
for (InstallSchedule installSchedule : schedules) { | |||||
installSchedules.addInstallSchedule( | |||||
new PeerInstallSchedule(installSchedule.getProcess(), installSchedule.getState())); | |||||
} | |||||
} | |||||
private List<InstallSchedule> readInstallSchedules(String ledgerAndNodeKey) { | |||||
String maxKey = ledgerAndNodeMaxKey(ledgerAndNodeKey); | |||||
String maxIdChars = dbConnection.get(maxKey); | |||||
if (maxIdChars == null || maxIdChars.length() == 0) { | |||||
return null; | |||||
} | |||||
int maxId = Integer.parseInt(maxIdChars); | |||||
List<InstallSchedule> schedules = new ArrayList<>(); | |||||
for (int i = 0; i <= maxId; i++) { | |||||
try { | |||||
String currentKey = ledgerAndNodeCurrentNewKey(ledgerAndNodeKey, i); | |||||
String jsonChars = dbConnection.get(currentKey); | |||||
if (jsonChars != null && jsonChars.length() > 0) { | |||||
schedules.add(JSON.parseObject(jsonChars, InstallSchedule.class)); | |||||
} | |||||
} catch (Exception e) { | |||||
// 打印错误,暂不处理其他 | |||||
LOGGER.error(e.toString()); | |||||
} | |||||
} | |||||
return schedules; | |||||
} | |||||
private List<LedgerInited> loadAllLedgerIniteds(String peerPath) { | |||||
List<LedgerInited> ledgerInitedsFromConf = ledgerService.allLedgerIniteds(peerPath); | |||||
if (!ledgerInitedsFromConf.isEmpty()) { | |||||
// 逐个检查其状态 | |||||
for (LedgerInited ledgerInited : ledgerInitedsFromConf) { | |||||
// 判断该账本对应的数据库是否存在 | |||||
if (!dbConnection.exist(ledgerInited.getDbUri())) { | |||||
ledgerInited.setStartupState(StartupState.DB_UNEXIST); | |||||
continue; | |||||
} | |||||
String peerVerify = ledgerService.peerVerifyKey(peerPath); | |||||
try { | |||||
if (!CommandUtils.isActive(peerVerify)) { | |||||
// 进程不存在 | |||||
LOGGER.info("Can not find Peer Process {} !!!", peerVerify); | |||||
ledgerInited.setStartupState(StartupState.UNLOAD); | |||||
continue; | |||||
} | |||||
} catch (Exception e) { | |||||
// 进程处理错误打印日志即可 | |||||
LOGGER.error(String.format("Command Check %s !!!", peerVerify), e); | |||||
} | |||||
// 查看该进程对应的监听端口 | |||||
try { | |||||
int listenPort = listenPort(peerVerify); | |||||
LOGGER.info("Find Listen Port = {} !", listenPort); | |||||
if (listenPort > 0) { | |||||
int maxSize = 5, checkIndex = 1; | |||||
boolean isRead = false; | |||||
while (maxSize > 0) { | |||||
try { | |||||
// 发送请求到对应地址 | |||||
JSONArray ledgerHashs = HttpJsonClientUtils.httpGet(ledgersUrl(listenPort), JSONArray.class, true); | |||||
if (ledgerHashs != null && !ledgerHashs.isEmpty()) { | |||||
for(Object hashObj : ledgerHashs) { | |||||
if (hashObj instanceof JSONObject) { | |||||
if (ledgerInited.getLedgerHash().equals(((JSONObject) hashObj).getString("value"))) { | |||||
// 说明该账本已经被加载 | |||||
ledgerInited.setStartupState(StartupState.LOADED); | |||||
isRead = true; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
if (isRead) { | |||||
break; | |||||
} | |||||
} | |||||
// 6秒休眠 | |||||
Thread.sleep(6000); | |||||
} catch (Exception e) { | |||||
LOGGER.error(String.format("Request LedgerHashs from PeerNode [%s]", checkIndex++), e); | |||||
} | |||||
maxSize --; | |||||
} | |||||
if (!isRead) { | |||||
// 表明等待加载,无须再启动 | |||||
ledgerInited.setStartupState(StartupState.LOADING); | |||||
} | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error(String.format("Command [%s] 'Listen Port Check !!!", peerVerify), e); | |||||
} | |||||
} | |||||
} | |||||
return ledgerInitedsFromConf; | |||||
} | |||||
private StartupState startupState(String ledgerHash, LedgerPeerInstall peerInstall) { | |||||
String peerPath = peerInstall.getPeerPath(); | |||||
// 首先检查文件中是否存在该Hash值 | |||||
LedgerBindingConf ledgerBindingConf = ledgerService.allLedgerHashs(peerPath); | |||||
Set<String> allLedgerHashs = ledgerBindingConf.getLedgerHashs(); | |||||
if (!allLedgerHashs.contains(ledgerHash)) { | |||||
// 文件中不存在 | |||||
return StartupState.UNEXIST; | |||||
} | |||||
// 判断该账本对应的数据库是否存在 | |||||
if (!ledgerService.dbExist(peerPath, ledgerHash)) { | |||||
// 该账本对应数据库不存在 | |||||
return StartupState.DB_UNEXIST; | |||||
} | |||||
// 文件中存在则检查进程是否存在 | |||||
// 进程存在标识为LOADED,否则标识为LOADING,暂时用不到LOADING | |||||
String peerVerify = ledgerService.peerVerifyKey(peerPath); | |||||
try { | |||||
if (!CommandUtils.isActive(peerVerify)) { | |||||
// 进程不存在 | |||||
return StartupState.UNLOAD; | |||||
} | |||||
} catch (Exception e) { | |||||
// 进程处理错误打印日志即可 | |||||
LOGGER.error(String.format("Command Check %s !!!", peerVerify), e); | |||||
} | |||||
// 查看该进程对应的监听端口 | |||||
try { | |||||
int listenPort = listenPort(peerVerify); | |||||
LOGGER.info("Find Listen Port = {} !", listenPort); | |||||
if (listenPort > 0) { | |||||
// 发送请求到对应地址 | |||||
JSONArray ledgerHashs = HttpJsonClientUtils.httpGet(ledgersUrl(listenPort), JSONArray.class, true); | |||||
if (ledgerHashs != null && !ledgerHashs.isEmpty()) { | |||||
for(Object hashObj : ledgerHashs) { | |||||
if (hashObj instanceof JSONObject) { | |||||
if (ledgerHash.equals(((JSONObject) hashObj).getString("value"))) { | |||||
// 说明该账本已经被加载 | |||||
return StartupState.LOADED; | |||||
} | |||||
} | |||||
} | |||||
// 表明等待加载,无须再启动 | |||||
return StartupState.LOADING; | |||||
} | |||||
} | |||||
} catch (Exception e) { | |||||
LOGGER.error(String.format("Command [%s] 'Listen Port Check !!!", peerVerify), e); | |||||
} | |||||
return StartupState.UNKNOWN; | |||||
} | |||||
private String ledgersUrl(int listenPort) { | |||||
return "http://" + LOCALHOST + ":" + listenPort + "/ledgers"; | |||||
} | |||||
private int listenPort(String peerVerify) throws Exception { | |||||
String portArg = mainArg(peerVerify, PORT_ARG); | |||||
if (portArg != null && portArg.length() > 0) { | |||||
return Integer.parseInt(portArg); | |||||
} | |||||
return 0; | |||||
} | |||||
private String mainArg(String processName, String argKey) throws Exception { | |||||
String[] cmdLineArray = mainArgs(processName); | |||||
if (cmdLineArray != null && cmdLineArray.length > 0) { | |||||
for (int i = 0; i < cmdLineArray.length; i++) { | |||||
String currArg = cmdLineArray[i].trim(); | |||||
if (currArg.equals(argKey) && (i + 1) < cmdLineArray.length) { | |||||
return cmdLineArray[i+1].trim(); | |||||
} | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
private String[] mainArgs(String processName) throws Exception { | |||||
String mainArgs = CommandUtils.mainArgs(processName); | |||||
if (mainArgs != null && mainArgs.length() > 0) { | |||||
///Users/shaozhuguang/Documents/newenv/peer4/system/deployment-peer-1.1.0-SNAPSHOT.jar -home=/Users/shaozhuguang/Documents/newenv/peer4 -c /Users/shaozhuguang/Documents/newenv/peer4/config/ledger-binding.conf -p 7080 | |||||
return mainArgs.split(" "); | |||||
} | |||||
return null; | |||||
} | |||||
private synchronized int maxIndex(String key) { | |||||
int maxIndex = 1; | |||||
String maxIndexChars = dbConnection.get(key); | |||||
if (maxIndexChars != null && maxIndexChars.length() > 0) { | |||||
maxIndex = Integer.parseInt(maxIndexChars) + 1; | |||||
} | |||||
return maxIndex; | |||||
} | |||||
private String usersKey(int userId) { | |||||
return String.format(USERS_KEY_FORMAT, userId); | |||||
} | |||||
private String peerInstallKey(int index) { | |||||
return String.format(PEER_INSTALL_KEY_FORMAT, index); | |||||
} | |||||
private String masterInstallKey(int index) { | |||||
return String.format(MASTER_INSTALL_KEY_FORMAT, index); | |||||
} | |||||
private String ledgerAndNodeConfigKey(String ledgerAndNodeKey) { | |||||
return ledgerAndNodeKey + LEDGER_NODE_KEY_CONFIG_SUFFIX; | |||||
} | |||||
private String ledgerAllNodeKey(String ledgerKey) { | |||||
return ledgerKey + LEDGER_NODE_KEY_SUFFIX; | |||||
} | |||||
private String ledgerAndNodeMaxKey(String ledgerAndNodeKey) { | |||||
return ledgerAndNodeKey + MAX_SIZE_KEY_SUFFIX; | |||||
} | |||||
private String ledgerAndNodeHashKey(String ledgerAndNodeKey) { | |||||
return ledgerAndNodeKey + LEDGER_HASH_KEY_SUFFIX; | |||||
} | |||||
private String ledgerAndNodeCurrentNewKey(String ledgerAndNodeKey, int currentId) { | |||||
return String.format( | |||||
ledgerAndNodeKey + CURRENT_INDEX_KEY_SUFFIX_FORMAT, | |||||
currentId); | |||||
} | |||||
private void saveKeys2Files(String keysDirPath, UserKeys userKeys) throws IOException { | |||||
// 写入私钥 | |||||
write(keysDirPath, userKeys.getName(), UmpConstant.PRIVATE_KEY_SUFFIX, userKeys.getPrivKey()); | |||||
// 写入公钥 | |||||
write(keysDirPath, userKeys.getName(), UmpConstant.PUBLIC_KEY_SUFFIX, userKeys.getPubKey()); | |||||
// 写入密钥 | |||||
write(keysDirPath, userKeys.getName(), UmpConstant.PWD_SUFFIX, userKeys.getEncodePwd()); | |||||
} | |||||
private void write(String keysDirPath, String name, String suffix, String writeContent) throws IOException { | |||||
String keyeFilePath = keysDirPath + File.separator + name + suffix; | |||||
File keysFile = new File(keyeFilePath); | |||||
if (keysFile.exists()) { | |||||
// 文件存在,备份文件 | |||||
FileUtils.copyFile(keysFile, new File(keyeFilePath + "_bak_" + currentTime())); | |||||
} | |||||
// 将Priv文件内容写入 | |||||
FileUtils.writeStringToFile(keysFile, writeContent, StandardCharsets.UTF_8); | |||||
} | |||||
private String currentTime() { | |||||
return SDF.format(new Date()); | |||||
} | |||||
} |
@@ -0,0 +1,15 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.ump.model.user.UserKeyBuilder; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
public interface UtilService { | |||||
UserKeys create(UserKeyBuilder builder); | |||||
UserKeys create(String name, String seed, String pwd); | |||||
UserKeys read(int id); | |||||
boolean verify(UserKeys userKeys, String pwd); | |||||
} |
@@ -0,0 +1,80 @@ | |||||
package com.jd.blockchain.ump.service; | |||||
import com.jd.blockchain.crypto.PrivKey; | |||||
import com.jd.blockchain.crypto.PubKey; | |||||
import com.jd.blockchain.crypto.service.classic.ClassicAlgorithm; | |||||
import com.jd.blockchain.crypto.utils.classic.ED25519Utils; | |||||
import com.jd.blockchain.ump.model.user.UserKeyBuilder; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.utils.codec.Base58Utils; | |||||
import com.jd.blockchain.utils.security.ShaUtils; | |||||
import org.bouncycastle.crypto.AsymmetricCipherKeyPair; | |||||
import org.bouncycastle.crypto.params.Ed25519PrivateKeyParameters; | |||||
import org.bouncycastle.crypto.params.Ed25519PublicKeyParameters; | |||||
import org.bouncycastle.crypto.prng.FixedSecureRandom; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.stereotype.Service; | |||||
import java.nio.charset.Charset; | |||||
import static com.jd.blockchain.tools.keygen.KeyGenCommand.encodePrivKey; | |||||
import static com.jd.blockchain.tools.keygen.KeyGenCommand.encodePubKey; | |||||
@Service | |||||
public class UtilServiceHandler implements UtilService { | |||||
private static final String UTF_8 = "UTF-8"; | |||||
@Autowired | |||||
private UmpStateService umpStateService; | |||||
@Override | |||||
public UserKeys create(UserKeyBuilder builder) { | |||||
return create(builder.getName(), builder.getSeed(), builder.getPwd()); | |||||
} | |||||
@Override | |||||
public UserKeys create(String name, String seed, String pwd) { | |||||
AsymmetricCipherKeyPair keyPair = ED25519Utils.generateKeyPair( | |||||
new FixedSecureRandom(seed.getBytes(Charset.forName(UTF_8)))); | |||||
PubKey pubKey = new PubKey(ClassicAlgorithm.ED25519, | |||||
((Ed25519PublicKeyParameters) keyPair.getPublic()).getEncoded()); | |||||
PrivKey privKey = new PrivKey(ClassicAlgorithm.ED25519, | |||||
((Ed25519PrivateKeyParameters) keyPair.getPrivate()).getEncoded()); | |||||
return create(name, pubKey, privKey, pwd); | |||||
} | |||||
@Override | |||||
public UserKeys read(int userId) { | |||||
return umpStateService.readUserKeys(userId); | |||||
} | |||||
@Override | |||||
public boolean verify(UserKeys userKeys, String pwd) { | |||||
String encodePwd = Base58Utils.encode((ShaUtils.hash_256(pwd.getBytes(Charset.forName(UTF_8))))); | |||||
if (encodePwd.equals(userKeys.getEncodePwd())) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
private UserKeys create(String name, PubKey pubKey, PrivKey privKey, String pwd) { | |||||
byte[] pwdBytes = ShaUtils.hash_256(pwd.getBytes(Charset.forName(UTF_8))); | |||||
return new UserKeys( | |||||
name, | |||||
encodePrivKey(privKey, pwdBytes), | |||||
encodePubKey(pubKey), | |||||
// pwd, // 密码不保存到数据库,防止泄露 | |||||
Base58Utils.encode(pwdBytes)); | |||||
} | |||||
} |
@@ -0,0 +1,21 @@ | |||||
package com.jd.blockchain.ump.service.consensus; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import java.util.List; | |||||
import java.util.Properties; | |||||
public interface ConsensusProvider { | |||||
String NEXT_LINE = "\r\n"; | |||||
String provider(); | |||||
String configFilePath(); | |||||
void setConfig(Properties properties); | |||||
Properties getConfig(); | |||||
byte[] handleSharedConfigs(List<PeerLocalConfig> sharedConfigs); | |||||
} |
@@ -0,0 +1,10 @@ | |||||
package com.jd.blockchain.ump.service.consensus; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import java.util.List; | |||||
public interface ConsensusService { | |||||
String initConsensusConf(String consensusProvider, List<PeerLocalConfig> sharedConfigs); | |||||
} |
@@ -0,0 +1,79 @@ | |||||
package com.jd.blockchain.ump.service.consensus; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.util.Base58Utils; | |||||
import org.reflections.Reflections; | |||||
import org.springframework.stereotype.Service; | |||||
import java.io.File; | |||||
import java.io.InputStream; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.Properties; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
@Service | |||||
public class ConsensusServiceHandler implements ConsensusService { | |||||
private static final Map<String, ConsensusProvider> CONSENSUS_PROVIDERS = new ConcurrentHashMap<>(); | |||||
static { | |||||
try { | |||||
initProviders(); | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
@Override | |||||
public String initConsensusConf(String consensusProvider, List<PeerLocalConfig> sharedConfigs) { | |||||
// 首先根据provider获取对应的配置信息 | |||||
ConsensusProvider provider = CONSENSUS_PROVIDERS.get(consensusProvider); | |||||
if (provider == null) { | |||||
throw new IllegalStateException( | |||||
String.format("ConsensusProvider[%s] can not find Manage-Class !!!", consensusProvider)); | |||||
} | |||||
byte[] result = provider.handleSharedConfigs(sharedConfigs); | |||||
return Base58Utils.encode(result); | |||||
} | |||||
private static void initProviders() { | |||||
// 初始化所有实现类 | |||||
Reflections reflections = new Reflections("com.jd.blockchain.ump.service.consensus"); | |||||
Set<Class<? extends ConsensusProvider>> providerSet = | |||||
reflections.getSubTypesOf(ConsensusProvider.class); | |||||
for (Class<? extends ConsensusProvider> clazz : providerSet) { | |||||
if (!clazz.isInterface()) { | |||||
try { | |||||
// 根据class生成对象 | |||||
ConsensusProvider provider = clazz.newInstance(); | |||||
String providerKey = provider.provider(); | |||||
if (providerKey != null && providerKey.length() > 0 && | |||||
!CONSENSUS_PROVIDERS.containsKey(providerKey)) { | |||||
// 根据value读取配置文件中的内容 | |||||
InputStream currentFileInputStream = ConsensusServiceHandler.class.getResourceAsStream( | |||||
File.separator + provider.configFilePath()); | |||||
Properties currentProps = new Properties(); | |||||
currentProps.load(currentFileInputStream); | |||||
provider.setConfig(currentProps); | |||||
CONSENSUS_PROVIDERS.put(providerKey, provider); | |||||
} | |||||
} catch (Exception e) { | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,162 @@ | |||||
package com.jd.blockchain.ump.service.consensus.providers; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerSharedConfig; | |||||
import com.jd.blockchain.ump.service.consensus.ConsensusProvider; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.*; | |||||
public class BftsmartConsensusProvider implements ConsensusProvider { | |||||
public static final String BFTSMART_PROVIDER = "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"; | |||||
public static final String BFTSMART_CONFIG_FILE = "bftsmart.config"; | |||||
private static final int MIN_PARTI_SIZE = 4; | |||||
private Properties bftsmartProps; | |||||
@Override | |||||
public String provider() { | |||||
return BFTSMART_PROVIDER; | |||||
} | |||||
@Override | |||||
public String configFilePath() { | |||||
return BFTSMART_CONFIG_FILE; | |||||
} | |||||
@Override | |||||
public void setConfig(Properties properties) { | |||||
bftsmartProps = properties; | |||||
} | |||||
@Override | |||||
public Properties getConfig() { | |||||
return bftsmartProps; | |||||
} | |||||
@Override | |||||
public byte[] handleSharedConfigs(List<PeerLocalConfig> sharedConfigs) { | |||||
// 首先校验其中的ConsensusNode是否完全一致,若完全一致则不可以 | |||||
verify(sharedConfigs); | |||||
StringBuilder sBuilder = new StringBuilder(); | |||||
// 先加入当前节点信息 | |||||
List<String> nodeConfigs = nodeConfigs(sharedConfigs); | |||||
for (String nodeConfig : nodeConfigs) { | |||||
sBuilder.append(nodeConfig).append(NEXT_LINE); | |||||
} | |||||
int nodeNum = sharedConfigs.size(); | |||||
// 写入之前配置文件中的内容 | |||||
for (Map.Entry<Object, Object> entry : bftsmartProps.entrySet()) { | |||||
// 获取Key-Value | |||||
String key = (String) entry.getKey(), value = (String) entry.getValue(); | |||||
// 对特殊的Key和Value单独处理 | |||||
/** | |||||
* system.servers.num = 4 | |||||
* | |||||
* system.servers.f = 1 | |||||
* | |||||
* system.initial.view = 0,1,2,3 | |||||
*/ | |||||
if (key.startsWith(BftsmartConstant.SERVERS_NUM_PREFIX)) { | |||||
sBuilder.append(BftsmartConstant.SERVERS_NUM_PREFIX + " = " + nodeNum).append(NEXT_LINE); | |||||
} else if (key.startsWith(BftsmartConstant.SERVERS_F_PREFIX)) { | |||||
sBuilder.append(BftsmartConstant.SERVERS_F_PREFIX + " = " + nodeFNum(nodeNum)).append(NEXT_LINE); | |||||
} else if (key.startsWith(BftsmartConstant.INIT_VIEW_PREFIX)) { | |||||
sBuilder.append(BftsmartConstant.INIT_VIEW_PREFIX + " = " + initView(nodeNum)).append(NEXT_LINE); | |||||
} else { | |||||
sBuilder.append(key + " = " + value).append(NEXT_LINE); | |||||
} | |||||
} | |||||
return sBuilder.toString().getBytes(StandardCharsets.UTF_8); | |||||
} | |||||
private String initView(int nodeNum) { | |||||
StringBuilder views = new StringBuilder(); | |||||
for (int i = 0; i < nodeNum; i++) { | |||||
if (views.length() > 0) { | |||||
views.append(","); | |||||
} | |||||
views.append(i); | |||||
} | |||||
return views.toString(); | |||||
} | |||||
private void verify(List<PeerLocalConfig> sharedConfigs) { | |||||
Set<String> consensusInfos = new HashSet<>(); | |||||
if (sharedConfigs == null) { | |||||
throw new IllegalStateException("Shared Configs is NULL !!!"); | |||||
} | |||||
if (sharedConfigs.size() < MIN_PARTI_SIZE) { | |||||
throw new IllegalStateException( | |||||
String.format("Shared Configs's size = %s, can not meet minimum %s !!!", | |||||
sharedConfigs.size(), MIN_PARTI_SIZE)); | |||||
} | |||||
for (PeerLocalConfig sharedConfig : sharedConfigs) { | |||||
String consensusInfo = sharedConfig.getConsensusNode(); | |||||
if (consensusInfos.contains(consensusInfo)) { | |||||
throw new IllegalStateException("Shared Configs's Consensus may be conflict !!!"); | |||||
} | |||||
consensusInfos.add(consensusInfo); | |||||
} | |||||
} | |||||
private List<String> nodeConfigs(List<PeerLocalConfig> sharedConfigs) { | |||||
List<String> nodeConfigs = new ArrayList<>(); | |||||
if (sharedConfigs != null && !sharedConfigs.isEmpty()) { | |||||
for (int i = 0; i < sharedConfigs.size(); i++) { | |||||
PeerSharedConfig sharedConfig = sharedConfigs.get(i); | |||||
String consensusNode = sharedConfig.getConsensusNode(); | |||||
String[] hostAndPort = consensusNode.split(":"); | |||||
nodeConfigs.add(String.format(BftsmartConstant.HOST_FORMAT, i, hostAndPort[0])); | |||||
nodeConfigs.add(String.format(BftsmartConstant.PORT_FORMAT, i, hostAndPort[1])); | |||||
nodeConfigs.add(String.format(BftsmartConstant.SECURE_FORMAT, i, false)); | |||||
} | |||||
} | |||||
return nodeConfigs; | |||||
} | |||||
private int nodeFNum(int nodeNum) { | |||||
/** | |||||
* 3F+1 | |||||
* | |||||
* 假设有4个节点,则可有一个,若有N个,则N-1/3 | |||||
*/ | |||||
if (nodeNum < 4) { | |||||
return 0; | |||||
} | |||||
return (nodeNum - 1) / 3; | |||||
} | |||||
} |
@@ -0,0 +1,17 @@ | |||||
package com.jd.blockchain.ump.service.consensus.providers; | |||||
public class BftsmartConstant { | |||||
public static final String HOST_FORMAT = "system.server.%s.network.host=%s"; | |||||
public static final String PORT_FORMAT = "system.server.%s.network.port=%s"; | |||||
public static final String SECURE_FORMAT = "system.server.%s.network.secure=%s"; | |||||
public static final String SERVERS_NUM_PREFIX = "system.servers.num"; | |||||
public static final String SERVERS_F_PREFIX = "system.servers.f"; | |||||
public static final String INIT_VIEW_PREFIX = "system.initial.view"; | |||||
} |
@@ -0,0 +1,41 @@ | |||||
package com.jd.blockchain.ump.service.consensus.providers; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.service.consensus.ConsensusProvider; | |||||
import java.util.List; | |||||
import java.util.Properties; | |||||
public class MsgQueueConsensusProvider implements ConsensusProvider { | |||||
private static final String MSGQUEUE_PROVIDER = "com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider"; | |||||
private static final String MSGQUEUE_CONFIG_FILE = "mq.config"; | |||||
private Properties msgQueueProps; | |||||
@Override | |||||
public String provider() { | |||||
return MSGQUEUE_PROVIDER; | |||||
} | |||||
@Override | |||||
public String configFilePath() { | |||||
return MSGQUEUE_CONFIG_FILE; | |||||
} | |||||
@Override | |||||
public void setConfig(Properties properties) { | |||||
this.msgQueueProps = properties; | |||||
} | |||||
@Override | |||||
public Properties getConfig() { | |||||
return msgQueueProps; | |||||
} | |||||
@Override | |||||
public byte[] handleSharedConfigs(List<PeerLocalConfig> sharedConfigs) { | |||||
return new byte[0]; | |||||
} | |||||
} |
@@ -0,0 +1,153 @@ | |||||
package com.jd.blockchain.ump.util; | |||||
import java.io.UnsupportedEncodingException; | |||||
import java.math.BigInteger; | |||||
public class Base58Utils { | |||||
public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); | |||||
private static final int[] INDEXES = new int[128]; | |||||
static { | |||||
for (int i = 0; i < INDEXES.length; i++) { | |||||
INDEXES[i] = -1; | |||||
} | |||||
for (int i = 0; i < ALPHABET.length; i++) { | |||||
INDEXES[ALPHABET[i]] = i; | |||||
} | |||||
} | |||||
/** | |||||
* Encodes the given bytes in base58. No checksum is appended. | |||||
*/ | |||||
public static String encode(byte[] input) { | |||||
if (input.length == 0) { | |||||
return ""; | |||||
} | |||||
input = copyOfRange(input, 0, input.length); | |||||
// Count leading zeroes. | |||||
int zeroCount = 0; | |||||
while (zeroCount < input.length && input[zeroCount] == 0) { | |||||
++zeroCount; | |||||
} | |||||
// The actual encoding. | |||||
byte[] temp = new byte[input.length * 2]; | |||||
int j = temp.length; | |||||
int startAt = zeroCount; | |||||
while (startAt < input.length) { | |||||
byte mod = divmod58(input, startAt); | |||||
if (input[startAt] == 0) { | |||||
++startAt; | |||||
} | |||||
temp[--j] = (byte) ALPHABET[mod]; | |||||
} | |||||
// Strip extra '1' if there are some after decoding. | |||||
while (j < temp.length && temp[j] == ALPHABET[0]) { | |||||
++j; | |||||
} | |||||
// Add as many leading '1' as there were leading zeros. | |||||
while (--zeroCount >= 0) { | |||||
temp[--j] = (byte) ALPHABET[0]; | |||||
} | |||||
byte[] output = copyOfRange(temp, j, temp.length); | |||||
try { | |||||
return new String(output, "US-ASCII"); | |||||
} catch (UnsupportedEncodingException e) { | |||||
throw new RuntimeException(e); // Cannot happen. | |||||
} | |||||
} | |||||
public static byte[] decode(String input) throws IllegalArgumentException { | |||||
if (input.length() == 0) { | |||||
return new byte[0]; | |||||
} | |||||
byte[] input58 = new byte[input.length()]; | |||||
// Transform the String to a base58 byte sequence | |||||
for (int i = 0; i < input.length(); ++i) { | |||||
char c = input.charAt(i); | |||||
int digit58 = -1; | |||||
if (c >= 0 && c < 128) { | |||||
digit58 = INDEXES[c]; | |||||
} | |||||
if (digit58 < 0) { | |||||
throw new IllegalArgumentException("Illegal character " + c + " at " + i); | |||||
} | |||||
input58[i] = (byte) digit58; | |||||
} | |||||
// Count leading zeroes | |||||
int zeroCount = 0; | |||||
while (zeroCount < input58.length && input58[zeroCount] == 0) { | |||||
++zeroCount; | |||||
} | |||||
// The encoding | |||||
byte[] temp = new byte[input.length()]; | |||||
int j = temp.length; | |||||
int startAt = zeroCount; | |||||
while (startAt < input58.length) { | |||||
byte mod = divmod256(input58, startAt); | |||||
if (input58[startAt] == 0) { | |||||
++startAt; | |||||
} | |||||
temp[--j] = mod; | |||||
} | |||||
// Do no add extra leading zeroes, move j to first non null byte. | |||||
while (j < temp.length && temp[j] == 0) { | |||||
++j; | |||||
} | |||||
return copyOfRange(temp, j - zeroCount, temp.length); | |||||
} | |||||
public static BigInteger decodeToBigInteger(String input) throws IllegalArgumentException { | |||||
return new BigInteger(1, decode(input)); | |||||
} | |||||
// | |||||
// number -> number / 58, returns number % 58 | |||||
// | |||||
private static byte divmod58(byte[] number, int startAt) { | |||||
int remainder = 0; | |||||
for (int i = startAt; i < number.length; i++) { | |||||
int digit256 = (int) number[i] & 0xFF; | |||||
int temp = remainder * 256 + digit256; | |||||
number[i] = (byte) (temp / 58); | |||||
remainder = temp % 58; | |||||
} | |||||
return (byte) remainder; | |||||
} | |||||
// | |||||
// number -> number / 256, returns number % 256 | |||||
// | |||||
private static byte divmod256(byte[] number58, int startAt) { | |||||
int remainder = 0; | |||||
for (int i = startAt; i < number58.length; i++) { | |||||
int digit58 = (int) number58[i] & 0xFF; | |||||
int temp = remainder * 58 + digit58; | |||||
number58[i] = (byte) (temp / 256); | |||||
remainder = temp % 256; | |||||
} | |||||
return (byte) remainder; | |||||
} | |||||
private static byte[] copyOfRange(byte[] source, int from, int to) { | |||||
byte[] range = new byte[to - from]; | |||||
System.arraycopy(source, from, range, 0, range.length); | |||||
return range; | |||||
} | |||||
} |
@@ -0,0 +1,133 @@ | |||||
package com.jd.blockchain.ump.util; | |||||
import sun.jvmstat.monitor.MonitoredHost; | |||||
import sun.jvmstat.monitor.MonitoredVm; | |||||
import sun.jvmstat.monitor.MonitoredVmUtil; | |||||
import sun.jvmstat.monitor.VmIdentifier; | |||||
import java.util.*; | |||||
public class CommandUtils { | |||||
public static void killVm(String processName) throws Exception { | |||||
MonitoredVm activeVm = activeVm(processName); | |||||
if (activeVm != null) { | |||||
killVm(activeVm); | |||||
} | |||||
} | |||||
public static void killVm(MonitoredVm vm) throws Exception { | |||||
if (vm != null) { | |||||
int vmId = vm.getVmIdentifier().getLocalVmId(); | |||||
List<String> killCmd = killCommand(vmId); | |||||
execute(killCmd); | |||||
} | |||||
} | |||||
public static List<String> toCommandList(String cmd) { | |||||
// 要求使用空格 | |||||
String[] cmdArray = cmd.split(" "); | |||||
if (cmdArray.length > 0) { | |||||
return Arrays.asList(cmdArray); | |||||
} | |||||
return null; | |||||
} | |||||
public static Process execute(List<String> cmds) throws Exception { | |||||
if (cmds == null || cmds.isEmpty()) { | |||||
throw new IllegalStateException("Command's List is NULL !!!"); | |||||
} | |||||
ProcessBuilder pBuilder = new ProcessBuilder(cmds); | |||||
Process process = pBuilder.start(); | |||||
return process; | |||||
} | |||||
public static boolean executeAndVerify(List<String> cmds, String verify) throws Exception { | |||||
if (cmds == null || cmds.isEmpty()) { | |||||
throw new IllegalStateException("Command's List is NULL !!!"); | |||||
} | |||||
ProcessBuilder pBuilder = new ProcessBuilder(cmds); | |||||
pBuilder.start(); | |||||
// 时延5s,再进行判断 | |||||
Thread.sleep(5000); | |||||
return isActive(verify); | |||||
} | |||||
public static MonitoredVm activeVm(String processName) throws Exception { | |||||
MonitoredHost localMonitored = MonitoredHost.getMonitoredHost("localhost"); | |||||
Set<Integer> activeVms = new HashSet<>(localMonitored.activeVms()); | |||||
for (Integer vmId : activeVms) { | |||||
try { | |||||
MonitoredVm vm = localMonitored.getMonitoredVm(new VmIdentifier("//" + vmId)); | |||||
String vmProcessName = MonitoredVmUtil.mainClass(vm, true); | |||||
if (vmProcessName.contains(processName)) { | |||||
return vm; | |||||
} | |||||
} catch (Exception e) { | |||||
// 此处异常打印即可,不需要处理 | |||||
System.err.println(e); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public static boolean isActive(String processName) throws Exception { | |||||
MonitoredVm activeVm = activeVm(processName); | |||||
return activeVm != null; | |||||
} | |||||
public static String mainArgs(MonitoredVm vm) { | |||||
if (vm != null) { | |||||
try { | |||||
return MonitoredVmUtil.mainArgs(vm); | |||||
} catch (Exception e) { | |||||
// 打印日志即可 | |||||
System.err.println(e); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
public static String mainArgs(String processName) throws Exception { | |||||
return mainArgs(activeVm(processName)); | |||||
} | |||||
public static List<String> killCommand(int vmId) { | |||||
if (vmId > 1) { | |||||
List<String> killCmd = new ArrayList<>(); | |||||
killCmd.add("kill"); | |||||
killCmd.add("-9"); | |||||
killCmd.add(String.valueOf(vmId)); | |||||
return killCmd; | |||||
} | |||||
throw new IllegalStateException(String.format("Can not kill Process ID = [%s]", vmId)); | |||||
} | |||||
} |
@@ -0,0 +1,289 @@ | |||||
/** | |||||
* Copyright: Copyright 2016-2020 JD.COM All Right Reserved | |||||
* FileName: com.jd.blockchain.utils.http.agent.HttpClientPool | |||||
* Author: shaozhuguang | |||||
* Department: 区块链研发部 | |||||
* Date: 2019/1/14 下午3:20 | |||||
* Description: | |||||
*/ | |||||
package com.jd.blockchain.ump.util; | |||||
import org.apache.http.*; | |||||
import org.apache.http.client.HttpRequestRetryHandler; | |||||
import org.apache.http.client.config.RequestConfig; | |||||
import org.apache.http.client.entity.UrlEncodedFormEntity; | |||||
import org.apache.http.client.methods.CloseableHttpResponse; | |||||
import org.apache.http.client.methods.HttpGet; | |||||
import org.apache.http.client.methods.HttpPost; | |||||
import org.apache.http.client.methods.HttpRequestBase; | |||||
import org.apache.http.client.protocol.HttpClientContext; | |||||
import org.apache.http.config.Registry; | |||||
import org.apache.http.config.RegistryBuilder; | |||||
import org.apache.http.conn.ConnectTimeoutException; | |||||
import org.apache.http.conn.routing.HttpRoute; | |||||
import org.apache.http.conn.socket.ConnectionSocketFactory; | |||||
import org.apache.http.conn.socket.LayeredConnectionSocketFactory; | |||||
import org.apache.http.conn.socket.PlainConnectionSocketFactory; | |||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; | |||||
import org.apache.http.entity.StringEntity; | |||||
import org.apache.http.impl.client.CloseableHttpClient; | |||||
import org.apache.http.impl.client.HttpClients; | |||||
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; | |||||
import org.apache.http.message.BasicNameValuePair; | |||||
import org.apache.http.util.EntityUtils; | |||||
import javax.net.ssl.SSLException; | |||||
import javax.net.ssl.SSLHandshakeException; | |||||
import java.io.IOException; | |||||
import java.io.InterruptedIOException; | |||||
import java.io.UnsupportedEncodingException; | |||||
import java.net.UnknownHostException; | |||||
import java.nio.charset.Charset; | |||||
import java.util.ArrayList; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
import java.util.Set; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
import java.util.concurrent.locks.Lock; | |||||
import java.util.concurrent.locks.ReentrantLock; | |||||
/** | |||||
* | |||||
* @author shaozhuguang | |||||
* @create 2019/1/14 | |||||
* @since 1.0.0 | |||||
*/ | |||||
public class HttpClientPool { | |||||
private static final int TEN_MINUTE = 600 * 1000; | |||||
private static final int TIME_OUT = TEN_MINUTE; | |||||
private static final int CONNECT_TIME_OUT = TEN_MINUTE; | |||||
private static final int SOCKET_TIME_OUT = TEN_MINUTE; | |||||
private static final int MAX_TOTAL = 200; | |||||
private static final int MAX_PER_ROUTE = 40; | |||||
private static final int MAX_ROUTE = 100; | |||||
private static final int RETRY_COUNT = 5; | |||||
private static final String DEFAULT_CHARSET = "UTF-8"; | |||||
private static final Map<String, CloseableHttpClient> httpClients = new ConcurrentHashMap<>(); | |||||
private final static Lock lock = new ReentrantLock(); | |||||
private static void config(HttpRequestBase httpRequestBase) { | |||||
// 配置请求的超时设置 | |||||
RequestConfig requestConfig = RequestConfig.custom() | |||||
.setConnectionRequestTimeout(TIME_OUT) | |||||
.setConnectTimeout(CONNECT_TIME_OUT) | |||||
.setSocketTimeout(SOCKET_TIME_OUT) | |||||
.build(); | |||||
httpRequestBase.setConfig(requestConfig); | |||||
} | |||||
/** | |||||
* 获取HttpClient对象 | |||||
* | |||||
* @param url | |||||
* @return | |||||
*/ | |||||
public static CloseableHttpClient getHttpClient(String url) { | |||||
String hostName = url.split("/")[2]; | |||||
int port = 80; | |||||
if (hostName.contains(":")) { | |||||
String[] arr = hostName.split(":"); | |||||
hostName = arr[0]; | |||||
port = Integer.parseInt(arr[1]); | |||||
} | |||||
return getHttpClient(hostName, port); | |||||
} | |||||
/** | |||||
* 获取HttpClient对象 | |||||
* | |||||
* @param hostName | |||||
* @param port | |||||
* @return | |||||
*/ | |||||
public static CloseableHttpClient getHttpClient(String hostName, int port) { | |||||
String key = hostName + ":" + port; | |||||
CloseableHttpClient httpClient = httpClients.get(key); | |||||
if (httpClient == null) { | |||||
try { | |||||
lock.lock(); | |||||
if (httpClient == null) { | |||||
httpClient = createHttpClient(MAX_TOTAL, MAX_PER_ROUTE, MAX_ROUTE, hostName, port); | |||||
httpClients.put(key, httpClient); | |||||
} | |||||
} finally { | |||||
lock.unlock(); | |||||
} | |||||
} | |||||
return httpClient; | |||||
} | |||||
/** | |||||
* 创建HttpClient | |||||
* | |||||
* @param maxTotal | |||||
* @param maxPerRoute | |||||
* @param maxRoute | |||||
* @param hostname | |||||
* @param port | |||||
* @return | |||||
*/ | |||||
public static CloseableHttpClient createHttpClient(int maxTotal, | |||||
int maxPerRoute, int maxRoute, String hostname, int port) { | |||||
ConnectionSocketFactory plainsf = PlainConnectionSocketFactory | |||||
.getSocketFactory(); | |||||
LayeredConnectionSocketFactory sslsf = SSLConnectionSocketFactory | |||||
.getSocketFactory(); | |||||
Registry<ConnectionSocketFactory> registry = RegistryBuilder | |||||
.<ConnectionSocketFactory> create().register("http", plainsf) | |||||
.register("https", sslsf).build(); | |||||
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager( | |||||
registry); | |||||
cm.setMaxTotal(maxTotal); | |||||
cm.setDefaultMaxPerRoute(maxPerRoute); | |||||
HttpHost httpHost = new HttpHost(hostname, port); | |||||
cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute); | |||||
HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> { | |||||
if (executionCount >= RETRY_COUNT) {// 最多重试5次 | |||||
return false; | |||||
}else if (exception instanceof NoHttpResponseException) { | |||||
return true; | |||||
}else if (exception instanceof SSLException) { | |||||
return false; | |||||
}else if (exception instanceof InterruptedIOException) { | |||||
return false; | |||||
}else if (exception instanceof SSLHandshakeException) { | |||||
return false; | |||||
}else if (exception instanceof UnknownHostException) { | |||||
return false; | |||||
}else if (exception instanceof ConnectTimeoutException) { | |||||
return false; | |||||
} | |||||
HttpClientContext clientContext = HttpClientContext | |||||
.adapt(context); | |||||
HttpRequest request = clientContext.getRequest(); | |||||
if (!(request instanceof HttpEntityEnclosingRequest)) { | |||||
return true; | |||||
} | |||||
return false; | |||||
}; | |||||
CloseableHttpClient httpClient = HttpClients.custom() | |||||
.setConnectionManager(cm) | |||||
.setRetryHandler(httpRequestRetryHandler).build(); | |||||
return httpClient; | |||||
} | |||||
private static void setPostParams(HttpPost httpPost, | |||||
Map<String, Object> params) { | |||||
List<NameValuePair> nameValuePairs = new ArrayList<>(); | |||||
Set<String> keySet = params.keySet(); | |||||
for (String key : keySet) { | |||||
nameValuePairs.add(new BasicNameValuePair(key, params.get(key).toString())); | |||||
} | |||||
try { | |||||
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, DEFAULT_CHARSET)); | |||||
} catch (UnsupportedEncodingException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
private static void setJsonPostParams(HttpPost httpPost, String json) { | |||||
httpPost.addHeader("Content-type","application/json; charset=utf-8"); | |||||
httpPost.setHeader("Accept", "application/json"); | |||||
httpPost.setEntity(new StringEntity(json, Charset.forName("UTF-8"))); | |||||
} | |||||
/** | |||||
* POST请求 | |||||
* | |||||
* @param url | |||||
* @param params | |||||
* @return String | |||||
* @throws IOException | |||||
*/ | |||||
public static String post(String url, Map<String, Object> params) throws IOException { | |||||
HttpPost httpPost = new HttpPost(url); | |||||
config(httpPost); | |||||
setPostParams(httpPost, params); | |||||
try (CloseableHttpResponse response = httpPost(url, httpPost)) { | |||||
return parseResponse(response); | |||||
} | |||||
} | |||||
public static String jsonPost(String url, String json) throws IOException { | |||||
HttpPost httpPost = new HttpPost(url); | |||||
config(httpPost); | |||||
setJsonPostParams(httpPost, json); | |||||
try (CloseableHttpResponse response = httpPost(url, httpPost)) { | |||||
return parseResponse(response); | |||||
} | |||||
} | |||||
/** | |||||
* GET请求 | |||||
* | |||||
* @param url | |||||
* @return String | |||||
*/ | |||||
public static String get(String url) throws IOException { | |||||
HttpGet httpGet = new HttpGet(url); | |||||
config(httpGet); | |||||
try (CloseableHttpResponse response = httpGet(url, httpGet)) { | |||||
return parseResponse(response); | |||||
} | |||||
} | |||||
/** | |||||
* Get请求的真实执行 | |||||
* | |||||
* @param url | |||||
* @param httpGet | |||||
* @return | |||||
* @throws IOException | |||||
*/ | |||||
private static CloseableHttpResponse httpGet(String url, HttpGet httpGet) throws IOException { | |||||
return getHttpClient(url) | |||||
.execute(httpGet, HttpClientContext.create()); | |||||
} | |||||
/** | |||||
* POST请求的真实执行 | |||||
* | |||||
* @param url | |||||
* @param httpPost | |||||
* @return | |||||
* @throws IOException | |||||
*/ | |||||
private static CloseableHttpResponse httpPost(String url, HttpPost httpPost) throws IOException { | |||||
return getHttpClient(url) | |||||
.execute(httpPost, HttpClientContext.create()); | |||||
} | |||||
/** | |||||
* 解析response | |||||
* | |||||
* @param response | |||||
* @return | |||||
* @throws IOException | |||||
*/ | |||||
private static String parseResponse(CloseableHttpResponse response) throws IOException { | |||||
HttpEntity entity = response.getEntity(); | |||||
String result = EntityUtils.toString(entity, DEFAULT_CHARSET); | |||||
EntityUtils.consume(entity); | |||||
return result; | |||||
} | |||||
} |
@@ -0,0 +1,61 @@ | |||||
package com.jd.blockchain.ump.util; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import com.jd.blockchain.ump.model.web.WebResponse; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
public class HttpJsonClientUtils { | |||||
private static final Logger LOGGER = LoggerFactory.getLogger(HttpJsonClientUtils.class); | |||||
public static <T> T httpPost(MasterAddr masterAddr, String url, Object body, Class<T> returnType, boolean isWrapper) { | |||||
try { | |||||
String responseJson = HttpClientPool.jsonPost(masterAddr.toHttpUrl() + url, JSON.toJSONString(body)); | |||||
LOGGER.info("Http Post Receive info =[ {} ] from {} ", responseJson, masterAddr.toHttpUrl() + url); | |||||
return response(responseJson, returnType, isWrapper); | |||||
} catch (Exception e) { | |||||
LOGGER.error("HttpPostRequestException {}", e.getMessage()); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
public static <T> T httpGet(String url, Class<T> returnType, boolean isWrapper) { | |||||
try { | |||||
String responseJson = HttpClientPool.get(url); | |||||
LOGGER.info("Http Get Receive info =[ {} ] from {} ", responseJson, url); | |||||
return response(responseJson, returnType, isWrapper); | |||||
} catch (Exception e) { | |||||
LOGGER.error("HttpGetRequestException {}", e.toString()); | |||||
throw new IllegalStateException(e); | |||||
} | |||||
} | |||||
private static <T> T response(String responseJson, Class<T> returnType, boolean isWrapper) { | |||||
if (isWrapper) { | |||||
// 封装类型的情况下使用的是WebResponse | |||||
WebResponse<T> webResponse = JSON.parseObject(responseJson, WebResponse.class); | |||||
LOGGER.info("Wrapper JSON Data = {}", JSON.toJSONString(webResponse)); | |||||
return webResponse.getData(); | |||||
} | |||||
if (!JSON.isValid(responseJson)) { | |||||
return (T)responseJson; | |||||
} | |||||
// 对responseJson进行转换 | |||||
T data = JSON.parseObject(responseJson, returnType); | |||||
LOGGER.info("UnWrapper JSON Data = {}", JSON.toJSONString(data)); | |||||
return data; | |||||
} | |||||
} |
@@ -0,0 +1,122 @@ | |||||
# Copyright (c) 2007-2013 Alysson Bessani, Eduardo Alchieri, Paulo Sousa, and the authors indicated in the @author tags | |||||
# | |||||
# Licensed under the Apache License, Version 2.0 (the "License"); | |||||
# you may not use this file except in compliance with the License. | |||||
# You may obtain a copy of the License at | |||||
# | |||||
# http://www.apache.org/licenses/LICENSE-2.0 | |||||
# | |||||
# Unless required by applicable law or agreed to in writing, software | |||||
# distributed under the License is distributed on an "AS IS" BASIS, | |||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |||||
# See the License for the specific language governing permissions and | |||||
# limitations under the License. | |||||
############################################ | |||||
####### Communication Configurations ####### | |||||
############################################ | |||||
#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||||
#This parameter is not currently being used | |||||
#system.authentication.hmacAlgorithm = HmacSHA1 | |||||
#Specify if the communication system should use a thread to send data (true or false) | |||||
system.communication.useSenderThread = true | |||||
#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments | |||||
#and benchmarks, but must not be used in production systems. | |||||
system.communication.defaultkeys = true | |||||
############################################ | |||||
### Replication Algorithm Configurations ### | |||||
############################################ | |||||
#Number of servers in the group | |||||
system.servers.num = 4 | |||||
#Maximum number of faulty replicas | |||||
system.servers.f = 1 | |||||
#Timeout to asking for a client request | |||||
system.totalordermulticast.timeout = 2000 | |||||
#Maximum batch size (in number of messages) | |||||
system.totalordermulticast.maxbatchsize = 400 | |||||
#Number of nonces (for non-determinism actions) generated | |||||
system.totalordermulticast.nonces = 10 | |||||
#if verification of leader-generated timestamps are increasing | |||||
#it can only be used on systems in which the network clocks | |||||
#are synchronized | |||||
system.totalordermulticast.verifyTimestamps = false | |||||
#Quantity of messages that can be stored in the receive queue of the communication system | |||||
system.communication.inQueueSize = 500000 | |||||
# Quantity of messages that can be stored in the send queue of each replica | |||||
system.communication.outQueueSize = 500000 | |||||
#Set to 1 if SMaRt should use signatures, set to 0 if otherwise | |||||
system.communication.useSignatures = 0 | |||||
#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise | |||||
system.communication.useMACs = 1 | |||||
#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise | |||||
system.debug = 0 | |||||
#Print information about the replica when it is shutdown | |||||
system.shutdownhook = true | |||||
############################################ | |||||
###### State Transfer Configurations ####### | |||||
############################################ | |||||
#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) | |||||
system.totalordermulticast.state_transfer = true | |||||
#Maximum ahead-of-time message not discarded | |||||
system.totalordermulticast.highMark = 10000 | |||||
#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) | |||||
system.totalordermulticast.revival_highMark = 10 | |||||
#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs | |||||
system.totalordermulticast.timeout_highMark = 200 | |||||
############################################ | |||||
###### Log and Checkpoint Configurations ### | |||||
############################################ | |||||
system.totalordermulticast.log = true | |||||
system.totalordermulticast.log_parallel = false | |||||
system.totalordermulticast.log_to_disk = false | |||||
system.totalordermulticast.sync_log = false | |||||
#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) | |||||
system.totalordermulticast.checkpoint_period = 1000 | |||||
system.totalordermulticast.global_checkpoint_period = 120000 | |||||
system.totalordermulticast.checkpoint_to_disk = false | |||||
system.totalordermulticast.sync_ckp = false | |||||
############################################ | |||||
###### Reconfiguration Configurations ###### | |||||
############################################ | |||||
#Replicas ID for the initial view, separated by a comma. | |||||
# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' | |||||
system.initial.view = 0,1,2,3 | |||||
#The ID of the trust third party (TTP) | |||||
system.ttp.id = 7002 | |||||
#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults | |||||
system.bft = true | |||||
#Custom View Storage; | |||||
#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage |
@@ -0,0 +1,83 @@ | |||||
<?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"> | |||||
<parent> | |||||
<artifactId>manager</artifactId> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<version>1.1.0-SNAPSHOT</version> | |||||
</parent> | |||||
<modelVersion>4.0.0</modelVersion> | |||||
<artifactId>ump-web</artifactId> | |||||
<name>ump-web</name> | |||||
<properties> | |||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||||
<maven.compiler.source>1.8</maven.compiler.source> | |||||
<maven.compiler.target>1.8</maven.compiler.target> | |||||
</properties> | |||||
<dependencies> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-web</artifactId> | |||||
<exclusions> | |||||
<exclusion> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-logging</artifactId> | |||||
</exclusion> | |||||
</exclusions> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-starter-security</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-configuration-processor</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>org.springframework.boot</groupId> | |||||
<artifactId>spring-boot-devtools</artifactId> | |||||
<optional>true</optional> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-service</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-model</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>utils-common</artifactId> | |||||
<version>${project.version}</version> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>com.jd.blockchain</groupId> | |||||
<artifactId>ump-explorer</artifactId> | |||||
</dependency> | |||||
<dependency> | |||||
<groupId>junit</groupId> | |||||
<artifactId>junit</artifactId> | |||||
<scope>test</scope> | |||||
</dependency> | |||||
</dependencies> | |||||
</project> |
@@ -0,0 +1,22 @@ | |||||
package com.jd.blockchain.ump.controller; | |||||
import com.jd.blockchain.ump.dao.DBConnection; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.PathVariable; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RequestMethod; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
@RestController | |||||
@RequestMapping(path = "/db/") | |||||
public class UmpDBController { | |||||
@Autowired | |||||
private DBConnection dbConnection; | |||||
@RequestMapping(method = RequestMethod.GET, path = "read/{key}") | |||||
public String read(@PathVariable(name = "key") String key) { | |||||
return dbConnection.get(key); | |||||
} | |||||
} |
@@ -0,0 +1,85 @@ | |||||
package com.jd.blockchain.ump.controller; | |||||
import com.jd.blockchain.ump.model.user.UserKeyBuilder; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.ump.model.user.UserKeysVv; | |||||
import com.jd.blockchain.ump.service.UmpStateService; | |||||
import com.jd.blockchain.ump.service.UtilService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import java.util.List; | |||||
@RestController | |||||
@RequestMapping(path = "/keys/") | |||||
public class UmpKeyController { | |||||
@Autowired | |||||
private UtilService utilService; | |||||
@Autowired | |||||
private UmpStateService umpStateService; | |||||
/** | |||||
* 创建用户 | |||||
* | |||||
* @param builder | |||||
* @return | |||||
*/ | |||||
@RequestMapping(method = RequestMethod.POST, path = "create") | |||||
public UserKeysVv create(@RequestBody final UserKeyBuilder builder) { | |||||
// 使用种子生成公私钥 | |||||
UserKeys userKeys = utilService.create(builder); | |||||
// 将userKeys保存至数据库 | |||||
umpStateService.save(userKeys); | |||||
return userKeys.toUserKeysVv(); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "list") | |||||
public List<UserKeysVv> list() { | |||||
// 从数据库中读取,返回 | |||||
return umpStateService.readUserKeysVvList(); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "read/{user}/{pubKey}") | |||||
public UserKeysVv read(@PathVariable(name = "user") int userId, | |||||
@PathVariable(name = "pubKey") String pubKey) { | |||||
UserKeys userKeys = utilService.read(userId); | |||||
if (userKeys != null) { | |||||
if (userKeys.getPubKey().equals(pubKey)) { | |||||
return userKeys.toUserKeysVv(); | |||||
} | |||||
} | |||||
throw new IllegalStateException(String.format("Can not find UserKeys by %s", pubKey)); | |||||
} | |||||
/** | |||||
* 解析UserKeys | |||||
* | |||||
* @param userId | |||||
* 用户ID | |||||
* @param pwd | |||||
* 密码(非编码后密码) | |||||
* @return | |||||
*/ | |||||
@RequestMapping(method = RequestMethod.GET, path = "resolve/{user}/{pwd}") | |||||
public UserKeys resolve(@PathVariable(name = "user") int userId, | |||||
@PathVariable(name = "pwd") String pwd) { | |||||
UserKeys userKeys = utilService.read(userId); | |||||
if (utilService.verify(userKeys, pwd)) { | |||||
return userKeys; | |||||
} | |||||
throw new IllegalStateException(String.format("Can not resolve UserKeys by %s", pwd)); | |||||
} | |||||
} |
@@ -0,0 +1,76 @@ | |||||
package com.jd.blockchain.ump.controller; | |||||
import com.jd.blockchain.ump.model.PeerSharedConfigs; | |||||
import com.jd.blockchain.ump.model.config.LedgerConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.state.InstallSchedule; | |||||
import com.jd.blockchain.ump.model.state.LedgerMasterInstall; | |||||
import com.jd.blockchain.ump.service.UmpService; | |||||
import com.jd.blockchain.ump.service.UmpStateService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import java.util.List; | |||||
import java.util.Map; | |||||
@RestController | |||||
@RequestMapping(path = "/master/") | |||||
public class UmpMasterController { | |||||
@Autowired | |||||
private UmpService umpService; | |||||
@Autowired | |||||
private UmpStateService umpStateService; | |||||
/** | |||||
* 需要支持的接口 | |||||
* 1、接收节点Share的信息 | |||||
* 2、接收节点发送来的状态信息 | |||||
* 3、接收前端查看某些节点状态的请求 | |||||
*/ | |||||
@RequestMapping(method = RequestMethod.POST, path = "share") | |||||
public LedgerConfig share(@RequestBody final PeerLocalConfig sharedConfig) { | |||||
PeerSharedConfigs sharedConfigs = umpService.loadPeerSharedConfigs(sharedConfig); | |||||
if (sharedConfigs == null) { | |||||
throw new IllegalStateException("PeerSharedConfig may be exits Conflict !!!"); | |||||
} | |||||
return umpService.response(sharedConfigs, sharedConfig); | |||||
} | |||||
/** | |||||
* 接收其他Peer节点发送的安装信息 | |||||
* | |||||
* @param installSchedule | |||||
* 安装信息 | |||||
* | |||||
* @return | |||||
*/ | |||||
@RequestMapping(method = RequestMethod.POST, path = "receive") | |||||
public String receive(@RequestBody final InstallSchedule installSchedule) { | |||||
try { | |||||
umpStateService.save(installSchedule, null); | |||||
} catch (Exception e) { | |||||
return "FAIL"; | |||||
} | |||||
return "SUCCESS"; | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "read/{ledgerKey}") | |||||
public Map<String, List<InstallSchedule>> readState(@PathVariable(name = "ledgerKey") String ledgerKey) { | |||||
return umpStateService.readStates(ledgerKey); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "list") | |||||
public List<LedgerMasterInstall> ledgerInstallList() { | |||||
// 返回当前Master收到的所有节点所有的安装信息 | |||||
return umpStateService.readLedgerMasterInstalls(); | |||||
} | |||||
} |
@@ -0,0 +1,147 @@ | |||||
package com.jd.blockchain.ump.controller; | |||||
import com.jd.blockchain.ump.model.MasterAddr; | |||||
import com.jd.blockchain.ump.model.UmpConstant; | |||||
import com.jd.blockchain.ump.model.config.LedgerConfig; | |||||
import com.jd.blockchain.ump.model.config.LedgerIdentification; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.config.PeerSharedConfigVv; | |||||
import com.jd.blockchain.ump.model.state.*; | |||||
import com.jd.blockchain.ump.model.user.UserKeys; | |||||
import com.jd.blockchain.ump.service.UmpService; | |||||
import com.jd.blockchain.ump.service.UmpStateService; | |||||
import com.jd.blockchain.ump.service.UtilService; | |||||
import com.jd.blockchain.ump.util.HttpJsonClientUtils; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.*; | |||||
import java.util.List; | |||||
@RestController | |||||
@RequestMapping(path = "/peer/") | |||||
public class UmpPeerController { | |||||
@Autowired | |||||
private UmpService umpService; | |||||
@Autowired | |||||
private UmpStateService umpStateService; | |||||
@Autowired | |||||
private UmpMasterController masterController; | |||||
@Autowired | |||||
private UtilService utilService; | |||||
// @RequestMapping(method = RequestMethod.POST, path = "share") | |||||
public LedgerIdentification share(@RequestBody PeerLocalConfig localConfig) { | |||||
//首先校验配置信息 | |||||
localConfig.verify(); | |||||
MasterAddr masterAddr = localConfig.masterAddr(); | |||||
LedgerConfig ledgerConfig; | |||||
if (localConfig.master()) { | |||||
// 当前节点本身是master,直接调用Controller方法 | |||||
ledgerConfig = masterController.share(localConfig); | |||||
} else { | |||||
ledgerConfig = HttpJsonClientUtils.httpPost(masterAddr, UmpConstant.REQUEST_SHARED_URL, localConfig, LedgerConfig.class, false); | |||||
} | |||||
if (ledgerConfig == null) { | |||||
// 未加载成功 | |||||
throw new IllegalStateException("Can not load Ledger-Config's Data from Master Node !!!"); | |||||
} | |||||
String ledgerAndNodeKey = umpService.save(masterAddr, ledgerConfig, localConfig); | |||||
int nodeId = ledgerConfig.getInitConfig().nodeId(localConfig.getPubKey()); | |||||
LedgerIdentification identification = new LedgerIdentification(nodeId, localConfig, | |||||
masterAddr, ledgerAndNodeKey, ledgerConfig.getInitConfig()); | |||||
// 将数据写入数据库 | |||||
umpStateService.save(identification); | |||||
return identification; | |||||
} | |||||
@RequestMapping(method = RequestMethod.POST, path = "share") | |||||
public LedgerIdentification share(@RequestBody PeerSharedConfigVv sharedConfigVv) { | |||||
String pubKey = sharedConfigVv.getPubKey(); | |||||
if (pubKey == null || pubKey.length() == 0) { | |||||
throw new IllegalStateException("Public Key can not be empty !!!"); | |||||
} | |||||
// 获取对应的UsersKey,转换为LocalConfig | |||||
UserKeys userKeys = utilService.read(sharedConfigVv.getUserId()); | |||||
if (userKeys == null || !pubKey.equals(userKeys.getPubKey())) { | |||||
throw new IllegalStateException(String.format("Can not find UserKeys by %s", pubKey)); | |||||
} | |||||
PeerLocalConfig localConfig = sharedConfigVv.toPeerLocalConfig(userKeys); | |||||
return share(localConfig); | |||||
} | |||||
@RequestMapping(method = RequestMethod.POST, path = "install/{ledgerAndNodeKey}") | |||||
public PeerInstallSchedules install(@PathVariable(name = "ledgerAndNodeKey") String ledgerAndNodeKey) { | |||||
return umpService.install(ledgerAndNodeKey); | |||||
} | |||||
@RequestMapping(method = RequestMethod.POST, path = "init/{ledgerAndNodeKey}") | |||||
public PeerInstallSchedules init(@PathVariable(name = "ledgerAndNodeKey") String ledgerAndNodeKey) { | |||||
return umpService.init(ledgerAndNodeKey); | |||||
} | |||||
@RequestMapping(method = RequestMethod.POST, path = "startup") | |||||
public PeerStartupSchedules startup() { | |||||
return umpService.startup(); | |||||
} | |||||
// @RequestMapping(method = RequestMethod.POST, path = "stop/{ledgerAndNodeKey}") | |||||
public boolean stop(@PathVariable(name = "ledgerAndNodeKey") String ledgerAndNodeKey) { | |||||
return umpService.stop(ledgerAndNodeKey); | |||||
} | |||||
@RequestMapping(method = RequestMethod.POST, path = "stop") | |||||
public boolean stop() { | |||||
return umpService.stop(); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "init/read/{ledgerAndNodeKey}") | |||||
public PeerInstallSchedules readInitState(@PathVariable(name = "ledgerAndNodeKey") String ledgerAndNodeKey) { | |||||
return umpStateService.readInitState(ledgerAndNodeKey); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "list") | |||||
public List<LedgerPeerInstall> ledgerInstallList() { | |||||
// 返回当前Peer节点所有的安装信息 | |||||
return umpStateService.readLedgerPeerInstalls(); | |||||
} | |||||
public List<LedgerPeerInited> ledgerInitedList(@RequestParam(name = "search", required = false) String search) { | |||||
// 返回当前Peer节点所有的初始化后信息 | |||||
return umpStateService.readLedgerPeerIniteds(search); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "initeds") | |||||
public List<LedgerInited> ledgerIniteds(@RequestParam(name = "search", required = false) String search) { | |||||
// 返回当前Peer节点所有的初始化后信息 | |||||
return umpStateService.readLedgerIniteds(search); | |||||
} | |||||
} |
@@ -0,0 +1,64 @@ | |||||
package com.jd.blockchain.ump.controller; | |||||
import com.jd.blockchain.ump.model.config.LedgerIdentification; | |||||
import com.jd.blockchain.ump.model.config.PeerLocalConfig; | |||||
import com.jd.blockchain.ump.model.state.PeerInstallSchedules; | |||||
import com.jd.blockchain.ump.service.UmpService; | |||||
import com.jd.blockchain.ump.service.UmpSimulateService; | |||||
import org.springframework.beans.factory.annotation.Autowired; | |||||
import org.springframework.web.bind.annotation.PathVariable; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RequestMethod; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import java.util.Map; | |||||
import java.util.concurrent.ConcurrentHashMap; | |||||
@RestController | |||||
@RequestMapping(path = "/peer/") | |||||
public class UmpPeerSimulateController { | |||||
private final Map<Integer, String> ledgerAndNodeKeys = new ConcurrentHashMap<>(); | |||||
@Autowired | |||||
private UmpService umpService; | |||||
@Autowired | |||||
private UmpSimulateService simulateService; | |||||
@Autowired | |||||
private UmpPeerController peerController; | |||||
@RequestMapping(method = RequestMethod.GET, path = "share/simulate/{node}") | |||||
public LedgerIdentification share(@PathVariable(name = "node") int nodeId) { | |||||
boolean isMaster = false; | |||||
if (nodeId == 0) { | |||||
isMaster = true; | |||||
} | |||||
PeerLocalConfig localConfig = simulateService.nodePeerLocalConfig(nodeId, isMaster); | |||||
LedgerIdentification identification = peerController.share(localConfig); | |||||
// 作为缓存使用 | |||||
ledgerAndNodeKeys.put(nodeId, identification.getLedgerAndNodeKey()); | |||||
return identification; | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "install/simulate/{node}") | |||||
public PeerInstallSchedules install(@PathVariable(name = "node") int nodeId) { | |||||
String ledgerAndNodeKey = ledgerAndNodeKeys.get(nodeId); | |||||
return umpService.install(ledgerAndNodeKey); | |||||
} | |||||
@RequestMapping(method = RequestMethod.GET, path = "init/simulate/{node}") | |||||
public PeerInstallSchedules init(@PathVariable(name = "node") int nodeId) { | |||||
return umpService.init(ledgerAndNodeKeys.get(nodeId)); | |||||
} | |||||
} |
@@ -0,0 +1,29 @@ | |||||
package com.jd.blockchain.ump.web; | |||||
import org.springframework.context.annotation.Configuration; | |||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry; | |||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; | |||||
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; | |||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; | |||||
@Configuration | |||||
public class ControllerConfigurer implements WebMvcConfigurer { | |||||
@Override | |||||
public void addInterceptors(InterceptorRegistry registry) { | |||||
// 添加打印日志的拦截器 | |||||
registry.addInterceptor(new LogPrintInterceptor()).addPathPatterns("/**"); | |||||
} | |||||
@Override | |||||
public void addResourceHandlers(ResourceHandlerRegistry registry) { | |||||
registry.addResourceHandler("/webjars/**") | |||||
.addResourceLocations("classpath:/META-INF/resources"); | |||||
} | |||||
@Override | |||||
public void addViewControllers(ViewControllerRegistry registry) { | |||||
registry.addViewController("/").setViewName("web/index.html"); | |||||
} | |||||
} |
@@ -0,0 +1,38 @@ | |||||
package com.jd.blockchain.ump.web; | |||||
import com.jd.blockchain.ump.model.web.ErrorCode; | |||||
import com.jd.blockchain.ump.model.web.WebResponse; | |||||
import com.jd.blockchain.utils.BusinessException; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.web.bind.annotation.ExceptionHandler; | |||||
import org.springframework.web.bind.annotation.ResponseBody; | |||||
import org.springframework.web.bind.annotation.RestControllerAdvice; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
@RestControllerAdvice | |||||
public class ExceptionResponseAdvice { | |||||
private final Logger logger = LoggerFactory.getLogger(getClass()); | |||||
@ExceptionHandler(value = Exception.class) | |||||
@ResponseBody | |||||
public WebResponse json(HttpServletRequest req, Exception ex) { | |||||
WebResponse.ErrorMessage message; | |||||
String reqURL = "[" + req.getMethod() + "] " + req.getRequestURL().toString(); | |||||
if (ex instanceof BusinessException) { | |||||
BusinessException businessException = (BusinessException) ex; | |||||
message = new WebResponse.ErrorMessage(businessException.getErrorCode(), businessException.getMessage()); | |||||
} else { | |||||
logger.error("Exception occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() | |||||
+ "]" + ex.getMessage(), ex); | |||||
message = new WebResponse.ErrorMessage(ErrorCode.UNEXPECTED.getValue(), ex.toString()); | |||||
} | |||||
return WebResponse.createFailureResult(message); | |||||
} | |||||
} |
@@ -0,0 +1,51 @@ | |||||
package com.jd.blockchain.ump.web; | |||||
import com.jd.blockchain.ump.model.config.LedgerConfig; | |||||
import com.jd.blockchain.ump.model.web.WebResponse; | |||||
import org.springframework.core.MethodParameter; | |||||
import org.springframework.http.MediaType; | |||||
import org.springframework.http.ResponseEntity; | |||||
import org.springframework.http.converter.HttpMessageConverter; | |||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; | |||||
import org.springframework.http.server.ServerHttpRequest; | |||||
import org.springframework.http.server.ServerHttpResponse; | |||||
import org.springframework.web.bind.annotation.RestControllerAdvice; | |||||
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; | |||||
@RestControllerAdvice | |||||
public class JsonResponseAdvice implements ResponseBodyAdvice<Object> { | |||||
@Override | |||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { | |||||
if (MappingJackson2HttpMessageConverter.class == converterType | |||||
&& (returnType.getContainingClass().getName().startsWith("com.jd.blockchain.ump") | |||||
|| returnType.getDeclaringClass().getName().startsWith("com.jd.blockchain.ump"))) { | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
@Override | |||||
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, | |||||
Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, | |||||
ServerHttpResponse response) { | |||||
if (body == null) { | |||||
return WebResponse.createSuccessResult(null); | |||||
} | |||||
if (body instanceof ResponseEntity) { | |||||
return body; | |||||
} | |||||
// LedgerConfig单独处理 | |||||
if (body instanceof LedgerConfig) { | |||||
return body; | |||||
} | |||||
// 把返回结果自动转换为 WebResponse; | |||||
if (body instanceof WebResponse) { | |||||
return body; | |||||
} | |||||
return WebResponse.createSuccessResult(body); | |||||
} | |||||
} |
@@ -0,0 +1,32 @@ | |||||
package com.jd.blockchain.ump.web; | |||||
import com.alibaba.fastjson.JSON; | |||||
import org.slf4j.Logger; | |||||
import org.slf4j.LoggerFactory; | |||||
import org.springframework.web.servlet.HandlerInterceptor; | |||||
import javax.servlet.http.HttpServletRequest; | |||||
import javax.servlet.http.HttpServletResponse; | |||||
import java.util.Map; | |||||
public class LogPrintInterceptor implements HandlerInterceptor { | |||||
private final Logger LOGGER = LoggerFactory.getLogger(getClass()); | |||||
@Override | |||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { | |||||
// 记录日志 | |||||
// 请求的参数: | |||||
String parameters = ""; | |||||
Map<String, String[]> requestParameters = request.getParameterMap(); | |||||
if (requestParameters != null && !requestParameters.isEmpty()) { | |||||
parameters = JSON.toJSONString(requestParameters); | |||||
} | |||||
LOGGER.info("Request[{}][{}], parameters=[{}]", | |||||
request.getRequestURL().toString(), // 请求URL | |||||
request.getMethod(), // 请求的方法 | |||||
parameters); // 请求的参数 | |||||
return true; | |||||
} | |||||
} |