diff --git a/.gitignore b/.gitignore index 579cb537..7e7ca472 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,42 @@ -/target/ -*.classpath -*.project +**.classpath +**.project +**/bin/ +**.class + +target/ + +*.bak +bin/ +*.iml +*.ipr +*.iws +.idea +.classpath +.project .settings/ +.DS_Store +.springBeans +.externalToolBuilders/ + +*.versionsBackup +.factorypath +source/**/config/currentView +source/gateway/data/ +source/peer/data/ +*.orig +source/peer/config/view/current.view +source/contract/contract-jar/src/main/java/com/jd/blockchain/contract/*.contract +source/contract/contract-jar/src/main/java/com/jd/blockchain/contract/contract.properties +source/contract/logs +source/contract/tmp +source/contract/contract-model/src/main/resources/contractCoreLib/ +*.db +*.log +source/test/test-integration/txs +contract-libs/coreLib/contract-model-0.6.0-SNAPSHOT.jar +contract-libs/coreLib/crypto-framework-0.6.0-SNAPSHOT.jar +contract-libs/coreLib/ledger-model-0.6.0-SNAPSHOT.jar +contract-libs/coreLib/sdk-base-0.6.0-SNAPSHOT.jar +contract-libs/coreLib/utils-common-1.6.1-SNAPSHOT.jar +source/tools/tools-initializer/bftsmart.config +test/test-integration/runtime/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..e69de29b diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..d6456956 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/README.md b/README.md new file mode 100644 index 00000000..47382e47 --- /dev/null +++ b/README.md @@ -0,0 +1,619 @@ +[TOC] +#JD区块链 0.8.3-SNAPSHOT + +------------------------------------------------------------------------ +### 版本修订历史 + + + + + + + + + + + + + + + + + + + + + + + + + +
版本号作 者修改日期备 注
0.0.6黄海泉2017-11-10 + 定义JD区块链项目的目标与关键能力;
+ 定义JD区块链的核心对象模型;
+ 定义“账户”的生成算法和“区块/交易/操作/账户”的关键属性;
+ 描述了编程接口的示例代码; +
0.0.7黄海泉2017-11-17 + 丰富了对“节点共识”、“节点分区”两项关键能力的详细描述; +
0.0.8黄海泉2018-07-17 + 增加部署图;增加智能合约开发的示例; +
+ +------------------------------------------------------------------------ + +## 一、概述 +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. 区块 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
属性名称说明
BlockHash当前区块 hash对区块中除此之外的其它所有属性一起进行哈希运算生成
BlockVersion 区块版本表示区块-交易的属性结构的版本号;
PreviousBlockHash上一区块 hash
BlockNumber区块高度区块高度是一个区块在链中的序号;
创始区块的高度为 0,每个新区块的高度依次递增;
AccountHash账户树hash账户的 Merkle Tree 根的 hash
AccountCount账户数量区块生成时账本中的全部账户的总数
TxTreeHash交易树 hash本区块的交易集合的 Merkle Tree 根的 hash
TxCount区块交易数量当前区块包含的交易的数量;
TxTotalCount账本交易总数截止到当前区块为止当前账本的所有交易的总数量;
CloseTime区块关闭时间生成当前区块时的区块链节点的网络时间;
+ +### 3. 交易 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
属性名称说明
Hash当前交易 hash对交易中除此之外的其它所有属性一起进行哈希运算生成
LedgerNumber区块高度交易被包含的区块高度
BlobHash交易数据块hash交易的数据块是交易的原始数据,包含客户端提交的交易的全部操作及其参数; +
交易的参与者需要使用私钥对交易数据块进行签名;
Operations操作列表交易的操作列表;
Sponsor交易发起人交易发起人的账户地址;
SequenceNumber交易序号交易序号记录了一个特定的发起人的交易的顺序号,等同于该发起人历史上发起的交易的总数;
Signatures签名列表由交易发起人和其它参与者对交易数据块的签名的列表;
Result交易结果0 - 表示执行成功;非零表示执行失败;
注:最终的账本只包含成功的交易;
+ +### 4. 操作 + + + + + + + + + + + + + + + + + + + + + +
属性名称说明
OpType操作类型 + 一级操作类型包括:注册账户、配置权限、写入键值数据、写入对象数据、定义合约代码、调用合约代码;
+ “键值数据写入”操作的子操作类型包括:填入键值、移除键、数值增加、数值减少;
+ “对象数据写入”操作的自操作类型包括:插入对象、更新对象、移除对象; +
Args参数列表与操作类型相对应的参数列表;
SubOps子操作列表“子操作”是“操作”的递归定义,由“操作类型”来标识;
+ +### 5. 账户 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
属性名称说明
Address地址账户的唯一标识
RegNumber注册号账户被注册到区块链的区块高度;
TxSquenceNumber交易序列号由账户发起的交易的序列号,初始为 0,账户每发起一个交易则增加1;
ModelVersion账户模型版本表示构成一个账户结构的属性模型的程序版本号;
Version账户版本初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;
注:交易序号的改变不会导致账户版本的增加;
PrivilegeHash权限 hash权限树的根hash;
PrivilegeVersion权限版本初始为 0, 每次对权限的变更都导致版本号加 1;
StateType状态类型账户的状态类型有3种:空类型(NIL);键值类型;对象类型;
StateVersion状态版本账户的状态类型有3种:空类型(NIL);键值类型;对象类型;
StateHash状态哈希数据状态的 merkle tree 的根hash;
CodeHash合约代码哈希由“账户地址+合约代码版本号+合约代码内容”生成的哈希;
CodeVersion代码版本初始为 0,每次对代码的变更都使版本加 1 ;
+ +## 五、编程接口 + +### 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 = ""; + + // Event + String event = ""; + + // args(注意参数的格式) + byte[] args = "20##30##abc".getBytes(); + + + // 提交合约执行代码 + txTemp.contractEvents().send(contractAddressBase58, event, args); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 生成私钥并使用私钥进行签名; + CryptoKeyPair keyPair = getSponsorKey(); + + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + assertTrue(transactionResponse.isSuccess()); + +``` \ No newline at end of file diff --git a/docs/images/deployment.png b/docs/images/deployment.png new file mode 100644 index 00000000..a0b2e722 Binary files /dev/null and b/docs/images/deployment.png differ diff --git a/docs/readme.zip b/docs/readme.zip new file mode 100644 index 00000000..5ad632fe Binary files /dev/null and b/docs/readme.zip differ diff --git a/source/base/pom.xml b/source/base/pom.xml new file mode 100644 index 00000000..e010425a --- /dev/null +++ b/source/base/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + base + \ No newline at end of file diff --git a/source/base/src/main/java/com/jd/blockchain/base/data/TypeCodes.java b/source/base/src/main/java/com/jd/blockchain/base/data/TypeCodes.java new file mode 100644 index 00000000..b2e28bd9 --- /dev/null +++ b/source/base/src/main/java/com/jd/blockchain/base/data/TypeCodes.java @@ -0,0 +1,148 @@ +package com.jd.blockchain.base.data; + +/** + * @author huanghaiquan + * + */ +public interface TypeCodes { + + public static final int BYTES_VALUE = 0x80; + + public static final int BLOCK_CHAIN_IDENTITY = 0x90; + + public static final int BLOCK = 0x100; + + public static final int BLOCK_BODY = 0x110; + + // public static final int BLOCK_LEDGER = 0x110; + + public static final int BLOCK_GENESIS = 0x120; + + public static final int DATA_SNAPSHOT = 0x130; + + public static final int TX = 0x200; + + public static final int TX_LEDGER = 0x201; + + public static final int TX_CONTENT = 0x210; + + public static final int TX_CONTENT_BODY = 0x220; + + public static final int TX_OP = 0x300; + + public static final int TX_OP_LEDGER_INIT = 0x301; + + public static final int TX_OP_USER_REG = 0x310; + public static final int TX_OP_USER_INFO_SET = 0x311; + public static final int TX_OP_USER_INFO_SET_KV = 0x312; + + public static final int TX_OP_DATA_ACC_REG = 0x320; + public static final int TX_OP_DATA_ACC_SET = 0x321; + public static final int TX_OP_DATA_ACC_SET_KV = 0x322; + + public static final int TX_OP_CONTRACT_DEPLOY = 0x330; + public static final int TX_OP_CONTRACT_UPDATE = 0x331; + + public static final int TX_OP_CONTRACT_EVENT_SEND = 0x340; + + public static final int TX_RESPONSE = 0x350; + + public static final int METADATA = 0x600; + + public static final int METADATA_INIT_SETTING = 0x610; + + public static final int METADATA_INIT_PERMISSION = 0x611; + + public static final int METADATA_INIT_DECISION = 0x612; + + public static final int METADATA_LEDGER_SETTING = 0x620; + + public static final int METADATA_CONSENSUS_PARTICIPANT = 0x621; + +// public static final int METADATA_CONSENSUS_NODE = 0x630; + + public static final int METADATA_CONSENSUS_SETTING = 0x631; + +// public static final int METADATA_PARTICIPANT_INFO = 0x640; + + public static final int METADATA_CRYPTO_SETTING = 0x642; + + // public static final int ACCOUNT = 0x700; + + public static final int ACCOUNT_HEADER = 0x710; + + public static final int USER = 0x800; + + public static final int DATA = 0x900; + + public static final int CONTRACT = 0xA00; + + public static final int HASH = 0xB00; + + public static final int HASH_OBJECT = 0xB10; + + public static final int ENUM_TYPE = 0xB20; + + public static final int ENUM_TYPE_CRYPTO_ALGORITHM = 0xB21; + + public static final int ENUM_TYPE_TRANSACTION_STATE = 0xB22; + + public static final int ENUM_TYPE_DATA_TYPE= 0xB23; + + public static final int DIGITALSIGNATURE = 0xB30; + + public static final int DIGITALSIGNATURE_BODY = 0xB31; + + public static final int CLIENT_IDENTIFICATION = 0xC00; + + public static final int CLIENT_IDENTIFICATIONS = 0xC10; + + public static final int REQUEST = 0xD00; + + public static final int REQUEST_NODE = 0xD10; + + public static final int REQUEST_ENDPOINT = 0xD20; + + + + // ------------------ 共识相关 ---------------- + + public static final int CONSENSUS = 0x1000; + + public static final int CONSENSUS_ACTION_REQUEST = CONSENSUS | 0x01; + + public static final int CONSENSUS_ACTION_RESPONSE = CONSENSUS | 0x02; + + + public static final int CONSENSUS_SETTINGS = CONSENSUS | 0x03; + + public static final int CONSENSUS_NODE_SETTINGS = CONSENSUS | 0x04; + + public static final int CONSENSUS_CLI_INCOMING_SETTINGS = CONSENSUS | 0x05; + + // ------------------ 共识相关(BFTSMART) ---------------- + public static final int CONSENSUS_BFTSMART = 0x1100; + + public static final int CONSENSUS_BFTSMART_SETTINGS = CONSENSUS_BFTSMART | 0x01; + + public static final int CONSENSUS_BFTSMART_NODE_SETTINGS = CONSENSUS_BFTSMART | 0x02; + + public static final int CONSENSUS_BFTSMART_CLI_INCOMING_SETTINGS = CONSENSUS_BFTSMART | 0x03; + + public static final int CONSENSUS_BFTSMART_BLOCK_SETTINGS = CONSENSUS_BFTSMART | 0x04; + + // ------------------ 共识相关(MSGQUEUE) ---------------- + public static final int CONSENSUS_MSGQUEUE = 0x1200; + + public static final int CONSENSUS_MSGQUEUE_SETTINGS = CONSENSUS_MSGQUEUE | 0x01; + + public static final int CONSENSUS_MSGQUEUE_NODE_SETTINGS = CONSENSUS_MSGQUEUE | 0x02; + + public static final int CONSENSUS_MSGQUEUE_CLI_INCOMING_SETTINGS = CONSENSUS_MSGQUEUE | 0x03; + + public static final int CONSENSUS_MSGQUEUE_NETWORK_SETTINGS = CONSENSUS_MSGQUEUE | 0x04; + + public static final int CONSENSUS_MSGQUEUE_BLOCK_SETTINGS = CONSENSUS_MSGQUEUE | 0x05; + + +} diff --git a/source/binary-proto/pom.xml b/source/binary-proto/pom.xml new file mode 100644 index 00000000..63ce3ffc --- /dev/null +++ b/source/binary-proto/pom.xml @@ -0,0 +1,19 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + binary-proto + + + + com.jd.blockchain + utils-common + ${project.version} + + + \ No newline at end of file diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinaryEncodingUtils.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinaryEncodingUtils.java new file mode 100644 index 00000000..66a9094b --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinaryEncodingUtils.java @@ -0,0 +1,61 @@ +package com.jd.blockchain.binaryproto; + +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; + +import com.jd.blockchain.binaryproto.impl2.DataContractContext; +import com.jd.blockchain.binaryproto.impl2.HeaderEncoder; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class BinaryEncodingUtils { + + + public static void encode(Object data, Class contractType, OutputStream out) { + DataContractEncoder encoder = DataContractContext.resolve(contractType); + if (encoder == null) { + throw new IllegalArgumentException("Contract Type not exist!--" + contractType.getName()); + } + BytesOutputBuffer buffer = new BytesOutputBuffer(); + encoder.encode(data, buffer); + buffer.writeTo(out); + } + + public static byte[] encode(Object data, Class contractType) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + encode(data, contractType, out); + return out.toByteArray(); + } + + public static T decode(InputStream in) { + byte[] bytes = BytesUtils.copyToBytes(in); + return decode(bytes); + } + + + + public static T decode(byte[] dataSegment) { + BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length); + int code = HeaderEncoder.resolveCode(bytes); + long version = HeaderEncoder.resolveVersion(bytes); + + DataContractEncoder encoder = DataContractContext.ENCODER_LOOKUP.lookup(code, version); + return encoder.decode(bytes.getInputStream()); + } + + + + public static T decodeAs(byte[] dataSegment, Class contractType) { + DataContractEncoder encoder = DataContractContext.ENCODER_LOOKUP.lookup(contractType); + if (encoder == null) { + throw new DataContractException("Contract type is not registered! --" + contractType.toString()); + } + BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length); + return encoder.decode(bytes.getInputStream()); + } + + + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySegmentHeader.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySegmentHeader.java new file mode 100644 index 00000000..82eba875 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySegmentHeader.java @@ -0,0 +1,80 @@ +//package com.jd.blockchain.binaryproto; +// +//import java.io.ByteArrayOutputStream; +//import java.io.InputStream; +//import java.io.OutputStream; +//import java.util.Arrays; +//import java.util.Collections; +//import java.util.List; +// +//import my.utils.io.BytesUtils; +// +///** +// * 二进制数据段的头部; +// * +// * @author huanghaiquan +// * +// */ +//public class BinarySegmentHeader { +// +// public static final BinarySliceSpec CODE_SLICE_SPEC = new BinarySliceSpec(4, false, "Code", "Data Contract Code"); +// +// public static final BinarySliceSpec VERSION_SLICE_SPEC = new BinarySliceSpec(8, false, "Version", "Data Contract Version"); +// +// public static final List HEADER_SLICES = Collections.unmodifiableList(Arrays.asList(CODE_SLICE_SPEC, VERSION_SLICE_SPEC)); +// +// private int code; +// +// private long version; +// +// public int getCode() { +// return code; +// } +// +// public long getVersion() { +// return version; +// } +// +// public BinarySegmentHeader(int code, long version) { +// this.code = code; +// this.version = version; +// } +// +// public static int resolveCode(InputStream in) { +// return BytesUtils.readInt(in); +// } +// +// +// public static long resolveVersion(InputStream in) { +// return BytesUtils.readLong(in); +// } +// +// public static BinarySegmentHeader resolveFrom(InputStream in) { +// int code = resolveCode(in); +// long version = resolveVersion(in); +// return new BinarySegmentHeader(code, version); +// } +// +// public static void writeCode(int code, OutputStream out) { +// BytesUtils.writeInt(code, out); +// } +// +// public static void writeVersion(long version, OutputStream out) { +// BytesUtils.writeLong(version, out); +// } +// +// public void writeTo(OutputStream out) { +// writeCode(code, out); +// writeVersion(version, out); +// } +// +// +// public byte[] toBytes() { +// ByteArrayOutputStream out =new ByteArrayOutputStream(); +// writeTo(out); +// return out.toByteArray(); +// } +// +// +// +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySliceSpec.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySliceSpec.java new file mode 100644 index 00000000..c98ffcc8 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/BinarySliceSpec.java @@ -0,0 +1,82 @@ +package com.jd.blockchain.binaryproto; + +/** + * 表示一个二进制数据片段的格式标准; + *

+ * + * 一个数据契约的实例输出生成的二进制数据段{@link BinarySegmentHeader}是由一系列小的标准化的数据片段组成; + * + * @author huanghaiquan + * + */ +public class BinarySliceSpec { + + private boolean repeatable; + + private int length; + + private boolean dynamic; + + private String name; + + private String description; + + /** + * 是否重复多次;true 表示以一个头部表示接下来的片段将重复的次数; + * + * @return + */ + public boolean isRepeatable() { + return repeatable; + } + + /** + * 字节长度; + * + * @return + */ + public int getLength() { + return length; + } + + /** + * 长度是动态扩展的; + * + * @return + */ + public boolean isDynamic() { + return dynamic; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + private BinarySliceSpec(String name, String description, boolean repeatable, int length, boolean dynamic) { + this.name = name; + this.description = description; + this.repeatable = repeatable; + this.length = length; + this.dynamic = dynamic; + } + + public static BinarySliceSpec newFixedSlice(int length, String name, String description) { + return new BinarySliceSpec(name, description, false, length, false); + } + + public static BinarySliceSpec newRepeatableFixedSlice(int length, String name, String description) { + return new BinarySliceSpec(name, description, true, length, false); + } + + public static BinarySliceSpec newDynamicSlice(String name, String description) { + return new BinarySliceSpec(name, description, false, -1, true); + } + + public static BinarySliceSpec newRepeatableDynamicSlice(String name, String description) { + return new BinarySliceSpec(name, description, true, -1, true); + } +} \ No newline at end of file diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DConstructor.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DConstructor.java new file mode 100644 index 00000000..620f6e22 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DConstructor.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.binaryproto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by zhangshuang3 on 2018/7/19. + */ +@Target({ ElementType.METHOD, ElementType.CONSTRUCTOR }) +@Retention(RetentionPolicy.RUNTIME) +public @interface DConstructor { + + /** + * 名称; + *

+ * 默认为属性的名称; + * + * @return + */ + String name() default ""; + +} + diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java new file mode 100644 index 00000000..dee872e3 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContract.java @@ -0,0 +1,48 @@ +package com.jd.blockchain.binaryproto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * {@link DataContract} 表示数据契约,用于把一个接口类型声明为一份标准化“数据契约”; + *

+ * “数据契约”的定义由类型编码({@link #code()})和属性列表(由标注 + * {@link DataField}定义)构成,其中属性列表中有唯一一个“主键字段”, 主键字段的值用于标识数据契约实例的唯一性; + *

+ * “数据契约”通过属性可以引用其它的“数据契约”(由 {@link DataField#refContract()} = true + * 定义),这样便构成了“数据契约”嵌套定义的关系图; + *

+ * 当对一个“数据契约”进行二进制序列化输出时,从根对象出发,对关系图中的每一个“数据契约”实例都输出为一个单独的二进制数据段{@link BinarySegmentHeader}, + * + * 父的数据段中会将子数据段的内容合并输出到对应字段的位置; + * + *

+ * 在序列化输出数据段时,将按顺序先后输出类型编号({@link #code()})、版本标识、属性值列表;

+ * 其中,“版本标识”是根据类型编号和属性列表(由 {@link DataField} 顺序和类型决定,与名称无关) 进行 SHA256 哈希后映射到 64 + * 位值空间的值,占用 8 字节空间; + * “版本标识” 用于在反序列化时校验数据格式的版本是否匹配,并允许数据契约升级后多版本数据并存; + * + * @author huanghaiquan + * + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface DataContract { + + /** + * 类型编号; + *

+ * 不同类型不能声明相同的编号; + *

+ * + * @return + */ + int code(); + + String name() default ""; + + String description() default ""; + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractEncoder.java new file mode 100644 index 00000000..1cb0fc75 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractEncoder.java @@ -0,0 +1,47 @@ +package com.jd.blockchain.binaryproto; + +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; + +/** + * 二进制编码器; + * + * @author huanghaiquan + * + */ +public interface DataContractEncoder { + + /** + * 数据契约的格式标准; + * + * @return + */ + DataSpecification getSepcification(); + + /** + * 数据契约的接口类型; + * + * @return + */ + Class getContractType(); + + /** + * 按照数据格式标准序列化输出指定的数据对象; + * + * @param dataContract + * 数据对象; + * @param buffer + * 要写入的缓冲区; + * @return 返回写入的字节数; + */ + int encode(Object dataContract, BytesOutputBuffer buffer); + + /** + * 按照数据格式标准将指定的二进制输入流反序列化生成数据对象; + * + * @param bytesStream + * @return + */ + T decode(BytesInputStream bytesStream); +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractException.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractException.java new file mode 100644 index 00000000..ae7e57c3 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractException.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.binaryproto; + +public class DataContractException extends RuntimeException { + + private static final long serialVersionUID = 5069307301932155810L; + + public DataContractException(String message) { + super(message); + } + public DataContractException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractRegistry.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractRegistry.java new file mode 100644 index 00000000..3c985b59 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataContractRegistry.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.impl2.DataContractContext; + +/** + * 数据实体注册表; + * + * @author huanghaiquan + * + */ +public class DataContractRegistry { + + private DataContractRegistry() { + } + + public static DataContractEncoder register(Class contractType) { + DataContractEncoder encoder = DataContractContext.resolve(contractType); + return encoder; + } + + public static DataContractEncoder getEncoder(Class contractType) { + return DataContractContext.ENCODER_LOOKUP.lookup(contractType); + } + + public static DataContractEncoder getEncoder(int contractCode, long version) { + return DataContractContext.ENCODER_LOOKUP.lookup(contractCode, version); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataField.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataField.java new file mode 100644 index 00000000..fbd02711 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataField.java @@ -0,0 +1,109 @@ +package com.jd.blockchain.binaryproto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.jd.blockchain.utils.ValueType; + +/** + * 标记一个接口的字段作为数据契约的字段; + *

+ * + * 字段的数据类型需要需要显式通过 + * {@link #primitiveType()}、{@link #refEnum()}、{@link #refContract()} + * 3个属性之一标注(只能标记一种); + * + * @author huanghaiquan + * + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface DataField { + + /** + * 字段顺序; + *

+ * + * 顺序编号实际并不会输出,仅用于对字段进行升序排列; + *

+ * + * 注:对于已经发布使用的数据契约,都不应该调整顺序,否则会导致无法正确解析已有数据; + * + * @return + */ + int order(); + + /** + * 基本数据类型; + *

+ * + * 如果字段的类型属于 {@link ValueType} 枚举中的基本数据类型,则需要显式指定一种具体的类型; + * + * @return + */ + ValueType primitiveType() default ValueType.NIL; + + /** + * 是否是枚举类型; + *

+ * 如果为 true,则属性的声明类型必须是枚举类型,且该枚举类型已经标记 {@link EnumContract}; + * + * @return + */ + boolean refEnum() default false; + + /** + * 嵌套的数据契约类型; + * + * 如果为 true,则属性的声明类型必须是接口类型,且该类型已经标记了 {@link DataContract}; + * + * @return + */ + boolean refContract() default false; + + /** + * 嵌套的契约类型是否根据实际的对象实现的契约接口动态写入; + * + * @return + */ + boolean genericContract() default false; + + /** + * 列表; + * + * @return + */ + boolean list() default false; + + /** + * 最大长度,单位为“byte” + *

+ * 仅对于文本、字节数组、大整数等相关的数据类型有效(即:{@link ValueType} 枚举中编码大于等于 0x20 + * {@link ValueType#TEXT}的数据类型); + * + * @return + */ + int maxSize() default -1; + + /** + * 名称; + *

+ * 默认为属性的名称; + * + * @return + */ + String name() default ""; + + /** + * 关于字段的说明; + *

+ * + * 说明内容将输出到数据段的数据结构描述文件; + * + * @return + */ + String decription() default ""; + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataSpecification.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataSpecification.java new file mode 100644 index 00000000..9577d69e --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/DataSpecification.java @@ -0,0 +1,65 @@ +package com.jd.blockchain.binaryproto; + +import java.util.List; + +/** + * {@link DataSpecification} 表示数据契约的格式标准; + *

+ * {@link DataSpecification} 提供了数据契约的格式描述(通过属性 {@link #getCode()}、 + * {@link #getVersion()}、 + * {@link #getFields()}),以及二进制数据片段序列的格式描述({@link #getSlices()}); + * + *

+ * 其中,数据片段列表({@link #getSlices()})反映了数据契约实际输出的二进制序列:
+ * 1、首个数据片段是数据契约的类型编码({@link #getCode()});
+ * 2、接下来是数据契约的版本({@link #getVersion()});
+ * 3、再接下来是与字段列表({@link #getFields()})一一对应的数据分片;

+ * + *

+ * + * + * @author huanghaiquan + * + */ +public interface DataSpecification { + + /** + * 数据契约的类型编码; + * + * @return + */ + int getCode(); + + /** + * 数据契约的版本; + *

+ * + * 由类型编码{@link #getCode()}和字段列表{@link #getFields()} 进行哈希生成的 64 位整数; + * + * @return + */ + long getVersion(); + + String getName(); + + String getDescription(); + + /** + * 按定义顺序排列的字段格式标准的列表; + *

+ * 字段的顺序由 {@link DataField#order()} 定义; + * + * @return + */ + List getFields(); + + /** + * 按顺序定义的二进制数据片段的格式标准的列表; + * + * @return + */ + List getSlices(); + + String toHtml(); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumContract.java new file mode 100644 index 00000000..d8e8c452 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumContract.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.binaryproto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EnumContract { + + /** + * 类型编号; + *

+ * + * 而且,不同类型不能声明相同的编号;

+ * + * @return + */ + int code() ; + + String name() default ""; + + String decription() default ""; + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumField.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumField.java new file mode 100644 index 00000000..43074e98 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumField.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.binaryproto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.jd.blockchain.utils.ValueType; + +@Target({ ElementType.FIELD, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EnumField { + + /** + * 枚举值的类型; + * + *

+ * 注:只支持 {@link ValueType#INT8} ~ {@link ValueType#INT32} 这几种类型; + * + * + * @return + */ + ValueType type(); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumSpecification.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumSpecification.java new file mode 100644 index 00000000..c86f0bf0 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/EnumSpecification.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.binaryproto; + +import java.util.Set; + +import com.jd.blockchain.utils.ValueType; + +public interface EnumSpecification { + + int getCode(); + + String getName(); + + String getDescription(); + + long getVersion(); + + ValueType getValueType(); + + int[] getItemValues(); + + String[] getItemNames(); + + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSetter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSetter.java new file mode 100644 index 00000000..1cea23ad --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSetter.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.binaryproto; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Created by zhangshuang3 on 2018/7/19. + */ +@Target({ ElementType.PARAMETER, ElementType.FIELD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface FieldSetter { + + /** + * 名称; + *

+ * 默认为属性的名称; + * + * @return + */ + String name() default ""; + String type() default ""; + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSpec.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSpec.java new file mode 100644 index 00000000..1c3621d6 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/FieldSpec.java @@ -0,0 +1,130 @@ +package com.jd.blockchain.binaryproto; + +import com.jd.blockchain.utils.ValueType; + +/** + * 表示数据契约字段的格式标准; + * + * @author huanghaiquan + * + */ +public interface FieldSpec { + +// /** +// * 字段值的类型编码; +// *

+// * +// * 该编码是对{@link #getPrimitiveType()}、{@link #getRefEnum()}、{@link #getRefContract()} +// * 3个属性的联合编码,通过首字节标识出类型为这3中类型中的一种;
+// * 其中:
+// * 1、首字节为 0 表示为基本类型 {@link #getPrimitiveType()};
+// * 2、首字节为 1 表示为枚举类型 {@link #getRefEnum()},紧接其后是枚举类型的编码
+// * 3、首字节为 2 表示为数据契约类型 {@link #getRefContract()};
+// * 4、首字节为 3 表示为基本类型的数组类型 {@link #isRepeatable()} ()};
+// * 5、首字节为 4 表示为PubKey类型 {@link #isRefPubKey()};
+// * 6、首字节为 5 表示为PrivKey类型 {@link #isRefPrivKey()};
+// * 7、首字节为 6 表示为HashDigest类型 {@link #isRefHashDigest()};
+// * 8、首字节为 7 表示为数据契约类型数组 {@link #isList(), @link #getRefContract()};
+// * 9、首字节为 8 表示为BlockChainIdentity数据类型 {@link #isRefIdentity()} +// * 10、首字节为9 表示为NetworkAddress数据类型 {@link #isRefNetworkAddr()};
+// * @return +// */ +// long getTypeCode(); + + /** + * 字段的值的类型; + *

+ * 如果不是字段的值不是基本类型,则返回 null(即: {@link DataField#primitiveType()} 设置为 + * {@link ValueType#NIL}); + * + * @return + */ + ValueType getPrimitiveType(); + + /** + * 字段的值引用的枚举契约; + *

+ * 如果字段的值不是枚举契约类型,则返回 null; + * + * @return + */ + EnumSpecification getRefEnum(); + + /** + * 字段的值引用的数据契约; + *

+ * 如果字段的值不是数据契约类型,则返回 null; + * + * @return + */ + DataSpecification getRefContract(); + + boolean isRepeatable(); + +// /** +// * 字段的值引用的PubKey; +// *

+// * 如果字段的值不是PubKey,则返回 false; +// * +// * @return +// */ +// boolean isRefPubKey(); +// /** +// * 字段的值引用的PrivKey; +// *

+// * 如果字段的值不是PrivKey,则返回 false; +// * +// * @return +// */ +// boolean isRefPrivKey(); +// /** +// * 字段的值引用的HashDigest; +// *

+// * 如果字段的值不是HashDigest,则返回 false; +// * +// * @return +// */ +// boolean isRefHashDigest(); +// /** +// * 字段的值引用的SignatureDigest; +// *

+// * 如果字段的值不是SignatureDigest,则返回 false; +// * +// * @return +// */ +// boolean isRefSignatureDigest(); +// /** +// * 字段的值引用的BlockChainIdentity; +// *

+// * 如果字段的值不是HashDigest,则返回 false; +// * +// * @return +// */ +// boolean isRefIdentity(); +// /** +// * 字段的值引用的NetworkAddress; +// *

+// * 如果字段的值不是NetworkAddress,则返回 false; +// * +// * @return +// */ +// boolean isRefNetworkAddr(); + /** + * 最大长度;单位为“byte”; + * + * @return + * @see {@link DataField#maxSize()} + */ + int getMaxSize(); + + String getName(); + + String getDescription(); + + /** + * 是否引用了一个通用数据契约类型,实际的类型需要根据实际的对象实例来定; + * @return + */ + boolean isGenericContract(); +// Class getContractTypeResolver(); +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/BinaryEncoderImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/BinaryEncoderImpl.java new file mode 100644 index 00000000..c0c0f147 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/BinaryEncoderImpl.java @@ -0,0 +1,861 @@ +//package com.jd.blockchain.binaryproto.impl; +// +//import my.utils.ValueType; +//import my.utils.io.*; +//import my.utils.net.NetworkAddress; +// +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.OutputStream; +//import java.lang.annotation.Annotation; +//import java.lang.reflect.Constructor; +//import java.lang.reflect.Field; +//import java.lang.reflect.InvocationTargetException; +//import java.lang.reflect.Method; +//import java.util.*; +// +//import com.jd.blockchain.binaryproto.BinaryEncoder; +//import com.jd.blockchain.binaryproto.ContractTypeResolver; +//import com.jd.blockchain.binaryproto.DConstructor; +//import com.jd.blockchain.binaryproto.DataContractException; +//import com.jd.blockchain.binaryproto.DataContractRegistry; +//import com.jd.blockchain.binaryproto.DataSpecification; +//import com.jd.blockchain.binaryproto.EnumSpecification; +//import com.jd.blockchain.binaryproto.FieldSetter; +//import com.jd.blockchain.binaryproto.FieldSpec; +// +///** +// * Created by zhangshuang3 on 2018/6/21. +// */ +//public class BinaryEncoderImpl implements BinaryEncoder { +// private DataSpecification spec; +// private Class contractType; +// +// public BinaryEncoderImpl(DataSpecification spec, Class contractType) { +// this.spec = spec; +// this.contractType = contractType; +// } +// +// @Override +// public DataSpecification getSepcification() { +// return spec; +// } +// +// @Override +// public Class getContractType() { +// return contractType; +// } +// +// public void write(FieldSpec spec, OutputStream out, Object value) { +// ValueType primitive = spec.getPrimitiveType(); +// EnumSpecification refEnum = spec.getRefEnum(); +// DataSpecification refContract = spec.getRefContract(); +// boolean list = spec.isRepeatable(); +// boolean refPubKey = spec.isRefPubKey(); +// boolean refPrivKey = spec.isRefPrivKey(); +// boolean refHashDigest = spec.isRefHashDigest(); +// boolean refSignatureDigest = spec.isRefSignatureDigest(); +// boolean refIdentity = spec.isRefIdentity(); +// boolean refNetworkAddr = spec.isRefNetworkAddr(); +// Class contractTypeResolverClass = spec.getContractTypeResolver(); +// +// try { +// // basic data type +// if ((primitive != ValueType.NIL) && (list == false)) { +// writePrimitive(value, primitive, out); +// } +// // enum type +// else if (refEnum != null) { +// writeEnum(value, refEnum, out); +// } +// // ref contract +// else if (refContract != null && list == false) { +// writeContract(value, refContract, contractTypeResolverClass, out); +// } +// // array type +// else if ((primitive != ValueType.NIL) && (list == true)) { +// writePrimitiveList(value, primitive, out); +// } +// // refcontract array +// else if (refContract != null && list == true) { +// writeContractList(value, refContract, contractTypeResolverClass, out); +// } +// // HashDigest type | PubKey type | PrivKey type +// else if (refPubKey == true | refPrivKey == true | refHashDigest == true | refSignatureDigest == true) { +// writeBytesSerializeObject(value, out); +// } +// // BlockChainIdentity type +// // else if (refIdentity == true) { +// // if (obj == null) { +// // BytesEncoding.writeInNormal(null, out); +// // } +// // else { +// // BytesEncoding.writeInNormal(obj.getClass().getName().getBytes(), out); +// // BytesWriter writer = (BytesWriter)obj; +// // writer.writeTo(out); +// // } +// // } +// // NetworkAddress type +// else if (refNetworkAddr == true) { +// wirteNetworkAddressValue(value, out); +// } else { +// throw new IllegalStateException("Unexpected contract field and value!"); +// } +// } catch (InstantiationException | IllegalAccessException e) { +// throw new DataContractException(e.getMessage(), e); +// } +// +// } +// +// private void wirteNetworkAddressValue(Object value, OutputStream out) { +// if (value == null) { +// BytesEncoding.writeInNormal(null, out); +// } else { +// // write host ,port and secure flag +// NetworkAddress address = (NetworkAddress) value; +// BytesEncoding.writeInNormal(address.getHost().getBytes(), out); +// BytesUtils.writeInt(address.getPort(), out); +// BytesUtils.writeByte((byte) (address.isSecure() ? 1 : 0), out); +// } +// } +// +// private void writeBytesSerializeObject(Object value, OutputStream out) { +// if (value == null) { +// BytesEncoding.writeInNormal(null, out); +// } else { +// BytesEncoding.writeInNormal(value.getClass().getName().getBytes(), out); +// BytesSerializable serializable = (BytesSerializable) value; +// // refPubKey:1bytes algorithm code , 1byte key type, and others are raw bytes +// // refPrivKey:1bytes algorithm code , 1byte key type, and others are raw bytes +// // refHashDigest:1bytes algorithm code , and others are raw bytes +// BytesEncoding.writeInNormal(serializable.toBytes(), out); +// } +// } +// +// private void writeContractList(Object value, DataSpecification contractSpeci, Class contractTypeResolverClass, +// OutputStream out) throws InstantiationException, IllegalAccessException { +// BinaryEncoder encoder1 = null; +// Object[] refContractArray = (Object[]) value; +// if (refContractArray == null) { +// BytesUtils.writeInt(0, out); +// } else { +// BytesUtils.writeInt(refContractArray.length, out); +// if (contractTypeResolverClass.isInterface() == true) { +// encoder1 = DataContractRegistry.getEncoder(contractSpeci.getCode(), contractSpeci.getVersion()); +// if (encoder1 == null) { +// throw new DataContractException("write: get encoder null error!"); +// } +// for (Object ref : refContractArray) { +// BytesUtils.writeInt(encoder1.getSepcification().getCode(), out); +// BytesUtils.writeLong(encoder1.getSepcification().getVersion(), out); +// // record class name +// BytesEncoding.writeInNormal(ref.getClass().getName().getBytes(), out); +// encoder1.encode(ref, out); +// } +// } else { +// // TODO: 不必每次都实例化,应该对此实例建立单例缓存; +// ContractTypeResolver resolver = (ContractTypeResolver) (contractTypeResolverClass.newInstance()); +// for (Object ref : refContractArray) { +// Class subContractType = resolver.getContractType(ref, null); +// encoder1 = DataContractRegistry.register(subContractType); +// if (encoder1 == null) { +// throw new DataContractException("write: regist sub contract type failed error!"); +// } +// BytesUtils.writeInt(encoder1.getSepcification().getCode(), out); +// BytesUtils.writeLong(encoder1.getSepcification().getVersion(), out); +// // record class name +// BytesEncoding.writeInNormal(ref.getClass().getName().getBytes(), out); +// encoder1.encode(ref, out); +// encoder1 = null; +// } +// } +// } +// } +// +// private void writePrimitiveList(Object value, ValueType primitive, OutputStream out) { +// switch (primitive) { +// case BOOLEAN: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// +// boolean[] boolArray = (boolean[]) value; +// BytesUtils.writeInt(boolArray.length, out); +// for (boolean i : boolArray) { +// BytesUtils.writeByte((byte) (i ? 1 : 0), out); +// } +// break; +// case INT8: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// byte[] byteArray = (byte[]) value; +// BytesUtils.writeInt(byteArray.length, out); +// for (byte i : byteArray) { +// BytesUtils.writeByte(i, out); +// } +// break; +// case INT16: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// short[] shortArray = (short[]) value; +// BytesUtils.writeInt(shortArray.length, out); +// for (short i : shortArray) { +// byte[] bytes = BytesUtils.toBytes(i); +// BytesEncoding.writeInShort(bytes, out); +// } +// break; +// case INT32: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// int[] intArray = (int[]) value; +// BytesUtils.writeInt(intArray.length, out); +// for (int i : intArray) { +// BytesUtils.writeInt(i, out); +// } +// break; +// case INT64: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// long[] longArray = (long[]) value; +// BytesUtils.writeInt(longArray.length, out); +// for (long i : longArray) { +// BytesUtils.writeLong(i, out); +// } +// break; +// case DATETIME: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// Date[] dateArray = (Date[]) value; +// BytesUtils.writeInt(dateArray.length, out); +// long elemSeconds; +// for (Date i : dateArray) { +// elemSeconds = i.getTime(); +// BytesUtils.writeLong(elemSeconds, out); +// } +// break; +// case BYTES: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// ByteArray[] byteArrays = (ByteArray[]) value; +// BytesUtils.writeInt(byteArrays.length, out); +// for (ByteArray elem : byteArrays) { +// BytesEncoding.writeInNormal(elem.bytes(), out); +// } +// break; +// case TEXT: +// case JSON: +// case XML: +// case BIG_INT: +// case IMG: +// case VIDEO: +// case LOCATION: +// if (value == null) { +// BytesUtils.writeInt(0, out); +// break; +// } +// Object[] dynamicArray = (Object[]) value; +// BytesUtils.writeInt(dynamicArray.length, out); +// for (Object i : dynamicArray) { +// BytesEncoding.writeInNormal(i.toString().getBytes(), out); +// } +// break; +// default: +// throw new DataContractException("write: array type error!"); +// } +// } +// +// private void writeContract(Object value, DataSpecification contractSpeci, Class contractTypeResolverClass, +// OutputStream out) throws InstantiationException, IllegalAccessException { +// BinaryEncoder encoder = null; +// if (contractTypeResolverClass.isInterface() == true) { +// encoder = DataContractRegistry.getEncoder(contractSpeci.getCode(), contractSpeci.getVersion()); +// if (encoder == null) { +// throw new DataContractException("write: get encoder null error!"); +// } +// } else { +// // get sub contract type +// ContractTypeResolver resolver = (ContractTypeResolver) (contractTypeResolverClass.newInstance()); +// if (resolver == null) { +// throw new DataContractException("write: newInstance null error!"); +// } +// Class subContractType = resolver.getContractType(value, null); +// encoder = DataContractRegistry.register(subContractType); +// if (encoder == null) { +// throw new DataContractException("write: regist sub contract type failed error!"); +// } +// } +// BytesUtils.writeInt(encoder.getSepcification().getCode(), out); +// BytesUtils.writeLong(encoder.getSepcification().getVersion(), out); +// if (value == null) { +// BytesEncoding.writeInNormal(null, out); +// } else { +// // record class name +// BytesEncoding.writeInNormal(value.getClass().getName().getBytes(), out); +// encoder.encode(value, out); +// } +// } +// +// private void writeEnum(Object value, EnumSpecification enumType, OutputStream out) { +// int code = 0; +// EnumSpecificationImpl refEnumImpl = (EnumSpecificationImpl) enumType; +// Map constant = refEnumImpl.getEnumConstants(); +// for (Object enumConstant : constant.keySet()) { +// if (enumConstant.toString().equals(value.toString()) == true) { +// code = constant.get(enumConstant); +// break; +// } +// } +// switch (refEnumImpl.getValueType()) { +// case INT8: +// BytesUtils.writeByte((byte) (code & 0xff), out); +// break; +// case INT16: +// byte[] bytes = BytesUtils.toBytes((short) code); +// BytesEncoding.writeInShort(bytes, out); +// break; +// case INT32: +// BytesUtils.writeInt(code, out); +// break; +// default: +// throw new DataContractException("write: enum type error!"); +// } +// } +// +// private void writePrimitive(Object value, ValueType primitive, OutputStream out) { +// switch (primitive) { +// case BOOLEAN: +// BytesUtils.writeByte((byte) ((boolean) value ? 1 : 0), out); +// break; +// case INT8: +// BytesUtils.writeByte((byte) value, out); +// break; +// case INT16: +// // TODO 可修改为short类型 +// byte[] bytes = BytesUtils.toBytes((short) value); +// BytesEncoding.writeInShort(bytes, out); +// break; +// case INT32: +// BytesUtils.writeInt((int) value, out); +// break; +// case INT64: +// BytesUtils.writeLong((long) value, out); +// break; +// case DATETIME: +// long seconds = ((Date) value).getTime(); +// BytesUtils.writeLong(seconds, out); +// break; +// case BYTES: +// ByteArray byteArray = (ByteArray) value; +// BytesEncoding.writeInNormal(byteArray.bytes(), out); +// break; +// case TEXT: +// case JSON: +// case XML: +// case BIG_INT: +// case IMG: +// case VIDEO: +// case LOCATION: +// if (value == null) { +// BytesEncoding.writeInNormal(null, out); +// } else { +// BytesEncoding.writeInNormal(value.toString().getBytes(), out); +// } +// break; +// default: +// throw new DataContractException("write: primitive type error!"); +// } +// } +// +// @Override +// public void encode(Object data, OutputStream out) { +// DataSpecificationImpl impl = (DataSpecificationImpl) this.getSepcification(); +// List fields = impl.getFields(); +// +// try { +// for (FieldSpec spec : fields) { +// Method mth = ((FieldSpecImpl) spec).getReadMethod(); +// // mth.setAccessible(true); +// Object obj = mth.invoke(data); +// write(spec, out, obj); +// } +// } catch (SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { +// throw new DataContractException(e.getMessage(), e); +// } +// } +// +// public void read(FieldSpec spec, Method mth, InputStream in, Map fieldOutter) { +// ValueType primitive = spec.getPrimitiveType(); +// EnumSpecification refEnum = spec.getRefEnum(); +// DataSpecification refContract = spec.getRefContract(); +// boolean list = spec.isRepeatable(); +// boolean refPubKey = spec.isRefPubKey(); +// boolean refPrivKey = spec.isRefPrivKey(); +// boolean refHashDigest = spec.isRefHashDigest(); +// boolean refSignatureDigest = spec.isRefSignatureDigest(); +// boolean refIdentity = spec.isRefIdentity(); +// boolean refNetworkAddr = spec.isRefNetworkAddr(); +// +// try { +// // primitive data type +// if ((primitive != ValueType.NIL) && (list == false)) { +// switch (primitive) { +// case BOOLEAN: +// boolean boolValue = BytesUtils.readByte(in) == 1 ? true : false; +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), boolValue); +// } +// break; +// case INT8: +// byte int8Value = BytesUtils.readByte(in); +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), int8Value); +// } +// break; +// case INT16: +// short shortValue = (short) ((BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in))); +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), shortValue); +// } +// break; +// case INT32: +// int intValue = BytesUtils.readInt(in); +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), intValue); +// } +// break; +// case INT64: +// long value = BytesUtils.readLong(in); +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), value); +// } +// break; +// case DATETIME: +// long seconds = BytesUtils.readLong(in); +// Date date = new Date(seconds); +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), date); +// } +// break; +// case BYTES: +// byte[] bytes = BytesEncoding.read(NumberMask.NORMAL, in); +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), ByteArray.wrap(bytes)); +// } +// break; +// case TEXT: +// case JSON: +// case XML: +// case BIG_INT: +// case IMG: +// case VIDEO: +// case LOCATION: +// byte[] dynamicResult = BytesEncoding.read(NumberMask.NORMAL, in); +// if (dynamicResult.length == 0) { +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), null); +// } +// } else { +// StringBuffer buffer = new StringBuffer(); +// for (byte i : dynamicResult) { +// buffer.append((char) i); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), buffer.toString()); +// } +// } +// break; +// default: +// throw new DataContractException("read: primitive type error!"); +// } +// } +// // enum type +// else if (refEnum != null) { +// int code = 0; +// switch (refEnum.getValueType()) { +// case INT8: +// code = BytesUtils.readByte(in); +// break; +// case INT16: +// code = (BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in)); +// break; +// case INT32: +// code = BytesUtils.readInt(in); +// break; +// default: +// throw new DataContractException("read: enum type error!"); +// } +// EnumSpecificationImpl refEnumImpl = (EnumSpecificationImpl) refEnum; +// Map constant = refEnumImpl.getEnumConstants(); +// Object enumConstant = null; +// for (Map.Entry vo : constant.entrySet()) { +// if (vo.getValue() == code) { +// enumConstant = vo.getKey(); +// break; +// } +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), enumConstant); +// } +// } +// // ref contract type +// else if (refContract != null && list == false) { +// BinaryEncoder encoder = null; +// Object object = null; +// int code = BytesUtils.readInt(in); +// long version = BytesUtils.readLong(in); +// encoder = DataContractRegistry.getEncoder(code, version); +// if (encoder == null) { +// throw new DataContractException("read: get encoder null error!"); +// } +// byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); +// if (className.length != 0) { +// StringBuffer buffer = new StringBuffer(); +// for (byte i : className) { +// buffer.append((char) i); +// } +// object = encoder.decode(in, null, Class.forName(buffer.toString())); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), object); +// } +// } +// // array type +// else if ((primitive != ValueType.NIL) && list == true) { +// int arrayCount = BytesUtils.readInt(in); +// int i; +// +// if (arrayCount == 0) { +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), null); +// } +// } else { +// switch (primitive) { +// case BOOLEAN: +// boolean[] boolArray = new boolean[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// boolArray[i] = (BytesUtils.readByte(in) == 1) ? true : false; +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), boolArray); +// } +// break; +// case INT8: +// byte[] byteArray = new byte[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// byteArray[i] = (BytesUtils.readByte(in)); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), byteArray); +// } +// break; +// case INT16: +// short[] shortArray = new short[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// shortArray[i] = (short) ((BytesUtils.readByte(in) << 8) | (BytesUtils.readByte(in))); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), shortArray); +// } +// break; +// case INT32: +// int[] intArray = new int[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// intArray[i] = BytesUtils.readInt(in); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), intArray); +// } +// break; +// case INT64: +// long[] longArray = new long[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// longArray[i] = BytesUtils.readLong(in); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), longArray); +// } +// break; +// case DATETIME: +// Date[] dateArray = new Date[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// long seconds = BytesUtils.readLong(in); +// dateArray[i].setTime(seconds); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), dateArray); +// } +// break; +// case TEXT: +// case JSON: +// case XML: +// case BYTES: +// case BIG_INT: +// case IMG: +// case VIDEO: +// case LOCATION: +// StringBuffer[] stringBufferArray = new StringBuffer[arrayCount]; +// String[] buffer = new String[arrayCount]; +// for (i = 0; i < arrayCount; i++) { +// byte[] dynamicResult = BytesEncoding.read(NumberMask.NORMAL, in); +// stringBufferArray[i] = new StringBuffer(); +// for (byte j : dynamicResult) { +// stringBufferArray[i].append((char) j); +// } +// buffer[i] = stringBufferArray[i].toString(); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), buffer); +// } +// break; +// default: +// throw new DataContractException("read: array type error!"); +// } +// } +// } +// // ref contract type array +// else if (refContract != null && list == true) { +// int code; +// long version; +// BinaryEncoder encoder = null; +// Object[] refContractArray = null; +// int refContractArraySize = BytesUtils.readInt(in); +// if (refContractArraySize != 0) { +// refContractArray = new Object[refContractArraySize]; +// for (int i = 0; i < refContractArray.length; i++) { +// code = BytesUtils.readInt(in); +// version = BytesUtils.readLong(in); +// encoder = DataContractRegistry.getEncoder(code, version); +// if (encoder == null) { +// throw new DataContractException("read: get encoder null error!"); +// } +// byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); +// if (className.length == 0) { +// refContractArray[i] = null; +// } else { +// StringBuffer buffer = new StringBuffer(); +// for (byte var : className) { +// buffer.append((char) var); +// } +// refContractArray[i] = encoder.decode(in, null, Class.forName(buffer.toString())); +// } +// } +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), refContractArray); +// } +// } else if (refPubKey == true | refPrivKey == true | refHashDigest == true | refSignatureDigest == true) { +// Object object = null; +// byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); +// +// if (className.length != 0) { +// StringBuffer buffer = new StringBuffer(); +// for (byte var : className) { +// buffer.append((char) var); +// } +// byte[] bytes = BytesEncoding.read(NumberMask.NORMAL, in); +// object = Class.forName(buffer.toString()).getConstructor(byte[].class).newInstance(bytes); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), object); +// } +// } +// // else if (refIdentity == true) { +// // BytesReader reader = null; +// // byte[] className = BytesEncoding.read(NumberMask.NORMAL, in); +// // if (className.length != 0) { +// // StringBuffer buffer = new StringBuffer(); +// // for (byte var : className) { +// // buffer.append((char) var); +// // } +// // reader = (BytesReader)Class.forName(buffer.toString()).newInstance(); +// // reader.resolvFrom(in); +// // } +// // if (fieldOutter != null) { +// // fieldOutter.put(mth.getName(), reader); +// // } +// // } +// else if (refNetworkAddr == true) { +// NetworkAddress networkAddress = null; +// byte[] buffer = BytesEncoding.read(NumberMask.NORMAL, in); +// if (buffer.length != 0) { +// StringBuffer host = new StringBuffer(); +// for (byte var : buffer) { +// host.append((char) var); +// } +// int port = BytesUtils.readInt(in); +// boolean secure = BytesUtils.readByte(in) == 1 ? true : false; +// networkAddress = new NetworkAddress(host.toString(), port, secure); +// } +// if (fieldOutter != null) { +// fieldOutter.put(mth.getName(), networkAddress); +// } +// } +// } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | NoSuchMethodException +// | InvocationTargetException e) { +// throw new DataContractException(e.getMessage(), e); +// } +// } +// +// public boolean checkConstructor(Class concretedDataType) { +// int count = 0; +// Constructor[] constructors = concretedDataType.getConstructors(); +// for (Constructor constructor : constructors) { +// if (constructor.getDeclaredAnnotation(DConstructor.class) != null) { +// count++; +// } +// } +// if (count >= 2) { +// return false; +// } +// return true; +// } +// +// // convert the first char to lowercase +// public String toLowerCaseFirstOne(String s) { +// if (Character.isLowerCase(s.charAt(0))) +// return s; +// else +// return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString(); +// } +// +// public void executeSet(List fields, Map fieldOutter, Object object, +// Class concretedDataType) { +// Method mth; +// try { +// for (FieldSpec spec : fields) { +// mth = ((FieldSpecImpl) spec).getReadMethod(); +// if (mth.getName().length() < 4) { +// throw new DataContractException("executeSet: mth name error!"); +// } +// // skip "get",get substring +// String getName = mth.getName().substring(3); +// // "set" concat with getmthName +// String setName = "set".concat(getName); +// Method[] allMths = concretedDataType.getMethods(); +// Method setMth = null; +// for (Method x : allMths) { +// if (x.getName().equals(setName)) { +// setMth = x; +// break; +// } +// } +// if (setMth != null) { +// // invoke related set method +// Object arg = fieldOutter.get(mth.getName()); +// if (arg != null) { +// setMth.invoke(object, arg); +// } +// } +// // set related member field +// else { +// String member = toLowerCaseFirstOne(getName); +// Field field = concretedDataType.getDeclaredField(member); +// if (field != null) { +// field.setAccessible(true); +// field.set(object, fieldOutter.get(mth.getName())); +// } +// } +// } +// } catch (IllegalAccessException | InvocationTargetException | NoSuchFieldException e) { +// throw new DataContractException(e.getMessage(), e); +// } +// } +// +// @Override +// public Object decode(InputStream in, Map fieldOutter, Class concretedDataType) { +// // TODO: 未缓存对实现类的解析; +// DataSpecificationImpl impl = (DataSpecificationImpl) this.getSepcification(); +// List fields = impl.getFields(); +// Constructor constructor = null; +// Object object = null; +// Method mthType = null; +// +// if (fieldOutter == null) { +// fieldOutter = new HashMap<>(); +// } +// +// try { +// // first check constructor with annotation, count >=2 throw exception +// if (checkConstructor(concretedDataType) == false) { +// throw new DataContractException("decode: constructor with annotation number error!"); +// } +// // get constructor with annotation +// for (Constructor construct : concretedDataType.getConstructors()) { +// if (construct.getDeclaredAnnotation(DConstructor.class) != null) { +// constructor = construct; +// break; +// } +// } +// // fill fieldOutter with fieldsetter +// for (FieldSpec spec : fields) { +// Method mth = ((FieldSpecImpl) spec).getReadMethod(); +// if (mth == null) { +// throw new DataContractException("decode: mth null error!"); +// } +// read(spec, mth, in, fieldOutter); +// } +// // save constructor parameters +// if (constructor != null) { +// Annotation[][] annotations = constructor.getParameterAnnotations(); +// Object[] obj = new Object[annotations.length]; +// int i = 0; +// for (Annotation[] annoArray : annotations) { +// for (Annotation annotation : annoArray) { +// FieldSetter anno = (FieldSetter) annotation; +// obj[i] = fieldOutter.get(anno.name()); +// for (FieldSpec spec : fields) { +// mthType = ((FieldSpecImpl) spec).getReadMethod(); +// // in case :constructor and data contract method name is same ,but return type +// // is different +// if (mthType.getName().equals(anno.name())) { +// String retType = mthType.getReturnType().getSimpleName(); +// String annoType = anno.type(); +// if ((retType.equals(annoType) == false) && (retType.equals("ByteArray")) +// && (annoType.equals("byte[]"))) { +// ByteArray byteArray = (ByteArray) obj[i]; +// obj[i] = byteArray.bytes(); +// break; +// } else if ((retType.equals(annoType) == false) && (retType.equals("byte[]")) +// && (annoType.equals("ByteArray"))) { +// byte[] bytes = (byte[]) obj[i]; +// obj[i] = ByteArray.wrap(bytes); +// break; +// } +// } +// } +// i++; +// } +// } +// // exec constructor with parameters +// object = constructor.newInstance(obj); +// } +// if (object == null) { +// // use default constructor, +// constructor = concretedDataType.getDeclaredConstructor(); +// if (!constructor.isAccessible()) { +// constructor.setAccessible(true); +// } +// object = constructor.newInstance(); +// } +// // exec set method +// executeSet(fields, fieldOutter, object, concretedDataType); +// return object; +// } catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException +// | SecurityException e) { +// throw new DataContractException(e.getMessage(), e); +// } +// } +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/DataSpecificationImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/DataSpecificationImpl.java new file mode 100644 index 00000000..4276c83c --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/DataSpecificationImpl.java @@ -0,0 +1,94 @@ +//package com.jd.blockchain.binaryproto.impl; +// +//import java.lang.reflect.Method; +//import java.util.*; +// +//import com.jd.blockchain.binaryproto.BinarySliceSpec; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.DataSpecification; +//import com.jd.blockchain.binaryproto.FieldSpec; +// +///** +// * Created by zhangshuang3 on 2018/6/21. +// */ +//public class DataSpecificationImpl implements DataSpecification { +// private int code; +// private long version; +// private String name; +// private String description; +// private List fieldList = new ArrayList(); +// private List sliceList = new ArrayList<>(); +// +// +// public DataSpecificationImpl() { +// +// } +// //sort method by order id +// public Map sortMapByValues (Map mths) { +// Set> mapEntries = mths.entrySet(); +// List> aList = new LinkedList>(mapEntries); +// //sort list +// Collections.sort(aList, new Comparator>() { +// @Override +// public int compare(Map.Entry ele1, +// Map.Entry ele2) { +// return (ele1.getValue().getAnnotation(DataField.class).order()) - (ele2.getValue().getAnnotation(DataField.class).order()); +// } +// }); +// int count = aList.size(); +// //init Capacity +// Map aMap = new LinkedHashMap(count); +// for(Map.Entry entry: aList) { +// aMap.put(entry.getKey(), entry.getValue()); +// } +// return aMap; +// } +// +// @Override +// public int getCode() { +// return code; +// } +// public void setCode(int code) { +// this.code = code; +// } +// +// @Override +// public long getVersion() { +// return version; +// } +// public void setVersion(long version) { +// this.version = version; +// } +// +// @Override +// public String getName() { +// return name; +// } +// public void setName(String name) { +// this.name = name; +// } +// +// @Override +// public String getDescription() { +// return description; +// } +// public void setDescription(String description) {this.description = description;} +// +// @Override +// public List getFields() { +// return fieldList; +// } +// public void setFields(FieldSpec field) {this.fieldList.add(field);} +// +// @Override +// public List getSlices() { +// return sliceList; +// } +// public void setSlices(BinarySliceSpec slice) { +// this.sliceList.add(slice); +// } +// @Override +// public String toHtml() { +// return null; +// } +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumContractRegistry.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumContractRegistry.java new file mode 100644 index 00000000..7ac27dee --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumContractRegistry.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.binaryproto.impl; + +import java.util.HashMap; +import java.util.Map; + +import com.jd.blockchain.binaryproto.EnumSpecification; + +/** + * Created by zhangshuang3 on 2018/6/27. + */ +public class EnumContractRegistry { + private static Map enumSpecs = new HashMap(); + + public EnumContractRegistry() { + } + + public static EnumSpecification getEnumSpec(Class contractType) { + //find encoder from dataSpecs by contractType + for (String key : enumSpecs.keySet()) + { + if (key.equals(contractType.getName())) { + return enumSpecs.get(key); + } + } + return null; + } + + public static void setEnumSpecs(String key, EnumSpecification value) { + enumSpecs.put(key, value); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumSpecificationImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumSpecificationImpl.java new file mode 100644 index 00000000..5a500802 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/EnumSpecificationImpl.java @@ -0,0 +1,84 @@ +//package com.jd.blockchain.binaryproto.impl; +// +//import java.lang.reflect.Method; +//import java.util.*; +// +//import com.jd.blockchain.binaryproto.EnumSpecification; +// +//import my.utils.ValueType; +// +///** +// * Created by zhangshuang3 on 2018/6/21. +// */ +//public class EnumSpecificationImpl implements EnumSpecification { +// private int code; +// private long version; +// private String name; +// private String description; +// private ValueType item; +// private Set intSet = new LinkedHashSet<>(); +// private Set stringSet = new LinkedHashSet<>(); +// private Map readEnumConstants = new HashMap(); +// +// public EnumSpecificationImpl(int code, String name, String description) { +// this.code = code; +// this.name = name; +// this.description = description; +// } +// @Override +// public int getCode() { +// return this.code; +// } +// public void setCode(int code) { +// this.code = code; +// } +// @Override +// public long getVersion(){ +// return this.version; +// } +// public void setVersion(long version) { +// this.version = version; +// } +// @Override +// public String getName() { +// return this.name; +// } +// public void setName(String name) { +// this.name = name; +// } +// @Override +// public String getDescription() { +// return this.description; +// } +// public void setDescription(String description) { +// this.description = description; +// } +// @Override +// public ValueType getValueType() { +// return this.item; +// } +// public void setItemType(ValueType item) { +// this.item = item; +// } +// @Override +// public Set getItemValues() { +// return this.intSet; +// } +// public void setItemValues(Integer item) { +// this.intSet.add(item); +// } +// @Override +// public Set getItemNames() { +// return this.stringSet; +// } +// public void setItemNames(String item) { +// this.stringSet.add(item); +// } +// +// public Map getEnumConstants() { +// return this.readEnumConstants; +// } +// public void setEnumConstants(Object enumConstant , Integer code) { +// this.readEnumConstants.put(enumConstant, code); +// } +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/FieldSpecImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/FieldSpecImpl.java new file mode 100644 index 00000000..fd47d667 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl/FieldSpecImpl.java @@ -0,0 +1,142 @@ +//package com.jd.blockchain.binaryproto.impl; +// +// +//import org.omg.CORBA.PUBLIC_MEMBER; +// +//import com.jd.blockchain.binaryproto.DataSpecification; +//import com.jd.blockchain.binaryproto.EnumSpecification; +//import com.jd.blockchain.binaryproto.FieldSpec; +// +//import my.utils.ValueType; +// +//import java.lang.reflect.Method; +// +///** +// * Created by zhangshuang3 on 2018/6/21. +// */ +//public class FieldSpecImpl implements FieldSpec { +// private int typeCode; +// private ValueType primitiveType; +// private EnumSpecification enumSpec; +// private DataSpecification dataSpec; +// private boolean isRefPubKey; +// private boolean isRefPrivKey; +// private boolean isRefHashDigest; +// private boolean isRefSignatureDigest; +// private boolean isRefIdentity; +// private boolean isRefNetworkAddr; +// private String name; +// private String description; +// private boolean isList; +// private int maxLength; +// private Class contractTypeResolver; +// private Method readMethod; +// +// public FieldSpecImpl() { +// +// } +// public Method getReadMethod() { +// return readMethod; +// } +// public void setReadMethod(Method readMethod) { +// this.readMethod = readMethod; +// readMethod.setAccessible(true); +// } +// @Override +// public int getTypeCode() {return typeCode;} +// public void setTypeCode(int typeCode) {this.typeCode = typeCode;} +// +// @Override +// public ValueType getPrimitiveType() {return primitiveType;} +// public void setPrimitiveType(ValueType primitiveType) { +// this.primitiveType = primitiveType; +// } +// @Override +// public EnumSpecification getRefEnum() {return enumSpec;} +// public void setRefEnum(EnumSpecification enumSpec) { +// this.enumSpec = enumSpec; +// } +// +// @Override +// public DataSpecification getRefContract() {return dataSpec;} +// public void setRefContract(DataSpecification dataSpec) { +// this.dataSpec = dataSpec; +// } +// +// @Override +// public boolean isRepeatable() {return isList;} +// public void setIsList(boolean isList) {this.isList = isList;} +// +// @Override +// public int getMaxSize() {return maxLength;} +// public void setMaxLength(int length) { +// this.maxLength = maxLength; +// } +// @Override +// public String getName() {return name;} +// public void setName(String name) { +// this.name = name; +// } +// +// @Override +// public String getDescription() {return description;} +// public void setDescription(String description) { +// this.description = description; +// } +// +// @Override +// public boolean isRefPubKey() { +// return isRefPubKey; +// } +// public void setRefPubKey(boolean pubKey) { +// this.isRefPubKey = pubKey; +// } +// +// @Override +// public boolean isRefPrivKey() { +// return isRefPrivKey; +// } +// public void setRefPrivKey(boolean privKey) { +// this.isRefPrivKey = privKey; +// } +// +// @Override +// public boolean isRefSignatureDigest() { +// return isRefSignatureDigest; +// } +// public void setRefSignatureDigest(boolean signatureDigest) { +// this.isRefSignatureDigest = signatureDigest; +// } +// +// @Override +// public boolean isRefHashDigest() { +// return isRefHashDigest; +// } +// public void setRefHashDigest(boolean hashDigest) { +// this.isRefHashDigest = hashDigest; +// } +// +// @Override +// public Class getContractTypeResolver() { +// return this.contractTypeResolver; +// } +// public void setContractTypeResolver(Class resolver) { +// this.contractTypeResolver = resolver; +// } +// +// @Override +// public boolean isRefIdentity() { +// return isRefIdentity; +// } +// public void setRefIdentity(boolean identity) { +// this.isRefIdentity = identity; +// } +// +// @Override +// public boolean isRefNetworkAddr() { +// return isRefNetworkAddr; +// } +// public void setRefNetworkAddr(boolean networkAddr) { +// this.isRefNetworkAddr = networkAddr; +// } +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractDynamicValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractDynamicValueConverter.java new file mode 100644 index 00000000..d4ca0593 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractDynamicValueConverter.java @@ -0,0 +1,46 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.NumberMask; + +public abstract class AbstractDynamicValueConverter implements DynamicValueConverter { + + protected Class valueType; + + private static final NumberMask SIZE_HEAD = NumberMask.NORMAL; + + private static final byte[] NULL_HEAD = new byte[1]; + + static { + SIZE_HEAD.writeMask(0, NULL_HEAD, 0); + } + + public AbstractDynamicValueConverter(Class valueType) { + this.valueType = valueType; + } + + @Override + public Class getValueType() { + return valueType; + } + + @Override + public Object getDefaultValue() { + return null; + } + + protected int writeSize(int size, BytesOutputBuffer buffer) { + int len = SIZE_HEAD.getMaskLength(size); + byte[] headerBytes = new byte[len]; + SIZE_HEAD.writeMask(size, headerBytes, 0); + buffer.write(headerBytes); + return len; + } + + protected int readSize(BytesInputStream bytesStream) { + return SIZE_HEAD.resolveMaskedNumber(bytesStream); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractFieldEncoder.java new file mode 100644 index 00000000..6a17f8d7 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/AbstractFieldEncoder.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.FieldSpec; + +public abstract class AbstractFieldEncoder implements FieldEncoder { + + protected BinarySliceSpec sliceSpec; + + protected FieldSpec fieldSpec; + + protected Method reader; + + public AbstractFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) { + this.sliceSpec = sliceSpec; + this.fieldSpec = fieldSpec; + this.reader = reader; + } + + @Override + public BinarySliceSpec getSliceSpecification() { + return sliceSpec; + } + + @Override + public FieldSpec getFieldSpecification() { + return fieldSpec; + } + + @Override + public Method getReader() { + return reader; + } + + + protected Object readValue(Object dataContract) { + try { + return reader.invoke(dataContract); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + protected Object[] readArrayValue(Object dataContract) { + return (Object[]) readValue(dataContract); + } +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolConverter.java new file mode 100644 index 00000000..0b32e040 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolConverter.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; + +public class BoolConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return boolean.class; + } + + @Override + public Object getDefaultValue() { + return false; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + buffer[offset] = ((Boolean)value).booleanValue() ? (byte)1 : (byte)0; + return 1; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getByte() == 1 ? Boolean.TRUE : Boolean.FALSE; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolWrapperConverter.java new file mode 100644 index 00000000..54592434 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BoolWrapperConverter.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; + +public class BoolWrapperConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Boolean.class; + } + + @Override + public Object getDefaultValue() { + return null; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + buffer[offset] = ((Boolean)value).booleanValue() ? (byte)1 : (byte)0; + return 1; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getByte() == 1 ? Boolean.TRUE : Boolean.FALSE; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesSerializableValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesSerializableValueConverter.java new file mode 100644 index 00000000..c0ad5acd --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesSerializableValueConverter.java @@ -0,0 +1,55 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; + +import com.jd.blockchain.binaryproto.DataContractException; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class BytesSerializableValueConverter extends AbstractDynamicValueConverter { + + private Constructor constructor; + + public BytesSerializableValueConverter(Class valueType) { + super(valueType); + if (!BytesSerializable.class.isAssignableFrom(valueType)) { + throw new IllegalArgumentException("The specified type cann't be assigned as BytesSerializable!"); + } + // 检查是否存在以 byte[] 为参数的构造器; + try { + constructor = valueType.getConstructor(byte[].class); + constructor.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new DataContractException("No constructor with byte's array argument! --" + e.getMessage(), e); + } catch (SecurityException e) { + throw new DataContractException(e.getMessage(), e); + } + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + if (dataSlice.getSize() == 0) { + return null; + } + byte[] bytes = dataSlice.getBytesCopy(); + try { + return constructor.newInstance(bytes); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { + throw new DataContractException(e.getMessage(), e); + } + } + + @Override + public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { + byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : ((BytesSerializable) value).toBytes(); + int size = bytes.length; + size += writeSize(size, buffer); + buffer.write(bytes); + return size; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesValueConverter.java new file mode 100644 index 00000000..27664b6c --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/BytesValueConverter.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class BytesValueConverter extends AbstractDynamicValueConverter { + + public BytesValueConverter() { + super(byte[].class); + } + + @Override + public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { + byte[] bytes =value == null ? BytesUtils.EMPTY_BYTES : (byte[]) value; + int size = bytes.length; + size += writeSize(size, buffer); + + buffer.write(bytes); + return size; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getBytesCopy(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractContext.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractContext.java new file mode 100644 index 00000000..82bdf68b --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractContext.java @@ -0,0 +1,790 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataContractEncoder; +import com.jd.blockchain.binaryproto.DataContractException; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.binaryproto.DataSpecification; +import com.jd.blockchain.binaryproto.EnumContract; +import com.jd.blockchain.binaryproto.EnumField; +import com.jd.blockchain.binaryproto.EnumSpecification; +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.binaryproto.impl2.EnumSpecificationInfo.EnumConstant; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.security.SHA256Hash; +import com.jd.blockchain.utils.security.ShaUtils; + +public class DataContractContext { + + public static DataContractEncoderLookup ENCODER_LOOKUP; + + private static final Object MUTEX = new Object(); + + private static final BinarySliceSpec HEAD_SLICE = BinarySliceSpec.newFixedSlice(HeaderEncoder.HEAD_BYTES, "HEAD", + "The code and version of data contract."); + + private static final byte SINGLE_TYPE = 0; + private static final byte REPEATABLE_TYPE = 1; + + /** + * 基本类型的字段; + */ + private static final byte PRIMITIVE_TYPE_FIELD = 0; + + /** + * 枚举类型的字段; + */ + private static final byte ENUM_CONTRACT_FIELD = 1; + + /** + * 引用一个具体的数据契约类型的字段; + */ + private static final byte DATA_CONTRACT_FIELD = 2; + + /** + * 动态的数据契约类型的字段; + */ + private static final byte DYNAMIC_CONTRACT_FIELD = 3; + + private static Map codeMap = new ConcurrentHashMap<>(); + private static Map, DataContractEncoder> typeMap = new ConcurrentHashMap<>(); + + private static Map, EnumSpecification> enumContractSpecMap = new ConcurrentHashMap<>(); + + private static Map, ValueConverter>> primitiveTypeConverters = new HashMap<>(); + + static { + addConverterMapping(ValueType.BOOLEAN, boolean.class, new BoolConverter()); + addConverterMapping(ValueType.BOOLEAN, Boolean.class, new BoolWrapperConverter()); + addConverterMapping(ValueType.INT8, byte.class, new Int8ByteConverter()); + addConverterMapping(ValueType.INT8, Byte.class, new Int8ByteWrapperConverter()); + addConverterMapping(ValueType.INT16, short.class, new Int16ShortConverter()); + addConverterMapping(ValueType.INT16, Short.class, new Int16ShortWrapperConverter()); + addConverterMapping(ValueType.INT16, char.class, new Int16CharConverter()); + addConverterMapping(ValueType.INT16, Character.class, new Int16CharWrapperConverter()); + addConverterMapping(ValueType.INT32, int.class, new Int32IntConverter()); + addConverterMapping(ValueType.INT32, Integer.class, new Int32IntWrapperConverter()); + addConverterMapping(ValueType.INT64, long.class, new Int64LongConverter()); + addConverterMapping(ValueType.INT64, Long.class, new Int64LongWrapperConverter()); + addConverterMapping(ValueType.TEXT, String.class, new StringValueConverter()); + addConverterMapping(ValueType.BYTES, byte[].class, new BytesValueConverter()); + + ENCODER_LOOKUP = new DataContractEncoderLookup() { + @Override + public DataContractEncoder lookup(int code, long version) { + ContractTypeVersionContext ctx = codeMap.get(code); + if (ctx == null) { + return null; + } + // TODO: 未实现多个版本的处理; + return ctx.contractEncoder; + } + + @Override + public DataContractEncoder lookup(Class contractType) { + return typeMap.get(contractType); + } + }; + } + + private static void addConverterMapping(ValueType protocalType, Class javaType, ValueConverter converter) { + Map, ValueConverter> converterMap = primitiveTypeConverters.get(protocalType); + if (converterMap == null) { + converterMap = new HashMap<>(); + primitiveTypeConverters.put(protocalType, converterMap); + } + converterMap.put(javaType, converter); + } + + private static ValueConverter getPrimitiveTypeConverter(ValueType protocalType, Class javaType) { + Map, ValueConverter> converterMap = primitiveTypeConverters.get(protocalType); + if (converterMap != null) { + ValueConverter converter = converterMap.get(javaType); + if (converter != null) { + return converter; + } + if (ValueType.BYTES == protocalType && BytesSerializable.class.isAssignableFrom(javaType)) { + converter = new BytesSerializableValueConverter(javaType); + converterMap.put(javaType, converter); + return converter; + } + } + throw new IllegalArgumentException(String.format("Unsupport types mapping: [PrimitiveType=%s]-[JavaType=%s]", + protocalType.toString(), javaType.toString())); + } + + public static DataContractEncoder resolve(Class contractType) { + DataContractEncoder encoder = typeMap.get(contractType); + if (encoder != null) { + return encoder; + } + synchronized (MUTEX) { + encoder = typeMap.get(contractType); + if (encoder != null) { + return encoder; + } + ContractTypeVersionContext ctx = resolveContract(contractType); + encoder = ctx.contractEncoder; + } + + return encoder; + } + + /** + * 解析数据契约;
+ * + * @param contractType + * @return + */ + private static ContractTypeVersionContext resolveContract(Class contractType) { + // TODO: 未处理可能存在的循环依赖问题,这会导致解析方法陷入死循环; + if (!contractType.isInterface()) { + throw new IllegalArgumentException( + "The specified contractType [" + contractType.toString() + "] is not a interface!"); + } + DataContract annoContract = contractType.getAnnotation(DataContract.class); + if (annoContract == null) { + throw new IllegalArgumentException( + "Class[" + contractType.toString() + "] isn't annotated as DataContract!"); + } + int contractCode = annoContract.code(); + ContractTypeVersionContext ctx = codeMap.get(contractCode); + if (ctx != null) { + if (ctx.contractType == contractType) { + return ctx; + } else { + throw new IllegalStateException(String.format( + "Contract Code[%s] has been registered by type[%s]! Cann't register again with type[%s]!", + contractCode, ctx.contractType.getName(), contractType.getName())); + } + } + + DataContractEncoder contractEncoder = resolveEncoder(contractType, annoContract); + + ctx = new ContractTypeVersionContext(contractType, contractEncoder); + + codeMap.put(contractCode, ctx); + typeMap.put(contractType, contractEncoder); + + return ctx; + } + + /** + * @param contractType + * @param annoContract + * @return + */ + private static DataContractEncoder resolveEncoder(Class contractType, DataContract annoContract) { + DataContractEncoder encoder = typeMap.get(contractType); + if (encoder != null) { + return encoder; + } + + if (!contractType.isInterface()) { + throw new IllegalArgumentException( + "The registering contract type is not a interface! --" + contractType.getName()); + } + + // 解析获得数据契约的有序的字段列表; + List allFields = resolveContractFields(contractType, annoContract); + + // 解析每一个字段,生成字段的编码器和二进制片段描述符; + FieldSpecInfo[] fieldSpecs = new FieldSpecInfo[allFields.size()]; + BinarySliceSpec[] dataSliceSpecs = new BinarySliceSpec[allFields.size() + 1]; + FieldEncoder[] fieldEncoders = new FieldEncoder[allFields.size()]; + + dataSliceSpecs[0] = HEAD_SLICE; + + SHA256Hash versionHash = ShaUtils.hash_256();// 用于计算 DataContract 的版本号的哈希生成器; + int i = 0; + for (FieldDeclaredInfo fieldInfo : allFields) { + fieldSpecs[i] = fieldInfo.fieldSpec; + + // 构建二进制片段; + dataSliceSpecs[i + 1] = buildSlice(fieldInfo.fieldSpec); + + fieldEncoders[i] = buildFieldEncoder(fieldInfo, dataSliceSpecs[i + 1]); + + // 按顺序计算字段类型, + byte[] fieldType = generateFieldTypeCode(fieldInfo.fieldSpec); + versionHash.update(fieldType); + + i++; + } + + // 数据契约的版本号取自对所有字段的数据类型的哈希前 8 位; + byte[] allFieldTypesHash = versionHash.complete(); + long version = BytesUtils.toLong(allFieldTypesHash); + + HeaderEncoder headerEncoder = new HeaderEncoder(HEAD_SLICE, annoContract.code(), version, annoContract.name(), + annoContract.description()); + + DataContractSpecification spec = new DataContractSpecification(annoContract.code(), version, + annoContract.name(), annoContract.description(), dataSliceSpecs, fieldSpecs); + + DataContractEncoderImpl contractEncoder = new DataContractEncoderImpl(contractType, spec, headerEncoder, + fieldEncoders); + + return contractEncoder; + } + + /** + * 解析获得数据契约的有序的字段列表; + * + * @param contractType + * @param annoContract + * @return + */ + private static List resolveContractFields(Class contractType, DataContract annoContract) { + // 解析每一个方法,获得标注的合约字段,并按照声明的合约类型进行分组; + Map, DeclaredFieldGroup> declaredFielGroups = new HashMap<>(); + Method[] methods = contractType.getMethods(); + for (Method method : methods) { + DataField annoField = method.getAnnotation(DataField.class); + if (annoField == null) { + continue; + } + Class declaredType = method.getDeclaringClass(); + DeclaredFieldGroup group = declaredFielGroups.get(declaredType); + if (group != null) { + FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField); + group.addField(method, annoField, fieldSpec); + continue; + } + if (declaredType == contractType) { + // 字段是由当前的数据契约类型所声明; + FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField); + group = new DeclaredFieldGroup(contractType, annoContract, method, annoField, fieldSpec); + declaredFielGroups.put(contractType, group); + continue; + } + // 字段由父接口声明,所以取父接口上的标注定义进行解析; + DataContract declaredContractAnnotation = declaredType.getAnnotation(DataContract.class); + if (declaredContractAnnotation == null) { + throw new DataContractException("Declare data contract field in a non-data-contract type! --[Type=" + + declaredType.getName() + "]"); + } + + FieldSpecInfo fieldSpec = resolveFieldSpec(method, annoField); + group = new DeclaredFieldGroup(declaredType, declaredContractAnnotation, method, annoField, fieldSpec); + declaredFielGroups.put(declaredType, group); + } + + DeclaredFieldGroup[] groups = declaredFielGroups.values() + .toArray(new DeclaredFieldGroup[declaredFielGroups.size()]); + for (DeclaredFieldGroup group : groups) { + // 计算继承距离; + int extendsionDistance = computeExtendsionDistance(contractType, group.declaredContractType); + if (extendsionDistance < 0) { + // 实际不会进入此分支; + throw new IllegalStateException("Illegal state that isn't expected to occur!"); + } + group.setExtendsionDistance(extendsionDistance); + } + + // 按继承距离和数据契约的编码进行倒序排序,如果继承距离相同,则编码小的在前; + // 达到的效果:父接口声明的字段在前,子接口声明的字段在后;同一个继承级别,则编码小的在前; + Arrays.sort(groups, + (g1, g2) -> (g2.extendsionDistance == g1.extendsionDistance + ? g1.declaredContractAnnotation.code() - g2.declaredContractAnnotation.code() + : g2.extendsionDistance - g1.extendsionDistance)); + + List allFields = new ArrayList<>(); + for (DeclaredFieldGroup grp : groups) { + allFields.addAll(grp.getFields()); + } + + return allFields; + } + + /** + * 创建字段的编码器; + * + * @param fieldInfo + * @param sliceSpec + * @return + */ + private static FieldEncoder buildFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { + FieldSpecInfo fieldSpec = fieldInfo.fieldSpec; + if (fieldSpec.getPrimitiveType() != null) { + return buildPrimitiveFieldEncoder(fieldInfo, sliceSpec); + } else if (fieldSpec.getRefEnum() != null) { + return buildEnumFieldEncoder(fieldInfo, sliceSpec); + } else if (fieldSpec.getRefContract() != null) { + return buildContractFieldEncoder(fieldInfo, sliceSpec); + } else { + throw new IllegalStateException("Illegal states that has no type definition for field! --[ReadMethod=" + + fieldInfo.reader.toString() + ""); + } + } + + /** + * 创建数据契约引用字段的编码器; + * + * @param fieldInfo + * @param sliceSpec + * @return + */ + private static FieldEncoder buildContractFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { + ValueConverter valueConverter; + if (fieldInfo.fieldSpec.isGenericContract()) { + Class contractType = fieldInfo.fieldSpec.getDataType(); + valueConverter = new DataContractGenericRefConverter(contractType, ENCODER_LOOKUP); + } else { + Class contractType = fieldInfo.fieldSpec.getDataType(); + DataContractEncoder encoder = typeMap.get(contractType); + valueConverter = new DataContractValueConverter(encoder); + } + + return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter); + } + + /** + * 创建枚举类型的字段编码器; + * + * @param fieldInfo + * @param sliceSpec + * @return + */ + private static FieldEncoder buildEnumFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { + // 枚举类型的值转换器是由枚举值的范围检查加上一个基本类型的值转换器组成; + Class enumType = fieldInfo.fieldSpec.getDataType(); + EnumSpecificationInfo enumSpec = (EnumSpecificationInfo) fieldInfo.fieldSpec.getRefEnum(); + int[] values = enumSpec.getItemValues(); + Object[] constants = enumSpec.getConstants(); + ValueType codeType = enumSpec.getValueType(); + + ValueConverter baseConverter = getPrimitiveTypeConverter(codeType, enumSpec.getDataType()); + + EnumValueConverter valueConverter = new EnumValueConverter(enumType, codeType, values, constants, + (FixedValueConverter) baseConverter); + + return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter); + } + + /** + * 创建基本类型字段的编码器; + * + * @param fieldInfo + * @param sliceSpec + * @return + */ + private static FieldEncoder buildPrimitiveFieldEncoder(FieldDeclaredInfo fieldInfo, BinarySliceSpec sliceSpec) { + ValueConverter valueConverter = getPrimitiveTypeConverter(fieldInfo.fieldSpec.getPrimitiveType(), + fieldInfo.fieldSpec.getDataType()); + return createFieldEncoder(sliceSpec, fieldInfo.fieldSpec, fieldInfo.reader, valueConverter); + } + + private static FieldEncoder createFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, + ValueConverter valueConverter) { + if (sliceSpec.isRepeatable()) { + if (sliceSpec.isDynamic()) { + return new DynamicArrayFieldEncoder(sliceSpec, fieldSpec, reader, + (DynamicValueConverter) valueConverter); + } else { + return new FixedArrayFieldEncoder(sliceSpec, fieldSpec, reader, (FixedValueConverter) valueConverter); + } + } else { + if (sliceSpec.isDynamic()) { + return new DynamicFieldEncoder(sliceSpec, fieldSpec, reader, (DynamicValueConverter) valueConverter); + } else { + return new FixedFieldEncoder(sliceSpec, fieldSpec, reader, (FixedValueConverter) valueConverter); + } + } + } + + private static BinarySliceSpec buildSlice(FieldSpecInfo fieldSpec) { + boolean fixed = false; + int len = -1; + ValueType fixedValueType = null; + if (fieldSpec.getPrimitiveType() != null && fieldSpec.getPrimitiveType() != ValueType.NIL) { + fixedValueType = fieldSpec.getPrimitiveType(); + } else if (fieldSpec.getRefEnum() != null) { + fixedValueType = fieldSpec.getRefEnum().getValueType(); + } + + if (fixedValueType != null) { + switch (fixedValueType) { + case BOOLEAN: + fixed = true; + len = 1; + break; + case INT8: + fixed = true; + len = 1; + break; + case INT16: + fixed = true; + len = 2; + break; + case INT32: + fixed = true; + len = 4; + break; + case INT64: + fixed = true; + len = 8; + break; + default: + break; + } + } + if (fieldSpec.isRepeatable()) { + if (fixed) { + return BinarySliceSpec.newRepeatableFixedSlice(len, fieldSpec.getName(), fieldSpec.getDescription()); + } else { + return BinarySliceSpec.newRepeatableDynamicSlice(fieldSpec.getName(), fieldSpec.getDescription()); + } + } else { + if (fixed) { + return BinarySliceSpec.newFixedSlice(len, fieldSpec.getName(), fieldSpec.getDescription()); + } else { + return BinarySliceSpec.newDynamicSlice(fieldSpec.getName(), fieldSpec.getDescription()); + } + } + } + + private static byte[] generateFieldTypeCode(FieldSpecInfo fieldSpec) { + byte repeatable = fieldSpec.isRepeatable() ? REPEATABLE_TYPE : SINGLE_TYPE; + byte[] codeBytes; + if (fieldSpec.getPrimitiveType() != null) { + // repeatable + type indicator + code of primitive type; + // 1 + 1 + 4; + codeBytes = new byte[6]; + codeBytes[0] = repeatable; + codeBytes[1] = PRIMITIVE_TYPE_FIELD; + BytesUtils.toBytes(fieldSpec.getPrimitiveType().CODE, codeBytes, 2); + } else if (fieldSpec.getRefEnum() != null) { + // repeatable + type indicator + code of enum contract + version of enum + // contract; + // 1+ 1 + 4 + 8; + codeBytes = new byte[14]; + codeBytes[0] = repeatable; + codeBytes[1] = ENUM_CONTRACT_FIELD; + EnumSpecification enumSpec = fieldSpec.getRefEnum(); + BytesUtils.toBytes(enumSpec.getCode(), codeBytes, 2); + BytesUtils.toBytes(enumSpec.getVersion(), codeBytes, 6); + } else if (fieldSpec.getRefContract() != null) { + // repeatable + type indicator + code of enum contract + version of enum + // contract; + // 1+ 1 + 4 + 8; + DataSpecification dataSpec = fieldSpec.getRefContract(); + codeBytes = new byte[14]; + codeBytes[0] = repeatable; + if (fieldSpec.isGenericContract()) { + codeBytes[1] = DYNAMIC_CONTRACT_FIELD; + } else { + codeBytes[1] = DATA_CONTRACT_FIELD; + } + BytesUtils.toBytes(dataSpec.getCode(), codeBytes, 2); + BytesUtils.toBytes(dataSpec.getVersion(), codeBytes, 6); + } else { + throw new DataContractException("Unknow field type!"); + } + return codeBytes; + } + + /** + * 计算指定两个接口类型之间的继承距离;
+ * + * 如果不具有继承关系,则返回 -1; + * + * @param subsTypes + * 子类型; + * @param superType + * 父类型; + * @return + */ + private static int computeExtendsionDistance(Class subsTypes, Class superType) { + if (subsTypes == superType) { + return 0; + } + Class[] superIntfs = subsTypes.getInterfaces(); + for (Class si : superIntfs) { + int dis = computeExtendsionDistance(si, superType); + if (dis > -1) { + return dis + 1; + } + } + return -1; + } + + private static FieldSpecInfo resolveFieldSpec(Method accessor, DataField annoField) { + String name = annoField.name(); + name = name == null ? null : name.trim(); + if (name == null || name.length() > 0) { + name = accessor.getName(); + } + String desc = annoField.decription(); + desc = desc == null ? null : desc.trim(); + + int order = annoField.order(); + + boolean repeatable = annoField.list(); + Class dataType = accessor.getReturnType(); + + if (repeatable) { + if (!dataType.isArray()) { + throw new DataContractException("The annotated repeatable type mismatch non-array type[" + + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + dataType = dataType.getComponentType(); + } + + int maxSize = annoField.maxSize(); + ValueType primitiveType = annoField.primitiveType(); + if (primitiveType != null) { + primitiveType = verifyPrimitiveType(primitiveType, dataType, accessor); + } + + boolean refEnum = annoField.refEnum(); + EnumSpecification enumSpecification = null; + if (refEnum) { + EnumContract annoEnumContract = dataType.getAnnotation(EnumContract.class); + if (annoEnumContract == null) { + throw new DataContractException("The data type of annotated enum field is not a EnumContract! --[Field=" + + accessor.toString() + "]"); + } + enumSpecification = resolveEnumContract(dataType, annoEnumContract); + } + + boolean refContract = annoField.refContract(); + DataSpecification contractSpecification = null; + // Class contractTypeResolverClazz = null; + if (refContract) { + // DataContract annoContract = dataType.getAnnotation(DataContract.class); + // if (annoContract == null) { + // throw new DataContractException( + // "The data type of annotated contract field is not a DataContract! --[Field=" + // + accessor.toString() + "]"); + // } + + ContractTypeVersionContext contractContext = resolveContract(dataType); + DataContractEncoder encoder = contractContext.contractEncoder; + contractSpecification = encoder.getSepcification(); + // contractTypeResolverClazz = annoField.contractTypeResolver(); + // if (contractTypeResolverClazz != null + // && (!ContractTypeResolver.class.isAssignableFrom(contractTypeResolverClazz))) + // { + // throw new DataContractException( + // "The contract type resolver of contract field doesn't implement + // ContractTypeResolver interface! --[Field=" + // + accessor.toString() + "]"); + // } + } + + if (primitiveType == null && enumSpecification == null && contractSpecification == null) { + throw new DataContractException( + "Miss data type definition of field! --[Field=" + accessor.toString() + "]"); + } + + FieldSpecInfo fieldSpec = null; + if (primitiveType != null) { + fieldSpec = new FieldSpecInfo(order, name, desc, primitiveType, repeatable, maxSize, dataType); + } else if (enumSpecification != null) { + fieldSpec = new FieldSpecInfo(order, name, desc, enumSpecification, repeatable, dataType); + } else { + fieldSpec = new FieldSpecInfo(order, name, desc, contractSpecification, repeatable, dataType, + annoField.genericContract()); + } + return fieldSpec; + } + + private static EnumSpecification resolveEnumContract(Class dataType, EnumContract annoEnumContract) { + EnumSpecificationInfo enumSpec = (EnumSpecificationInfo) enumContractSpecMap.get(dataType); + if (enumSpec != null) { + return enumSpec; + } + try { + if (!dataType.isEnum()) { + throw new DataContractException("Field's type is not a enum type! --[" + dataType.toString() + "]"); + } + // TODO:暂时硬编码检索 code 字段; + Field codeField = dataType.getField("CODE"); + if (codeField == null) { + throw new DataContractException("Enum type miss the 'CODE' field! --[" + dataType.toString() + "]"); + } + EnumField fieldAnno = codeField.getAnnotation(EnumField.class); + if (fieldAnno == null) { + throw new DataContractException("Enum's 'CODE' field is not annotated with @EnumField !"); + } + // TODO: 暂时未实现枚举契约的版本号计算; + long version = 0; + enumSpec = new EnumSpecificationInfo(fieldAnno.type(), annoEnumContract.code(), version, + annoEnumContract.name(), annoEnumContract.decription(), codeField.getType()); + // get enum constants and CODE + Object[] enumItems = dataType.getEnumConstants(); + for (Object item : enumItems) { + int code = codeField.getInt(item); + EnumConstant constant = new EnumConstant(code, item.toString(), item); + enumSpec.addConstant(constant); + // enumSpec.setVersion(); if need + } + enumContractSpecMap.put(dataType, enumSpec); + return enumSpec; + } catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { + throw new DataContractException(e.getMessage(), e); + } + } + + /** + * 解析指定的类型是否匹配;
+ * + * 要求必须是显式地声明类型,因此不会根据 Java 语言的声明类型做自动转换; + * + * @param primitiveType + * @param dataType + * @return + */ + private static ValueType verifyPrimitiveType(ValueType primitiveType, Class dataType, Method accessor) { + switch (primitiveType) { + case NIL: + return null; + case BOOLEAN: + if (dataType != Boolean.class && dataType != boolean.class) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + case INT8: + if (dataType != Byte.class && dataType != byte.class) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + case INT16: + if (dataType != Character.class && dataType != char.class && dataType != short.class) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + case INT32: + if (dataType != Integer.class && dataType != int.class) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + case INT64: + if (dataType != Long.class && dataType != long.class) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + case TEXT: + if (dataType != String.class) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + case BYTES: + if (dataType != byte[].class && (!BytesSerializable.class.isAssignableFrom(dataType))) { + throw new DataContractException("The annotated primitive type[" + primitiveType.toString() + + "] mismatch java type[" + dataType.getName() + "]! --[Field=" + accessor.toString() + "]"); + } + break; + default: + throw new DataContractException("Unsupported primitive type[" + primitiveType.toString() + "] ! --[Field=" + + accessor.toString() + "]"); + } + + return primitiveType; + } + + private static class DeclaredFieldGroup { + + /** + * 声明的合约类型; + */ + public Class declaredContractType; + + /** + * + */ + public DataContract declaredContractAnnotation; + + /** + * 声明类型距离要解析的类型的继承距离;直接继承的父接口的距离为 1,父接口的父接口为 2,以此类推; + */ + private int extendsionDistance; + + private TreeMap orderedFields = new TreeMap<>(); + + public Collection getFields() { + return orderedFields.values(); + } + + public DeclaredFieldGroup(Class declaredContractType, DataContract declaredContractAnnotation, + Method accessor, DataField fieldAnnotation, FieldSpecInfo fieldSpec) { + this.declaredContractType = declaredContractType; + this.declaredContractAnnotation = declaredContractAnnotation; + + addField(accessor, fieldAnnotation, fieldSpec); + } + + private void addField(Method accessor, DataField fieldAnnotation, FieldSpecInfo fieldSpec) { + // 检查字段的是否有重复序号; + FieldDeclaredInfo fieldInfo = new FieldDeclaredInfo(accessor, fieldAnnotation, fieldSpec); + FieldDeclaredInfo conflictedField = orderedFields.put(fieldSpec.getOrder(), fieldInfo); + if (conflictedField != null) { + // 有两个字段都声明了相同的序号,这容易导致无序状态; + throw new DataContractException(String.format("Declare two fields with the same order! --[%s][%s]", + fieldInfo.reader.toString(), conflictedField.reader.toString())); + } + } + + @SuppressWarnings("unused") + public int getExtendsionDistance() { + return extendsionDistance; + } + + public void setExtendsionDistance(int extendsionDistance) { + this.extendsionDistance = extendsionDistance; + } + + } + + private static class FieldDeclaredInfo { + public FieldSpecInfo fieldSpec; + + public Method reader; + + public DataField annoField; + + public FieldDeclaredInfo(Method accessor, DataField annoField, FieldSpecInfo fieldSpec) { + this.reader = accessor; + this.annoField = annoField; + this.fieldSpec = fieldSpec; + } + } + + private static class ContractTypeVersionContext { + + public Class contractType; + + public DataContractEncoder contractEncoder; + + // TODO:未实现多版本; + // private HashMap versionMap = new HashMap<>(); + + public ContractTypeVersionContext(Class contractType, DataContractEncoder encoder) { + this.contractType = contractType; + this.contractEncoder = encoder; + } + + } +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderImpl.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderImpl.java new file mode 100644 index 00000000..2c9c40b6 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderImpl.java @@ -0,0 +1,133 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import com.jd.blockchain.binaryproto.DataContractEncoder; +import com.jd.blockchain.binaryproto.DataSpecification; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; + +public class DataContractEncoderImpl implements DataContractEncoder { + + private Class[] contractTypeArray; + + private Class contractType; + + private DataSpecification specification; + + private HeaderEncoder headEncoder; + + private FieldEncoder[] fieldEncoders; + + // 字段的 Get 方法与编码器的映射表; + private Map fieldIndexMap; + + /** + * @param contractType + * @param specification + * @param headEncoder + * 头部编码器; + * @param fieldEncoders + * 按顺序排列的字段编码器列表; + */ + public DataContractEncoderImpl(Class contractType, DataSpecification specification, HeaderEncoder headEncoder, + FieldEncoder[] fieldEncoders) { + this.contractType = contractType; + this.contractTypeArray = new Class[] { contractType }; + this.specification = specification; + this.headEncoder = headEncoder; + this.fieldEncoders = fieldEncoders; + this.fieldIndexMap = new HashMap<>(); + int i = 0; + for (FieldEncoder fieldEncoder : fieldEncoders) { + fieldIndexMap.put(fieldEncoder.getReader(), i); + i++; + } + } + + HeaderEncoder getHeaderEncoder() { + return headEncoder; + } + + Class[] getContractTypeAsArray() { + return contractTypeArray; + } + + int getFieldCount() { + return fieldEncoders.length; + } + + FieldEncoder getFieldEncoder(int id) { + return fieldEncoders[id]; + } + + /** + * 通过字段的声明方法返回字段的序号; + * + * @param declaredMethod + * 声明并标注为数据契约字段的方法;注:不能是覆盖的非标注方法,也不能是实现方法; + * @return 字段序号; 如果不存在,则返回 -1; + */ + int getFieldId(Method declaredMethod) { + Integer id = fieldIndexMap.get(declaredMethod); + return id == null ? -1 : id.intValue(); + } + + /** + * 通过字段的声明方法返回字段的编码器; + * + * @param declaredMethod + * 声明并标注为数据契约字段的方法;注:不能是覆盖的非标注方法,也不能是实现方法; + * @return + */ + FieldEncoder getFieldEncoder(Method declaredMethod) { + Integer idx = fieldIndexMap.get(declaredMethod); + if (idx == null) { + return null; + } + return fieldEncoders[idx.intValue()]; + } + + @Override + public DataSpecification getSepcification() { + return specification; + } + + @Override + public Class getContractType() { + return contractType; + } + + @Override + public int encode(Object dataContract, BytesOutputBuffer buffer) { + int size = 0; + size += headEncoder.encode(dataContract, buffer); + if (dataContract != null) { + for (SliceEncoder sliceEncoder : fieldEncoders) { + size += sliceEncoder.encode(dataContract, buffer); + } + } + return size; + } + + @SuppressWarnings("unchecked") + @Override + public T decode(BytesInputStream bytesStream) { + BytesSlice headerSlice = bytesStream.getSlice(HeaderEncoder.HEAD_BYTES); + if (!headEncoder.verifyHeaders(headerSlice)) { + throw new IllegalArgumentException(String.format( + "The code and version resolved from bytes stream is not match this data contract encoder! --[expected=%s, %s][actual=%s, %s].", + headEncoder.getCode(), headEncoder.getVersion(), HeaderEncoder.resolveCode(headerSlice), + HeaderEncoder.resolveVersion(headerSlice))); + } + if (bytesStream.getSize() == HeaderEncoder.HEAD_BYTES) { + // 只有头部,没有值,表示空值; + return null; + } + return (T) DynamicDataContract.createContract(bytesStream, this); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderLookup.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderLookup.java new file mode 100644 index 00000000..41de58b0 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractEncoderLookup.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.binaryproto.DataContractEncoder; + +public interface DataContractEncoderLookup { + + /** + * 检索指定类型的编码器; + * + * @param contractType + * @return + */ + DataContractEncoder lookup(Class contractType); + + /** + * 检索指定 code 和 version 的编码器; + * + * @param contractType + * @return + */ + DataContractEncoder lookup(int code, long version); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractGenericRefConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractGenericRefConverter.java new file mode 100644 index 00000000..a87c4daa --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractGenericRefConverter.java @@ -0,0 +1,94 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataContractEncoder; +import com.jd.blockchain.binaryproto.DataContractException; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; + +public class DataContractGenericRefConverter extends AbstractDynamicValueConverter { + + private final Object mutex = new Object(); + + private DataContractEncoderLookup encoderLookup; + + private Map, DataContractEncoder> encoderCache; + + public DataContractGenericRefConverter(Class baseType, DataContractEncoderLookup encoderLookup) { + super(baseType); + this.encoderLookup = encoderLookup; + this.encoderCache = new ConcurrentHashMap<>(); + } + + private DataContractEncoder lookupEncoder(Class dataObjectType) { + DataContractEncoder encoder = encoderCache.get(dataObjectType); + if (encoder != null) { + return encoder; + } + synchronized (mutex) { + encoder = encoderCache.get(dataObjectType); + if (encoder != null) { + return encoder; + } + Class[] intfs = dataObjectType.getInterfaces(); + Class contractType = null; + DataContract anno = null; + for (Class itf : intfs) { + anno = itf.getAnnotation(DataContract.class); + if (anno != null) { + if (contractType == null) { + contractType = itf; + } else { + throw new DataContractException(String.format( + "Data object implements more than one DataContract interface! --[DataObject=%s]", + dataObjectType.toString())); + } + } + } + if (contractType == null) { + throw new DataContractException( + String.format("Data object doesn't implement any DataContract interface! --[DataObject=%s]", + dataObjectType.toString())); + } + + encoder = encoderLookup.lookup(contractType); + if (encoder == null) { + throw new DataContractException(String.format( + "DataContract of the specified data object hasn't been registered! --[DataContract=%s][DataObject=%s]", + contractType.toString(), dataObjectType.toString())); + } + encoderCache.put(dataObjectType, encoder); + } + + return encoder; + } + + @Override + public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { + DataContractEncoder contractEncoder = lookupEncoder(value.getClass()); + + BytesOutputBuffer contractBuffer = new BytesOutputBuffer(); + int size = contractEncoder.encode(value, contractBuffer); + + size += writeSize(size, buffer); + + buffer.write(contractBuffer); + return size; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + int code = HeaderEncoder.resolveCode(dataSlice); + long version = HeaderEncoder.resolveVersion(dataSlice); + DataContractEncoder contractEncoder = encoderLookup.lookup(code, version); + if (contractEncoder == null) { + throw new DataContractException( + String.format("No data contract was registered with code[%s] and version[%s]!", code, version)); + } + return contractEncoder.decode(dataSlice.getInputStream()); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractHeader.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractHeader.java new file mode 100644 index 00000000..33700b65 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractHeader.java @@ -0,0 +1,28 @@ +//package com.jd.blockchain.binaryproto.impl2; +// +//public class DataContractHeader { +// +// private int code; +// +// private int version; +// +// private String name; +// +// private String description; +// +// public DataContractHeader(int contractCode, int contractVersion, String name, String description) { +// this.code = contractCode; +// this.version = contractVersion; +// this.name = name ; +// this.description = description; +// } +// +// public int getContractVersion() { +// return version; +// } +// +// public int getContractCode() { +// return code; +// } +// +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractSpecification.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractSpecification.java new file mode 100644 index 00000000..7bdb2b65 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractSpecification.java @@ -0,0 +1,65 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.DataSpecification; +import com.jd.blockchain.binaryproto.FieldSpec; + +public class DataContractSpecification implements DataSpecification { + + private int code; + private long version; + private String name; + private String description; + + private List fieldList; + private List sliceList; + + public DataContractSpecification(int code, long version, String name, String description, BinarySliceSpec[] slices, FieldSpec[] fields) { + this.code = code; + this.version = version; + this.name = name; + this.description = description; + this.fieldList = Collections.unmodifiableList(Arrays.asList(fields)); + this.sliceList = Collections.unmodifiableList(Arrays.asList(slices)); + } + + @Override + public int getCode() { + return code; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public List getFields() { + return fieldList; + } + + @Override + public List getSlices() { + return sliceList; + } + + @Override + public String toHtml() { + throw new IllegalStateException("Not implemented!"); + } +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractValueConverter.java new file mode 100644 index 00000000..e9d9d401 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DataContractValueConverter.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.binaryproto.DataContractEncoder; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; + +public class DataContractValueConverter extends AbstractDynamicValueConverter { + + private DataContractEncoder contractEncoder; + + public DataContractValueConverter(DataContractEncoder contractEncoder) { + super(contractEncoder.getContractType()); + this.contractEncoder =contractEncoder; + } + + @Override + public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { + BytesOutputBuffer contractBuffer = new BytesOutputBuffer(); + int size = contractEncoder.encode(value, contractBuffer); + + size += writeSize(size, buffer); + + buffer.write(contractBuffer); + return size; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return contractEncoder.decode(dataSlice.getInputStream()); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicArrayFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicArrayFieldEncoder.java new file mode 100644 index 00000000..06d8b987 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicArrayFieldEncoder.java @@ -0,0 +1,76 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesSlices; +import com.jd.blockchain.utils.io.DynamicBytesSliceArray; +import com.jd.blockchain.utils.io.NumberMask; + +public class DynamicArrayFieldEncoder extends AbstractFieldEncoder { + + private DynamicValueConverter valueConverter; + + public DynamicArrayFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, + DynamicValueConverter valueConverter) { + super(sliceSpec, fieldSpec, reader); + this.valueConverter = valueConverter; + } + + @Override + public int encode(Object dataContract, BytesOutputBuffer buffer) { + int size = 0; + Object[] values = readArrayValue(dataContract); + size += encodeArrayDynamic(values, buffer); + + return size; + } + + /** + * 对数组类型的值进行非固定长度的编码; + * + * @param values + * @param buffer + * @return + */ + private int encodeArrayDynamic(Object[] values, BytesOutputBuffer buffer) { + int size = 0; + + int count = values == null ? 0 : values.length; + byte[] countBytes = NumberMask.NORMAL.generateMask(count); + buffer.write(countBytes); + size += countBytes.length; + + for (int i = 0; i < count; i++) { + size += valueConverter.encodeDynamicValue(values[i], buffer); + } + + return size; + } + + @Override + public BytesSlices decode(BytesInputStream bytesStream) { + return DynamicBytesSliceArray.resolve(bytesStream); + } + + @Override + public Object decodeField(BytesSlices fieldBytes) { + Object[] values = (Object[]) Array.newInstance(valueConverter.getValueType(), fieldBytes.getCount()); + BytesSlice itemSlice; + for (int i = 0; i < values.length; i++) { + itemSlice = fieldBytes.getDataSlice(i); + if (itemSlice.getSize() == 0) { + values[i] = valueConverter.getDefaultValue(); + } else { + values[i] = valueConverter.decodeValue(itemSlice); + } + } + return values; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicDataContract.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicDataContract.java new file mode 100644 index 00000000..01240304 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicDataContract.java @@ -0,0 +1,86 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesSlices; + +class DynamicDataContract implements InvocationHandler { + + public static Method METHOD_GET_CLASS; + + static { + try { + METHOD_GET_CLASS = Object.class.getMethod("getClass"); + } catch (NoSuchMethodException | SecurityException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private DataContractEncoderImpl contractEncoder; + +// private BytesSlice contractBytes; + + // 字段的数据片段列表,首个是 HeaderSlice,其次是按字段顺序排列的数据片段; + private BytesSlices[] dataSlices; + + private DynamicDataContract(BytesInputStream bytesStream, DataContractEncoderImpl contractEncoder) { + this.contractEncoder = contractEncoder; + + init(bytesStream); + } + + private void init(BytesInputStream bytesStream) { + // 解析出所有的数据片段; + dataSlices = new BytesSlices[contractEncoder.getFieldCount() + 1]; + + dataSlices[0] = contractEncoder.getHeaderEncoder().decode(bytesStream); + + for (int i = 1; i < dataSlices.length; i++) { + dataSlices[i] = contractEncoder.getFieldEncoder(i - 1).decode(bytesStream); + } + } + + @SuppressWarnings("unchecked") + public static T createContract(BytesInputStream bytesStream, DataContractEncoderImpl contractEncoder) { + return (T) Proxy.newProxyInstance(contractEncoder.getContractType().getClassLoader(), + contractEncoder.getContractTypeAsArray(), new DynamicDataContract(bytesStream, contractEncoder)); + } + + @SuppressWarnings("unchecked") + public static T createContract(byte[] contractBytes, DataContractEncoderImpl contractEncoder) { + return (T) Proxy.newProxyInstance(contractEncoder.getContractType().getClassLoader(), + contractEncoder.getContractTypeAsArray(), + new DynamicDataContract(new BytesInputStream(contractBytes, 0, contractBytes.length), contractEncoder)); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + int fieldId = contractEncoder.getFieldId(method); + if (fieldId > -1) { + FieldEncoder encoder = contractEncoder.getFieldEncoder(fieldId); + return encoder.decodeField(dataSlices[fieldId + 1]); + } + if (METHOD_GET_CLASS == method) { + return contractEncoder.getContractType(); + } + // invoke method declared in type Object; + Object result; + try { + //for some special case, interface's method without annotation + result = method.invoke(this, args); + } catch (Exception e) { + if (method.getReturnType().isPrimitive()) { + result = 0; + } + else { + result = null; + } + e.printStackTrace(); + } + return result; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicFieldEncoder.java new file mode 100644 index 00000000..ea70b10c --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicFieldEncoder.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Method; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlices; +import com.jd.blockchain.utils.io.SingleBytesSliceArray; + +public class DynamicFieldEncoder extends AbstractFieldEncoder { + + private DynamicValueConverter valueConverter; + + public DynamicFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, + DynamicValueConverter valueConverter) { + super(sliceSpec, fieldSpec, reader); + this.valueConverter = valueConverter; + } + + @Override + public int encode(Object dataContract, BytesOutputBuffer buffer) { + int size = 0; + Object value = readValue(dataContract); + size += valueConverter.encodeDynamicValue(value, buffer); + + return size; + } + + @Override + public BytesSlices decode(BytesInputStream bytesStream) { + return SingleBytesSliceArray.resolveDynamic(bytesStream); + } + + @Override + public Object decodeField(BytesSlices fieldBytes) { + // 非数组的字段,最多只有一个数据片段; + if (fieldBytes.getCount() == 0) { + return valueConverter.getDefaultValue(); + } + return valueConverter.decodeValue(fieldBytes.getDataSlice(0)); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicValueConverter.java new file mode 100644 index 00000000..81d18c63 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/DynamicValueConverter.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; + +public interface DynamicValueConverter extends ValueConverter { + + /** + * 写入一个动态长度的值;以一个头部的长度字节开始 + * @param value + * @param buffer + * @return + */ + int encodeDynamicValue(Object value, BytesOutputBuffer buffer); + + Object decodeValue(BytesSlice dataSlice); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumSpecificationInfo.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumSpecificationInfo.java new file mode 100644 index 00000000..a93d064d --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumSpecificationInfo.java @@ -0,0 +1,127 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.util.LinkedHashSet; +import java.util.Set; + +import com.jd.blockchain.binaryproto.EnumSpecification; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/6/21. + */ +public class EnumSpecificationInfo implements EnumSpecification { + + private ValueType valueType; + + private Class dataType; + + private int code; + private long version; + private String name; + private String description; + + private Set items = new LinkedHashSet<>(); + // private Map itemCodeMapping = new HashMap<>(); + // private Map codeItemMapping = new HashMap<>(); + + public EnumSpecificationInfo(ValueType valueType, int code, long version, String name, String description, Class dataType) { + this.valueType = valueType; + this.code = code; + this.version = version; + this.name = name; + this.description = description; + this.dataType = dataType; + } + + @Override + public int getCode() { + return this.code; + } + + @Override + public long getVersion() { + return this.version; + } + + @Override + public String getName() { + return this.name; + } + + @Override + public String getDescription() { + return this.description; + } + + @Override + public ValueType getValueType() { + return this.valueType; + } + + @Override + public int[] getItemValues() { + int[] values = new int[items.size()]; + int i = 0; + for (EnumConstant it : items) { + values[i] = it.code; + i++; + } + return values; + } + + @Override + public String[] getItemNames() { + String[] names = new String[items.size()]; + int i = 0; + for (EnumConstant it : items) { + names[i] = it.name; + i++; + } + return names; + } + + public Class getDataType() { + return dataType; + } + + public Object[] getConstants() { + Object[] constants = new Object[items.size()]; + int i = 0; + for (EnumConstant it : items) { + constants[i] = it.constant; + i++; + } + return constants; + } + + public void addConstant(EnumConstant item) { + items.add(item); + } + + public static class EnumConstant { + + private int code; + + private String name; + + private Object constant; + + public int getCode() { + return code; + } + + public String getName() { + return name; + } + + public Object getConstant() { + return constant; + } + + public EnumConstant(int code, String name, Object constant) { + this.code = code; + this.name = name; + this.constant = constant; + } + } +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumValueConverter.java new file mode 100644 index 00000000..b3c621aa --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/EnumValueConverter.java @@ -0,0 +1,103 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.binaryproto.DataContractException; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.io.BytesSlice; + +public class EnumValueConverter implements FixedValueConverter { + + private Class enumType; + + private ValueType codeType; + + private int[] values; + + private Object[] constants; + + private FixedValueConverter valueConverter; + + public EnumValueConverter(Class enumType, ValueType codeType, int[] values, Object[] constants, FixedValueConverter valueConverter) { + this.enumType = enumType; + this.values = values; + this.constants = constants; + this.valueConverter = valueConverter; + this.codeType = codeType; + + } + + @Override + public Class getValueType() { + return enumType; + } + + @Override + public Object getDefaultValue() { + return null; + } + + //lookup CODE value + private Object getEnumCode(Object value, int[] codes, Object[] constants) { + int codeIndex = 0; + + for (int i = 0; i < constants.length; i++) { + if (value.toString().equals(constants[i].toString())) { + codeIndex = i; + break; + } + } + switch (codeType) { + case INT8: + return (byte)codes[codeIndex]; + case INT16: + return (short)codes[codeIndex]; + case INT32: + return codes[codeIndex]; + default: + throw new DataContractException(String.format("Enum code error!")); + } + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + // 注:由于是通过反射调用的,已经在外围做了检查,此处不需要重复检查枚举值的范围; + //首先把枚举常量转换成对应的CODE + Object code = getEnumCode(value, values, constants); + return valueConverter.encodeValue(code, buffer, offset); + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + + Object v = valueConverter.decodeValue(dataSlice); + switch (codeType) { + case INT8: + for (int i = 0; i < values.length; i++) { + if ((byte)values[i] == (byte)v) { + return constants[i]; + } + } + throw new DataContractException(String.format( + "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString())); + case INT16: + for (int i = 0; i < values.length; i++) { + if ((short)values[i] == (short)v) { + return constants[i]; + } + } + throw new DataContractException(String.format( + "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString())); + case INT32: + for (int i = 0; i < values.length; i++) { + if ((int)values[i] == (int)v) { + return constants[i]; + } + } + throw new DataContractException(String.format( + "The decoding value is out of all enum constants! --[value=%s][enum type=%s]", v, enumType.toString())); + default: + throw new DataContractException(String.format("Enum code error!")); + + } + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldEncoder.java new file mode 100644 index 00000000..404b9334 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldEncoder.java @@ -0,0 +1,16 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Method; + +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.utils.io.BytesSlices; + +public interface FieldEncoder extends SliceEncoder { + + Method getReader(); + + FieldSpec getFieldSpecification(); + + Object decodeField(BytesSlices fieldBytes); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldSpecInfo.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldSpecInfo.java new file mode 100644 index 00000000..f682a57b --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FieldSpecInfo.java @@ -0,0 +1,121 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.binaryproto.DataSpecification; +import com.jd.blockchain.binaryproto.EnumSpecification; +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.utils.ValueType; + +public class FieldSpecInfo implements FieldSpec { + + private int order; + + private String name; + + private String description; + + private boolean repeatable; + + private ValueType primitiveType; + + private EnumSpecification enumSpec; + + private DataSpecification contractTypeSpec; + + private int maxSize; + + private Class dataType; + + private boolean isGenericContract = false; + + public FieldSpecInfo(int order, String name, String decription, ValueType primitiveType, boolean repeatable, + int maxSize, Class dataType) { + if (primitiveType == null) { + throw new IllegalArgumentException("primitiveType is null!"); + } + this.order = order; + this.name = name; + this.description = decription; + this.primitiveType = primitiveType; + this.repeatable = repeatable; + this.maxSize = maxSize; + this.dataType = dataType; + } + + public FieldSpecInfo(int order, String name, String decription, EnumSpecification enumSpec, boolean repeatable, + Class enumType) { + if (enumSpec == null) { + throw new IllegalArgumentException("enum specification is null!"); + } + this.order = order; + this.name = name; + this.description = decription; + this.enumSpec = enumSpec; + this.repeatable = repeatable; + this.maxSize = -1; + this.dataType = enumType; + } + + public FieldSpecInfo(int order, String name, String decription, DataSpecification contractTypeSpec, + boolean repeatable, Class contractType, boolean isGenericContract) { + if (contractTypeSpec == null) { + throw new IllegalArgumentException("contractType is null!"); + } + this.order = order; + this.name = name; + this.description = decription; + this.contractTypeSpec = contractTypeSpec; + this.repeatable = repeatable; + this.maxSize = -1; + this.dataType = contractType; + this.isGenericContract = isGenericContract; + } + + @Override + public ValueType getPrimitiveType() { + return primitiveType; + } + + @Override + public EnumSpecification getRefEnum() { + return enumSpec; + } + + @Override + public DataSpecification getRefContract() { + return contractTypeSpec; + } + + @Override + public boolean isRepeatable() { + return repeatable; + } + + @Override + public int getMaxSize() { + return maxSize; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getDescription() { + return description; + } + + @Override + public boolean isGenericContract() { + return isGenericContract; + } + + public int getOrder() { + return order; + } + + public Class getDataType() { + return dataType; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedArrayFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedArrayFieldEncoder.java new file mode 100644 index 00000000..43341d2b --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedArrayFieldEncoder.java @@ -0,0 +1,79 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Array; +import java.lang.reflect.Method; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesSlices; +import com.jd.blockchain.utils.io.FixedBytesSliceArray; +import com.jd.blockchain.utils.io.NumberMask; + +public class FixedArrayFieldEncoder extends AbstractFieldEncoder { + + private FixedValueConverter valueConverter; + + public FixedArrayFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, FixedValueConverter valueConverter) { + super(sliceSpec, fieldSpec, reader); + this.valueConverter = valueConverter; + } + + @Override + public int encode(Object dataContract, BytesOutputBuffer buffer) { + int size = 0; + + Object[] values = readArrayValue(dataContract); + size += encodeArray(values, buffer); + + return size; + } + + /** + * 对数组类型的值进行固定长度的编码; + * + * @param values + * @param buffer + * @return + */ + private int encodeArray(Object[] values, BytesOutputBuffer buffer) { + int count = values == null ? 0 : values.length; + + int counterSize = NumberMask.NORMAL.getMaskLength(count); + int elementSize = sliceSpec.getLength(); + + int size = counterSize + elementSize * count; + byte[] outbuff = new byte[size]; + NumberMask.NORMAL.writeMask(count, outbuff, 0); + + for (int i = 0; i < count; i++) { + valueConverter.encodeValue(values[i], outbuff, counterSize + elementSize * i); + } + + buffer.write(outbuff); + return size; + } + + @Override + public BytesSlices decode(BytesInputStream bytesStream) { + return FixedBytesSliceArray.resolve(bytesStream, sliceSpec.getLength()); + } + + @Override + public Object decodeField(BytesSlices fieldBytes) { + Object[] values = (Object[]) Array.newInstance(valueConverter.getValueType(), fieldBytes.getCount()); + BytesSlice itemSlice; + for (int i = 0; i < values.length; i++) { + itemSlice = fieldBytes.getDataSlice(i); + if (itemSlice.getSize() == 0) { + values[i] = valueConverter.getDefaultValue(); + } else { + values[i] = valueConverter.decodeValue(itemSlice); + } + } + return values; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedFieldEncoder.java new file mode 100644 index 00000000..d1d8d9d6 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedFieldEncoder.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.binaryproto.impl2; + +import java.lang.reflect.Method; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.binaryproto.FieldSpec; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlices; +import com.jd.blockchain.utils.io.SingleBytesSliceArray; + +public class FixedFieldEncoder extends AbstractFieldEncoder { + + private FixedValueConverter valueConverter; + + public FixedFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader, FixedValueConverter valueConverter) { + super(sliceSpec, fieldSpec, reader); + this.valueConverter = valueConverter; + } + + @Override + public int encode(Object dataContract, BytesOutputBuffer buffer) { + int size = 0; + Object value = readValue(dataContract); + size += encodeValue(value, buffer); + + return size; + } + + /** + * 把固定长度的值序列化到指定的缓冲区; + * + * @param value + * @param buffer + * @return + */ + private int encodeValue(Object value, BytesOutputBuffer buffer) { + byte[] valueBytes = new byte[sliceSpec.getLength()]; + int size = valueConverter.encodeValue(value, valueBytes, 0); + buffer.write(valueBytes); + return size; + } + + @Override + public BytesSlices decode(BytesInputStream bytesStream) { + return SingleBytesSliceArray.create(bytesStream, sliceSpec.getLength()); + } + + @Override + public Object decodeField(BytesSlices fieldBytes) { + // 非数组的字段,最多只有一个数据片段; + if (fieldBytes.getCount() == 0) { + return valueConverter.getDefaultValue(); + } + return valueConverter.decodeValue(fieldBytes.getDataSlice(0)); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedValueConverter.java new file mode 100644 index 00000000..2a39e2f8 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/FixedValueConverter.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; + +public interface FixedValueConverter extends ValueConverter { + + /** + * 将把固定长度的值序列化到指定的缓冲区; + *

+ * + * 注:实现者应用确保写入的范围不要越界(超出 {@link #getSliceSpecification()} 属性指定的长度); + * + * @param value + * 要序列化的值; + * @param buffer + * 保存结果的缓冲区; + * @param offset + * 缓冲区的写入起始位置; + * @return 返回写入的长度; + */ + int encodeValue(Object value, byte[] buffer, int offset); + + Object decodeValue(BytesSlice dataSlice); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/GenericFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/GenericFieldEncoder.java new file mode 100644 index 00000000..1f856e65 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/GenericFieldEncoder.java @@ -0,0 +1,199 @@ +//package com.jd.blockchain.binaryproto.impl2; +// +//import java.lang.reflect.Array; +//import java.lang.reflect.InvocationTargetException; +//import java.lang.reflect.Method; +// +//import com.jd.blockchain.binaryproto.BinarySliceSpec; +//import com.jd.blockchain.binaryproto.FieldSpec; +// +//import my.utils.io.BytesInputStream; +//import my.utils.io.BytesOutputBuffer; +//import my.utils.io.BytesSlice; +//import my.utils.io.BytesSlices; +//import my.utils.io.DynamicBytesSliceArray; +//import my.utils.io.FixedBytesSliceArray; +//import my.utils.io.NumberMask; +//import my.utils.io.SingleBytesSliceArray; +// +//public abstract class GenericFieldEncoder implements FieldEncoder { +// +// protected BinarySliceSpec sliceSpec; +// +// protected FieldSpec fieldSpec; +// +// protected Method reader; +// +// public GenericFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) { +// this.sliceSpec = sliceSpec; +// this.fieldSpec = fieldSpec; +// this.reader = reader; +// } +// +// @Override +// public BinarySliceSpec getSliceSpecification() { +// return sliceSpec; +// } +// +// @Override +// public FieldSpec getFieldSpecification() { +// return fieldSpec; +// } +// +// @Override +// public Method getReader() { +// return reader; +// } +// +// @Override +// public int encode(Object dataContract, BytesOutputBuffer buffer) { +// int size = 0; +// if (sliceSpec.isRepeatable()) { +// Object[] values = readArrayValue(dataContract); +// if (sliceSpec.isDynamic()) { +// size += encodeArrayDynamic(values, buffer); +// } else { +// size += encodeArray(values, buffer); +// } +// } else { +// Object value = readValue(dataContract); +// if (sliceSpec.isDynamic()) { +// size += encodeDynamicValue(value, buffer); +// } else { +// size += encodeValue(value, buffer); +// } +// } +// +// return size; +// } +// +// /** +// * 把固定长度的值序列化到指定的缓冲区; +// * +// * @param value +// * @param buffer +// * @return +// */ +// private int encodeValue(Object value, BytesOutputBuffer buffer) { +// byte[] valueBytes = new byte[sliceSpec.getLength()]; +// return encodeValue(value, valueBytes, 0); +// } +// +// /** +// * 将把固定长度的值序列化到指定的缓冲区; +// *

+// * +// * 注:实现者应用确保写入的范围不要越界(超出 {@link #getSliceSpecification()} 属性指定的长度); +// * +// * @param value +// * 要序列化的值; +// * @param buffer +// * 保存结果的缓冲区; +// * @param offset +// * 缓冲区的写入起始位置; +// * @return 返回写入的长度; +// */ +// abstract int encodeValue(Object value, byte[] buffer, int offset); +// +// abstract int encodeDynamicValue(Object value, BytesOutputBuffer buffer); +// +// /** +// * 对数组类型的值进行固定长度的编码; +// * +// * @param values +// * @param buffer +// * @return +// */ +// private int encodeArray(Object[] values, BytesOutputBuffer buffer) { +// int count = values == null ? 0 : values.length; +// +// int counterSize = NumberMask.NORMAL.getMaskLength(count); +// int elementSize = sliceSpec.getLength(); +// +// int size = counterSize + elementSize * count; +// byte[] outbuff = new byte[size]; +// NumberMask.NORMAL.writeMask(count, outbuff, 0); +// +// for (int i = 0; i < count; i++) { +// encodeValue(values[i], outbuff, counterSize + elementSize * i); +// } +// +// buffer.write(outbuff); +// return size; +// } +// +// /** +// * 对数组类型的值进行非固定长度的编码; +// * +// * @param values +// * @param buffer +// * @return +// */ +// private int encodeArrayDynamic(Object[] values, BytesOutputBuffer buffer) { +// int size = 0; +// +// int count = values == null ? 0 : values.length; +// byte[] countBytes = NumberMask.NORMAL.generateMask(count); +// buffer.write(countBytes); +// size += countBytes.length; +// +// for (int i = 0; i < count; i++) { +// size += encodeDynamicValue(values[i], buffer); +// } +// +// return size; +// } +// +// private Object readValue(Object dataContract) { +// try { +// return reader.invoke(dataContract); +// } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } +// } +// +// private Object[] readArrayValue(Object dataContract) { +// return (Object[]) readValue(dataContract); +// } +// +// @Override +// public BytesSlices decode(BytesInputStream bytesStream) { +// if (sliceSpec.isRepeatable()) { +// if (sliceSpec.isDynamic()) { +// return DynamicBytesSliceArray.resolve(bytesStream); +// }else { +// return FixedBytesSliceArray.resolve(bytesStream, sliceSpec.getLength()); +// } +// }else { +// if (sliceSpec.isDynamic()) { +// return SingleBytesSliceArray.resolveDynamic(bytesStream); +// }else { +// return SingleBytesSliceArray.create(bytesStream, sliceSpec.getLength()); +// } +// } +// } +// +// @Override +// public Object decodeField(BytesSlices fieldBytes) { +// if (sliceSpec.isRepeatable()) { +// Object[] values = (Object[]) Array.newInstance(getFieldType(), fieldBytes.getCount()); +// for (int i = 0; i < values.length; i++) { +// values[i] = decodeValue(fieldBytes.getDataSlice(i)); +// } +// return values; +// } +// //非数组的字段,最多只有一个数据片段; +// if (fieldBytes.getCount() == 0) { +// return getNullValue(); +// } +// return decodeValue(fieldBytes.getDataSlice(0)); +// } +// +// +// abstract Class getFieldType(); +// +// abstract Object getNullValue(); +// +// abstract Object decodeValue(BytesSlice dataSlice); +// +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/HeaderEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/HeaderEncoder.java new file mode 100644 index 00000000..a1fe4c36 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/HeaderEncoder.java @@ -0,0 +1,102 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesSliceArrayWrapper; +import com.jd.blockchain.utils.io.BytesSlices; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 分段编码器; + * + * @author huanghaiquan + * + */ +public class HeaderEncoder implements SliceEncoder { + + public static final int HEAD_BYTES = 12; + + private BinarySliceSpec sliceSpec; + + private int code; + + private long version; + + private String name; + + private String description; + + public int getCode() { + return code; + } + + public long getVersion() { + return version; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } + + public HeaderEncoder(BinarySliceSpec sliceSpec, int code, long version, String name, String description) { + this.sliceSpec = sliceSpec; + this.code = code; + this.version = version; + this.name = name; + this.description = description; + } + + /** + * 校验指定的数据契约字节列表的头部是否一致; + * + * @param dataContractBytes + * @return + */ + boolean verifyHeaders(BytesSlice dataContractBytes) { + int code = resolveCode(dataContractBytes); + if (code != this.code) { + return false; + } + long version = resolveVersion(dataContractBytes); + return version == this.version; + } + + public static int resolveCode(BytesSlice dataContractBytes) { + return dataContractBytes.getInt(); + } + + public static long resolveVersion(BytesSlice dataContractBytes) { + return dataContractBytes.getLong(4); + } + + @Override + public BinarySliceSpec getSliceSpecification() { + return sliceSpec; + } + + @Override + public int encode(Object dataContract, BytesOutputBuffer buffer) { + byte[] headBytes = new byte[HEAD_BYTES]; + BytesUtils.toBytes(code, headBytes); + BytesUtils.toBytes(version, headBytes, 4); + buffer.write(headBytes); + return HEAD_BYTES; + } + + @Override + public BytesSlices decode(BytesInputStream bytesStream) { + return BytesSliceArrayWrapper.wrap(bytesStream.readSlice(HEAD_BYTES)); + } + + // @Override + // public BytesSlices decode(byte[] dataContractBytes, int offset) { + // return SingleBytesSliceArray.create(dataContractBytes, offset, HEAD_BYTES); + // } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharConverter.java new file mode 100644 index 00000000..f026ea48 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int16CharConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return char.class; + } + + @Override + public Object getDefaultValue() { + return '\u0000'; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((char)value, buffer, offset); + return 2; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getChar(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharWrapperConverter.java new file mode 100644 index 00000000..10707ff2 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16CharWrapperConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int16CharWrapperConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Character.class; + } + + @Override + public Object getDefaultValue() { + return null; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((char)value, buffer, offset); + return 2; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getChar(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortConverter.java new file mode 100644 index 00000000..e7572687 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int16ShortConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return short.class; + } + + @Override + public Object getDefaultValue() { + return 0; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((short)value, buffer, offset); + return 2; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getShort(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortWrapperConverter.java new file mode 100644 index 00000000..b7861c45 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int16ShortWrapperConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int16ShortWrapperConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Short.class; + } + + @Override + public Object getDefaultValue() { + return null; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((short)value, buffer, offset); + return 2; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getShort(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntConverter.java new file mode 100644 index 00000000..79356f2a --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int32IntConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return int.class; + } + + @Override + public Object getDefaultValue() { + return 0; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((int)value, buffer, offset); + return 4; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getInt(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntWrapperConverter.java new file mode 100644 index 00000000..2dbe3bb2 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int32IntWrapperConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int32IntWrapperConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Integer.class; + } + + @Override + public Object getDefaultValue() { + return null; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((int)value, buffer, offset); + return 4; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getInt(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongConverter.java new file mode 100644 index 00000000..9810ae73 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int64LongConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Long.class; + } + + @Override + public Object getDefaultValue() { + return 0; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((long)value, buffer, offset); + return 8; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getLong(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongWrapperConverter.java new file mode 100644 index 00000000..ebf09087 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int64LongWrapperConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Int64LongWrapperConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Long.class; + } + + @Override + public Object getDefaultValue() { + return null; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + BytesUtils.toBytes((long)value, buffer, offset); + return 8; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getLong(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteConverter.java new file mode 100644 index 00000000..d38b7afc --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteConverter.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; + +public class Int8ByteConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return byte.class; + } + + @Override + public Object getDefaultValue() { + return 0; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + buffer[offset] = (byte)value; + return 1; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getByte(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteWrapperConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteWrapperConverter.java new file mode 100644 index 00000000..d8cf510b --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/Int8ByteWrapperConverter.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesSlice; + +public class Int8ByteWrapperConverter implements FixedValueConverter{ + + @Override + public Class getValueType() { + return Byte.class; + } + + @Override + public Object getDefaultValue() { + return null; + } + + @Override + public int encodeValue(Object value, byte[] buffer, int offset) { + buffer[offset] = (byte)value; + return 1; + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getByte(); + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/RepeatableFieldEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/RepeatableFieldEncoder.java new file mode 100644 index 00000000..fbf15a47 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/RepeatableFieldEncoder.java @@ -0,0 +1,31 @@ +//package com.jd.blockchain.binaryproto.impl2; +// +//import java.lang.reflect.Method; +// +//import com.jd.blockchain.binaryproto.BinarySliceSpec; +//import com.jd.blockchain.binaryproto.FieldSpec; +// +//import my.utils.io.BytesOutputBuffer; +// +//public class RepeatableFieldEncoder extends AbstractFieldEncoder { +// +// +// public RepeatableFieldEncoder(BinarySliceSpec sliceSpec, FieldSpec fieldSpec, Method reader) { +// super(sliceSpec, fieldSpec, reader); +// } +// +// @Override +// public Object decodeValue(byte[] dataContractBytes, int offset) { +// // TODO Auto-generated method stub +// return null; +// } +// +// @Override +// public int encode(Object dataContract, BytesOutputBuffer buffer) { +// // TODO Auto-generated method stub +// return 0; +// } +// +// +// +//} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/SliceEncoder.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/SliceEncoder.java new file mode 100644 index 00000000..1f87e0f2 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/SliceEncoder.java @@ -0,0 +1,55 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.binaryproto.BinarySliceSpec; +import com.jd.blockchain.utils.io.BytesInputStream; +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlices; + +/** + * 分段编码器; + * + * @author huanghaiquan + * + */ +public interface SliceEncoder { + + BinarySliceSpec getSliceSpecification(); + + /** + * 将此编码器表示的数据契约分段输出的指定的缓冲区; + * + * @param dataContract + * 数据契约的实例;当前编码器从中读取分段的值; + * @param buffer + * 要写入的缓冲区;调用者需要确保 + * @param offset + * 缓冲区的写入起始位置; + * @return 写入的字节数; + */ + int encode(Object dataContract, BytesOutputBuffer buffer); + +// /** +// * 从指定的数据契约的数据中读取当前编码器表示的分段的数据; +// * +// * @param dataContractBytes +// * @return +// */ +// BytesSlices decode(BytesSlice dataContractBytes, int offset); + + /** + * 从指定的数据契约的数据中读取当前编码器表示的分段的数据; + * + * @param dataContractBytes + * @return + */ + BytesSlices decode(BytesInputStream bytesStream); + +// /** +// * 从指定的数据契约的数据中读取当前编码器表示的分段的数据; +// * +// * @param dataContractBytes +// * @return +// */ +// BytesSlices decode(byte[] dataContractBytes, int offset); + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/StringValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/StringValueConverter.java new file mode 100644 index 00000000..eaf96a21 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/StringValueConverter.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.binaryproto.impl2; + +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public class StringValueConverter extends AbstractDynamicValueConverter { + + public StringValueConverter() { + super(String.class); + } + + @Override + public Object decodeValue(BytesSlice dataSlice) { + return dataSlice.getString(); + } + + @Override + public int encodeDynamicValue(Object value, BytesOutputBuffer buffer) { + byte[] bytes = value == null ? BytesUtils.EMPTY_BYTES : BytesUtils.toBytes((String) value); + int size = bytes.length; + size += writeSize(bytes.length, buffer); + buffer.write(bytes); + return size; + } + +} diff --git a/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/ValueConverter.java b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/ValueConverter.java new file mode 100644 index 00000000..74bf0a00 --- /dev/null +++ b/source/binary-proto/src/main/java/com/jd/blockchain/binaryproto/impl2/ValueConverter.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.binaryproto.impl2; + +public interface ValueConverter { + + Class getValueType(); + + /** + * 返回类型的默认初始值; + * + * @return + */ + Object getDefaultValue(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/BinaryEncodingTest.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/BinaryEncodingTest.java new file mode 100644 index 00000000..fba70646 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/BinaryEncodingTest.java @@ -0,0 +1,547 @@ +package test.com.jd.blockchain.binaryproto; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractException; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.NumberMask; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class BinaryEncodingTest { + + /** + * 此测试用例是对序列化过程的验证,包括:头部、基本类型的值(boolean、整数、字符串、字节数组、BytesSerializable)、字段顺序的正确性; + */ + @Test + public void testEncoding_Header_OrderedPrimitiveFields() { + DataContractRegistry.register(PrimitiveDatas.class); + PrimitiveDatasImpl pd = new PrimitiveDatasImpl(); + NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); + pd.setId(123); + pd.setEnable(true); + pd.setBoy((byte) 10); + pd.setAge((short) 100); + pd.setName("John"); + pd.setImage("Image of John".getBytes()); + pd.setFlag('x'); + pd.setValue(93239232); + pd.setConfig(Bytes.fromString("Configuration of something.")); + pd.setNetworkAddress(networkAddress); + + byte[] bytes = BinaryEncodingUtils.encode(pd, PrimitiveDatas.class); + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12; + assertEquals(0x05, code); + offset = assertFieldEncoding(pd, bytes, offset); + + // 到了结尾; + assertEquals(bytes.length, offset); + } + + private int assertFieldEncoding(PrimitiveDatas pd, byte[] bytes, int offset) { + + // Code 和 Version 的占据了 12 字节; + int id = BytesUtils.toInt(bytes, offset); + offset += 4; + assertEquals(pd.getId(), id); + + byte enable = bytes[offset]; + byte expEnable = pd.isEnable() ? (byte) 1 : (byte) 0; + offset += 1; + assertEquals(expEnable, enable); + + byte boy = bytes[offset]; + offset += 1; + assertEquals(pd.isBoy(), boy); + + short age = BytesUtils.toShort(bytes, offset); + offset += 2; + assertEquals(pd.getAge(), age); + + byte[] nameBytes = BytesEncoding.readInNormal(bytes, offset); + String name = BytesUtils.toString(nameBytes); + int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + offset += nameBytes.length + maskLen; + assertEquals(pd.getName().length(), name.length()); + + long value = BytesUtils.toLong(bytes, offset); + offset += 8; + assertEquals(pd.getValue(), value); + + byte[] image = BytesEncoding.readInNormal(bytes, offset); + maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + offset += image.length + maskLen; + assertTrue(BytesUtils.equals(pd.getImage(), image)); + + char flag = BytesUtils.toChar(bytes, offset); + offset += 2; + assertEquals(pd.getFlag(), flag); + + byte[] config = BytesEncoding.readInNormal(bytes, offset); + maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + offset += config.length + maskLen; + assertTrue(BytesUtils.equals(pd.getConfig().toBytes(), config)); + + byte[] setting = BytesEncoding.readInNormal(bytes, offset); + assertEquals(0, setting.length); + maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + offset += setting.length + maskLen; + + byte[] networkaddr = BytesEncoding.readInNormal(bytes, offset); + maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + offset += networkaddr.length + maskLen; + assertTrue(BytesUtils.equals(pd.getNetworkAddr().toBytes(), networkaddr)); + + return offset; + } + + @Test + public void testEncoding_Null() { + DataContractRegistry.register(PrimitiveDatas.class); + + byte[] bytes = BinaryEncodingUtils.encode(null, PrimitiveDatas.class); + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12;// 暂不校验 version; + assertEquals(0x05, code); + + // 到了结尾; + assertEquals(bytes.length, offset); + } + + /** + * 此测试用例是对序列化过程的验证,包括:头部、基本类型的值(boolean、整数、字符串、字节数组、BytesSerializable)、字段顺序的正确性; + */ + @Test + public void testDecoding_Header_OrderedPrimitiveFields() { + DataContractRegistry.register(PrimitiveDatas.class); + PrimitiveDatasImpl pd = new PrimitiveDatasImpl(); + NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); + pd.setId(123); + pd.setEnable(true); + pd.setBoy((byte) 10); + pd.setAge((short) 100); + pd.setName("John"); + pd.setImage("Image of John".getBytes()); + pd.setFlag('x'); + pd.setValue(93239232); + pd.setConfig(Bytes.fromString("Configuration of something.")); + pd.setNetworkAddress(networkAddress); + + byte[] bytes = BinaryEncodingUtils.encode(pd, PrimitiveDatas.class); + PrimitiveDatas decodeData = BinaryEncodingUtils.decode(bytes); + assertEquals(pd.getId(), decodeData.getId()); + assertEquals(pd.isEnable(), decodeData.isEnable()); + assertEquals(pd.isBoy(), decodeData.isBoy()); + assertEquals(pd.getAge(), decodeData.getAge()); + assertEquals(pd.getName(), decodeData.getName()); + assertTrue(BytesUtils.equals(pd.getImage(), decodeData.getImage())); + assertEquals(pd.getFlag(), decodeData.getFlag()); + assertEquals(pd.getValue(), decodeData.getValue()); + assertEquals(pd.getConfig(), decodeData.getConfig()); + assertNull(decodeData.getSetting()); + assertEquals(pd.getNetworkAddr().getHost(), decodeData.getNetworkAddr().getHost()); + assertEquals(pd.getNetworkAddr().getPort(), decodeData.getNetworkAddr().getPort()); + } + + /** + * 此测试用例是对序列化过程的验证,包括:头部、枚举值、字段顺序的正确性; + */ + @Test + public void testEncoding_Header_OrderedEnumFields() { + DataContractRegistry.register(EnumDatas.class); + EnumDatasImpl enumDatas = new EnumDatasImpl(); + enumDatas.setLevel(EnumLevel.V1); + + byte[] bytes = BinaryEncodingUtils.encode(enumDatas, EnumDatas.class); + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12; + assertEquals(0x07, code); + + byte enumCode = bytes[offset]; + offset += 1; + assertEquals(enumDatas.getLevel().CODE, enumCode); + + // 到了结尾; + assertEquals(bytes.length, offset); + } + + /** + * 此测试用例是对反序列化过程的验证,包括:头部、枚举值、字段顺序的正确性; + */ + @Test + public void testDecoding_Header_OrderedEnumFields() { + DataContractRegistry.register(EnumDatas.class); + EnumDatasImpl enumDatas = new EnumDatasImpl(); + enumDatas.setLevel(EnumLevel.V1); + + byte[] bytes = BinaryEncodingUtils.encode(enumDatas, EnumDatas.class); + EnumDatas decodeData = BinaryEncodingUtils.decode(bytes); + assertEquals(enumDatas.getLevel(), decodeData.getLevel()); + } + + /** + * 此测试用例是对序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testEncoding_Header_SingleRefContractField() { + DataContractRegistry.register(RefContractDatas.class); + + RefContractDatasImpl refContractDatas = new RefContractDatasImpl(); + PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); + NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); + + primitiveDatas.setId(123); + primitiveDatas.setEnable(true); + primitiveDatas.setBoy((byte) 10); + primitiveDatas.setAge((short) 100); + primitiveDatas.setName("John"); + primitiveDatas.setImage("Image of John".getBytes()); + primitiveDatas.setFlag('x'); + primitiveDatas.setValue(93239232); + primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); + primitiveDatas.setNetworkAddress(networkAddress); + + refContractDatas.setPrimitiveDatas(primitiveDatas); + + byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class); + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12; + assertEquals(0x08, code); + + // 引用合约字段的字节; + byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset); + int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + + // 获得引用合约码与版本 + int fieldOffset = 0; + int refCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset); + fieldOffset += 12; + assertEquals(0x05, refCode); + + fieldOffset = assertFieldEncoding(refContractDatas.getPrimitive(), primitiveDataFieldBytes, fieldOffset); + assertEquals(primitiveDataFieldBytes.length, fieldOffset); + offset += primitiveDataFieldBytes.length + maskLen; + // 到了结尾; + assertEquals(bytes.length, offset); + + } + + /** + * 此测试用例是对序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testEncoding_Header_NullRefContractFields() { + DataContractRegistry.register(RefContractDatas.class); + + RefContractDatasImpl refContractDatas = new RefContractDatasImpl(); + + byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class); + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12; + assertEquals(0x08, code); + + // 引用合约字段的字节; + byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset); + int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + assertEquals(12, primitiveDataFieldBytes.length); + + // 获得引用合约码与版本 + int fieldOffset = 0; + int refCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset); + fieldOffset += 12; + assertEquals(0x05, refCode); + + assertEquals(primitiveDataFieldBytes.length, fieldOffset); + offset += primitiveDataFieldBytes.length + maskLen; + // 到了结尾; + assertEquals(bytes.length, offset); + } + + /** + * 此测试用例是对反序列化过程的验证,包括:头部、引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testDecoding_Header_OrderedRefContractFields() { + DataContractRegistry.register(RefContractDatas.class); + + RefContractDatasImpl refContractDatas = new RefContractDatasImpl(); + PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); + NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); + + primitiveDatas.setId(123); + primitiveDatas.setEnable(true); + primitiveDatas.setBoy((byte) 10); + primitiveDatas.setAge((short) 100); + primitiveDatas.setName("John"); + primitiveDatas.setImage("Image of John".getBytes()); + primitiveDatas.setFlag('x'); + primitiveDatas.setValue(93239232); + primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); + primitiveDatas.setNetworkAddress(networkAddress); + + refContractDatas.setPrimitiveDatas(primitiveDatas); + + byte[] bytes = BinaryEncodingUtils.encode(refContractDatas, RefContractDatas.class); + RefContractDatas decodeData = BinaryEncodingUtils.decode(bytes); + + assertEquals(refContractDatas.getPrimitive().getId(), decodeData.getPrimitive().getId()); + assertEquals(refContractDatas.getPrimitive().isEnable(), decodeData.getPrimitive().isEnable()); + assertEquals(refContractDatas.getPrimitive().isBoy(), decodeData.getPrimitive().isBoy()); + assertEquals(refContractDatas.getPrimitive().getAge(), decodeData.getPrimitive().getAge()); + assertEquals(refContractDatas.getPrimitive().getName(), decodeData.getPrimitive().getName()); + assertTrue(BytesUtils.equals(refContractDatas.getPrimitive().getImage(), decodeData.getPrimitive().getImage())); + assertEquals(refContractDatas.getPrimitive().getFlag(), decodeData.getPrimitive().getFlag()); + assertEquals(refContractDatas.getPrimitive().getValue(), decodeData.getPrimitive().getValue()); + assertEquals(refContractDatas.getPrimitive().getConfig(), decodeData.getPrimitive().getConfig()); + assertEquals(refContractDatas.getPrimitive().getNetworkAddr().getHost(), decodeData.getPrimitive().getNetworkAddr().getHost()); + assertEquals(refContractDatas.getPrimitive().getNetworkAddr().getPort(), decodeData.getPrimitive().getNetworkAddr().getPort()); + } + + /** + * 此测试用例是对序列化过程的验证,包括:头部、Generic引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testEncoding_Header_GenericRefContractArrayFields() { + + DataContractRegistry.register(GenericRefContractDatas.class); + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(SubOperation.class); + + GenericRefContractDatasImpl genericRefContractDatas = new GenericRefContractDatasImpl(); + SubOperationImpl subOperation = new SubOperationImpl(); + subOperation.setUserName("Jerry"); + + Operation[] operations = new Operation[1]; + operations[0] = subOperation; + genericRefContractDatas.setOperations(operations); + + byte[] bytes = BinaryEncodingUtils.encode(genericRefContractDatas, GenericRefContractDatas.class); + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12; + assertEquals(0xb, code); + + offset = assertGenericRefContractArrayFields(bytes, offset, operations); + + // 到了结尾; + assertEquals(bytes.length, offset); + } + + private int assertGenericRefContractArrayFields(byte[] bytes, int offset, Operation[] expectedOperations) { + // count of operations; + int opCount = NumberMask.NORMAL.resolveMaskedNumber(bytes, offset); + byte opCountHeadBytes = bytes[offset]; + int maskLen = NumberMask.NORMAL.resolveMaskLength(opCountHeadBytes); + offset += maskLen; + assertEquals(expectedOperations.length, opCount); + + // Field: operations; + for (int i = 0; i < opCount; i++) { + byte[] opertionItemBytes = BytesEncoding.readInNormal(bytes, offset); + + int itemMaskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + offset += opertionItemBytes.length + itemMaskLen; + + // verify subOperation; + int itemOffset = 0; + int itemCode = BytesUtils.toInt(opertionItemBytes, itemOffset); + itemOffset += 12; + assertEquals(0xa, itemCode); + + byte[] userNameBytes = BytesEncoding.readInNormal(opertionItemBytes, itemOffset); + int nameMaskLen = NumberMask.NORMAL.resolveMaskLength(opertionItemBytes[itemOffset]); + itemOffset += userNameBytes.length + nameMaskLen; + String userName = BytesUtils.toString(userNameBytes); + assertEquals(((SubOperation) expectedOperations[i]).getUserName(), userName); + + assertEquals(opertionItemBytes.length, itemOffset); + } + + return offset; + } + + /** + * 此测试用例是对反序列化过程的验证,包括:头部、Generic引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testDecoding_Header_OrderedGenericRefContractFields() { + DataContractRegistry.register(GenericRefContractDatas.class); + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(SubOperation.class); + + GenericRefContractDatasImpl genericRefContractDatas = new GenericRefContractDatasImpl(); + SubOperationImpl subOperation = new SubOperationImpl(); + subOperation.setUserName("Jerry"); + + Operation[] operations = new Operation[1]; + operations[0] = subOperation; + genericRefContractDatas.setOperations(operations); + + byte[] bytes = BinaryEncodingUtils.encode(genericRefContractDatas, GenericRefContractDatas.class); + + GenericRefContractDatas decodeData = BinaryEncodingUtils.decode(bytes); + + assertEquals("Jerry", ((SubOperation) (decodeData.getOperations()[0])).getUserName()); + } + + /** + * 此测试用例是对序列化过程的验证,包括:头部、Primitive, Enum, 引用数据契约, Generic引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testEncoding_Header_OrderedCompositeFields() { + DataContractRegistry.register(SubOperation.class); + DataContractRegistry.register(CompositeDatas.class); + + CompositeDatasImpl compositeDatas = new CompositeDatasImpl(); + PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); + SubOperationImpl subOperation = new SubOperationImpl(); + NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); + + compositeDatas.setEnable(false); + compositeDatas.setLevel(EnumLevel.V1); + + primitiveDatas.setId(123); + primitiveDatas.setEnable(true); + primitiveDatas.setBoy((byte) 10); + primitiveDatas.setAge((short) 100); + primitiveDatas.setName("John"); + primitiveDatas.setImage("Image of John".getBytes()); + primitiveDatas.setFlag('x'); + primitiveDatas.setValue(93239232); + primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); + primitiveDatas.setNetworkAddress(networkAddress); + + compositeDatas.setPrimitiveDatas(primitiveDatas); + + subOperation.setUserName("Jerry"); + Operation[] operations = new Operation[1]; + operations[0] = subOperation; + compositeDatas.setOperations(operations); + + byte[] bytes = BinaryEncodingUtils.encode(compositeDatas, CompositeDatas.class); + + int offset = 0; + int code = BytesUtils.toInt(bytes, offset); + offset += 12; + assertEquals(0xc, code); + + // primitive type + assertEquals(compositeDatas.isEnable() ? (byte) 1 : (byte) 0, bytes[offset]); + offset += 1; + + byte enumCode = bytes[offset]; + offset += 1; + assertEquals(compositeDatas.getLevel().CODE, enumCode); + + // ---------------- + // 引用合约字段的字节; + byte[] primitiveDataFieldBytes = BytesEncoding.readInNormal(bytes, offset); + int maskLen = NumberMask.NORMAL.resolveMaskLength(bytes[offset]); + + // 获得引用合约码与版本 + int fieldOffset = 0; + int primitiveDataCode = BytesUtils.toInt(primitiveDataFieldBytes, fieldOffset); + fieldOffset += 12; + assertEquals(0x05, primitiveDataCode); + + fieldOffset = assertFieldEncoding(compositeDatas.getPrimitive(), primitiveDataFieldBytes, fieldOffset); + assertEquals(primitiveDataFieldBytes.length, fieldOffset); + offset += primitiveDataFieldBytes.length + maskLen; + + // field: operation; + offset = assertGenericRefContractArrayFields(bytes, offset, operations); + + // primitive + assertEquals(compositeDatas.getAge(), BytesUtils.toShort(bytes, offset)); + offset += 2; + + // 到了结尾; + assertEquals(bytes.length, offset); + } + + /** + * 此测试用例是对反序列化过程的验证,包括:头部、Primitive, Enum, 引用数据契约, Generic引用数据契约字段值、字段顺序的正确性; + */ + @Test + public void testDecoding_Header_OrderedCompositeFields() { + DataContractRegistry.register(SubOperation.class); + DataContractRegistry.register(CompositeDatas.class); + + CompositeDatasImpl compositeDatas = new CompositeDatasImpl(); + PrimitiveDatasImpl primitiveDatas = new PrimitiveDatasImpl(); + SubOperationImpl subOperation = new SubOperationImpl(); + NetworkAddress networkAddress = new NetworkAddress("192.168.1.1", 9001, false); + + compositeDatas.setEnable(false); + compositeDatas.setLevel(EnumLevel.V1); + compositeDatas.setAge((short) 100); + + primitiveDatas.setId(123); + primitiveDatas.setEnable(true); + primitiveDatas.setBoy((byte) 10); + primitiveDatas.setAge((short) 100); + primitiveDatas.setName("John"); + primitiveDatas.setImage("Image of John".getBytes()); + primitiveDatas.setFlag('x'); + primitiveDatas.setValue(93239232); + primitiveDatas.setConfig(Bytes.fromString("Configuration of something.")); + primitiveDatas.setNetworkAddress(networkAddress); + + compositeDatas.setPrimitiveDatas(primitiveDatas); + + subOperation.setUserName("Jerry"); + Operation[] operations = new Operation[1]; + operations[0] = subOperation; + compositeDatas.setOperations(operations); + + byte[] bytes = BinaryEncodingUtils.encode(compositeDatas, CompositeDatas.class); + CompositeDatas decodeData = BinaryEncodingUtils.decode(bytes); + + assertEquals(compositeDatas.isEnable(), decodeData.isEnable()); + assertEquals(compositeDatas.getAge(), decodeData.getAge()); + assertEquals(compositeDatas.getPrimitive().getId(), decodeData.getPrimitive().getId()); + assertEquals(compositeDatas.getPrimitive().isEnable(), decodeData.getPrimitive().isEnable()); + assertEquals(compositeDatas.getPrimitive().isBoy(), decodeData.getPrimitive().isBoy()); + assertEquals(compositeDatas.getPrimitive().getAge(), decodeData.getPrimitive().getAge()); + assertEquals(compositeDatas.getPrimitive().getName(), decodeData.getPrimitive().getName()); + assertTrue(BytesUtils.equals(compositeDatas.getPrimitive().getImage(), decodeData.getPrimitive().getImage())); + assertEquals(compositeDatas.getPrimitive().getFlag(), decodeData.getPrimitive().getFlag()); + assertEquals(compositeDatas.getPrimitive().getValue(), decodeData.getPrimitive().getValue()); + assertEquals(compositeDatas.getPrimitive().getConfig(), decodeData.getPrimitive().getConfig()); + assertEquals(compositeDatas.getPrimitive().getNetworkAddr().getHost(), decodeData.getPrimitive().getNetworkAddr().getHost()); + assertEquals(compositeDatas.getPrimitive().getNetworkAddr().getPort(), decodeData.getPrimitive().getNetworkAddr().getPort()); + assertEquals("Jerry", ((SubOperation) (decodeData.getOperations()[0])).getUserName()); + assertEquals(compositeDatas.getLevel(), decodeData.getLevel()); + + } + + /** + * 验证解析一个定义有顺序重复的两个字段的类型时,将引发异常 {@link DataContractException} + */ + @Test + public void testFields_Order_confliction() { + DataContractException ex = null; + try { + DataContractRegistry.register(FieldOrderConflictedDatas.class); + } catch (DataContractException e) { + ex = e; + System.out.println( + "expected error of [" + FieldOrderConflictedDatas.class.toString() + "] --" + e.getMessage()); + } + assertNotNull(ex); + } +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatas.java new file mode 100644 index 00000000..be60a558 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatas.java @@ -0,0 +1,29 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/11/30. + */ +@DataContract(code = 0xc, name = "CompositeDatas", description = "") +public interface CompositeDatas { + + @DataField(order = 1, primitiveType = ValueType.BOOLEAN) + boolean isEnable(); + + @DataField(order = 2, refEnum = true) + EnumLevel getLevel(); + + @DataField(order = 3, refContract = true) + PrimitiveDatas getPrimitive(); + + @DataField(order=4, list = true, refContract=true, genericContract = true) + Operation[] getOperations(); + + @DataField(order = 5, primitiveType = ValueType.INT16) + short getAge(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatasImpl.java new file mode 100644 index 00000000..d1cbee50 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/CompositeDatasImpl.java @@ -0,0 +1,68 @@ +package test.com.jd.blockchain.binaryproto; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by zhangshuang3 on 2018/11/30. + */ +public class CompositeDatasImpl implements CompositeDatas{ + private boolean enable; + private EnumLevel level; + PrimitiveDatas primitiveDatas; + private List operationList = new ArrayList(); + private short age; + + @Override + public boolean isEnable() { + return this.enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + @Override + public EnumLevel getLevel() { + return this.level; + } + + public void setLevel(EnumLevel level) { + this.level = level; + } + + @Override + public PrimitiveDatas getPrimitive() { + return this.primitiveDatas; + } + + public void setPrimitiveDatas(PrimitiveDatas primitiveDatas) { + this.primitiveDatas = primitiveDatas; + } + + @Override + public Operation[] getOperations() { + return operationList.toArray(new Operation[operationList.size()]); + } + + public void setOperations(Object[] operations) { + for (Object operation : operations) { + Operation op = (Operation)operation; + addOperation(op); + } + } + + public void addOperation(Operation operation) { + operationList.add(operation); + } + + @Override + public short getAge() { + return this.age; + } + + public void setAge(short age) { + this.age = age; + } + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatas.java new file mode 100644 index 00000000..629dc173 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatas.java @@ -0,0 +1,15 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +@DataContract(code = 0x07, name = "EnumDatas", description = "") +public interface EnumDatas { + + @DataField(order = 1, refEnum = true) + EnumLevel getLevel(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatasImpl.java new file mode 100644 index 00000000..f39e1b40 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumDatasImpl.java @@ -0,0 +1,17 @@ +package test.com.jd.blockchain.binaryproto; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +public class EnumDatasImpl implements EnumDatas { + private EnumLevel level; + + @Override + public EnumLevel getLevel() { + return this.level; + } + public void setLevel(EnumLevel level) { + this.level = level; + } + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumLevel.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumLevel.java new file mode 100644 index 00000000..a736e334 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/EnumLevel.java @@ -0,0 +1,27 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.EnumContract; +import com.jd.blockchain.binaryproto.EnumField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +@EnumContract(code=0x0100, name = "EnumLevel", decription = "") +public enum EnumLevel { + + V1((byte) 1), + + V2((byte) 2); + + @EnumField(type= ValueType.INT8) + public final byte CODE; + public byte getCode() { + return CODE; + } + private EnumLevel(byte code) { + this.CODE = code; + } + +} + diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatas.java new file mode 100644 index 00000000..2c17922c --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatas.java @@ -0,0 +1,33 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +@DataContract(code = 0x06, name = "Primitive", description = "") +public interface FieldOrderConflictedDatas { + + @DataField(order = 2, primitiveType = ValueType.BOOLEAN) + boolean isEnable(); + + @DataField(order = 3, primitiveType = ValueType.INT8) + byte isBoy(); + + @DataField(order = 7, primitiveType = ValueType.INT16) + short getAge(); + + @DataField(order = -1, primitiveType = ValueType.INT32) + int getId(); + + @DataField(order = 6, primitiveType = ValueType.TEXT) + String getName(); + + @DataField(order = 7, primitiveType = ValueType.INT64) + long getValue(); + + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatasImpl.java new file mode 100644 index 00000000..3c95639d --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/FieldOrderConflictedDatasImpl.java @@ -0,0 +1,71 @@ +package test.com.jd.blockchain.binaryproto; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +public class FieldOrderConflictedDatasImpl implements FieldOrderConflictedDatas { + + private boolean enable; + + private byte boy; + private short age; + private int id; + private String name; + + private long value; + + @Override + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public short getAge() { + return age; + } + + public void setAge(short age) { + this.age = age; + } + + @Override + public byte isBoy() { + return this.boy; + } + + public void setBoy(byte boy) { + this.boy = boy; + } + + @Override + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + @Override + public long getValue() { + return value; + } + + public void setValue(long value) { + this.value = value; + } + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatas.java new file mode 100644 index 00000000..6c949d57 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatas.java @@ -0,0 +1,15 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +@DataContract(code = 0xb, name = "GenericRefContractDatas", description = "") +public interface GenericRefContractDatas { + + @DataField(order=1, list = true, refContract=true, genericContract = true) + Operation[] getOperations(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatasImpl.java new file mode 100644 index 00000000..774dd5c7 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/GenericRefContractDatasImpl.java @@ -0,0 +1,30 @@ +package test.com.jd.blockchain.binaryproto; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +public class GenericRefContractDatasImpl implements GenericRefContractDatas{ + private List operationList = new ArrayList(); + + @Override + public Operation[] getOperations() { + return operationList.toArray(new Operation[operationList.size()]); + } + + public void setOperations(Object[] operations) { + for (Object operation : operations) { + Operation op = (Operation)operation; + addOperation(op); + } + + } + public void addOperation(Operation operation) { + operationList.add(operation); + } + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/Operation.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/Operation.java new file mode 100644 index 00000000..6f165db9 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/Operation.java @@ -0,0 +1,10 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +@DataContract(code = 0x09, name = "Operation", description = "") +public interface Operation { +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatas.java new file mode 100644 index 00000000..f855785c --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatas.java @@ -0,0 +1,48 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +@DataContract(code = 0x05, name = "Primitive", description = "") +public interface PrimitiveDatas { + + @DataField(order = 2, primitiveType = ValueType.BOOLEAN) + boolean isEnable(); + + @DataField(order = 3, primitiveType = ValueType.INT8) + byte isBoy(); + + @DataField(order = 4, primitiveType = ValueType.INT16) + short getAge(); + + @DataField(order = -1, primitiveType = ValueType.INT32) + int getId(); + + @DataField(order = 6, primitiveType = ValueType.TEXT) + String getName(); + + @DataField(order = 7, primitiveType = ValueType.INT64) + long getValue(); + + @DataField(order = 12, primitiveType = ValueType.BYTES) + byte[] getImage(); + + @DataField(order = 100, primitiveType = ValueType.INT16) + char getFlag(); + + @DataField(order = 200, primitiveType = ValueType.BYTES) + Bytes getConfig(); + + @DataField(order = 201, primitiveType = ValueType.BYTES) + Bytes getSetting(); + + @DataField(order = 202, primitiveType = ValueType.BYTES) + NetworkAddress getNetworkAddr(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatasImpl.java new file mode 100644 index 00000000..ec0f6a4b --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/PrimitiveDatasImpl.java @@ -0,0 +1,130 @@ +package test.com.jd.blockchain.binaryproto; + +import org.omg.CORBA.PUBLIC_MEMBER; + +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +public class PrimitiveDatasImpl implements PrimitiveDatas { + + private boolean enable; + + private byte boy; + private short age; + private int id; + private String name; + + private long value; + + private char flag; + + private byte[] image; + + private Bytes config; + + private Bytes setting; + + private NetworkAddress networkAddress; + + @Override + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public short getAge() { + return age; + } + + public void setAge(short age) { + this.age = age; + } + + @Override + public byte isBoy() { + return this.boy; + } + + public void setBoy(byte boy) { + this.boy = boy; + } + + @Override + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + @Override + public long getValue() { + return value; + } + + @Override + public char getFlag() { + return flag; + } + + public void setValue(long value) { + this.value = value; + } + + public void setFlag(char flag) { + this.flag = flag; + } + + @Override + public byte[] getImage() { + return image; + } + + @Override + public Bytes getConfig() { + return config; + } + + public void setImage(byte[] image) { + this.image = image; + } + + public void setConfig(Bytes config) { + this.config = config; + } + + public void setSetting(Bytes setting) { + this.setting = setting; + } + + @Override + public Bytes getSetting() { + return setting; + } + + @Override + public NetworkAddress getNetworkAddr() { + return networkAddress; + } + + public void setNetworkAddress(NetworkAddress networkAddress) { + this.networkAddress = networkAddress; + } +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatas.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatas.java new file mode 100644 index 00000000..4bc61955 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatas.java @@ -0,0 +1,15 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +@DataContract(code = 0x08, name = "RefContractDatas", description = "") +public interface RefContractDatas { + + @DataField(order = 1, refContract = true) + PrimitiveDatas getPrimitive(); +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatasImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatasImpl.java new file mode 100644 index 00000000..40880428 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/RefContractDatasImpl.java @@ -0,0 +1,18 @@ +package test.com.jd.blockchain.binaryproto; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +public class RefContractDatasImpl implements RefContractDatas{ + PrimitiveDatas primitiveDatas; + + @Override + public PrimitiveDatas getPrimitive() { + return this.primitiveDatas; + } + + public void setPrimitiveDatas(PrimitiveDatas primitiveDatas) { + this.primitiveDatas = primitiveDatas; + } + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperation.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperation.java new file mode 100644 index 00000000..a72a6077 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperation.java @@ -0,0 +1,16 @@ +package test.com.jd.blockchain.binaryproto; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +@DataContract(code = 0xa, name = "SubOperation", description = "") +public interface SubOperation extends Operation { + + @DataField(order=1, primitiveType = ValueType.TEXT) + String getUserName(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperationImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperationImpl.java new file mode 100644 index 00000000..6966fc63 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/SubOperationImpl.java @@ -0,0 +1,17 @@ +package test.com.jd.blockchain.binaryproto; + +/** + * Created by zhangshuang3 on 2018/11/29. + */ +public class SubOperationImpl implements SubOperation { + String userName; + + @Override + public String getUserName() { + return this.userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Address.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Address.java new file mode 100644 index 00000000..013d414e --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Address.java @@ -0,0 +1,16 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; +// +//@DataContract(code=0x02, name="Address" , description="") +//public interface Address { +// +// @DataField(order=1, primitiveType=ValueType.TEXT) +// String getStreet(); +// +// @DataField(order=2, primitiveType=ValueType.INT32) +// int getNumber(); +// +//} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressCodeDuplicate.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressCodeDuplicate.java new file mode 100644 index 00000000..7e60715e --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressCodeDuplicate.java @@ -0,0 +1,19 @@ +package test.com.jd.blockchain.binaryproto.contract; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/7/9. + */ +@DataContract(code=0x02, name="Address" , description="") +public interface AddressCodeDuplicate { + + @DataField(order=1, primitiveType= ValueType.TEXT) + String getStreet(); + + @DataField(order=2, primitiveType=ValueType.INT32) + int getNumber(); + +} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressOrderDuplicate.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressOrderDuplicate.java new file mode 100644 index 00000000..cf23898f --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/AddressOrderDuplicate.java @@ -0,0 +1,19 @@ +package test.com.jd.blockchain.binaryproto.contract; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/7/9. + */ +@DataContract(code=0x03, name="Address" , description="") +public interface AddressOrderDuplicate { + + @DataField(order=1, primitiveType= ValueType.TEXT) + String getStreet(); + + @DataField(order=1, primitiveType=ValueType.INT32) + int getNumber(); + +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Array.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Array.java new file mode 100644 index 00000000..802cfb15 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Array.java @@ -0,0 +1,25 @@ +package test.com.jd.blockchain.binaryproto.contract; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +@DataContract(code=0x08, name="Array" , description="") +public interface Array { + + @DataField(order=1, primitiveType= ValueType.INT32, list=true) + int[] getScores(); + + @DataField(order=2, primitiveType=ValueType.TEXT, list=true) + String[] getFeatures(); + + @DataField(order=3, primitiveType=ValueType.BYTES) + byte[] getFamilyMemberAges(); + + @DataField(order=4, primitiveType=ValueType.INT64, list=true) + long[] getFamilyMemberIds(); + +} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoAlgorithm.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoAlgorithm.java new file mode 100644 index 00000000..d4900959 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoAlgorithm.java @@ -0,0 +1,158 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.EnumContract; +//import com.jd.blockchain.binaryproto.EnumField; +//import com.jd.blockchain.binaryproto.ValueType; +//import com.jd.blockchain.crypto.CryptoAlgorithmType; +//import my.utils.io.BytesUtils; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@EnumContract(code=0x0102) +//public enum CryptoAlgorithm { +// +// // Hash 类; +// // SHA_128(CryptoAlgorithmMask.HASH, (byte) 0x01, false, false), +// +// SHA_256(CryptoAlgorithmType.HASH, (byte) 0x01, false, false), +// +// RIPLE160(CryptoAlgorithmType.HASH, (byte) 0x02, false, false), +// +// SM3(CryptoAlgorithmType.HASH, (byte) 0x03, false, false), +// +// // 非对称签名/加密算法; +// +// /** +// * RSA 签名算法;可签名,可加密; +// */ +// RSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x01, true, true), +// +// /** +// * ED25519 签名算法;只用于签名,没有加密特性; +// */ +// ED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x02, true, false), +// +// /** +// * ECDSA 签名算法;只用于签名,没有加密特性;??? +// */ +// ECDSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x03, true, false), +// +// /** +// * 国密 SM2 算法;可签名,可加密; +// */ +// SM2(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x04, true, true), +// +// // 对称加密; +// /** +// * AES 算法;可加密; +// */ +// AES(CryptoAlgorithmType.SYMMETRIC, (byte) 0x01, false, true), +// +// SM4(CryptoAlgorithmType.SYMMETRIC, (byte) 0x02, false, true), +// +// // 随机性; +// /** +// * ?????  一种随机数算法,待定; +// */ +// JAVA_SECURE(CryptoAlgorithmType.RANDOM, (byte) 0x01, false, false); +// +// /** +// * 密码算法的代号;
+// * 注:只占16位; +// */ +// @EnumField(type = ValueType.INT8) +// public final byte CODE; +// +// private final boolean signable; +// +// private final boolean encryptable; +// +// private CryptoAlgorithm(byte algType, byte algId, boolean signable, boolean encryptable) { +// this.CODE = (byte) (algType | algId); +// this.signable = signable; +// this.encryptable = encryptable; +// } +// +// /** +// * 是否属于摘要算法; +// * +// * @return +// */ +// public boolean isHash() { +// return (CODE & CryptoAlgorithmType.HASH) == CryptoAlgorithmType.HASH; +// } +// +// /** +// * 是否属于非对称密码算法; +// * +// * @return +// */ +// public boolean isAsymmetric() { +// return (CODE & CryptoAlgorithmType.ASYMMETRIC) == CryptoAlgorithmType.ASYMMETRIC; +// } +// +// /** +// * 是否属于对称密码算法; +// * +// * @return +// */ +// public boolean isSymmetric() { +// return (CODE & CryptoAlgorithmType.SYMMETRIC) == CryptoAlgorithmType.SYMMETRIC; +// } +// +// /** +// * 是否属于随机数算法; +// * +// * @return +// */ +// public boolean isRandom() { +// return (CODE & CryptoAlgorithmType.RANDOM) == CryptoAlgorithmType.RANDOM; +// } +// +// /** +// * 是否支持签名操作; +// * +// * @return +// */ +// public boolean isSignable() { +// return signable; +// } +// +// /** +// * 是否支持加密操作; +// * +// * @return +// */ +// public boolean isEncryptable() { +// return encryptable; +// } +// +// /** +// * 返回指定编码对应的枚举实例;
+// * +// * 如果不存在,则返回 null; +// * +// * @param code +// * @return +// */ +// public static CryptoAlgorithm valueOf(byte code) { +// for (CryptoAlgorithm alg : CryptoAlgorithm.values()) { +// if (alg.CODE == code) { +// return alg; +// } +// } +// throw new IllegalArgumentException("CryptoAlgorithm doesn't support enum code[" + code + "]!"); +// } +// +// // /** +// // * @return +// // */ +// // public byte[] toBytes() { +// // byte[] bytes = BytesUtils.toBytes(CODE); +// // byte[] result = new byte[BYTES_SIZE]; +// // System.arraycopy(bytes, 2, result, 0, 2); +// // // TODO: 只返回最后2个字节; +// // return result; +// // } +//} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoSetting.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoSetting.java new file mode 100644 index 00000000..35662d50 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/CryptoSetting.java @@ -0,0 +1,41 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@DataContract(code=0x0d, name="CryptoSetting", description = "Crypto setting") +//public interface CryptoSetting { +// +// /** +// * 系统中使用的 Hash 算法;
+// * +// * 对于历史数据,如果它未发生更改,则总是按照该数据产生时采用的算法进行校验,即使当时指定的Hash算法和当前的不同;
+// * +// * 如果对数据进行了更新,则采用新的 Hash 算法来计算生成完整性证明; +// * +// * @return +// */ +// @DataField(order=1, refEnum=true) +// public HashAlgorithm getHashAlgorithm(); +// +// @DataField(order=2, refEnum=true) +// public CryptoAlgorithm getHashAlgorithm1(); +// +// /** +// * 当有完整性证明的数据被从持久化介质中加载时,是否对其进行完整性校验(重新计算 hash 比对是否一致);
+// * +// * 如果为 true ,则自动进行校验,如果校验失败,会引发异常;
+// * +// * 注意:开启此选项将对性能会产生负面影响,因此使用者需要在性能和数据安全性之间做出权衡; +// * +// * @return +// */ +// @DataField(order=3, primitiveType= ValueType.BOOLEAN) +// public boolean getAutoVerifyHash();//func name is getxxxxx type +// +//} +// diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/HashAlgorithm.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/HashAlgorithm.java new file mode 100644 index 00000000..7f8296ff --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/HashAlgorithm.java @@ -0,0 +1,50 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.EnumContract; +//import com.jd.blockchain.binaryproto.EnumField; +//import com.jd.blockchain.binaryproto.ValueType; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@EnumContract(code=0x0101) +//public enum HashAlgorithm { +// +// RIPE160((byte) 1), +// +// SHA256((byte) 2), +// +// SM3((byte) 4); +// +// @EnumField(type = ValueType.INT8) +// public final byte CODE; +// +// private HashAlgorithm(byte algorithm) { +// CODE = algorithm; +// } +// +// public byte getAlgorithm() { +// return CODE; +// } +// +// public static HashAlgorithm valueOf(byte algorithm) { +// for (HashAlgorithm hashAlgorithm : HashAlgorithm.values()) { +// if (hashAlgorithm.CODE == algorithm) { +// return hashAlgorithm; +// } +// } +// throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); +// } +// +// public static void checkHashAlgorithm(HashAlgorithm algorithm) { +// switch (algorithm) { +// case RIPE160: +// break; +// case SHA256: +// break; +// default: +// throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); +// } +// } +//} +// diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerBlock.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerBlock.java new file mode 100644 index 00000000..74e206be --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerBlock.java @@ -0,0 +1,28 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; +//import com.jd.blockchain.crypto.hash.HashDigest; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@DataContract(code=0x12, name="LedgerBlock", description ="LedgerBlock") +//public interface LedgerBlock extends LedgerDataSnapshot{ +// +// @DataField(order=1, refHashDigest=true) +// HashDigest getHash(); +// +// @DataField(order=2, refHashDigest=true) +// HashDigest getPreviousHash(); +// +// @DataField(order=3, refHashDigest=true) +// HashDigest getLedgerHash(); +// +// @DataField(order=4, primitiveType=ValueType.INT64) +// long getHeight(); +// +// @DataField(order=5, refHashDigest=true) +// HashDigest getTransactionSetHash(); +//} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerDataSnapshot.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerDataSnapshot.java new file mode 100644 index 00000000..683e13f4 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerDataSnapshot.java @@ -0,0 +1,34 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.crypto.hash.HashDigest; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@DataContract(code=0x11, name="LedgerDataSnapshot", description ="LedgerDataSnapshot") +//public interface LedgerDataSnapshot { +// +// @DataField(order=1, refHashDigest=true) +// HashDigest getAdminAccountHash(); +// +// @DataField(order=2, refHashDigest=true) +// HashDigest getUserAccountSetHash(); +// +// @DataField(order=3, refHashDigest=true) +// HashDigest getUserPrivilegeHash(); +// +// @DataField(order=4, refHashDigest=true) +// HashDigest getDataAccountSetHash(); +// +// @DataField(order=5, refHashDigest=true) +// HashDigest getDataPrivilegeHash(); +// +// @DataField(order=6, refHashDigest=true) +// HashDigest getContractAccountSetHash(); +// +// @DataField(order=7, refHashDigest=true) +// HashDigest getContractPrivilegeHash(); +// +//} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerMetadata.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerMetadata.java new file mode 100644 index 00000000..d40babdf --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerMetadata.java @@ -0,0 +1,25 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@DataContract(code=0x0b, name="LedgerMetadata", description = "Ledger meta data") +//public interface LedgerMetadata { +// +// @DataField(order=1, primitiveType= ValueType.INT8, list=true) +// byte[] getSeed(); +// +// @DataField(order = 2, refContract=true) +// LedgerSetting getSetting(); +// +// @DataField(order=3, primitiveType=ValueType.INT8, list=true) +// byte[] getPrivilegesHash(); +// +// @DataField(order=4, primitiveType=ValueType.INT8, list=true) +// byte[] getParticipantsHash(); +// +//} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerSetting.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerSetting.java new file mode 100644 index 00000000..6f2f383b --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/LedgerSetting.java @@ -0,0 +1,21 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@DataContract(code=0x0c, name="LedgerSetting", description = "Ledger setting") +//public interface LedgerSetting { +// +// //@DataField(order=1, refContract=true) +// //ConsensusSetting getConsensusSetting(); +// +// @DataField(order=2, refContract=true) +// CryptoSetting getCryptoSetting(); +// +// @DataField(order=3, refContract=true) +// PrivilegeModelSetting getPrivilegesModelSetting(); +// +//} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Level.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Level.java new file mode 100644 index 00000000..7b74768c --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Level.java @@ -0,0 +1,25 @@ +package test.com.jd.blockchain.binaryproto.contract; + +import com.jd.blockchain.binaryproto.EnumContract; +import com.jd.blockchain.binaryproto.EnumField; +import com.jd.blockchain.utils.ValueType; + +@EnumContract(code=0x0100) +public enum Level { + + V1((byte) 1), + + V2((byte) 2); + + @EnumField(type=ValueType.INT8) + public final byte CODE; + public byte getCode() { + return CODE; + } + private Level(byte code) { + this.CODE = code; + } + +} + + diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Privilege.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Privilege.java new file mode 100644 index 00000000..3b1d53de --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Privilege.java @@ -0,0 +1,18 @@ +//package test.com.jd.blockchain.binaryproto.contract; +// +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; +// +///** +// * Created by zhangshuang3 on 2018/7/30. +// */ +//@DataContract(code=0x10, name="Privilege", description ="Privilege") +//public interface Privilege { +// +// //SortedSet getOpCodes(); implement later +// +// @DataField(order=2, primitiveType= ValueType.INT64) +// long getVersion(); +// +//} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/PrivilegeModelSetting.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/PrivilegeModelSetting.java new file mode 100644 index 00000000..65df415b --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/PrivilegeModelSetting.java @@ -0,0 +1,20 @@ +package test.com.jd.blockchain.binaryproto.contract; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * Created by zhangshuang3 on 2018/7/30. + */ +@DataContract(code=0x0f, name="PrivilegeModelSetting", description ="Privilege Model setting") +public interface PrivilegeModelSetting { + + @DataField(order=1, primitiveType= ValueType.INT64) + long getLatestVersion(); + + //@DataField(order=2, refContract=true) + //Privilege getPrivilege(long version); + +} + diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefContract.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefContract.java new file mode 100644 index 00000000..0decffb1 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefContract.java @@ -0,0 +1,20 @@ +//package test.com.jd.blockchain.binaryproto.contract; + +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +/* +@DataContract(code=0x07, name="RefContract" , description="") +public interface RefContract { + + @DataField(order=1, refContract=true) + Address getAddress(); + + @DataField(order=2, refContract=true) + Address getAddress1(); + +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefEnum.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefEnum.java new file mode 100644 index 00000000..793d0457 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/RefEnum.java @@ -0,0 +1,15 @@ +package test.com.jd.blockchain.binaryproto.contract; + +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +@DataContract(code=0x06, name="RefEnum" , description="") +public interface RefEnum { + + @DataField(order=1, refEnum=true) + Level getLevel(); + +} \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Student.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Student.java new file mode 100644 index 00000000..29d6dae5 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/Student.java @@ -0,0 +1,41 @@ +//package test.com.jd.blockchain.binaryproto.contract; + +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; + +/* +@DataContract(code=0x01, name="测试数据契约1", description = "用于测试的数据契约") +public interface Student { + + @DataField(order=1, primitiveType=ValueType.INT32) + int getId(); + + @DataField(order=2, primitiveType=ValueType.INT8) + byte getFamilyMemberNum(); + + @DataField(order=3, primitiveType=ValueType.TEXT) + String getName(); + + @DataField(order=4, primitiveType=ValueType.INT16) + short getAge(); + + @DataField(order=5, refEnum=true) + Level getLevel(); + + @DataField(order=6, refContract=true) + Address getAddress(); + + @DataField(order=7, primitiveType=ValueType.INT32, list=true) + int[] getScores(); + + @DataField(order=8, primitiveType=ValueType.TEXT, list=true) + String[] getFeatures(); + + @DataField(order=9, primitiveType=ValueType.INT8, list=true) + byte[] getFamilyMemberAges(); + + @DataField(order=10, primitiveType=ValueType.INT64, list=true) + long[] getFamilyMemberIds(); +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/StudentInvert.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/StudentInvert.java new file mode 100644 index 00000000..b82f23c1 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/StudentInvert.java @@ -0,0 +1,44 @@ +//package test.com.jd.blockchain.binaryproto.contract; + +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; + +/** + * Created by zhangshuang3 on 2018/7/9. + */ +/* +@DataContract(code=0x04, name="测试数据契约1", description = "用于测试的数据契约") +public interface StudentInvert { + + @DataField(order=10, primitiveType=ValueType.INT64, list=true) + long[] getFamilyMemberIds(); + + @DataField(order=9, primitiveType=ValueType.INT8, list=true) + byte[] getFamilyMemberAges(); + + @DataField(order=8, primitiveType=ValueType.TEXT, list=true) + String[] getFeatures(); + + @DataField(order=7, primitiveType=ValueType.INT32, list=true) + int[] getScores(); + + @DataField(order=6, refContract=true) + Address getAddress(); + + @DataField(order=5, refEnum=true) + Level getLevel(); + + @DataField(order=4, primitiveType=ValueType.INT16) + short getAge(); + + @DataField(order=3, primitiveType=ValueType.TEXT) + String getName(); + + @DataField(order=2, primitiveType=ValueType.INT8) + byte getFamilyMemberNum(); + + @DataField(order=1, primitiveType= ValueType.INT32) + int getId(); +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/User.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/User.java new file mode 100644 index 00000000..fa6adef1 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/User.java @@ -0,0 +1,31 @@ +//package test.com.jd.blockchain.binaryproto.contract; + +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.ValueType; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +//import my.utils.io.ByteArray; + +/** + * Created by zhangshuang3 on 2018/7/25. + */ +/* +@DataContract(code=0x09, name="UserAccount", description = "用户账户") +public interface User { + + @DataField(order=1, primitiveType= ValueType.TEXT) + String getAddress(); + + @DataField(order=2, refPubKey = true) + PubKey getPubKey(); + + @DataField(order=3, refPubKey = true) + PubKey getDataPubKey(); + + @DataField(order=4, primitiveType = ValueType.BYTES) + ByteArray getDataHash(); + + @DataField(order=5, primitiveType = ValueType.BYTES) + ByteArray getPrivilegeHash(); +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/AddressImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/AddressImpl.java new file mode 100644 index 00000000..c2af3153 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/AddressImpl.java @@ -0,0 +1,29 @@ +/* +package test.com.jd.blockchain.binaryproto.contract.impl; + +import test.com.jd.blockchain.binaryproto.contract.Address; + +public class AddressImpl implements Address { + private String street; + private int number; + + @Override + public String getStreet() { + // TODO Auto-generated method stub + return this.street; + } + public void setStreet(String street) { + this.street = street; + } + + @Override + public int getNumber() { + // TODO Auto-generated method stub + return this.number; + } + public void setNumber(int number) { + this.number = number; + } + +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/ArrayImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/ArrayImpl.java new file mode 100644 index 00000000..6c9cd628 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/ArrayImpl.java @@ -0,0 +1,47 @@ +package test.com.jd.blockchain.binaryproto.contract.impl; + +import test.com.jd.blockchain.binaryproto.contract.*; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +public class ArrayImpl implements Array { + + private byte[] familyMemberAges; + private int[] scores; + private String[] features; + private long[] familyMemberIds; + + @Override + public int[] getScores() { + // TODO Auto-generated method stub + return this.scores; + } + public void setScores(int[] scores) { + this.scores = scores; + } + + @Override + public String[] getFeatures() { + return this.features; + } + public void setFeatures(String[] features) { + this.features = features; + } + + @Override + public byte[] getFamilyMemberAges() { + return this.familyMemberAges; + } + public void setFamilyMemberAges(byte[] familyMemberAge) { + this.familyMemberAges = familyMemberAge; + } + + @Override + public long[] getFamilyMemberIds() { + return this.familyMemberIds; + } + public void setFamilyMemberIds(long[] familyMemberId) { + this.familyMemberIds = familyMemberId; + } +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/CryptoSettingImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/CryptoSettingImpl.java new file mode 100644 index 00000000..2fe4cb23 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/CryptoSettingImpl.java @@ -0,0 +1,40 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import test.com.jd.blockchain.binaryproto.contract.CryptoAlgorithm; +//import test.com.jd.blockchain.binaryproto.contract.CryptoSetting; +//import test.com.jd.blockchain.binaryproto.contract.HashAlgorithm; + +/** + * Created by zhangshuang3 on 2018/7/30. + */ +/* +public class CryptoSettingImpl implements CryptoSetting { + private HashAlgorithm hashAlgorithm; + private CryptoAlgorithm cryptoAlgorithm; + private boolean isAuto; + + @Override + public HashAlgorithm getHashAlgorithm() { + return this.hashAlgorithm; + } + public void setHashAlgorithm(HashAlgorithm hashAlgorithm) { + this.hashAlgorithm = hashAlgorithm; + } + + @Override + public CryptoAlgorithm getHashAlgorithm1() { + return this.cryptoAlgorithm; + } + public void setHashAlgorithm1(CryptoAlgorithm cryptoAlgorithm) { + this.cryptoAlgorithm = cryptoAlgorithm; + } + @Override + public boolean getAutoVerifyHash() { + return isAuto; + } + public void setAutoVerifyHash(boolean isAuto) { + this.isAuto = isAuto; + } + +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerBlockImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerBlockImpl.java new file mode 100644 index 00000000..803b4b5a --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerBlockImpl.java @@ -0,0 +1,147 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import com.jd.blockchain.binaryproto.DConstructor; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.binaryproto.FieldSetter; +//import com.jd.blockchain.crypto.hash.HashDigest; +//import test.com.jd.blockchain.binaryproto.contract.Address; +//import test.com.jd.blockchain.binaryproto.contract.LedgerBlock; + +/** + * Created by zhangshuang3 on 2018/7/30. + */ +/* +public class LedgerBlockImpl implements LedgerBlock { + + private HashDigest hash; + + private long height; + + private HashDigest ledgerHash; + + private HashDigest previousHash; + + private HashDigest adminAccountHash; + + private HashDigest userAccountSetHash; + + private HashDigest userPrivilegeHash; + + private HashDigest dataAccountSetHash; + + private HashDigest dataPrivilegeHash; + + private HashDigest contractAccountSetHash; + + private HashDigest contractPrivilegeHash; + + private HashDigest transactionSetHash; + + public void setAdminAccountHash(HashDigest adminAccountHash) { + this.adminAccountHash = adminAccountHash; + } + + public void setUserAccountSetHash(HashDigest userAccountSetHash) { + this.userAccountSetHash = userAccountSetHash; + } + + public void setUserPrivilegeHash(HashDigest userPrivilegeHash) { + this.userPrivilegeHash = userPrivilegeHash; + } + + public void setDataAccountSetHash(HashDigest dataAccountSetHash) { + this.dataAccountSetHash = dataAccountSetHash; + } + + public void setDataPrivilegeHash(HashDigest dataPrivilegeHash) { + this.dataPrivilegeHash = dataPrivilegeHash; + } + + public void setContractAccountSetHash(HashDigest contractAccountSetHash) { + this.contractAccountSetHash = contractAccountSetHash; + } + + public void setContractPrivilegeHash(HashDigest contractPrivilegeHash) { + this.contractPrivilegeHash = contractPrivilegeHash; + } + + public void setTransactionSetHash(HashDigest transactionSetHash) { + this.transactionSetHash = transactionSetHash; + } + + public LedgerBlockImpl() { + } + + @DConstructor(name="LedgerBlockImpl") + public LedgerBlockImpl(@FieldSetter(name="getHeight", type="long") long height, @FieldSetter(name="getLedgerHash", type="HashDigest") HashDigest ledgerHash, @FieldSetter(name="getPreviousHash", type="HashDigest") HashDigest previousHash) { + this.height = height; + this.ledgerHash = ledgerHash; + this.previousHash = previousHash; + } + + @Override + public HashDigest getHash() { + return hash; + } + + @Override + public HashDigest getPreviousHash() { + return previousHash; + } + + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + + @Override + public long getHeight() { + return height; + } + + @Override + public HashDigest getAdminAccountHash() { + return adminAccountHash; + } + + @Override + public HashDigest getUserAccountSetHash() { + return userAccountSetHash; + } + + @Override + public HashDigest getUserPrivilegeHash() { + return userPrivilegeHash; + } + + @Override + public HashDigest getDataAccountSetHash() { + return dataAccountSetHash; + } + + @Override + public HashDigest getDataPrivilegeHash() { + return dataPrivilegeHash; + } + + @Override + public HashDigest getContractAccountSetHash() { + return contractAccountSetHash; + } + + @Override + public HashDigest getContractPrivilegeHash() { + return contractPrivilegeHash; + } + + @Override + public HashDigest getTransactionSetHash() { + return transactionSetHash; + } + + public void setHash(HashDigest blockHash) { + this.hash = blockHash; + } + +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerMetadataImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerMetadataImpl.java new file mode 100644 index 00000000..7267377b --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerMetadataImpl.java @@ -0,0 +1,56 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import test.com.jd.blockchain.binaryproto.contract.*; +//import test.com.jd.blockchain.binaryproto.contract.LedgerMetadata; +//import test.com.jd.blockchain.binaryproto.contract.LedgerSetting; + +/** + * Created by zhangshuang3 on 2018/7/30. + */ +/* +public class LedgerMetadataImpl implements LedgerMetadata { + private byte[] seed; + + private LedgerSetting setting; + + private byte[] privilegesHash; + + private byte[] participantsHash; + + @Override + public byte[] getSeed() { + return seed; + } + + @Override + public LedgerSetting getSetting() { + return setting; + } + + @Override + public byte[] getPrivilegesHash() { + return privilegesHash; + } + + @Override + public byte[] getParticipantsHash() { + return participantsHash; + } + + public void setSeed(byte[] seed) { + this.seed = seed; + } + + public void setSetting(LedgerSetting setting) { + this.setting = setting; + } + + public void setPrivilegesHash(byte[] privilegesHash) { + this.privilegesHash = privilegesHash; + } + + public void setParticipantsHash(byte[] participantsHash) { + this.participantsHash = participantsHash; + } +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerSettingImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerSettingImpl.java new file mode 100644 index 00000000..1af4b7a5 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/LedgerSettingImpl.java @@ -0,0 +1,30 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import test.com.jd.blockchain.binaryproto.contract.LedgerSetting; +//import test.com.jd.blockchain.binaryproto.contract.CryptoSetting; +//import test.com.jd.blockchain.binaryproto.contract.PrivilegeModelSetting; + +/** + * Created by zhangshuang3 on 2018/7/30. + */ +/* +public class LedgerSettingImpl implements LedgerSetting { + private CryptoSetting cryptoSetting; + private PrivilegeModelSetting privilegeModelSetting; + + @Override + public CryptoSetting getCryptoSetting() { + return this.cryptoSetting; + } + public void setCryptoSetting(CryptoSetting cryptoSetting) { + this.cryptoSetting = cryptoSetting; + } + @Override + public PrivilegeModelSetting getPrivilegesModelSetting() { + return this.privilegeModelSetting; + } + public void setPrivilegesModelSetting(PrivilegeModelSetting privilegeModelSetting) { + this.privilegeModelSetting = privilegeModelSetting; + } +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/PrivilegeModelSettingImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/PrivilegeModelSettingImpl.java new file mode 100644 index 00000000..5c1b8c5b --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/PrivilegeModelSettingImpl.java @@ -0,0 +1,22 @@ +package test.com.jd.blockchain.binaryproto.contract.impl; + +import test.com.jd.blockchain.binaryproto.contract.PrivilegeModelSetting; + +/** + * Created by zhangshuang3 on 2018/7/30. + */ +public class PrivilegeModelSettingImpl implements PrivilegeModelSetting { + long latestVersion; + + @Override + public long getLatestVersion() { + return this.latestVersion; + } + + public void setLatestVersion(long latestVersion) { + this.latestVersion = latestVersion; + } + + //Privilege getPrivilege(long version) { + //} +} diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefContractImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefContractImpl.java new file mode 100644 index 00000000..700cbf66 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefContractImpl.java @@ -0,0 +1,32 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import test.com.jd.blockchain.binaryproto.contract.Address; +//import test.com.jd.blockchain.binaryproto.contract.RefContract; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +/* +public class RefContractImpl implements RefContract { + private Address address; + private Address address1; + + @Override + public Address getAddress() { + // TODO Auto-generated method stub + return this.address; + } + public void setAddress(Address address) { + this.address = address; + } + + @Override + public Address getAddress1() { + // TODO Auto-generated method stub + return this.address1; + } + public void setAddress1(Address address) { + this.address1 = address; + } +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefEnumImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefEnumImpl.java new file mode 100644 index 00000000..64bcc30b --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/RefEnumImpl.java @@ -0,0 +1,22 @@ +package test.com.jd.blockchain.binaryproto.contract.impl; + +import test.com.jd.blockchain.binaryproto.contract.Level; +import test.com.jd.blockchain.binaryproto.contract.RefEnum; + +/** + * Created by zhangshuang3 on 2018/7/11. + */ +public class RefEnumImpl implements RefEnum { + + private Level level; + + @Override + public Level getLevel() { + // TODO Auto-generated method stub + return this.level; + } + public void setLevel(Level level) { + this.level = level; + } +} + diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/StudentImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/StudentImpl.java new file mode 100644 index 00000000..5b8cfacd --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/StudentImpl.java @@ -0,0 +1,108 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import test.com.jd.blockchain.binaryproto.contract.Address; +//import test.com.jd.blockchain.binaryproto.contract.Level; +//import test.com.jd.blockchain.binaryproto.contract.Student; +//import test.com.jd.blockchain.binaryproto.contract.StudentInvert; + +/* +public class StudentImpl implements Student,StudentInvert { + + private int id; + private short age; + private String name; + private Level level; + private Address address; + private int[] scores; + private String[] features; + private byte familyMemberNum; + private byte[] familyMemberAges; + private long[] familyMemberIds; + + + @Override + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public String getName() { + // TODO Auto-generated method stub + return this.name; + } + public void setName(String name) { + this.name = name; + } + + @Override + public Level getLevel() { + // TODO Auto-generated method stub + return this.level; + } + public void setLevel(Level level) { + this.level = level; + } + + @Override + public Address getAddress() { + // TODO Auto-generated method stub + return this.address; + } + public void setAddress(Address address) { + this.address = address; + } + + public short getAge() { + return age; + } + + public void setAge(short age) { + this.age = age; + } + + @Override + public int[] getScores() { + // TODO Auto-generated method stub + return this.scores; + } + public void setScores(int[] scores) { + this.scores = scores; + } + + @Override + public String[] getFeatures() { + return this.features; + } + public void setFeatures(String[] features) { + this.features = features; + } + + @Override + public byte getFamilyMemberNum() { + return this.familyMemberNum; + } + public void setFamilyMemberNum(byte familyMemberNum) { + this.familyMemberNum = familyMemberNum; + } + + @Override + public byte[] getFamilyMemberAges() { + return this.familyMemberAges; + } + public void setFamilyMemberAges(byte[] familyMemberAge) { + this.familyMemberAges = familyMemberAge; + } + + @Override + public long[] getFamilyMemberIds() { + return this.familyMemberIds; + } + public void setFamilyMemberIds(long[] familyMemberId) { + this.familyMemberIds = familyMemberId; + } +} +*/ \ No newline at end of file diff --git a/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/UserImpl.java b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/UserImpl.java new file mode 100644 index 00000000..09fb3cf1 --- /dev/null +++ b/source/binary-proto/src/test/java/test/com/jd/blockchain/binaryproto/contract/impl/UserImpl.java @@ -0,0 +1,51 @@ +//package test.com.jd.blockchain.binaryproto.contract.impl; + +//import com.jd.blockchain.binaryproto.DConstructor; +//import com.jd.blockchain.binaryproto.FieldSetter; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +//import my.utils.io.ByteArray; +//import test.com.jd.blockchain.binaryproto.contract.User; + +/** + * Created by zhangshuang3 on 2018/7/24. + */ +/* +public class UserImpl implements User { + String address; + PubKey pubKey; + PubKey dataPubKey; + ByteArray dataHash; + ByteArray privHash; + + @DConstructor(name = "UserImpl") + public UserImpl(@FieldSetter(name = "getAddress", type = "String") String address, @FieldSetter(name = "getPubKey",type = "PubKey") PubKey pubKey, @FieldSetter(name = "getDataPubKey",type = "PubKey") PubKey dataPubKey, + @FieldSetter(name = "getDataHash", type = "byte[]") byte[] dataHash, @FieldSetter(name = "getPrivilegeHash", type = "byte[]") byte[] privHash) { + this.address = address; + this.pubKey = pubKey; + this.dataPubKey = dataPubKey; + this.dataHash = ByteArray.wrap(dataHash); + this.privHash = ByteArray.wrap(privHash); + } + + @Override + public String getAddress() { + return address; + } + @Override + public PubKey getPubKey() { + return pubKey; + } + @Override + public PubKey getDataPubKey() { + return dataPubKey; + } + @Override + public ByteArray getDataHash() { + return dataHash; + } + @Override + public ByteArray getPrivilegeHash() { + return privHash; + } +} +*/ \ No newline at end of file diff --git a/source/consensus/consensus-bftsmart/pom.xml b/source/consensus/consensus-bftsmart/pom.xml new file mode 100644 index 00000000..ddf7b73a --- /dev/null +++ b/source/consensus/consensus-bftsmart/pom.xml @@ -0,0 +1,66 @@ + + 4.0.0 + + com.jd.blockchain + consensus + 0.8.2.RELEASE + + consensus-bftsmart + + + + com.jd.blockchain + bft-smart + + + com.jd.blockchain + base + ${project.version} + + + com.jd.blockchain + consensus-framework + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + org.apache.commons + commons-pool2 + + + com.jd.blockchain + ledger-model + ${project.version} + + + com.jd.blockchain + tools-keygen + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + + \ No newline at end of file diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingConfig.java new file mode 100644 index 00000000..d8e7c115 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingConfig.java @@ -0,0 +1,67 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.crypto.asymmetric.PubKey; + +public class BftsmartClientIncomingConfig implements BftsmartClientIncomingSettings { + + private BftsmartConsensusSettings consensusSettings; + + private byte[] topology; + + private byte[] tomConfig; + + private int clientId; + + private PubKey pubKey; + + + @Override + public BftsmartConsensusSettings getConsensusSettings() { + return consensusSettings; + } + + public void setConsensusSettings(BftsmartConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + } + + @Override + public byte[] getTopology() { + return topology; + } + + public void setTopology(byte[] topology) { + this.topology = topology; + } + + @Override + public int getClientId() { + return clientId; + } + + @Override + public String getProviderName() { + return BftsmartConsensusProvider.NAME; + } + + public void setClientId(int clientId) { + this.clientId = clientId; + } + + @Override + public byte[] getTomConfig() { + return tomConfig; + } + + public void setTomConfig(byte[] tomConfig) { + this.tomConfig = tomConfig; + } + + @Override + public PubKey getPubKey() { + return pubKey; + } + + public void setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingSettings.java new file mode 100644 index 00000000..9a1548b6 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartClientIncomingSettings.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_CLI_INCOMING_SETTINGS) +public interface BftsmartClientIncomingSettings extends ClientIncomingSettings { + + @DataField(order = 1, primitiveType = ValueType.BYTES) + byte[] getTopology(); + + @DataField(order = 2, primitiveType = ValueType.BYTES) + byte[] getTomConfig(); + + @DataField(order = 3, primitiveType=ValueType.BYTES) + PubKey getPubKey(); + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockConfig.java new file mode 100644 index 00000000..23f682ce --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockConfig.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.consensus.bftsmart; + +public class BftsmartCommitBlockConfig implements BftsmartCommitBlockSettings { + + private int txSizePerBlock; + + private long maxDelayMilliSecondsPerBlock; + + + public BftsmartCommitBlockConfig() { + + } + + public BftsmartCommitBlockConfig(int txSizePerBlock, long maxDelayMilliSecondsPerBlock) { + this.txSizePerBlock = txSizePerBlock; + this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; + } + @Override + public int getTxSizePerBlock() { + return txSizePerBlock; + } + + public void setTxSizePerBlock(int txSizePerBlock) { + this.txSizePerBlock = txSizePerBlock; + } + + @Override + public long getMaxDelayMilliSecondsPerBlock() { + return maxDelayMilliSecondsPerBlock; + } + + public void setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) { + this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockSettings.java new file mode 100644 index 00000000..749fdaed --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartCommitBlockSettings.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + + +@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_BLOCK_SETTINGS) +public interface BftsmartCommitBlockSettings { + + @DataField(order = 0, primitiveType = ValueType.INT32) + int getTxSizePerBlock(); + + @DataField(order = 1, primitiveType = ValueType.INT64) + long getMaxDelayMilliSecondsPerBlock(); +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusConfig.java new file mode 100644 index 00000000..ca3a301e --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusConfig.java @@ -0,0 +1,52 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.utils.Property; + +public class BftsmartConsensusConfig implements BftsmartConsensusSettings { + + private Property[] bftsmartSystemConfig; + + private BftsmartNodeSettings[] nodes; + + private BftsmartCommitBlockSettings commitBlockSettings; + + static { + DataContractRegistry.register(BftsmartConsensusSettings.class); + } + /** + * 创建 bftsmart 共识配置; + * + * @param nodes + * 节点列表 + * @param commitBlockSettings + * 结块设置; + * @param bftsmartSystemConfigs + * bftsmart系统配置; + */ + public BftsmartConsensusConfig(BftsmartNodeSettings[] nodes, BftsmartCommitBlockSettings commitBlockSettings, Property[] bftsmartSystemConfigs) { + this.nodes = nodes; + this.commitBlockSettings = commitBlockSettings; + this.bftsmartSystemConfig = bftsmartSystemConfigs; + } + + @Override + public BftsmartNodeSettings[] getNodes() { + return nodes; + } + + @Override + public Property[] getSystemConfigs() { + return bftsmartSystemConfig; + } + + @Override + public BftsmartCommitBlockSettings getCommitBlockSettings() { + return commitBlockSettings; + } + + + public void setCommitBlockSettings(BftsmartCommitBlockSettings commitBlockSettings) { + this.commitBlockSettings = commitBlockSettings; + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusProvider.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusProvider.java new file mode 100644 index 00000000..a8290286 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusProvider.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.SettingsFactory; +import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClientFactory; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServerFactory; +import com.jd.blockchain.consensus.client.ClientFactory; +import com.jd.blockchain.consensus.service.NodeServerFactory; + +public class BftsmartConsensusProvider implements ConsensusProvider { + + public static final String NAME = BftsmartConsensusProvider.class.getName(); + + private static BftsmartSettingsFactory settingsFactory = new BftsmartSettingsFactory(); + + private static BftsmartConsensusClientFactory clientFactory = new BftsmartConsensusClientFactory(); + + private static BftsmartNodeServerFactory nodeServerFactory = new BftsmartNodeServerFactory(); + + + @Override + public String getName() { + return NAME; + } + + @Override + public SettingsFactory getSettingsFactory() { + return settingsFactory; + } + + @Override + public ClientFactory getClientFactory() { + return clientFactory; + } + + @Override + public NodeServerFactory getServerFactory() { + return nodeServerFactory; + } + + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettings.java new file mode 100644 index 00000000..ae72383e --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettings.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.utils.Property; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_SETTINGS) +public interface BftsmartConsensusSettings extends ConsensusSettings { + + @DataField(order = 1, primitiveType = ValueType.BYTES, list=true) + Property[] getSystemConfigs(); + + @DataField(order = 2, refContract = true) + BftsmartCommitBlockSettings getCommitBlockSettings(); + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettingsBuilder.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettingsBuilder.java new file mode 100644 index 00000000..6c25f0ac --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartConsensusSettingsBuilder.java @@ -0,0 +1,213 @@ +package com.jd.blockchain.consensus.bftsmart; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.PropertiesUtils; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.ConsensusSettingsBuilder; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +public class BftsmartConsensusSettingsBuilder implements ConsensusSettingsBuilder { + + private static final int DEFAULT_TXSIZE = 1000; + + private static final int DEFAULT_MAXDELAY = 1000; + + private static final String CONFIG_TEMPLATE_FILE = "bftsmart.config"; + + /** + * 参数键:节点数量; + */ + public static final String SERVER_NUM_KEY = "system.servers.num"; + + /** + * 参数键:结块条件设置; + */ + public static final String BFTSMART_BLOCK_TXSIZE_KEY = "system.block.txsize"; + + public static final String BFTSMART_BLOCK_MAXDELAY_KEY = "system.block.maxdelay"; + + // /** + // * 参数键格式:节点地址; + // */ + // public static final String ADDRESS_PATTERN = "node.%s.address"; + + /** + * 参数键格式:节点公钥; + */ + public static final String PUBKEY_PATTERN = "system.server.%s.pubkey"; + + /** + * 参数键格式:节点共识服务的网络地址; + */ + public static final String CONSENSUS_HOST_PATTERN = "system.server.%s.network.host"; + + /** + * 参数键格式:节点共识服务的端口; + */ + public static final String CONSENSUS_PORT_PATTERN = "system.server.%s.network.port"; + + /** + * 参数键格式:节点共识服务的通讯是否开启安全选项; + */ + public static final String CONSENSUS_SECURE_PATTERN = "system.server.%s.network.secure"; + + private static Properties CONFIG_TEMPLATE; + static { + ClassPathResource configResource = new ClassPathResource(CONFIG_TEMPLATE_FILE); + try { + try (InputStream in = configResource.getInputStream()) { + CONFIG_TEMPLATE = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET); + } + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + //解析得到结块的相关配置信息 + public BftsmartCommitBlockConfig createBlockConfig(Properties resolvingProps) { + BftsmartCommitBlockConfig blockConfig = new BftsmartCommitBlockConfig(); + + String txSizeString = PropertiesUtils.getRequiredProperty(resolvingProps, BFTSMART_BLOCK_TXSIZE_KEY); + resolvingProps.remove(BFTSMART_BLOCK_TXSIZE_KEY); + + if (txSizeString == null || txSizeString.length() == 0) { + blockConfig.setTxSizePerBlock(DEFAULT_TXSIZE); + } + else { + blockConfig.setTxSizePerBlock(Integer.parseInt(txSizeString)); + } + + String maxDelayString = PropertiesUtils.getRequiredProperty(resolvingProps, BFTSMART_BLOCK_MAXDELAY_KEY); + resolvingProps.remove(BFTSMART_BLOCK_MAXDELAY_KEY); + + if (maxDelayString == null || maxDelayString.length() == 0) { + blockConfig.setMaxDelayMilliSecondsPerBlock(DEFAULT_MAXDELAY); + } + else { + blockConfig.setMaxDelayMilliSecondsPerBlock(Long.parseLong(maxDelayString)); + } + + return blockConfig; + } + + @Override + public Properties createPropertiesTemplate() { + return PropertiesUtils.cloneFrom(CONFIG_TEMPLATE); + } + + @Override + public BftsmartConsensusSettings createSettings(Properties props) { + Properties resolvingProps = PropertiesUtils.cloneFrom(props); + int serversNum = PropertiesUtils.getInt(resolvingProps, SERVER_NUM_KEY); + if (serversNum < 0) { + throw new IllegalArgumentException(String.format("Property[%s] is negative!", SERVER_NUM_KEY)); + } + if (serversNum < 4) { + throw new IllegalArgumentException(String.format("Property[%s] is less than 4!", SERVER_NUM_KEY)); + } + + BftsmartCommitBlockConfig blockConfig = createBlockConfig(resolvingProps); + + BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[serversNum]; + for (int i = 0; i < serversNum; i++) { + int id = i; + + String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id); + String base58PubKey = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfPubkey); +// PubKey pubKey = new PubKey(Base58Utils.decode(base58PubKey)); + PubKey pubKey = KeyGenCommand.decodePubKey(base58PubKey); + resolvingProps.remove(keyOfPubkey); + + String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id); + String networkAddressHost = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfHost); + resolvingProps.remove(keyOfHost); + + String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id); + int networkAddressPort = PropertiesUtils.getInt(resolvingProps, keyOfPort); + resolvingProps.remove(keyOfPort); + + String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id); + boolean networkAddressSecure = PropertiesUtils.getBoolean(resolvingProps, keyOfSecure); + resolvingProps.remove(keyOfSecure); + + BftsmartNodeConfig nodeConfig = new BftsmartNodeConfig(pubKey, id, + new NetworkAddress(networkAddressHost, networkAddressPort, networkAddressSecure)); + nodesSettings[i] = nodeConfig; + } + + BftsmartConsensusConfig config = new BftsmartConsensusConfig(nodesSettings, blockConfig, + PropertiesUtils.getOrderedValues(resolvingProps)); + return config; + } + + private static String keyOfNode(String pattern, int id) { + return String.format(pattern, id); + } + + @Override + public void writeSettings(ConsensusSettings settings, Properties props) { + int serversNum = PropertiesUtils.getInt(props, SERVER_NUM_KEY); + if (serversNum > 0) { + for (int i = 0; i < serversNum; i++) { + int id = i; + String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id); + props.remove(keyOfPubkey); + + String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id); + props.remove(keyOfHost); + + String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id); + props.remove(keyOfPort); + + String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id); + props.remove(keyOfSecure); + } + } + + BftsmartConsensusSettings bftsmartSettings = (BftsmartConsensusSettings) settings; + BftsmartNodeSettings[] nodesSettings = (BftsmartNodeSettings[]) bftsmartSettings.getNodes(); + serversNum = nodesSettings.length; + props.setProperty(SERVER_NUM_KEY, serversNum + ""); + + //获得结块相关的属性信息 + BftsmartCommitBlockSettings blockSettings = bftsmartSettings.getCommitBlockSettings(); + if (blockSettings == null) { + props.setProperty(BFTSMART_BLOCK_TXSIZE_KEY, DEFAULT_TXSIZE + ""); + props.setProperty(BFTSMART_BLOCK_MAXDELAY_KEY, DEFAULT_MAXDELAY + ""); + } else { + int txSize = blockSettings.getTxSizePerBlock(); + long maxDelay = blockSettings.getMaxDelayMilliSecondsPerBlock(); + props.setProperty(BFTSMART_BLOCK_TXSIZE_KEY, txSize + ""); + props.setProperty(BFTSMART_BLOCK_MAXDELAY_KEY, maxDelay + ""); + } + + for (int i = 0; i < serversNum; i++) { + BftsmartNodeSettings ns = nodesSettings[i]; + int id = i; + String keyOfPubkey = keyOfNode(PUBKEY_PATTERN, id); + props.setProperty(keyOfPubkey, ns.getPubKey().toBase58()); + + String keyOfHost = keyOfNode(CONSENSUS_HOST_PATTERN, id); + props.setProperty(keyOfHost, ns.getNetworkAddress() == null ? "" : ns.getNetworkAddress().getHost()); + + String keyOfPort = keyOfNode(CONSENSUS_PORT_PATTERN, id); + props.setProperty(keyOfPort, ns.getNetworkAddress() == null ? "" : ns.getNetworkAddress().getPort() + ""); + + String keyOfSecure = keyOfNode(CONSENSUS_SECURE_PATTERN, id); + props.setProperty(keyOfSecure, ns.getNetworkAddress() == null ? "false" : ns.getNetworkAddress().isSecure() + ""); + } + + PropertiesUtils.setValues(props, bftsmartSettings.getSystemConfigs()); + } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeConfig.java new file mode 100644 index 00000000..005e05c5 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeConfig.java @@ -0,0 +1,66 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class BftsmartNodeConfig implements BftsmartNodeSettings { + + private int id; + + private String address; + + private PubKey pubKey; + + private NetworkAddress networkAddress; + + public BftsmartNodeConfig() { + } + + static { + DataContractRegistry.register(BftsmartNodeSettings.class); + } + + public BftsmartNodeConfig(PubKey pubKey, int id, NetworkAddress networkAddress) { + this.address = AddressEncoding.generateAddress(pubKey).toBase58(); + this.pubKey = pubKey; + this.id = id; + this.networkAddress = networkAddress; + } + + @Override + public String getAddress() { + return address; + } + + @Override + public int getId() { + return id; + } + + @Override + public NetworkAddress getNetworkAddress() { + return networkAddress; + } + + public void setId(int id) { + this.id = id; + } + + public void setAddress(String address) { + this.address = address; + } + + public void setNetworkAddress(NetworkAddress networkAddress) { + this.networkAddress = networkAddress; + } + + public PubKey getPubKey() { + return pubKey; + } + + public void setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeSettings.java new file mode 100644 index 00000000..50a5e1a1 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartNodeSettings.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.net.NetworkAddress; + +@DataContract(code = TypeCodes.CONSENSUS_BFTSMART_NODE_SETTINGS) +public interface BftsmartNodeSettings extends NodeSettings { + + /** + * 节点所属的参与方的区块链地址; + */ +// @DataField(order = 0, primitiveType = ValueType.TEXT) +// @Override +// String getAddress(); + + /** + * Base58 格式的公钥; + * + * @return + */ +// @DataField(order = 1, primitiveType = ValueType.BYTES) +// PubKey getPubKey(); + + /** + * 节点的ID; + * + * @return + */ + @DataField(order = 2, primitiveType = ValueType.INT32) + int getId(); + + /** + * 共识协议的网络地址; + * + * @return + */ + @DataField(order = 3, primitiveType = ValueType.BYTES) + NetworkAddress getNetworkAddress(); + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartSettingsFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartSettingsFactory.java new file mode 100644 index 00000000..45be9423 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartSettingsFactory.java @@ -0,0 +1,73 @@ +package com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.*; +import com.jd.blockchain.utils.io.BytesEncoder; + +public class BftsmartSettingsFactory implements SettingsFactory { + + private static ConsensusSettingsEncoder CS_ENCODER = new ConsensusSettingsEncoder(); + + private static ClientIncomingSettingsEncoder CI_ENCODER =new ClientIncomingSettingsEncoder(); + + static { + DataContractRegistry.register(BftsmartConsensusSettings.class); + + DataContractRegistry.register(BftsmartClientIncomingSettings.class); + } + + @Override + public BftsmartConsensusSettingsBuilder getConsensusSettingsBuilder() { + + return new BftsmartConsensusSettingsBuilder(); + } + + @Override + public BytesEncoder getConsensusSettingsEncoder() { + return CS_ENCODER; + } + + @Override + public BytesEncoder getIncomingSettingsEncoder() { + return CI_ENCODER; + } + + + + private static class ConsensusSettingsEncoder implements BytesEncoder{ + + @Override + public byte[] encode(ConsensusSettings data) { + if (data instanceof BftsmartConsensusSettings) { + return BinaryEncodingUtils.encode(data, BftsmartConsensusSettings.class); + } + throw new IllegalArgumentException("Settings data isn't supported! Accept BftsmartConsensusSettings only!"); + } + + @Override + public ConsensusSettings decode(byte[] bytes) { + return BinaryEncodingUtils.decodeAs(bytes, BftsmartConsensusSettings.class); + } + + } + + private static class ClientIncomingSettingsEncoder implements BytesEncoder{ + + @Override + public byte[] encode(ClientIncomingSettings data) { + if (data instanceof BftsmartClientIncomingSettings) { + return BinaryEncodingUtils.encode(data, BftsmartClientIncomingSettings.class); + } + throw new IllegalArgumentException("Settings data isn't supported! Accept BftsmartClientIncomingSettings only!"); + } + + @Override + public ClientIncomingSettings decode(byte[] bytes) { + return BinaryEncodingUtils.decodeAs(bytes, BftsmartClientIncomingSettings.class); + } + + } + + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTopology.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTopology.java new file mode 100644 index 00000000..f6832fb8 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTopology.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.consensus.bftsmart; + +import bftsmart.reconfiguration.views.View; +import com.jd.blockchain.consensus.Topology; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +public class BftsmartTopology implements Topology { + + private static final long serialVersionUID = -3042599438265726240L; + + private View view; + + public BftsmartTopology(View view){ + this.view = view; + } + + @Override + public int getId() { + return view.getId(); + } + + @Override + public Topology copyOf() { + return BinarySerializeUtils.copyOf(this); + } + + public View getView() { + return view; + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTransactionType.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTransactionType.java new file mode 100644 index 00000000..bf42510f --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/BftsmartTransactionType.java @@ -0,0 +1,16 @@ +//package com.jd.blockchain.consensus.bftsmart; +// +//public enum BftsmartTransactionType { +// TRANSACTION((int)0), +// COMMITBLOCK((int)1); +// +// public final int CODE; +// +// public int getCode() { +// return CODE; +// } +// private BftsmartTransactionType(int code) { +// this.CODE = code; +// } +// +//} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientConfig.java new file mode 100644 index 00000000..6bb9e238 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientConfig.java @@ -0,0 +1,79 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; + + +public class BftsmartClientConfig implements BftsmartClientSettings { + + private int clientId; + private PubKey clientPubkey; + private ConsensusSettings consensusSettings; + private byte[] topology; + private byte[] tomConfig; + BftsmartClientIncomingSettings clientIncomingSettings; + + public BftsmartClientConfig(int clientId, PubKey clientPubkey, ConsensusSettings consensusSettings, byte[] topology, byte[] tomConfig) { + this.clientId = clientId; + this.clientPubkey = clientPubkey; + this.consensusSettings = consensusSettings; + this.topology = topology; + this.tomConfig = tomConfig; + } + + public BftsmartClientConfig(BftsmartClientIncomingSettings clientIncomingSettings) { + this.clientIncomingSettings = clientIncomingSettings; + this.clientId = clientIncomingSettings.getClientId(); + this.clientPubkey = clientIncomingSettings.getPubKey(); + this.consensusSettings = clientIncomingSettings.getConsensusSettings(); + this.topology = clientIncomingSettings.getTopology(); + this.tomConfig = clientIncomingSettings.getTomConfig(); + + } + @Override + public int getClientId() { + return clientId; + } + + public void setClientId(int clientId) { + this.clientId = clientId; + } + + + @Override + public PubKey getClientPubKey() { + return clientPubkey; + } + + public void setClientPubkey(PubKey clientPubkey) { + this.clientPubkey = clientPubkey; + } + + @Override + public ConsensusSettings getConsensusSettings() { + return consensusSettings; + } + + public void setConsensusSettings(ConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + } + + public byte[] getTopology() { + return topology; + } + + public void setTopology(byte[] topology) { + this.topology = topology; + } + + @Override + public byte[] getTomConfig() { + return tomConfig; + } + + public void setTomConfig(byte[] tomConfig) { + this.tomConfig = tomConfig; + } +} + diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientIdentification.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientIdentification.java new file mode 100644 index 00000000..3b046e1f --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientIdentification.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; + +import java.util.Arrays; + +public class BftsmartClientIdentification implements ClientIdentification { + + private byte[] identityInfo; + private PubKey pubKey; + private SignatureDigest signatureDigest; + + public BftsmartClientIdentification() { + + } + + public BftsmartClientIdentification(ClientIdentification clientIdentification) { + identityInfo = clientIdentification.getIdentityInfo(); + pubKey = clientIdentification.getPubKey(); + signatureDigest = clientIdentification.getSignature(); + } + + @Override + public byte[] getIdentityInfo() { + return identityInfo; + } + + public void setIdentityInfo(byte[] identityInfo) { + this.identityInfo = identityInfo; + } + + @Override + public PubKey getPubKey() { + return pubKey; + } + + public void setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + } + + @Override + public SignatureDigest getSignature() { + return signatureDigest; + } + + @Override + public String getProviderName() { + return BftsmartConsensusProvider.NAME; + } + + public void setSignatureDigest(SignatureDigest signatureDigest) { + this.signatureDigest = signatureDigest; + } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientSettings.java new file mode 100644 index 00000000..94bf3d53 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartClientSettings.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import com.jd.blockchain.consensus.client.ClientSettings; + + +public interface BftsmartClientSettings extends ClientSettings { + + byte[] getTopology(); + + byte[] getTomConfig(); + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClient.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClient.java new file mode 100644 index 00000000..25a995c7 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClient.java @@ -0,0 +1,131 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import com.jd.blockchain.consensus.MessageService; +import com.jd.blockchain.consensus.client.ClientSettings; +import com.jd.blockchain.consensus.client.ConsensusClient; +import java.util.concurrent.atomic.AtomicInteger; + +public class BftsmartConsensusClient implements ConsensusClient { + + + private final AtomicInteger addId = new AtomicInteger(); + + private BftsmartPeerProxyPool asyncPeerProxyPool; + + private int gatewayId; + + private ClientSettings clientSettings; + + public BftsmartConsensusClient(ClientSettings clientSettings) { + + this.clientSettings = clientSettings; + this.gatewayId = clientSettings.getClientId(); + + connect(); + } + + + public BftsmartPeerProxyPool getConsensusClientPool() { + return this.asyncPeerProxyPool; + } + + @Override + public MessageService getMessageService() { + return new BftsmartMessageService(asyncPeerProxyPool); + } + + @Override + public ClientSettings getSettings() { + return clientSettings; + } + + @Override + public boolean isConnected() { + return this.asyncPeerProxyPool != null; + } + + @Override + public void connect() { + + //consensus client pool + BftsmartPeerProxyFactory peerProxyFactory = new BftsmartPeerProxyFactory((BftsmartClientSettings)clientSettings, gatewayId); + this.asyncPeerProxyPool = new BftsmartPeerProxyPool(peerProxyFactory); + +// MemoryBasedViewStorage viewStorage = new MemoryBasedViewStorage(((BftsmartClientSettings)clientSettings).getTopology().getView()); +// TOMConfiguration tomConfiguration = ((BftsmartConsensusConfig)clientSettings.getConsensusSettings()).getBftsmartConfig(); +// +// //by serialize keep origin tom config +// byte[] tomBytes = BinarySerializeUtils.serialize(tomConfiguration); +// TOMConfiguration decodeTom = BinarySerializeUtils.deserialize(tomBytes); +// +// int clientId = gatewayId *100 + addId.incrementAndGet(); +// +// //every proxy client has unique id; +// decodeTom.setProcessId(clientId); +// this.peerProxy = new AsynchServiceProxy(decodeTom, viewStorage); + + } + + @Override + public void close() { + if (asyncPeerProxyPool != null) { + asyncPeerProxyPool.close(); + } + } + +// public void asyncSendOrdered(byte[] message, AsyncCallback callback) { +// AsyncReplier replier = new AsyncReplier(callback, peerProxy); +// peerProxy.invokeAsynchRequest(message, replier, TOMMessageType.ORDERED_REQUEST); +// } + +// private static class AsyncReplier implements ReplyListener { +// +// private AsynchServiceProxy peerProxy; +// +// private AtomicInteger replies = new AtomicInteger(0); +// +// private AsyncCallback messageHandle; +// +// public AsyncReplier(AsyncCallback messageHandle, AsynchServiceProxy peerProxy) { +// this.messageHandle = messageHandle; +// this.peerProxy = peerProxy; +// } +// +// @Override +// public void reset() { +// replies.set(0); +// } +// +// @Override +// public void replyReceived(RequestContext context, TOMMessage reply) { +// int replyCount = replies.incrementAndGet(); +// +// double q = Math.ceil((double) (peerProxy.getViewManager().getCurrentViewN() +// + peerProxy.getViewManager().getCurrentViewF() + 1) / 2.0); +// +// if (replyCount >= q) { +// peerProxy.cleanAsynchRequest(context.getOperationId()); +// messageHandle.complete(reply.getContent(), null); +// } +// } +// +// } + +// private static class BftsmartAsyncFuture extends CompletableAsyncFuture { +// @Override +// public void setSuccess(T value) { +// super.setSuccess(value); +// } +// +// @Override +// public void setError(Throwable ex) { +// super.setError(ex); +// } +// +// @Override +// public void setError(String errorCode) { +// super.setError(errorCode); +// } +// } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClientFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClientFactory.java new file mode 100644 index 00000000..51afe4d2 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartConsensusClientFactory.java @@ -0,0 +1,137 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import bftsmart.reconfiguration.util.TOMConfiguration; +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusManageService; +import com.jd.blockchain.consensus.ConsensusSecurityException; +import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartConsensusManageService; +import com.jd.blockchain.consensus.client.ClientFactory; +import com.jd.blockchain.consensus.client.ClientSettings; +import com.jd.blockchain.consensus.client.ConsensusClient; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.utils.http.agent.HttpServiceAgent; +import com.jd.blockchain.utils.http.agent.ServiceEndpoint; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BftsmartConsensusClientFactory implements ClientFactory { + + + private AtomicInteger addId = new AtomicInteger(); + + private String localDomain = "localhost"; + private String localIp = "127.0.0.1"; + + public BftsmartConsensusClientFactory() { + + } + + + @Override + public BftsmartClientIdentification buildAuthId(CryptoKeyPair clientKeyPair) { + + PubKey pubKey = clientKeyPair.getPubKey(); + PrivKey privKey = clientKeyPair.getPrivKey(); + + SignatureFunction signatureFunction = CryptoUtils.sign(pubKey.getAlgorithm()); + SignatureDigest signatureDigest = signatureFunction.sign(privKey, pubKey.toBytes()); + + BftsmartClientIdentification bftsmartClientIdentification = new BftsmartClientIdentification(); + bftsmartClientIdentification.setIdentityInfo(pubKey.toBytes()); + bftsmartClientIdentification.setPubKey(pubKey); + bftsmartClientIdentification.setSignatureDigest(signatureDigest); + + return bftsmartClientIdentification; + } + + @Override + public ClientSettings buildClientSettings(ClientIncomingSettings incomingSettings) { + + BftsmartClientIncomingSettings clientIncomingSettings = (BftsmartClientIncomingSettings) incomingSettings; + + BftsmartClientSettings clientSettings = new BftsmartClientConfig(clientIncomingSettings); + + return clientSettings; + } + + @Override + public ConsensusClient setupClient(ClientSettings settings) { + + return new BftsmartConsensusClient(settings); + } + + @Override + public ConsensusManageService createManageServiceClient(String[] serviceNodes) { +// BftsmartConsensusManageService consensusManageService = null; +// BftsmartClientIncomingSettings clientIncomingSettings; +// +// +// try { +// if (serviceNodes == null) { +// throw new ConsensusSecurityException("createManageServiceClient param error!"); +// } +// +// for (int i = 0; i < serviceNodes.length; i++) { +// +// NetworkAddress networkAddress = getIpPortFromUrl(serviceNodes[i]); +// if (networkAddress == null) { +// continue; +// } +// ServiceEndpoint peerServer = new ServiceEndpoint(networkAddress.getHost(), networkAddress.getPort(), false); +// consensusManageService = HttpServiceAgent.createService(BftsmartConsensusManageService.class, peerServer); +// clientIncomingSettings = consensusManageService.authClientIncoming(clientIdentification); +// +// if (clientIncomingSettings == null) { +// consensusManageService = null; +// } else { +// //认证成功 +// break; +// } +// } +// +// } catch (Exception e) { +// e.printStackTrace(); +// } + +// return consensusManageService; + return null; + } + + private NetworkAddress getIpPortFromUrl(String url) { + + // 1.check null + if (url == null || url.trim().equals("")) { + return null; + } + + // 2. localhost replace to 127.0.0.1 + if(url.startsWith("http://" + localDomain) ){ + url = url.replace("http://" + localDomain, "http://" + localIp) ; + } + + String host = ""; + Pattern p = Pattern.compile("(?<=//|)((\\w)+\\.)+\\w+(:\\d{0,5})?"); + Matcher matcher = p.matcher(url); + if (matcher.find()) { + host = matcher.group() ; + } + + if(host.contains(":") == false){ + //default port :80 + return new NetworkAddress(host, 80); + } + else { + String[] ipPortArr = host.split(":"); + return new NetworkAddress(ipPortArr[0], Integer.parseInt(ipPortArr[1])); + } + } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartMessageService.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartMessageService.java new file mode 100644 index 00000000..53a5f0a8 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartMessageService.java @@ -0,0 +1,73 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import bftsmart.tom.AsynchServiceProxy; +import com.jd.blockchain.consensus.MessageService; +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; +import com.jd.blockchain.utils.io.BytesUtils; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class BftsmartMessageService implements MessageService { + + private BftsmartPeerProxyPool asyncPeerProxyPool; + + public BftsmartMessageService(BftsmartPeerProxyPool peerProxyPool) { + this.asyncPeerProxyPool = peerProxyPool; + } + + @Override + public AsyncFuture sendOrdered(byte[] message) { + return sendOrderedMessage(message); + } + + private AsyncFuture sendOrderedMessage(byte[] message) { + CompletableAsyncFuture asyncFuture = new CompletableAsyncFuture<>(); + AsynchServiceProxy asynchServiceProxy = null; + try { + asynchServiceProxy = asyncPeerProxyPool.borrowObject(); +// //0: Transaction msg, 1: Commitblock msg +// byte[] msgType = BytesUtils.toBytes(0); +// byte[] wrapMsg = new byte[message.length + 4]; +// System.arraycopy(message, 0, wrapMsg, 4, message.length); +// System.arraycopy(msgType, 0, wrapMsg, 0, 4); +// +// System.out.printf("BftsmartMessageService invokeOrdered time = %s, id = %s threadId = %s \r\n", +// System.currentTimeMillis(), asynchServiceProxy.getProcessId(), Thread.currentThread().getId()); + + byte[] result = asynchServiceProxy.invokeOrdered(message); + asyncFuture.complete(result); + + } catch (Exception e) { + throw new RuntimeException(); + + } finally { + asyncPeerProxyPool.returnObject(asynchServiceProxy); + } + + return asyncFuture; + } + + @Override + public AsyncFuture sendUnordered(byte[] message) { + return sendUnorderedMessage(message); + } + + private AsyncFuture sendUnorderedMessage(byte[] message) { + CompletableAsyncFuture asyncFuture = new CompletableAsyncFuture<>(); + AsynchServiceProxy asynchServiceProxy = null; + try { + asynchServiceProxy = asyncPeerProxyPool.borrowObject(); + byte[] result = asynchServiceProxy.invokeUnordered(message); + asyncFuture.complete(result); + + } catch (Exception e) { + throw new RuntimeException(e); + } finally { + asyncPeerProxyPool.returnObject(asynchServiceProxy); + } + return asyncFuture; + } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyFactory.java new file mode 100644 index 00000000..306a71f0 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyFactory.java @@ -0,0 +1,47 @@ +package com.jd.blockchain.consensus.bftsmart.client; + +import bftsmart.reconfiguration.util.TOMConfiguration; +import bftsmart.reconfiguration.views.MemoryBasedViewStorage; +import bftsmart.tom.AsynchServiceProxy; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusConfig; +import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +import org.apache.commons.pool2.BasePooledObjectFactory; +import org.apache.commons.pool2.PooledObject; +import org.apache.commons.pool2.impl.DefaultPooledObject; + +import java.util.concurrent.atomic.AtomicInteger; + +public class BftsmartPeerProxyFactory extends BasePooledObjectFactory { + + private BftsmartClientSettings bftsmartClientSettings; + + private int gatewayId; + + private AtomicInteger index = new AtomicInteger(1); + + public BftsmartPeerProxyFactory(BftsmartClientSettings bftsmartClientSettings, int gatewayId) { + this.bftsmartClientSettings = bftsmartClientSettings; + this.gatewayId = gatewayId; + } + + @Override + public AsynchServiceProxy create() throws Exception { + + BftsmartTopology topology = BinarySerializeUtils.deserialize(bftsmartClientSettings.getTopology()); + + MemoryBasedViewStorage viewStorage = new MemoryBasedViewStorage(topology.getView()); + TOMConfiguration tomConfiguration = BinarySerializeUtils.deserialize(bftsmartClientSettings.getTomConfig()); + + //every proxy client has unique id; + tomConfiguration.setProcessId(gatewayId + index.getAndIncrement()); + AsynchServiceProxy peerProxy = new AsynchServiceProxy(tomConfiguration, viewStorage); + return peerProxy; + } + + @Override + public PooledObject wrap(AsynchServiceProxy asynchServiceProxy) { + return new DefaultPooledObject<>(asynchServiceProxy); + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPool.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPool.java new file mode 100644 index 00000000..1b5a5f8e --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPool.java @@ -0,0 +1,25 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.bft.BftsmartConsensusClientPool + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/10/30 下午6:50 + * Description: + */ +package com.jd.blockchain.consensus.bftsmart.client; + +import bftsmart.tom.AsynchServiceProxy; +import org.apache.commons.pool2.PooledObjectFactory; +import org.apache.commons.pool2.impl.GenericObjectPool; +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +public class BftsmartPeerProxyPool extends GenericObjectPool { + + public BftsmartPeerProxyPool(PooledObjectFactory factory) { + this(factory, null); + } + + public BftsmartPeerProxyPool(PooledObjectFactory factory, GenericObjectPoolConfig config) { + super(factory, config == null ? new BftsmartPeerProxyPoolConfig() : config); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPoolConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPoolConfig.java new file mode 100644 index 00000000..fb0b3884 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/client/BftsmartPeerProxyPoolConfig.java @@ -0,0 +1,32 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.bft.BftsmartConsensusClientPool + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/10/30 下午6:50 + * Description: + */ +package com.jd.blockchain.consensus.bftsmart.client; + +import org.apache.commons.pool2.impl.GenericObjectPoolConfig; + +public class BftsmartPeerProxyPoolConfig extends GenericObjectPoolConfig { + + public static final int MAX_TOTAL = 100; + + private final int MIN_IDLE = 0; + + private final int MAX_IDLE = 100; + + public BftsmartPeerProxyPoolConfig() { + setMaxTotal(MAX_TOTAL); + setMinIdle(MIN_IDLE); + setMaxIdle(MAX_IDLE); + } + + public BftsmartPeerProxyPoolConfig(int maxTotal, int minIdle, int maxIdle) { + setMaxTotal(maxTotal); + setMinIdle(minIdle); + setMaxIdle(maxIdle); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartConsensusManageService.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartConsensusManageService.java new file mode 100644 index 00000000..8331a969 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartConsensusManageService.java @@ -0,0 +1,59 @@ +package com.jd.blockchain.consensus.bftsmart.service; + +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ConsensusManageService; +import com.jd.blockchain.consensus.ConsensusSecurityException; +import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingConfig; +import com.jd.blockchain.consensus.bftsmart.BftsmartClientIncomingSettings; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class BftsmartConsensusManageService implements ConsensusManageService { + + public static final int CLIENT_RANGE = 100 * 1000; + + private BftsmartNodeServer nodeServer; + private int clientId; + private static final Lock authLock = new ReentrantLock(); + + public BftsmartConsensusManageService(BftsmartNodeServer nodeServer) { + this.nodeServer = nodeServer; + //Assume that each peer node corresponds to up to 100 gateways + clientId = nodeServer.getServerId() * CLIENT_RANGE; + } + + @Override + public BftsmartClientIncomingSettings authClientIncoming(ClientIdentification authId) { + if (verify(authId)) { + BftsmartClientIncomingSettings clientIncomingSettings = new BftsmartClientIncomingConfig(); + + ((BftsmartClientIncomingConfig) clientIncomingSettings).setTopology(BinarySerializeUtils.serialize(nodeServer.getTopology())); + ((BftsmartClientIncomingConfig) clientIncomingSettings).setTomConfig(BinarySerializeUtils.serialize(nodeServer.getTomConfig())); + ((BftsmartClientIncomingConfig) clientIncomingSettings).setConsensusSettings(nodeServer.getConsensusSetting()); + ((BftsmartClientIncomingConfig) clientIncomingSettings).setPubKey(authId.getPubKey()); + //compute gateway id + try { + authLock.lock(); + ((BftsmartClientIncomingConfig) clientIncomingSettings).setClientId(clientId++); + } finally { + authLock.unlock(); + } + + return clientIncomingSettings; + + } + + return null; + } + + public boolean verify(ClientIdentification authId) { + + SignatureFunction signatureFunction = CryptoUtils.sign(authId.getPubKey().getAlgorithm()); + + return signatureFunction.verify(authId.getSignature(), authId.getPubKey(), authId.getIdentityInfo()); + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java new file mode 100644 index 00000000..f25c8d61 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServer.java @@ -0,0 +1,365 @@ +package com.jd.blockchain.consensus.bftsmart.service; + +import java.io.ByteArrayOutputStream; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import bftsmart.tom.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.jd.blockchain.consensus.ConsensusManageService; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.consensus.service.ServerSettings; +import com.jd.blockchain.consensus.service.StateHandle; +import com.jd.blockchain.consensus.service.StateMachineReplicate; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.utils.PropertiesUtils; +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.jd.blockchain.utils.io.BytesUtils; +import bftsmart.reconfiguration.util.HostsConfig; +import bftsmart.reconfiguration.util.TOMConfiguration; +import bftsmart.tom.core.messages.TOMMessage; +import bftsmart.tom.server.defaultservices.DefaultRecoverable; + +public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer { + + private static Logger LOGGER = LoggerFactory.getLogger(BftsmartNodeServer.class); + + private List stateHandles = new CopyOnWriteArrayList<>(); + + // TODO 暂不处理队列溢出问题 + private ExecutorService notifyReplyExecutors = Executors.newSingleThreadExecutor(); + + private volatile Status status = Status.STOPPED; + + private final Object mutex = new Object(); + + private volatile ServiceReplica replica; + + private StateMachineReplicate stateMachineReplicate; + + private ServerSettings serverSettings; + + private BftsmartConsensusManageService manageService; + + + private volatile BftsmartTopology topology; + + private volatile BftsmartConsensusSettings setting; + + private TOMConfiguration tomConfig; + + private HostsConfig hostsConfig; + private Properties systemConfig; + + private MessageHandle messageHandle; + + private String providerName; + + private String realmName; + + private int serverId; + + public BftsmartNodeServer() { + + } + + public BftsmartNodeServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicate) { + this.serverSettings = serverSettings; + this.realmName = serverSettings.getRealmName(); + //used later + this.stateMachineReplicate = stateMachineReplicate; + this.messageHandle = messageHandler; + this.manageService = new BftsmartConsensusManageService(this); + createConfig(); + serverId = findServerId(); + initConfig(serverId, systemConfig, hostsConfig); + } + + protected int findServerId() { + int serverId = 0; + + for (int i = 0; i < hostsConfig.getNum(); i++) { + String host = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getHost(); + int port = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getPort(); + + if (hostsConfig.getHost(i).equals(host) && hostsConfig.getPort(i) == port) { + serverId = i; + break; + } + } + + return serverId; + } + + public int getServerId() { + return serverId; + } + + protected void createConfig() { + + setting = ((BftsmartServerSettings) serverSettings).getConsensusSettings(); + + List configList = new ArrayList(); + + NodeSettings[] nodeSettingsArray = setting.getNodes(); + for (NodeSettings nodeSettings : nodeSettingsArray) { + BftsmartNodeSettings node = (BftsmartNodeSettings)nodeSettings; + configList.add(new HostsConfig.Config(node.getId(), node.getNetworkAddress().getHost(), node.getNetworkAddress().getPort())); + } + + //create HostsConfig instance based on consensus realm nodes + hostsConfig = new HostsConfig(configList.toArray(new HostsConfig.Config[configList.size()])); + + systemConfig = PropertiesUtils.createProperties(setting.getSystemConfigs()); + + return; + } + + protected void initConfig(int id, String systemConfig, String hostsConfig) { + + this.tomConfig = new TOMConfiguration(id, systemConfig, hostsConfig); + + } + + protected void initConfig(int id, Properties systemsConfig, HostsConfig hostConfig) { + this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig); + } + + @Override + public ConsensusManageService getManageService() { + return manageService; + } + + @Override + public ServerSettings getSettings() { + return serverSettings; + } + + @Override + public String getProviderName() { + return BftsmartConsensusProvider.NAME; + } + + public TOMConfiguration getTomConfig() { + return tomConfig; + } + + public int getId() { + return tomConfig.getProcessId(); + } + + public void setId(int id) { + if (id < 0) { + throw new IllegalArgumentException("ReplicaID is negative!"); + } + this.tomConfig.setProcessId(id); + + } + + public BftsmartConsensusSettings getConsensusSetting() { + return setting; + } + + public BftsmartTopology getTopology() { + return topology; + } + + public Status getStatus() { + return status; + } + + @Override + public boolean isRunning() { + return status == Status.RUNNING; + } + + public byte[] appExecuteUnordered(byte[] bytes, MessageContext messageContext) { + return messageHandle.processUnordered(bytes).get(); + } + + @Override + public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) { + return appExecuteBatch(commands, msgCtxs, fromConsensus, null); + } + + @Override + public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus, List replyList) { + + if (replyList == null || replyList.size() == 0) { + throw new IllegalArgumentException(); + } + // todo 此部分需要重新改造 + /** + * 默认BFTSmart接口提供的commands是一个或多个共识结果的顺序集合 + * 根据共识的规定,目前的做法是将其根据msgCtxs的内容进行分组,每组都作为一个结块标识来处理 + * 从msgCtxs可以获取对应commands的分组情况 + */ + int manageConsensusId = msgCtxs[0].getConsensusId(); + List manageConsensusCmds = new ArrayList<>(); + List manageReplyMsgs = new ArrayList<>(); + + int index = 0; + for (MessageContext msgCtx : msgCtxs) { + if (msgCtx.getConsensusId() == manageConsensusId) { + manageConsensusCmds.add(commands[index]); + manageReplyMsgs.add(replyList.get(index)); + } else { + // 达到结块标准,需要进行结块并应答 + blockAndReply(manageConsensusCmds, manageReplyMsgs); + // 重置链表和共识ID + manageConsensusCmds = new ArrayList<>(); + manageReplyMsgs = new ArrayList<>(); + manageConsensusId = msgCtx.getConsensusId(); + manageConsensusCmds.add(commands[index]); + manageReplyMsgs.add(replyList.get(index)); + } + index++; + } + // 结束时,肯定有最后一个结块请求未处理 + if (!manageConsensusCmds.isEmpty()) { + blockAndReply(manageConsensusCmds, manageReplyMsgs); + } + return null; + } + + private void blockAndReply(List manageConsensusCmds, List replyList) { + String batchId = messageHandle.beginBatch(realmName); + List> asyncFutureLinkedList = new ArrayList<>(manageConsensusCmds.size()); + try { + int msgId = 0; + for (byte[] txContent : manageConsensusCmds) { + AsyncFuture asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); + asyncFutureLinkedList.add(asyncFuture); + } + messageHandle.completeBatch(realmName, batchId); + messageHandle.commitBatch(realmName, batchId); + } catch (Exception e) { + // todo 需要处理应答码 404 + messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); + } + + // 通知线程单独处理应答 + notifyReplyExecutors.execute(() -> { + // 应答对应的结果 + int replyIndex = 0; + for(ReplyContextMessage msg : replyList) { + msg.setReply(asyncFutureLinkedList.get(replyIndex).get()); + TOMMessage request = msg.getTomMessage(); + ReplyContext replyContext = msg.getReplyContext(); + request.reply = new TOMMessage(replyContext.getId(), request.getSession(), request.getSequence(), + request.getOperationId(), msg.getReply(), replyContext.getCurrentViewId(), + request.getReqType()); + + if (replyContext.getNumRepliers() > 0) { + bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to " + + request.getSender() + " with sequence number " + request.getSequence() + + " and operation ID " + request.getOperationId() + " via ReplyManager"); + replyContext.getRepMan().send(request); + } else { + bftsmart.tom.util.Logger.println("(ServiceReplica.receiveMessages) sending reply to " + + request.getSender() + " with sequence number " + request.getSequence() + + " and operation ID " + request.getOperationId()); + replyContext.getReplier().manageReply(request, msg.getMessageContext()); + } + replyIndex++; + } + }); + } + + //notice + public byte[] getSnapshot() { + LOGGER.debug("------- GetSnapshot...[replica.id=" + this.getId() + "]"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesUtils.writeInt(stateHandles.size(), out); + for (StateHandle stateHandle : stateHandles) { + // TODO: 测试代码; + return stateHandle.takeSnapshot(); + } + return out.toByteArray(); + } + + public void installSnapshot(byte[] snapshot) { +// System.out.println("Not implement!"); + } + + @Override + public void start() { + if (this.getId() < 0) { + throw new IllegalStateException("Unset server node ID!"); + } + LOGGER.debug("=============================== Start replica ==================================="); + + if (status != Status.STOPPED) { + return; + } + synchronized (mutex) { + if (status != Status.STOPPED) { + return; + } + status = Status.STARTING; + + try { + LOGGER.debug("Start replica...[ID=" + getId() + "]"); + this.replica = new ServiceReplica(tomConfig, this, this); + this.topology = new BftsmartTopology(replica.getReplicaContext().getCurrentView()); + status = Status.RUNNING; +// createProxyClient(); + LOGGER.debug( + "=============================== Replica started success! ==================================="); + } catch (RuntimeException e) { + status = Status.STOPPED; + throw e; + } + } + + } + + @Override + public void stop() { + if (status != Status.RUNNING) { + return; + } + synchronized (mutex) { + if (status != Status.RUNNING) { + return; + } + status = Status.STOPPING; + + try { + ServiceReplica rep = this.replica; + if (rep != null) { + LOGGER.debug("Stop replica...[ID=" + rep.getId() + "]"); + this.replica = null; + this.topology = null; + + rep.kill(); + LOGGER.debug("Replica had stopped! --[ID=" + rep.getId() + "]"); + } + } finally { + status = Status.STOPPED; + } + } + } + + enum Status { + + STARTING, + + RUNNING, + + STOPPING, + + STOPPED + + } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServerFactory.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServerFactory.java new file mode 100644 index 00000000..50ffd90f --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartNodeServerFactory.java @@ -0,0 +1,110 @@ +package com.jd.blockchain.consensus.bftsmart.service; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.consensus.service.NodeServerFactory; +import com.jd.blockchain.consensus.service.ServerSettings; +import com.jd.blockchain.consensus.service.StateMachineReplicate; +import com.jd.blockchain.utils.net.NetworkAddress; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class BftsmartNodeServerFactory implements NodeServerFactory { + + private static Map nodeServerMap = new ConcurrentHashMap<>(); + + + @Override + public ServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress) { + + NodeSettings serverNode = null; + + BftsmartServerSettingConfig serverSettings = new BftsmartServerSettingConfig(); + + //find current node according to current address + for (NodeSettings nodeSettings : consensusSetting.getNodes()) { + if (nodeSettings.getAddress().equals(currentNodeAddress)) { + serverNode = nodeSettings; + break; + } + } + + if (serverNode == null) { + throw new IllegalArgumentException(); + } + + //set server settings + serverSettings.setRealmName(realmName); + + serverSettings.setReplicaSettings(serverNode); + + serverSettings.setConsensusSettings((BftsmartConsensusSettings) consensusSetting); + + return serverSettings; + + } + + @Override + public NodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler, + StateMachineReplicate stateMachineReplicator) { + + NodeSettings[] currNodeSettings = (((BftsmartServerSettings)serverSettings).getConsensusSettings()).getNodes(); + + //check conflict realm + if (!hasIntersection(currNodeSettings)) { + BftsmartNodeServer nodeServer = new BftsmartNodeServer(serverSettings, messageHandler, stateMachineReplicator); + nodeServerMap.put(serverSettings.getRealmName(), currNodeSettings); + return nodeServer; + } + else { + throw new IllegalArgumentException("setupServer serverSettings parameters error!"); + } + } + + + //check if consensus realm conflict, by this support multi ledgers + private boolean hasIntersection(NodeSettings[] currNodeSettings) { + + int currHashCode = getHashcode(currNodeSettings); + + //first check if is same consensus realm + for (NodeSettings[] exisitNodeSettings : nodeServerMap.values()) { + if (currHashCode == getHashcode(exisitNodeSettings)) { + return false; + } + } + //check conflict + for (NodeSettings[] exisitNodeSettings : nodeServerMap.values()) { + for (NodeSettings curr : currNodeSettings) { + for (NodeSettings exist : exisitNodeSettings) { + if (((BftsmartNodeSettings)curr).getNetworkAddress().equals(((BftsmartNodeSettings)exist).getNetworkAddress())) { + return true; + } + } + } + } + + return false; + } + + //compute hashcode for consensus nodes + private int getHashcode(NodeSettings[] nodeSettings) { + + int i = 0; + NetworkAddress[] nodeAddrs = new NetworkAddress[nodeSettings.length]; + for (NodeSettings setting : nodeSettings) { + + nodeAddrs[i++] = ((BftsmartNodeSettings)setting).getNetworkAddress(); + } + int hashCode = Arrays.hashCode(nodeAddrs); + return hashCode; + + } + +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettingConfig.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettingConfig.java new file mode 100644 index 00000000..16420932 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettingConfig.java @@ -0,0 +1,43 @@ +package com.jd.blockchain.consensus.bftsmart.service; + +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartCommitBlockSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; +import com.jd.blockchain.consensus.service.ServerSettings; + +public class BftsmartServerSettingConfig implements BftsmartServerSettings { + private NodeSettings replicaSettings; + private String realmName; + private BftsmartConsensusSettings consensusSettings; + + + @Override + public String getRealmName() { + return realmName; + } + + public void setRealmName(String realmName) { + this.realmName = realmName; + } + + + @Override + public NodeSettings getReplicaSettings() { + return replicaSettings; + } + + public void setReplicaSettings(NodeSettings replicaSettings) { + this.replicaSettings = replicaSettings; + } + + + @Override + public BftsmartConsensusSettings getConsensusSettings() { + return consensusSettings; + } + + public void setConsensusSettings(BftsmartConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + } +} diff --git a/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettings.java b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettings.java new file mode 100644 index 00000000..1cf627d2 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/java/com/jd/blockchain/consensus/bftsmart/service/BftsmartServerSettings.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.consensus.bftsmart.service; + +import com.jd.blockchain.consensus.bftsmart.BftsmartCommitBlockSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +import com.jd.blockchain.consensus.service.ServerSettings; + +public interface BftsmartServerSettings extends ServerSettings { + + BftsmartConsensusSettings getConsensusSettings(); + +} diff --git a/source/consensus/consensus-bftsmart/src/main/resources/bftsmart.config b/source/consensus/consensus-bftsmart/src/main/resources/bftsmart.config new file mode 100644 index 00000000..df69caf5 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/main/resources/bftsmart.config @@ -0,0 +1,144 @@ +# 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 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 ### +############################################ + +#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 ###### +############################################ + +#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 + + +#Number of servers in the group +system.servers.num = 4 + +#Maximum number of faulty replicas +system.servers.f = 1 + +#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 + +#Configuration of all node servers; +#PubKey of node server with specified ID, with base58 encoding. +system.server.0.pubkey= +system.server.0.network.host=127.0.0.1 +system.server.0.network.port=8900 +system.server.0.network.secure=false + +system.server.1.pubkey= +system.server.1.network.host=127.0.0.1 +system.server.1.network.port=8910 +system.server.1.network.secure=false + +system.server.2.pubkey= +system.server.2.network.host=127.0.0.1 +system.server.2.network.port=8920 +system.server.2.network.secure=false + +system.server.3.pubkey= +system.server.3.network.host=127.0.0.1 +system.server.3.network.port=8920 +system.server.3.network.secure=false diff --git a/source/consensus/consensus-bftsmart/src/test/java/test/com/jd/blockchain/consensus/bftsmart/proxyClientTest.java b/source/consensus/consensus-bftsmart/src/test/java/test/com/jd/blockchain/consensus/bftsmart/proxyClientTest.java new file mode 100644 index 00000000..10462343 --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/test/java/test/com/jd/blockchain/consensus/bftsmart/proxyClientTest.java @@ -0,0 +1,136 @@ +package test.com.jd.blockchain.consensus.bftsmart; + +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.bftsmart.*; +import com.jd.blockchain.consensus.bftsmart.client.BftsmartClientConfig; +import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClient; +import com.jd.blockchain.consensus.bftsmart.client.BftsmartMessageService; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServer; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartServerSettingConfig; +import com.jd.blockchain.consensus.service.ServerSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.utils.PropertiesUtils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class proxyClientTest { + + int number = 1500000; + + int peerStartPort = 11000; + + int nodeNum = 4; + + Random random = new Random(); + + byte[] bytes = null; + + CountDownLatch startPeer = new CountDownLatch(nodeNum); + + private static Properties bftsmartConf; + + private final ExecutorService nodeStartPools = Executors.newCachedThreadPool(); + + private final ExecutorService txSendPools = Executors.newFixedThreadPool(20); + + static { + ClassPathResource configResource = new ClassPathResource("system.config"); + try { + try (InputStream in = configResource.getInputStream()) { + bftsmartConf = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET); + } + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public void peerStart(BftsmartNodeServer[] nodeServers) { + + BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[nodeNum]; + + for (int i = 0; i < nodeNum; i++) { + BlockchainKeyPair keyPair = BlockchainKeyGenerator.getInstance().generate(); + PubKey pubKey = keyPair.getPubKey(); + NetworkAddress peerNodeServ = new NetworkAddress("127.0.0.1", peerStartPort + i * 10); + NodeSettings node = new BftsmartNodeConfig(pubKey, i, peerNodeServ); + nodesSettings[i] = (BftsmartNodeSettings) node; + } + + BftsmartConsensusConfig consensusConfig = new BftsmartConsensusConfig(nodesSettings, null, + PropertiesUtils.getOrderedValues(bftsmartConf)); + + for (int j = 0; j < nodeNum; j++) { + ServerSettings serverSettings = new BftsmartServerSettingConfig(); + ((BftsmartServerSettingConfig) serverSettings).setReplicaSettings(nodesSettings[j]); + ((BftsmartServerSettingConfig) serverSettings).setConsensusSettings(consensusConfig); + BftsmartNodeServer server = new BftsmartNodeServer(serverSettings, null, null); + nodeServers[j] = server; + nodeStartPools.execute(() -> { + server.start(); + startPeer.countDown(); + }); + } + } + + public void proxyClientSend(BftsmartNodeServer nodeServer) { + BftsmartClientIncomingConfig clientIncomingConfig = new BftsmartClientIncomingConfig(); + BlockchainKeyPair keyPair = BlockchainKeyGenerator.getInstance().generate(); + clientIncomingConfig.setPubKey(keyPair.getPubKey()); + clientIncomingConfig.setClientId(0); + clientIncomingConfig.setConsensusSettings(nodeServer.getConsensusSetting()); + clientIncomingConfig.setTomConfig(BinarySerializeUtils.serialize(nodeServer.getTomConfig())); + clientIncomingConfig.setTopology(BinarySerializeUtils.serialize(nodeServer.getTopology())); + + BftsmartClientConfig clientSettings = new BftsmartClientConfig(clientIncomingConfig); + BftsmartConsensusClient consensusClient = new BftsmartConsensusClient(clientSettings); + bytes = new byte[1024]; + + BftsmartMessageService messageService = (BftsmartMessageService) consensusClient.getMessageService(); + + for (int j = 0; j < number; j++) { + txSendPools.execute(() -> { + random.nextBytes(bytes); + messageService.sendOrdered(bytes); + }); + } + + } + +// @Test + public void sendTest() { + + BftsmartNodeServer[] nodeServers = new BftsmartNodeServer[nodeNum]; + //启动服务 + peerStart(nodeServers); + + try { + startPeer.await(); + Thread.sleep(5000); + } catch (Exception e) { + e.printStackTrace(); + } + + proxyClientSend(nodeServers[0]); + + + try { + Thread.sleep(50000); + System.out.println("send test complete!"); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/source/consensus/consensus-bftsmart/src/test/resources/bftsmart.config b/source/consensus/consensus-bftsmart/src/test/resources/bftsmart.config new file mode 100644 index 00000000..970ddf4b --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/test/resources/bftsmart.config @@ -0,0 +1,178 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +############################################ +###### Consensus Participant0 ###### +############################################ +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna + +system.server.0.network.host=127.0.0.1 + +system.server.0.network.port=8910 + +system.server.0.network.secure=false + +############################################ +###### #Consensus Participant1 ###### +############################################ +system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ + +system.server.1.network.host=127.0.0.1 + +system.server.1.network.port=8920 + +system.server.1.network.secure=false + +############################################ +###### #Consensus Participant2 ###### +############################################ +system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R + +system.server.2.network.host=127.0.0.1 + +system.server.2.network.port=8930 + +system.server.2.network.secure=false + +############################################ +###### Consensus Participant3 ###### +############################################ +system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR + +system.server.3.network.host=127.0.0.1 + +system.server.3.network.port=8940 + +system.server.3.network.secure=false + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/consensus/consensus-bftsmart/src/test/resources/system.config b/source/consensus/consensus-bftsmart/src/test/resources/system.config new file mode 100644 index 00000000..275edc4e --- /dev/null +++ b/source/consensus/consensus-bftsmart/src/test/resources/system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/consensus/consensus-framework/pom.xml b/source/consensus/consensus-framework/pom.xml new file mode 100644 index 00000000..53f87df7 --- /dev/null +++ b/source/consensus/consensus-framework/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + + com.jd.blockchain + consensus + 0.8.2.RELEASE + + consensus-framework + + + + com.jd.blockchain + binary-proto + ${project.version} + + + com.jd.blockchain + crypto-framework + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ActionMessage.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ActionMessage.java new file mode 100644 index 00000000..6b99fe9e --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ActionMessage.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.consensus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author huanghaiquan + * + */ +@Target({ ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ActionMessage { + + /** + * 请求参数转换器; + *

+ * 指定一个 {@link BinaryMessageConverter} 接口的实现类; + * + * @return + */ + Class converter(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncActionResponse.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncActionResponse.java new file mode 100644 index 00000000..992e448a --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncActionResponse.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.consensus; + +public interface AsyncActionResponse { + + byte[] process(); + +} \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncInvoker.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncInvoker.java new file mode 100644 index 00000000..72969da7 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncInvoker.java @@ -0,0 +1,59 @@ +package com.jd.blockchain.consensus; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +public class AsyncInvoker { + + private static ThreadLocal> resultHolder; + + static { + resultHolder = new ThreadLocal<>(); + } + + + @SuppressWarnings("unchecked") + public static T asynchorize(Class serviceClazz, T serviceInstance) { + if (serviceInstance instanceof AsyncService) { + return (T) Proxy.newProxyInstance(serviceClazz.getClassLoader(), new Class[] { serviceClazz }, + new AsyncInvocationHandle(serviceClazz, (AsyncService) serviceInstance)); + } + throw new IllegalArgumentException("The specified service instance is not supported by this asynchronize util!"); + } + + @SuppressWarnings("unchecked") + public static AsyncFuture call(T methodCall){ + AsyncFuture result = (AsyncFuture) resultHolder.get(); + resultHolder.set(null); + return result; + } + + + + private static class AsyncInvocationHandle implements InvocationHandler { + + private Class serviceClazz; + private AsyncService serviceInstance; + + public AsyncInvocationHandle(Class serviceClazz, AsyncService serviceInstance) { + this.serviceInstance = serviceInstance; + this.serviceClazz = serviceClazz; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + resultHolder.remove(); + if (method.getDeclaringClass() == serviceClazz) { + //async invoke; + AsyncFuture asyncResult = serviceInstance.invoke(method, args); + resultHolder.set(asyncResult); + return null; + } + return method.invoke(serviceInstance, args); + } + + } +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncService.java new file mode 100644 index 00000000..2b113718 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/AsyncService.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.consensus; + +import java.lang.reflect.Method; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +public interface AsyncService { + + AsyncFuture invoke(Method method, Object[] args); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/BinaryMessageConverter.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/BinaryMessageConverter.java new file mode 100644 index 00000000..5fe2f44e --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/BinaryMessageConverter.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.consensus; + +public interface BinaryMessageConverter { + + byte[] encode(Object message); + + Object decode(byte[] messageBytes); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentification.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentification.java new file mode 100644 index 00000000..73198cb5 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentification.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 客户端的身份证明; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.CLIENT_IDENTIFICATION) +public interface ClientIdentification { + + /** + * 身份信息; + * + * @return + */ + @DataField(order = 0, primitiveType = ValueType.BYTES) + byte[] getIdentityInfo(); + + /** + * 客户端的公钥; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + PubKey getPubKey(); + + /** + * 客户端对认证信息的签名; + * + * @return + */ + @DataField(order = 2, primitiveType = ValueType.BYTES) + SignatureDigest getSignature(); + + /** + * 具体实现类 + * + * @return + */ + @DataField(order = 3, primitiveType = ValueType.TEXT) + String getProviderName(); +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentifications.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentifications.java new file mode 100644 index 00000000..0b620074 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentifications.java @@ -0,0 +1,26 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.ClientIdentifications + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/19 下午3:58 + * Description: + */ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +/** + * + * @author shaozhuguang + * @create 2018/12/19 + * @since 1.0.0 + */ +@DataContract(code = TypeCodes.CLIENT_IDENTIFICATIONS) +public interface ClientIdentifications { + + @DataField(order = 0, list = true, refContract = true, genericContract = true) + ClientIdentification[] getClientIdentifications(); +} \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentificationsProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentificationsProvider.java new file mode 100644 index 00000000..e2e83453 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIdentificationsProvider.java @@ -0,0 +1,33 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.ClientIdentificationsProvider + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/19 下午3:59 + * Description: + */ +package com.jd.blockchain.consensus; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * @author shaozhuguang + * @create 2018/12/19 + * @since 1.0.0 + */ + +public class ClientIdentificationsProvider implements ClientIdentifications { + + private List clientIdentifications = new ArrayList<>(); + + public void add(ClientIdentification clientIdentification) { + clientIdentifications.add(clientIdentification); + } + + @Override + public ClientIdentification[] getClientIdentifications() { + return clientIdentifications.toArray(new ClientIdentification[clientIdentifications.size()]); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIncomingSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIncomingSettings.java new file mode 100644 index 00000000..d0644443 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ClientIncomingSettings.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * 共识网络的客户接入参数; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.CONSENSUS_CLI_INCOMING_SETTINGS) +public interface ClientIncomingSettings { + + /** + * 分配的客户端ID; + * + * @return + */ + @DataField(order = 0, primitiveType = ValueType.INT32) + int getClientId(); + + /** + * ProviderName + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.TEXT) + String getProviderName(); + + /** + * 共识网络的配置参数; + * + * @return + */ + @DataField(order = 2, refContract = true, genericContract = true) + ConsensusSettings getConsensusSettings(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusManageService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusManageService.java new file mode 100644 index 00000000..c589fc6b --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusManageService.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.consensus; + +/** + * 共识节点的管理服务; + * + * @author huanghaiquan + * + */ +public interface ConsensusManageService { + + /** + * 对客户端的接入进行认证; + * + * @param authId + * 客户端的身份信息; + * @return 如果通过认证,则返回接入参数;如果认证失败,则返回 null; + */ + ClientIncomingSettings authClientIncoming(ClientIdentification authId) throws ConsensusSecurityException; + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProvider.java new file mode 100644 index 00000000..f3c41ef9 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProvider.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.consensus.client.ConsensusClientProvider; +import com.jd.blockchain.consensus.service.ConsensusServiceProvider; + +public interface ConsensusProvider extends ConsensusClientProvider, ConsensusServiceProvider { + + String getName(); + + SettingsFactory getSettingsFactory(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProviders.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProviders.java new file mode 100644 index 00000000..c1b8c58c --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusProviders.java @@ -0,0 +1,43 @@ +package com.jd.blockchain.consensus; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.BeanUtils; +import org.springframework.util.ClassUtils; + +public class ConsensusProviders { + + private static final Object mutex = new Object(); + + private static Map providers = new ConcurrentHashMap<>(); + + public static ConsensusProvider getProvider(String className) { + ConsensusProvider provider = providers.get(className); + if (provider == null) { + synchronized (mutex) { + provider = providers.get(className); + if (provider == null) { + provider = loadProvider(ConsensusProvider.class, className); + providers.put(className, provider); + } + } + } + return provider; + } + + private static T loadProvider(Class assignableTo, String implementClassName) { + Class providerClass = ClassUtils.resolveClassName(implementClassName, + ConsensusProviders.class.getClassLoader()); + if (!assignableTo.isAssignableFrom(providerClass)) { + throw new IllegalArgumentException( + String.format("%s is not implement %s!", implementClassName, assignableTo.getName())); + } + return BeanUtils.instantiateClass(providerClass, assignableTo); + } + + public static void registerProvider(ConsensusProvider provider) { + providers.put(provider.getName(), provider); + } + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSecurityException.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSecurityException.java new file mode 100644 index 00000000..36968fd6 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSecurityException.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.consensus; + +public class ConsensusSecurityException extends Exception{ + + private static final long serialVersionUID = -164820276123627155L; + + public ConsensusSecurityException() { + } + + public ConsensusSecurityException(String message) { + super(message); + } + + public ConsensusSecurityException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusService.java new file mode 100644 index 00000000..f7cf1cf8 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusService.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.consensus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ConsensusService { + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettings.java new file mode 100644 index 00000000..a2d99ad3 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettings.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +/** + * 共识网络的配置参数; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.CONSENSUS_SETTINGS) +public interface ConsensusSettings { + + /** + * 共识网络中的节点列表; + * + * @return + */ + @DataField(order = 0, refContract = true, list = true, genericContract = true) + NodeSettings[] getNodes(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettingsBuilder.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettingsBuilder.java new file mode 100644 index 00000000..8adc61dd --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/ConsensusSettingsBuilder.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.consensus; + +import java.util.Properties; + +public interface ConsensusSettingsBuilder { + + /** + * 从属性表中解析生成共识网络的参数配置; + * + * @param props + * 属性表; + * @param keyPrefix + * 属性的key 的前缀;
+ * 在解析过程中,以具体协议实现的标准参数的key 加入此前缀后从属性表中检索参数值;
+ * 如果指定为 null 或者空白,则忽略此参数; + * @return + */ + ConsensusSettings createSettings(Properties props); + + Properties createPropertiesTemplate(); + + void writeSettings(ConsensusSettings settings, Properties props); +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/GroupIndexer.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/GroupIndexer.java new file mode 100644 index 00000000..11330d16 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/GroupIndexer.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.consensus; + +/** + * @author huanghaiquan + * + */ +public interface GroupIndexer { + + byte[] getGroupId(Object[] messageObjects); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/MessageService.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/MessageService.java new file mode 100644 index 00000000..4740e6d9 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/MessageService.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +public interface MessageService { + + AsyncFuture sendOrdered(byte[] message); + + AsyncFuture sendUnordered(byte[] message); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/NodeSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/NodeSettings.java new file mode 100644 index 00000000..31ae9a36 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/NodeSettings.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.ValueType; + +/** + * 节点的配置参数; + * + * @author huanghaiquan + * + */ +@DataContract(code=TypeCodes.CONSENSUS_NODE_SETTINGS) +public interface NodeSettings { + + /** + * 用于标识一个节点的地址;
+ * + * 该值没有一个通用定义,可以是具体的通讯地址,也可以只是标识符,或者区块链地址,而是由特定的共识服务提供者的实现进行定义; + * + * @return + */ + @DataField(order=0, primitiveType=ValueType.TEXT) + String getAddress(); + + /** + * Base58 格式的公钥; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + PubKey getPubKey(); +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/OrderedAction.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/OrderedAction.java new file mode 100644 index 00000000..f9415d64 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/OrderedAction.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.consensus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标识一个共识方法调用模式为“有序的消息调用”; + * + * @author huanghaiquan + * + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface OrderedAction { + + /** + * 请求分组的索引器;
+ * + * 指定一个 {@link GroupIndexer} 接口的实现类,用于根据请求消息列表来生成共识的分组ID; + * @return + */ + Class groupIndexer() ; + + /** + * 回复消息转换器; + *

+ * 指定一个 {@link BinaryMessageConverter} 接口的实现类; + * + * @return + */ + Class responseConverter(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/SettingsFactory.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/SettingsFactory.java new file mode 100644 index 00000000..449e39fa --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/SettingsFactory.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.consensus; + +import com.jd.blockchain.utils.io.BytesEncoder; + +/** + * 配置参数的编码器; + * + * @author huanghaiquan + * + */ +public interface SettingsFactory { + + ConsensusSettingsBuilder getConsensusSettingsBuilder(); + + BytesEncoder getConsensusSettingsEncoder(); + + BytesEncoder getIncomingSettingsEncoder(); + + + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/Topology.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/Topology.java new file mode 100644 index 00000000..dbb2fd3b --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/Topology.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.consensus; + +import java.io.Serializable; + +public interface Topology extends Serializable { + int getId(); + + Topology copyOf(); +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/UnorderedAction.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/UnorderedAction.java new file mode 100644 index 00000000..aa62b9fe --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/UnorderedAction.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.consensus; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author huanghaiquan + * + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface UnorderedAction { + /** + * 请求分组的索引器;
+ * + * 指定一个 {@link GroupIndexer} 接口的实现类,用于根据请求消息列表来生成共识的分组ID; + * @return + */ + Class groupIndexer() ; +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequest.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequest.java new file mode 100644 index 00000000..b88fc0f5 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequest.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.consensus.action; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.CONSENSUS_ACTION_REQUEST) +public interface ActionRequest { + + @DataField(order=1, list=true, primitiveType= ValueType.INT8) + byte[] getGroupId(); + + @DataField(order=2, primitiveType=ValueType.TEXT) + String getHandleType(); + + @DataField(order=3, primitiveType=ValueType.TEXT) + String getHandleMethod(); + +// String getMessageType(); + + @DataField(order=4, list=true, primitiveType= ValueType.INT8) + byte[] getMessageBody(); + + @DataField(order=5, primitiveType= ValueType.TEXT) + String getTransactionType(); + +// String getReponseType(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequestData.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequestData.java new file mode 100644 index 00000000..aa1514bb --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionRequestData.java @@ -0,0 +1,60 @@ +//package com.jd.blockchain.consensus.action; +// +//public class ActionRequestData implements ActionRequest{ +// +// private byte[] groupId; +// +// private String handleType; +// +// private String handleMethod; +// +// private byte[] messageBody; +// +// private String transactionType; +// +// @Override +// public byte[] getGroupId() { +// return groupId; +// } +// +// @Override +// public String getHandleType() { +// return handleType; +// } +// +// @Override +// public String getHandleMethod() { +// return handleMethod; +// } +// +// @Override +// public byte[] getMessageBody() { +// return messageBody; +// } +// +// @Override +// public String getTransactionType() { +// return transactionType; +// } +// +// public void setGroupId(byte[] groupId) { +// this.groupId = groupId; +// } +// +// public void setHandleType(String handleType) { +// this.handleType = handleType; +// } +// +// public void setHandleMethod(String handleMethod) { +// this.handleMethod = handleMethod; +// } +// +// public void setMessageBody(byte[] messageBody) { +// this.messageBody = messageBody; +// } +// +// public void setTransactionType(String transactionType) { +// this.transactionType = transactionType; +// } +// +//} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponse.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponse.java new file mode 100644 index 00000000..8e388aa2 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponse.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.consensus.action; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.CONSENSUS_ACTION_RESPONSE) +public interface ActionResponse { + + @DataField(order=1, list=true, primitiveType= ValueType.INT8) + byte[] getMessage(); + + @DataField(order=2, primitiveType=ValueType.BOOLEAN) + boolean getError(); + + @DataField(order=3, primitiveType=ValueType.TEXT) + String getErrorMessage(); + + @DataField(order=4, primitiveType=ValueType.TEXT) + String getErrorType(); + +} \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponseData.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponseData.java new file mode 100644 index 00000000..3a8e3ffb --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/action/ActionResponseData.java @@ -0,0 +1,49 @@ +package com.jd.blockchain.consensus.action; + +public class ActionResponseData implements ActionResponse { + + private byte[] message; + + private boolean error = false; + + private String errorMessage; + + private String errorType; + + @Override + public byte[] getMessage() { + return message; + } + + public void setMessage(byte[] message) { + this.message = message; + } + + @Override + public boolean getError() { + return error; + } + + public void setError(boolean error) { + this.error = error; + } + + @Override + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + @Override + public String getErrorType() { + return errorType; + } + + public void setErrorType(String errorType) { + this.errorType = errorType; + } + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientFactory.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientFactory.java new file mode 100644 index 00000000..0f35c76e --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientFactory.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.consensus.client; + +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusManageService; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; + +public interface ClientFactory { + + /** + * 创建客户端的认证身份; + * + * @param clientKeyPair + * @return + */ + ClientIdentification buildAuthId(CryptoKeyPair clientKeyPair); + + /** + * 根据接入配置信息创建客户端的本地连接配置; + * + * @param incomingSettings + * @return + */ + ClientSettings buildClientSettings(ClientIncomingSettings incomingSettings); + + /** + * 创建共识管理服务的客户端代理;
+ * + * 当一个共识网络的节点启动共识服务之后,其中的一些或者全部节点会暴露一个专门的管理服务端口,用于提供客户端接入认证服务和新节点接入认证服务; + * + *
+ * + * 作为客户端,可以选择其中的一个或者多个节点的管理服务端口进行接入认证;
+ * + * 不同的共识算法可以自行决定实现不同的连接策略,例如:从参数指定的地址列表中随机挑选一个可成功建立连接的节点,或者同时连接多个以进行客户端交叉验证; + * + * @param serviceNodes + * @return + */ + ConsensusManageService createManageServiceClient(String[] serviceNodes); + + /** + * 根据共识客户端的配置信息建立一个共识客户端实例; + * + * @param settings + * @return + */ + ConsensusClient setupClient(ClientSettings settings); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientSettings.java new file mode 100644 index 00000000..5551e18a --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ClientSettings.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.consensus.client; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +/** + * 共识客户端的配置参数; + * + * @author huanghaiquan + * + */ +public interface ClientSettings { + + /** + * 客户端ID; + * + * @return + */ + int getClientId(); + + /** + * 客户端的公钥; + * + * @return + */ + PubKey getClientPubKey(); + + /** + * 共识网络的配置参数; + * + * @return + */ + ConsensusSettings getConsensusSettings(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClient.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClient.java new file mode 100644 index 00000000..7e41f1d4 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClient.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.consensus.client; + +import java.io.Closeable; + +import com.jd.blockchain.consensus.MessageService; + +public interface ConsensusClient extends Closeable { + + /** + * 消息服务; + * + * @return + */ + MessageService getMessageService(); + + /** + * 共识客户端的配置信息; + * + * @return + */ + ClientSettings getSettings(); + + /** + * 是否已连接; + * + * @return + */ + boolean isConnected(); + + /** + * 接入共识网络; + */ + void connect(); + + /** + * 断开与共识网络的连接; + */ + @Override + void close(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClientProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClientProvider.java new file mode 100644 index 00000000..fe1c2c54 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/client/ConsensusClientProvider.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.consensus.client; + +import com.jd.blockchain.consensus.SettingsFactory; + +public interface ConsensusClientProvider { + + + ClientFactory getClientFactory(); +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventEntity.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventEntity.java new file mode 100644 index 00000000..e93ccd9b --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventEntity.java @@ -0,0 +1,29 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: EventEntity + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午11:18 + * Description: + */ +package com.jd.blockchain.consensus.event; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class EventEntity { + + private T entity; + + public T getEntity() { + return this.entity; + } + + public void setEntity(T entity) { + this.entity = entity; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventProducer.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventProducer.java new file mode 100644 index 00000000..baa5e0a3 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/event/EventProducer.java @@ -0,0 +1,21 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.exchange.EventProducer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午11:15 + * Description: + */ +package com.jd.blockchain.consensus.event; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public interface EventProducer { + + public void publish(T t); +} \ No newline at end of file diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ConsensusServiceProvider.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ConsensusServiceProvider.java new file mode 100644 index 00000000..8feea616 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ConsensusServiceProvider.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.consensus.service; + +public interface ConsensusServiceProvider { + + NodeServerFactory getServerFactory(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/MessageHandle.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/MessageHandle.java new file mode 100644 index 00000000..4e0e1b89 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/MessageHandle.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.consensus.service; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +/** + * 消息处理器; + * + * @author huanghaiquan + * + */ +public interface MessageHandle { + + /** + * 开始一个新批次来处理有序的消息; + * + * @return 返回新批次的 ID ; + */ + String beginBatch(String realmName); + + /** + * 处理有序的消息; + * + * @param messageId + * 消息ID; + * @param message + * 消息内容; + * @param batchId + * 批次ID; + */ + AsyncFuture processOrdered(int messageId, byte[] message, String realmName, String batchId); + + /** + * 完成处理批次,返回要进行一致性校验的状态快照; + * + * @param batchId + * @return + */ + StateSnapshot completeBatch(String realmName, String batchId); + + /** + * 提交处理批次; + * + * @param batchId + */ + void commitBatch(String realmName, String batchId); + + /** + * 回滚处理批次; + * + * @param batchId + */ + void rollbackBatch(String realmName, String batchId, int reasonCode); + + /** + * 处理无序消息; + * + * @param message + * @return + */ + AsyncFuture processUnordered(byte[] message); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServer.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServer.java new file mode 100644 index 00000000..8fb50f2d --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServer.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.consensus.service; + +import com.jd.blockchain.consensus.ConsensusManageService; + +public interface NodeServer { + + String getProviderName(); + + ConsensusManageService getManageService(); + + ServerSettings getSettings(); + + boolean isRunning(); + + void start(); + + void stop(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServerFactory.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServerFactory.java new file mode 100644 index 00000000..67e60d22 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/NodeServerFactory.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.consensus.service; + +import com.jd.blockchain.consensus.ConsensusSettings; + +/** + * 共识节点服务器的工厂; + * + * @author huanghaiquan + * + */ +public interface NodeServerFactory { + + /** + * 构建一个共识节点的参数配置; + * + * @param realmName + * 共识域的名称; + * @param consensusSetting + * 共识配置; + * @param currentNodeAddress + * 共识节点的虚拟地址;必须是 {@link ConsensusSettings#getNodes()} 中的一项; + * @return 共识节点的参数配置; + */ + ServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress); + + /** + * 创建一个节点服务器; + * + * @param serverSettings + * 服务器配置; + * @param messageHandler + * @param stateMachineReplicator + * @return + */ + NodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler, + StateMachineReplicate stateMachineReplicator); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ServerSettings.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ServerSettings.java new file mode 100644 index 00000000..14454a72 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/ServerSettings.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.consensus.service; + +import com.jd.blockchain.consensus.NodeSettings; + +/** + * Replica 服务器的本地配置; + * + * @author huanghaiquan + * + */ +public interface ServerSettings { + + String getRealmName(); + + NodeSettings getReplicaSettings(); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateHandle.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateHandle.java new file mode 100644 index 00000000..b0aca23f --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateHandle.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.consensus.service; + +public interface StateHandle { + + byte[] takeSnapshot(); + + void installSnapshot(byte[] snapshot); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateMachineReplicate.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateMachineReplicate.java new file mode 100644 index 00000000..a9929c0b --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateMachineReplicate.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.consensus.service; + +import java.io.InputStream; +import java.util.Iterator; + +/** + * 状态机复制接口; + * + * @author huanghaiquan + * + */ +public interface StateMachineReplicate { + + /** + * 获取最新的状态编号;
+ * + * 注:新的状态编号总数比旧的状态编号大; + * + * @return + */ + long getLatestStateID(String realmName); + + /** + * 返回指定状态编号的快照; + * + * @param stateId + * @return + */ + StateSnapshot getSnapshot(String realmName, long stateId); + + /** + * 返回包含指定的起止状态编号在内的全部状态快照; + * + * @param fromStateId + * 起始的状态编号(含); + * @param toStateId + * 截止的状态编号(含); + * @return + */ + Iterator getSnapshots(String realmName, long fromStateId, long toStateId); + + /** + * 读状态数据; + * + * @param stateId + * @return + */ + InputStream readState(String realmName, long stateId); + + /** + * 装载状态数据; + * + * @param snapshot + * @param state + */ + void setupState(String realmName, StateSnapshot snapshot, InputStream state); + +} diff --git a/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateSnapshot.java b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateSnapshot.java new file mode 100644 index 00000000..883e4e03 --- /dev/null +++ b/source/consensus/consensus-framework/src/main/java/com/jd/blockchain/consensus/service/StateSnapshot.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.consensus.service; + +/** + * 状态快照; + * + * @author huanghaiquan + * + */ +public interface StateSnapshot { + + /** + * 状态的唯一编号; + * + * @return + */ + long getId(); + + /** + * 状态的快照数据; + * + * @return + */ + byte[] getSnapshot(); +} diff --git a/source/consensus/consensus-mq/pom.xml b/source/consensus/consensus-mq/pom.xml new file mode 100644 index 00000000..7e5a6a0b --- /dev/null +++ b/source/consensus/consensus-mq/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + + com.jd.blockchain + consensus + 0.8.2.RELEASE + + consensus-mq + + consensus-mq + + + UTF-8 + 1.8 + 1.8 + + + + + com.jd.blockchain + consensus-framework + ${project.version} + + + com.jd.blockchain + tools-keygen + ${project.version} + + + com.jd.blockchain + ledger-model + ${project.version} + + + + com.lmax + disruptor + + + io.nats + jnats + + + + com.rabbitmq + amqp-client + + + + junit + junit + test + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + + diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusProvider.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusProvider.java new file mode 100644 index 00000000..4bd555ce --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusProvider.java @@ -0,0 +1,55 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/18 下午2:50 + * Description: + */ +package com.jd.blockchain.consensus.mq; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.SettingsFactory; +import com.jd.blockchain.consensus.client.ClientFactory; +import com.jd.blockchain.consensus.mq.client.MsgQueueClientFactory; +import com.jd.blockchain.consensus.mq.config.MsgQueueSettingsFactory; +import com.jd.blockchain.consensus.mq.server.MsgQueueNodeServerFactory; +import com.jd.blockchain.consensus.service.NodeServerFactory; + +/** + * + * @author shaozhuguang + * @create 2018/12/18 + * @since 1.0.0 + */ + +public class MsgQueueConsensusProvider implements ConsensusProvider { + + public static final String NAME = MsgQueueConsensusProvider.class.getName(); + + private static MsgQueueSettingsFactory settingsFactory = new MsgQueueSettingsFactory(); + + private static MsgQueueClientFactory clientFactory = new MsgQueueClientFactory(); + + private static MsgQueueNodeServerFactory nodeServerFactory = new MsgQueueNodeServerFactory(); + + @Override + public String getName() { + return NAME; + } + + @Override + public SettingsFactory getSettingsFactory() { + return settingsFactory; + } + + @Override + public ClientFactory getClientFactory() { + return clientFactory; + } + + @Override + public NodeServerFactory getServerFactory() { + return nodeServerFactory; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusSettingsBuilder.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusSettingsBuilder.java new file mode 100644 index 00000000..025ffcef --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/MsgQueueConsensusSettingsBuilder.java @@ -0,0 +1,253 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.MsgQueueConsensusSettingsBuilder + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午1:46 + * Description: + */ +package com.jd.blockchain.consensus.mq; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.ConsensusSettingsBuilder; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueNetworkConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueNodeConfig; +import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.PropertiesUtils; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.springframework.core.io.ClassPathResource; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueConsensusSettingsBuilder implements ConsensusSettingsBuilder { + + private static final String DEFAULT_TOPIC_TX = "tx-topic"; + + private static final String DEFAULT_TOPIC_BL = "bl-topic"; + + private static final String DEFAULT_TOPIC_MSG = "msg-topic"; + + private static final int DEFAULT_TXSIZE = 1000; + + private static final int DEFAULT_MAXDELAY = 1000; + + /** + * + */ + private static final String CONFIG_TEMPLATE_FILE = "mq.config"; + + /** + * 参数键:节点数量; + */ + public static final String SERVER_NUM_KEY = "system.servers.num"; + + /** + * 参数键格式:节点公钥; + */ + public static final String PUBKEY_PATTERN = "system.server.%s.pubkey"; + + public static final String MSG_QUEUE_SERVER = "system.msg.queue.server"; + + public static final String MSG_QUEUE_TOPIC_TX = "system.msg.queue.topic.tx"; + + public static final String MSG_QUEUE_TOPIC_BL = "system.msg.queue.topic.bl"; + + public static final String MSG_QUEUE_TOPIC_MSG = "system.msg.queue.topic.msg"; + + public static final String MSG_QUEUE_BLOCK_TXSIZE = "system.msg.queue.block.txsize"; + + public static final String MSG_QUEUE_BLOCK_MAXDELAY = "system.msg.queue.block.maxdelay"; + + private static Properties CONFIG_TEMPLATE; + + static { + if (FileUtils.existFile(CONFIG_TEMPLATE_FILE)) { + ClassPathResource configResource = new ClassPathResource(CONFIG_TEMPLATE_FILE); + try { + try (InputStream in = configResource.getInputStream()) { + CONFIG_TEMPLATE = PropertiesUtils.load(in, BytesUtils.DEFAULT_CHARSET); + } + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + } + + @Override + public MsgQueueConsensusSettings createSettings(Properties props) { + MsgQueueNetworkConfig networkConfig = new MsgQueueNetworkConfig(); + Properties resolvingProps = PropertiesUtils.cloneFrom(props); + + String server = PropertiesUtils.getProperty(resolvingProps, MSG_QUEUE_SERVER, true); + if (server == null || server.length()<= 0) { + throw new IllegalArgumentException(String.format("Property[%s] is empty!", MSG_QUEUE_SERVER)); + } + networkConfig.setServer(server) + .setTxTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_TX, DEFAULT_TOPIC_TX)) + .setBlTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_BL, DEFAULT_TOPIC_BL)) + .setMsgTopic(initProp(resolvingProps, MSG_QUEUE_TOPIC_MSG, DEFAULT_TOPIC_MSG)) + ; + + MsgQueueBlockConfig blockConfig = new MsgQueueBlockConfig() + .setTxSizePerBlock(initProp(resolvingProps, MSG_QUEUE_BLOCK_TXSIZE, DEFAULT_TXSIZE)) + .setMaxDelayMilliSecondsPerBlock(initProp(resolvingProps, MSG_QUEUE_BLOCK_MAXDELAY, DEFAULT_MAXDELAY)) + ; + + MsgQueueConsensusConfig consensusConfig = new MsgQueueConsensusConfig() + .setBlockSettings(blockConfig) + .setNetworkSettings(networkConfig) + ; + // load node settings + int serversNum = PropertiesUtils.getInt(resolvingProps, SERVER_NUM_KEY); + for (int i = 0; i < serversNum; i++) { + int id = i; + + String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id); + + String base58PubKey = PropertiesUtils.getRequiredProperty(resolvingProps, keyOfPubkey); + PubKey pubKey = KeyGenCommand.decodePubKey(base58PubKey); + +// PubKey pubKey = new PubKey(Base58Utils.decode(base58PubKey)); + resolvingProps.remove(keyOfPubkey); + Bytes address = AddressEncoding.generateAddress(pubKey); + + String networkAddress = address.toBase58(); + MsgQueueNodeConfig nodeConfig = new MsgQueueNodeConfig() + .setAddress(networkAddress) + .setPubKey(pubKey) + ; + consensusConfig.addNodeSettings(nodeConfig); + } + return consensusConfig; + } + + @Override + public Properties createPropertiesTemplate() { + return PropertiesUtils.cloneFrom(CONFIG_TEMPLATE); + } + + @Override + public void writeSettings(ConsensusSettings settings, Properties props) { + + if (!(settings instanceof MsgQueueConsensusSettings)) { + throw new IllegalArgumentException("ConsensusSettings data isn't supported! Accept MsgQueueConsensusSettings only!"); + } + + MsgQueueConsensusSettings consensusSettings = (MsgQueueConsensusSettings) settings; + + MsgQueueNetworkSettings networkSettings = consensusSettings.getNetworkSettings(); + if (networkSettings == null || networkSettings.getServer() == null || networkSettings.getServer().length() <= 0) { + throw new IllegalArgumentException("MsgQueue Consensus server is empty!"); + } + + String server = networkSettings.getServer(); + props.setProperty(MSG_QUEUE_SERVER, server); + + String txTopic = networkSettings.getTxTopic(); + if (txTopic == null || txTopic.length() <= 0) { + txTopic = DEFAULT_TOPIC_TX; + } + props.setProperty(MSG_QUEUE_TOPIC_TX, txTopic); + + String blTopic = networkSettings.getBlTopic(); + if (blTopic == null || blTopic.length() <= 0) { + blTopic = DEFAULT_TOPIC_BL; + } + props.setProperty(MSG_QUEUE_TOPIC_BL, blTopic); + + String msgTopic = networkSettings.getMsgTopic(); + if (msgTopic == null || msgTopic.length() <= 0) { + msgTopic = DEFAULT_TOPIC_MSG; + } + props.setProperty(MSG_QUEUE_TOPIC_MSG, msgTopic); + + MsgQueueBlockSettings blockSettings = consensusSettings.getBlockSettings(); + if (blockSettings == null) { + props.setProperty(MSG_QUEUE_BLOCK_TXSIZE, DEFAULT_TXSIZE + ""); + props.setProperty(MSG_QUEUE_BLOCK_MAXDELAY, DEFAULT_MAXDELAY + ""); + } else { + int txSize = blockSettings.getTxSizePerBlock(); + long maxDelay = blockSettings.getMaxDelayMilliSecondsPerBlock(); + props.setProperty(MSG_QUEUE_BLOCK_TXSIZE, txSize + ""); + props.setProperty(MSG_QUEUE_BLOCK_MAXDELAY, maxDelay + ""); + } + + +// int serversNum = PropertiesUtils.getInt(props, SERVER_NUM_KEY); +// if (serversNum > 0) { +// for (int i = 0; i < serversNum; i++) { +// int id = i; +// String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id); +// props.remove(keyOfPubkey); +// +// String keyOfHost = nodeKey(CONSENSUS_HOST_PATTERN, id); +// props.remove(keyOfHost); +// } +// } +// +// NodeSettings[] nodesSettings = consensusSettings.getNodes(); +// serversNum = nodesSettings.length; +// props.setProperty(SERVER_NUM_KEY, serversNum + ""); +// +// for (int i = 0; i < serversNum; i++) { +// MsgQueueNodeSettings mqns = (MsgQueueNodeSettings) nodesSettings[i]; +// int id = i; +// String keyOfPubkey = nodeKey(PUBKEY_PATTERN, id); +// props.setProperty(keyOfPubkey, mqns.getPubKey().toBase58()); +// +// String keyOfHost = nodeKey(CONSENSUS_HOST_PATTERN, id); +// props.setProperty(keyOfHost, mqns.getAddress() == null ? "" : mqns.getAddress()); +// } + } + + private String initProp(Properties resolvingProps, String key, String defaultVal) { + try { + String value = PropertiesUtils.getProperty(resolvingProps, key, true); + if (value == null || value.length() <= 0) { + value = defaultVal; + } + return value; + } catch (Exception e) { + return defaultVal; + } + } + + private int initProp(Properties resolvingProps, String key, int defaultVal) { + try { + int value = PropertiesUtils.getInt(resolvingProps, key); + if (value <= 0) { + value = defaultVal; + } + return value; + } catch (Exception e) { + return defaultVal; + } + } + + private static String nodeKey(String pattern, int id) { + return String.format(pattern, id); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/DefaultMessageTransmitter.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/DefaultMessageTransmitter.java new file mode 100644 index 00000000..61333cfc --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/DefaultMessageTransmitter.java @@ -0,0 +1,311 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.client.DefaultMessageTransmitter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午3:05 + * Description: + */ +package com.jd.blockchain.consensus.mq.client; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.lmax.disruptor.EventHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jd.blockchain.consensus.MessageService; +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.event.TxBlockedEvent; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class DefaultMessageTransmitter implements MessageTransmitter, MessageService { + + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultMessageTransmitter.class); + + private final ExecutorService messageExecutorArray = Executors.newFixedThreadPool(10); + +// private final ExecutorService blockExecutor = Executors.newSingleThreadExecutor(); +// +// private final ExecutorService extendExecutor = Executors.newSingleThreadExecutor(); + + private final Map messageListeners = new ConcurrentHashMap<>(); + + private final BlockEventHandler blockEventHandler = new BlockEventHandler(); + + private final ExtendEventHandler extendEventHandler = new ExtendEventHandler(); + + private MsgQueueProducer txProducer; + + private MsgQueueProducer msgProducer; + + private MsgQueueConsumer blConsumer; + + private MsgQueueConsumer msgConsumer; + + private boolean isConnected = false; + + public DefaultMessageTransmitter setTxProducer(MsgQueueProducer txProducer) { + this.txProducer = txProducer; + return this; + } + + public DefaultMessageTransmitter setMsgProducer(MsgQueueProducer msgProducer) { + this.msgProducer = msgProducer; + return this; + } + + public DefaultMessageTransmitter setBlConsumer(MsgQueueConsumer blConsumer) { + this.blConsumer = blConsumer; + return this; + } + + public DefaultMessageTransmitter setMsgConsumer(MsgQueueConsumer msgConsumer) { + this.msgConsumer = msgConsumer; + return this; + } + + @Override + public AsyncFuture sendOrdered(byte[] message) { + + AsyncFuture messageFuture; + + try { + publishMessage(txProducer, message); + messageFuture = messageHandle(message); + } catch (Exception e) { + throw new RuntimeException(e); + } + return messageFuture; + } + + @Override + public AsyncFuture sendUnordered(byte[] message) { + AsyncFuture messageFuture; + try { + publishMessage(msgProducer, message); + messageFuture = messageHandle(message); + } catch (Exception e) { + throw new RuntimeException(e); + } + return messageFuture; + } + + @Override + public void connect() throws Exception{ + if (!isConnected) { + this.txProducer.connect(); + this.blConsumer.connect(blockEventHandler); + this.msgProducer.connect(); + this.msgConsumer.connect(extendEventHandler); + isConnected = true; + blConsumer.start(); + msgConsumer.start(); +// blockConsumerListening(); +// extendConsumerListening(); + } + } + + @Override + public void publishMessage(MsgQueueProducer producer, byte[] message) throws Exception { + producer.publish(message); + } + + @Override + public void close() { + try { + txProducer.close(); + blConsumer.close(); + msgProducer.close(); + msgConsumer.close(); + isConnected = false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private AsyncFuture messageHandle(byte[] message) throws Exception { +// 异步回调 +// 需要监听MQ结块的应答 +// 首先需要一个Consumer,在子类已实现 + String messageKey = messageKey(message); + AsyncFuture messageFuture = registerMessageListener(messageKey); + return messageFuture; + } + + private String messageKey(byte[] message) { + return MessageConvertUtil.messageKey(message); + } + + private AsyncFuture registerMessageListener(String messageKey) { + CompletableAsyncFuture future = new CompletableAsyncFuture<>(); + MessageListener messageListener = new MessageListener(messageKey, future); + messageListener.addListener(); + return future; + } + +// private void blockConsumerListening() { +// // 区块事件由单独一个线程处理 +// blockExecutor.execute(() -> { +// while(isConnected) { +// try { +// byte[] txBlockedEventBytes = blConsumer.start(); +// // 交由事件处理机制来处理 +// if (txBlockedEventBytes != null && txBlockedEventBytes.length > 0) { +// txBlockedEventHandle(txBlockedEventBytes); +// } +// } catch (Exception e) { +// LOGGER.error("process block listening message exception {}", e.getMessage()); +// } +// } +// }); +// } + +// private void extendConsumerListening() { +// extendExecutor.execute(() -> { +// while (isConnected) { +// try { +// byte[] msgBytes = msgConsumer.start(); +// // 交由事件处理机制来处理 +// if (msgBytes != null && msgBytes.length > 0) { +// extendMessageHandle(msgBytes); +// } +// } catch (Exception e) { +// LOGGER.error("process extend listening message exception {}", e.getMessage()); +// } +// } +// }); +// } + + private void txBlockedEventHandle(byte[] bytes) { + messageExecutorArray.execute(() -> { + if (!this.messageListeners.isEmpty()) { + // 首先将字节数组转换为BlockEvent + final TxBlockedEvent txBlockedEvent = + MessageConvertUtil.convertBytes2TxBlockedEvent(bytes); + if (txBlockedEvent != null) { + // 需要判断该区块是否需要处理 + if (isTxBlockedEventNeedManage(txBlockedEvent)) { + dealTxBlockedEvent(txBlockedEvent); + } + } + } + }); + } + + private void extendMessageHandle(byte[] message) { + messageExecutorArray.execute(() -> { + String messageKey = messageKey(message); + if (messageListeners.containsKey(messageKey)) { + dealExtendMessage(messageKey, message); + } + }); + } + + private boolean isTxBlockedEventNeedManage(final TxBlockedEvent txBlockedEvent) { + if (this.messageListeners.isEmpty()) { + return false; + } + if (messageListeners.containsKey(txBlockedEvent.getTxKey())) { + return true; + } + // 无须处理区块高度 + return false; + } + + private void dealTxBlockedEvent(final TxBlockedEvent txBlockedEvent) { + String txKey = txBlockedEvent.getTxKey(); + MessageListener txListener = this.messageListeners.get(txKey); + if (txListener != null) { + txListener.received(txBlockedEvent); + this.messageListeners.remove(txKey); + } + } + + private void dealExtendMessage(final String messageKey, final byte[] message) { + MessageListener txListener = this.messageListeners.get(messageKey); + if (txListener != null) { + txListener.received(message); + this.messageListeners.remove(messageKey); + } + } + + private class MessageListener { + + final String messageKey; + + final CompletableAsyncFuture future; + + final AtomicBoolean isDeal = new AtomicBoolean(false); + + public MessageListener(String messageKey, CompletableAsyncFuture future) { + this.messageKey = messageKey; + this.future = future; + addListener(); + } + + public void addListener() { + synchronized (messageListeners) { + messageListeners.put(messageKey, this); + } + } + + public void received(final TxBlockedEvent txBlockedEvent) { + // 期望是false,假设是false则设置为true,成功的情况下表示是第一次 + byte[] txResp = txBlockedEvent.txResponseBytes(); + if (txResp != null) { + if (isDeal.compareAndSet(false, true)) { + //生成对应的交易应答 + future.complete(txResp); + } + } + } + + public void received(final byte[] message) { + // 期望是false,假设是false则设置为true,成功的情况下表示是第一次 + if (message != null) { + if (isDeal.compareAndSet(false, true)) { + //生成对应的交易应答 + future.complete(message); + } + } + } + } + + public class BlockEventHandler implements EventHandler> { + + @Override + public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception { + byte[] txBlockedEventBytes = event.getEntity(); + if (txBlockedEventBytes != null && txBlockedEventBytes.length > 0) { + txBlockedEventHandle(txBlockedEventBytes); + } + } + } + + public class ExtendEventHandler implements EventHandler> { + + @Override + public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception { + byte[] msgBytes = event.getEntity(); + if (msgBytes != null && msgBytes.length > 0) { + extendMessageHandle(msgBytes); + } + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MessageTransmitter.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MessageTransmitter.java new file mode 100644 index 00000000..73c574e0 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MessageTransmitter.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.client.MessageTransmitter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:21 + * Description: + */ +package com.jd.blockchain.consensus.mq.client; + + +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public interface MessageTransmitter { + + void connect() throws Exception; + + void publishMessage(MsgQueueProducer producer, byte[] message) throws Exception; + +// void processMsg(byte[] message); + + void close(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientFactory.java new file mode 100644 index 00000000..2158f223 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientFactory.java @@ -0,0 +1,97 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.client.MsgQueueClientFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:23 + * Description: + */ +package com.jd.blockchain.consensus.mq.client; + +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusManageService; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.client.ClientFactory; +import com.jd.blockchain.consensus.client.ClientSettings; +import com.jd.blockchain.consensus.mq.config.MsgQueueClientConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig; +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; + +import java.lang.reflect.Proxy; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueClientFactory implements ClientFactory { + + @Override + public MsgQueueClientIdentification buildAuthId(CryptoKeyPair clientKeyPair) { + PubKey pubKey = clientKeyPair.getPubKey(); + byte[] address = pubKey.toBytes(); // 使用公钥地址作为认证信息 + + SignatureFunction signatureFunction = CryptoUtils.sign(pubKey.getAlgorithm()); + SignatureDigest signatureDigest = signatureFunction.sign(clientKeyPair.getPrivKey(), address); + + MsgQueueClientIdentification mqci = new MsgQueueClientIdentification() + .setPubKey(clientKeyPair.getPubKey()) + .setIdentityInfo(address) + .setSignature(signatureDigest) + ; + return mqci; + } + + @Override + public MsgQueueClientSettings buildClientSettings(ClientIncomingSettings incomingSettings) { + + MsgQueueClientIncomingSettings mqcic = (MsgQueueClientIncomingSettings)incomingSettings; + if (mqcic != null) { + return buildClientSettings(mqcic.getClientId(), mqcic.getPubKey(), (MsgQueueConsensusSettings)(mqcic.getConsensusSettings())); + } + throw new IllegalArgumentException("ClientIncomingSettings data isn't supported! Accept MsgQueueClientIncomingSettings only!"); + } + + private MsgQueueClientSettings buildClientSettings(int clientId, PubKey pubKey, MsgQueueConsensusSettings mqcs) { + + MsgQueueClientSettings msgQueueClientConfig = new MsgQueueClientConfig() + .setId(clientId) + .setPubKey(pubKey) + .setConsensusSettings(mqcs) + ; + return msgQueueClientConfig; + } + + @Override + public ConsensusManageService createManageServiceClient(String[] serviceNodes) { + // todo serviceNodes // IP:port + return null; + } + + @Override + public MsgQueueConsensusClient setupClient(ClientSettings settings) { + if (settings instanceof MsgQueueClientSettings) { + return setupClient((MsgQueueClientSettings)settings); + } + throw new IllegalArgumentException("ClientSettings data isn't supported! Accept MsgQueueClientSettings only!"); + } + + private MsgQueueConsensusClient setupClient(MsgQueueClientSettings settings) { + MsgQueueConsensusClient mqcc = new MsgQueueConsensusClient() + .setClientSettings(settings) + .setMsgQueueNetworkSettings(settings.getMsgQueueNetworkSettings()) + ; + mqcc.init(); + return mqcc; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientIdentification.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientIdentification.java new file mode 100644 index 00000000..b34b70b3 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueClientIdentification.java @@ -0,0 +1,78 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.client.MsgQueueClientIdentification + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午2:04 + * Description: + */ +package com.jd.blockchain.consensus.mq.client; + +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; + +import java.util.Arrays; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueClientIdentification implements ClientIdentification { + + private byte[] identityInfo; + + private PubKey pubKey; + + private SignatureDigest signature; + + public MsgQueueClientIdentification() { + } + + public MsgQueueClientIdentification(ClientIdentification clientIdentification) { + identityInfo = clientIdentification.getIdentityInfo(); + pubKey = clientIdentification.getPubKey(); + signature = clientIdentification.getSignature(); + } + + public MsgQueueClientIdentification setIdentityInfo(byte[] identityInfo) { + this.identityInfo = identityInfo; + return this; + } + + public MsgQueueClientIdentification setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + return this; + } + + public MsgQueueClientIdentification setSignature(SignatureDigest signature) { + this.signature = signature; + return this; + } + + @Override + public byte[] getIdentityInfo() { + return this.identityInfo; + } + + @Override + public PubKey getPubKey() { + return this.pubKey; + } + + @Override + public SignatureDigest getSignature() { + return this.signature; + } + + @Override + public String getProviderName() { + return MsgQueueConsensusProvider.NAME; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueConsensusClient.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueConsensusClient.java new file mode 100644 index 00000000..6432dcb3 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/client/MsgQueueConsensusClient.java @@ -0,0 +1,100 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.client.MsgQueueConsensusClient + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午3:23 + * Description: + */ +package com.jd.blockchain.consensus.mq.client; + +import com.jd.blockchain.consensus.MessageService; +import com.jd.blockchain.consensus.client.ClientSettings; +import com.jd.blockchain.consensus.client.ConsensusClient; +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueConsensusClient implements ConsensusClient { + + private boolean isConnected; + + private DefaultMessageTransmitter transmitter; + + private MsgQueueNetworkSettings msgQueueNetworkSettings; + + private MsgQueueClientSettings clientSettings; + + public MsgQueueConsensusClient setClientSettings(MsgQueueClientSettings clientSettings) { + this.clientSettings = clientSettings; + return this; + } + + public MsgQueueConsensusClient setMsgQueueNetworkSettings(MsgQueueNetworkSettings msgQueueNetworkSettings) { + this.msgQueueNetworkSettings = msgQueueNetworkSettings; + return this; + } + + public void init() { + String server = msgQueueNetworkSettings.getServer(); + String txTopic = msgQueueNetworkSettings.getTxTopic(); + String blTopic = msgQueueNetworkSettings.getBlTopic(); + String msgTopic = msgQueueNetworkSettings.getMsgTopic(); + + MsgQueueProducer txProducer = MsgQueueFactory.newProducer(server, txTopic); + MsgQueueProducer msgProducer = MsgQueueFactory.newProducer(server, msgTopic); + MsgQueueConsumer blConsumer = MsgQueueFactory.newConsumer(server, blTopic); + MsgQueueConsumer msgConsumer = MsgQueueFactory.newConsumer(server, msgTopic); + + transmitter = new DefaultMessageTransmitter() + .setTxProducer(txProducer) + .setMsgProducer(msgProducer) + .setBlConsumer(blConsumer) + .setMsgConsumer(msgConsumer) + ; + } + + @Override + public MessageService getMessageService() { + return transmitter; + } + + @Override + public ClientSettings getSettings() { + return clientSettings; + } + + @Override + public boolean isConnected() { + return isConnected; + } + + @Override + public synchronized void connect() { + if (!isConnected) { + try { + this.transmitter.connect(); + isConnected = true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public synchronized void close() { + if (isConnected) { + transmitter.close(); + isConnected = false; + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueBlockConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueBlockConfig.java new file mode 100644 index 00000000..7a8a7560 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueBlockConfig.java @@ -0,0 +1,47 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午2:57 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; + +import java.lang.reflect.Method; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public class MsgQueueBlockConfig implements MsgQueueBlockSettings { + + private int txSizePerBlock; + + private long maxDelayMilliSecondsPerBlock; + + @Override + public int getTxSizePerBlock() { + return txSizePerBlock; + } + + public MsgQueueBlockConfig setTxSizePerBlock(int txSizePerBlock) { + this.txSizePerBlock = txSizePerBlock; + return this; + } + + @Override + public long getMaxDelayMilliSecondsPerBlock() { + return maxDelayMilliSecondsPerBlock; + } + + public MsgQueueBlockConfig setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) { + this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; + return this; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientConfig.java new file mode 100644 index 00000000..5fec7a69 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientConfig.java @@ -0,0 +1,65 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午2:23 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueClientConfig implements MsgQueueClientSettings { + + private int id; + + private PubKey pubKey; + + private MsgQueueConsensusSettings consensusSettings; + + public MsgQueueClientConfig setId(int id) { + this.id = id; + return this; + } + + public MsgQueueClientConfig setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + return this; + } + + public MsgQueueClientConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + return this; + } + + @Override + public int getClientId() { + return this.id; + } + + @Override + public PubKey getClientPubKey() { + return this.pubKey; + } + + @Override + public MsgQueueConsensusSettings getConsensusSettings() { + return this.consensusSettings; + } + + @Override + public MsgQueueNetworkSettings getMsgQueueNetworkSettings() { + return this.consensusSettings.getNetworkSettings(); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientIncomingConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientIncomingConfig.java new file mode 100644 index 00000000..8e947f26 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueClientIncomingConfig.java @@ -0,0 +1,69 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.config.MsgQueueClientIncomingConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:50 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +import java.lang.reflect.Method; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueClientIncomingConfig implements MsgQueueClientIncomingSettings { + + private int clientId; + + private PubKey pubKey; + + private MsgQueueConsensusSettings consensusSettings; + + public MsgQueueClientIncomingConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + return this; + } + + public MsgQueueClientIncomingConfig setClientId(int clientId) { + this.clientId = clientId; + return this; + } + + public MsgQueueClientIncomingConfig setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + return this; + } + + @Override + public int getClientId() { + return this.clientId; + } + + @Override + public String getProviderName() { + return MsgQueueConsensusProvider.NAME; + } + + @Override + public MsgQueueConsensusSettings getConsensusSettings() { + return this.consensusSettings; + } + + @Override + public PubKey getPubKey() { + return pubKey; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueConsensusConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueConsensusConfig.java new file mode 100644 index 00000000..526ac603 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueConsensusConfig.java @@ -0,0 +1,65 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.config.MsgQueueConsensusConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:26 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.mq.settings.*; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; + +/** + * 设置消息队列的信息 + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueConsensusConfig implements MsgQueueConsensusSettings { + + private List nodeSettingsList = new ArrayList<>(); + + private MsgQueueNetworkSettings networkSettings; + + private MsgQueueBlockSettings blockSettings; + + public MsgQueueConsensusConfig addNodeSettings(MsgQueueNodeSettings nodeSettings) { + nodeSettingsList.add(nodeSettings); + return this; + } + + public MsgQueueConsensusConfig setNetworkSettings(MsgQueueNetworkSettings networkSettings) { + this.networkSettings = networkSettings; + return this; + } + + public MsgQueueConsensusConfig setBlockSettings(MsgQueueBlockSettings blockSettings) { + this.blockSettings = blockSettings; + return this; + } + + @Override + public NodeSettings[] getNodes() { + return nodeSettingsList.toArray(new NodeSettings[nodeSettingsList.size()]); + } + + @Override + public MsgQueueNetworkSettings getNetworkSettings() { + return networkSettings; + } + + @Override + public MsgQueueBlockSettings getBlockSettings() { + return blockSettings; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNetworkConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNetworkConfig.java new file mode 100644 index 00000000..d73c6b65 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNetworkConfig.java @@ -0,0 +1,71 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueNetworkConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午4:55 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; + +import java.lang.reflect.Method; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueNetworkConfig implements MsgQueueNetworkSettings { + + private String server; + + private String txTopic; + + private String blTopic; + + private String msgTopic; + + public MsgQueueNetworkConfig setServer(String server) { + this.server = server; + return this; + } + + public MsgQueueNetworkConfig setTxTopic(String txTopic) { + this.txTopic = txTopic; + return this; + } + + public MsgQueueNetworkConfig setBlTopic(String blTopic) { + this.blTopic = blTopic; + return this; + } + + public MsgQueueNetworkConfig setMsgTopic(String msgTopic) { + this.msgTopic = msgTopic; + return this; + } + + @Override + public String getServer() { + return server; + } + + @Override + public String getTxTopic() { + return txTopic; + } + + @Override + public String getBlTopic() { + return blTopic; + } + + @Override + public String getMsgTopic() { + return msgTopic; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNodeConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNodeConfig.java new file mode 100644 index 00000000..c7cc03d7 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueNodeConfig.java @@ -0,0 +1,46 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.config.MsgQueueNodeConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:33 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +/** + * peer节点IP + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueNodeConfig implements MsgQueueNodeSettings { + + private String address; + + private PubKey pubKey; + + public MsgQueueNodeConfig setAddress(String address) { + this.address = address; + return this; + } + + public MsgQueueNodeConfig setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + return this; + } + + @Override + public String getAddress() { + return this.address; + } + + @Override + public PubKey getPubKey() { + return this.pubKey; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueServerConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueServerConfig.java new file mode 100644 index 00000000..7b7a79b9 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueServerConfig.java @@ -0,0 +1,72 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.config.MsgQueueServerConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:32 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings; + +/** + * peer节点配置 + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueServerConfig implements MsgQueueServerSettings { + + private MsgQueueBlockSettings blockSettings; + + private MsgQueueConsensusSettings consensusSettings; + + private MsgQueueNodeSettings nodeSettings; + + private String realmName; + + public MsgQueueServerConfig setRealmName(String realmName) { + this.realmName = realmName; + return this; + } + + public MsgQueueServerConfig setBlockSettings(MsgQueueBlockSettings blockSettings) { + this.blockSettings = blockSettings; + return this; + } + + public MsgQueueServerConfig setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + return setBlockSettings(consensusSettings.getBlockSettings()); + } + + public MsgQueueServerConfig setNodeSettings(MsgQueueNodeSettings nodeSettings) { + this.nodeSettings = nodeSettings; + return this; + } + + @Override + public String getRealmName() { + return this.realmName; + } + + @Override + public MsgQueueNodeSettings getReplicaSettings() { + return nodeSettings; + } + + @Override + public MsgQueueBlockSettings getBlockSettings() { + return blockSettings; + } + + @Override + public MsgQueueConsensusSettings getConsensusSettings() { + return consensusSettings; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueSettingsFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueSettingsFactory.java new file mode 100644 index 00000000..e87adb9e --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/config/MsgQueueSettingsFactory.java @@ -0,0 +1,105 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.config.MsgQueueSettingsFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:49 + * Description: + */ +package com.jd.blockchain.consensus.mq.config; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.SettingsFactory; +import com.jd.blockchain.consensus.mq.MsgQueueConsensusSettingsBuilder; +import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; +import com.jd.blockchain.utils.io.BytesEncoder; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueSettingsFactory implements SettingsFactory { + + static { + DataContractRegistry.register(NodeSettings.class); + + DataContractRegistry.register(MsgQueueNodeSettings.class); + + DataContractRegistry.register(ConsensusSettings.class); + + DataContractRegistry.register(MsgQueueConsensusSettings.class); + + DataContractRegistry.register(MsgQueueNetworkSettings.class); + + DataContractRegistry.register(MsgQueueBlockSettings.class); + + DataContractRegistry.register(MsgQueueClientIncomingSettings.class); + + DataContractRegistry.register(ClientIncomingSettings.class); + } + + private static final MsgQueueConsensusSettingsEncoder MQCS_ENCODER = new MsgQueueConsensusSettingsEncoder(); + + private static final MsgQueueClientIncomingSettingsEncoder MQCIS_ENCODER = new MsgQueueClientIncomingSettingsEncoder(); + + private static final MsgQueueConsensusSettingsBuilder BUILDER = new MsgQueueConsensusSettingsBuilder(); + + @Override + public MsgQueueConsensusSettingsBuilder getConsensusSettingsBuilder() { + return BUILDER; + } + + @Override + public BytesEncoder getConsensusSettingsEncoder() { + return MQCS_ENCODER; + } + + @Override + public BytesEncoder getIncomingSettingsEncoder() { + return MQCIS_ENCODER; + } + + private static class MsgQueueConsensusSettingsEncoder implements BytesEncoder{ + + @Override + public byte[] encode(ConsensusSettings data) { + if (data instanceof MsgQueueConsensusSettings) { + return BinaryEncodingUtils.encode(data, MsgQueueConsensusSettings.class); + } + throw new IllegalArgumentException("Settings data isn't supported! Accept MsgQueueConsensusSettings only!"); + } + + @Override + public MsgQueueConsensusSettings decode(byte[] bytes) { + return BinaryEncodingUtils.decodeAs(bytes, MsgQueueConsensusSettings.class); + } + } + + private static class MsgQueueClientIncomingSettingsEncoder implements BytesEncoder{ + + @Override + public byte[] encode(ClientIncomingSettings data) { + if (data instanceof MsgQueueClientIncomingSettings) { + return BinaryEncodingUtils.encode(data, MsgQueueClientIncomingSettings.class); + } + throw new IllegalArgumentException("Settings data isn't supported! Accept MsgQueueClientIncomingSettings only!"); + } + + @Override + public MsgQueueClientIncomingSettings decode(byte[] bytes) { + return BinaryEncodingUtils.decodeAs(bytes, MsgQueueClientIncomingSettings.class); + } + + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/AbstractConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/AbstractConsumer.java new file mode 100644 index 00000000..9c39d8e9 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/AbstractConsumer.java @@ -0,0 +1,44 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.consumer.AbstractConsumer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/29 下午12:31 + * Description: + */ +package com.jd.blockchain.consensus.mq.consumer; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.jd.blockchain.consensus.event.EventProducer; +import com.jd.blockchain.consensus.mq.exchange.BytesEventFactory; +import com.jd.blockchain.consensus.mq.exchange.BytesEventProducer; +import com.lmax.disruptor.BlockingWaitStrategy; +import com.lmax.disruptor.EventHandler; +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; + +/** + * + * @author shaozhuguang + * @create 2018/12/29 + * @since 1.0.0 + */ + +public abstract class AbstractConsumer implements MsgQueueConsumer { + + protected EventProducer eventProducer; + + protected void initEventHandler(EventHandler eventHandler) { + Disruptor> disruptor = + new Disruptor<>(new BytesEventFactory(), + BytesEventFactory.BUFFER_SIZE, r -> { + return new Thread(r); + }, ProducerType.SINGLE, new BlockingWaitStrategy()); + + disruptor.handleEventsWith(eventHandler); + disruptor.start(); + RingBuffer> ringBuffer = disruptor.getRingBuffer(); + this.eventProducer = new BytesEventProducer(ringBuffer); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/MsgQueueConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/MsgQueueConsumer.java new file mode 100644 index 00000000..43908abb --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/MsgQueueConsumer.java @@ -0,0 +1,28 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: MsgQueueConsumer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:38 + * Description: + */ +package com.jd.blockchain.consensus.mq.consumer; + +import com.lmax.disruptor.EventHandler; + +import java.io.Closeable; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public interface MsgQueueConsumer extends Closeable { + + void connect(EventHandler eventHandler) throws Exception; + + void start() throws Exception; +} + diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/NatsConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/NatsConsumer.java new file mode 100644 index 00000000..0df9c6dd --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/NatsConsumer.java @@ -0,0 +1,76 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.nats.RabbitConsumer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:40 + * Description: + */ +package com.jd.blockchain.consensus.mq.consumer; + +import com.jd.blockchain.utils.ConsoleUtils; +import com.lmax.disruptor.EventHandler; +import io.nats.client.*; + +import java.io.IOException; +import java.time.Duration; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class NatsConsumer extends AbstractConsumer implements MsgQueueConsumer { + + private final ExecutorService msgListener = Executors.newSingleThreadExecutor(); + + private Connection nc; + + private Subscription sub; + + private String server; + + private String topic; + + public NatsConsumer(String server, String topic) { + this.server = server; + this.topic = topic; + } + + @Override + public void connect(EventHandler eventHandler) throws Exception { + initEventHandler(eventHandler); + Options options = new Options.Builder().server(server).noReconnect().build(); + this.nc = Nats.connect(options); + this.sub = nc.subscribe(topic); + this.nc.flush(Duration.ZERO); + ConsoleUtils.info("[*] NatsConsumer[%s, %s] connect success !!!", this.server, this.topic); + } + + @Override + public void start() { + msgListener.execute(() -> { + for (;;) { + try { + Message msg = this.sub.nextMessage(Duration.ZERO); + eventProducer.publish(msg.getData()); + } catch (Exception e) { + e.printStackTrace(); + } + } + }); + } + + @Override + public void close() throws IOException { + try { + nc.close(); + } catch (Exception e) { + throw new IOException(e); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/RabbitConsumer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/RabbitConsumer.java new file mode 100644 index 00000000..5076537a --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/consumer/RabbitConsumer.java @@ -0,0 +1,92 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.nats.RabbitConsumer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:40 + * Description: + */ +package com.jd.blockchain.consensus.mq.consumer; + +import com.jd.blockchain.consensus.mq.factory.RabbitFactory; +import com.jd.blockchain.utils.ConsoleUtils; +import com.lmax.disruptor.EventHandler; +import com.rabbitmq.client.*; + +import java.io.IOException; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class RabbitConsumer extends AbstractConsumer implements MsgQueueConsumer { + + private Connection connection; + + private Channel channel; + + private String exchangeName; + + private String server; + + private String queueName; + + public RabbitConsumer(String server, String topic) { + this.server = server; + this.exchangeName = topic; + } + + private void rabbitConsumerHandle() throws Exception { + rabbitConsumerHandleByQueue(); + } + + private void rabbitConsumerHandleByQueue() throws IOException { + DefaultConsumer consumer = new DefaultConsumer(channel) { + @Override + public void handleDelivery(String consumerTag, Envelope envelope, + AMQP.BasicProperties properties, byte[] body) { + // 此处将收到的消息加入队列即可 + try { + eventProducer.publish(body); + channel.basicAck(envelope.getDeliveryTag(), false); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + this.channel.basicConsume(this.queueName, false, consumer); + } + + @Override + public void connect(EventHandler eventHandler) throws Exception { + initEventHandler(eventHandler); + ConnectionFactory factory = RabbitFactory.initConnectionFactory(server); + connection = factory.newConnection(); + channel = connection.createChannel(); + + channel.exchangeDeclare(this.exchangeName, "fanout"); + queueName = channel.queueDeclare().getQueue(); + channel.queueBind(queueName, this.exchangeName, ""); + channel.basicQos(8); + + ConsoleUtils.info("[*] RabbitConsumer[%s, %s] connect success !!!", this.server, this.exchangeName); + } + + @Override + public void start() throws Exception { + rabbitConsumerHandle(); + } + + @Override + public void close() throws IOException { + try { + this.channel.close(); + this.connection.close(); + } catch (Exception e) { + throw new IOException(e); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/BlockEvent.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/BlockEvent.java new file mode 100644 index 00000000..03b92113 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/BlockEvent.java @@ -0,0 +1,64 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: BlockEvent + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/20 上午11:32 + * Description: + */ +package com.jd.blockchain.consensus.mq.event; + +import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @author shaozhuguang + * @create 2018/11/20 + * @since 1.0.0 + */ + +public class BlockEvent { + + private Map txMap = new HashMap<>(); + + public Map getTxMap() { + return txMap; + } + + public void setTxMap(Map txMap) { + this.txMap = txMap; + } + + public void put(String txKey, String txResp) { + txMap.put(txKey, txResp); + } + + public void put(String txKey, byte[] txResp) { + put(txKey, MessageConvertUtil.base64Encode(txResp)); + } + + public String getTxResp(String txKey) { + return txMap.get(txKey); + } + + public byte[] getTxRespBytes(String txKey) { + String txResp = getTxResp(txKey); + if (txResp != null && txResp.length() > 0) { + // 字符串转字节数组 + return MessageConvertUtil.base64Decode(txResp); + } + return null; + } + + public boolean containTxResp(String txKey) { + return txMap.containsKey(txKey); + } + + public boolean isEmpty() { + if (txMap == null) return true; + return txMap.isEmpty(); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/MessageEvent.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/MessageEvent.java new file mode 100644 index 00000000..94710a43 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/MessageEvent.java @@ -0,0 +1,44 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.peer.consensus.MessageEvent + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/23 上午11:45 + * Description: + */ +package com.jd.blockchain.consensus.mq.event; + +/** + * + * @author shaozhuguang + * @create 2018/11/23 + * @since 1.0.0 + */ + +public class MessageEvent { + + String messageKey; + + byte[] message; + + public MessageEvent(String messageKey, byte[] message) { + this.messageKey = messageKey; + this.message = message; + } + + public String getMessageKey() { + return messageKey; + } + + public void setMessageKey(String messageKey) { + this.messageKey = messageKey; + } + + public byte[] getMessage() { + return message; + } + + public void setMessage(byte[] message) { + this.message = message; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/TxBlockedEvent.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/TxBlockedEvent.java new file mode 100644 index 00000000..5c0515d6 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/event/TxBlockedEvent.java @@ -0,0 +1,58 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: BlockEvent + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/20 上午11:32 + * Description: + */ +package com.jd.blockchain.consensus.mq.event; + + +import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; + +/** + * + * @author shaozhuguang + * @create 2018/11/20 + * @since 1.0.0 + */ + +public class TxBlockedEvent { + + private String txKey; + + private String transaction; + + public TxBlockedEvent() { + } + + public TxBlockedEvent(String txKey, String transaction) { + this.txKey = txKey; + this.transaction = transaction; + } + + public void setTxKey(String txKey) { + this.txKey = txKey; + } + + public void setTransaction(String transaction) { + this.transaction = transaction; + } + + public String getTxKey() { + return txKey; + } + + public String getTransaction() { + return transaction; + } + + public byte[] txResponseBytes() { + if (transaction != null && transaction.length() > 0) { + // 字符串转字节数组 + return MessageConvertUtil.base64Decode(transaction); + } + return null; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventFactory.java new file mode 100644 index 00000000..557c2649 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventFactory.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 上午10:48 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.lmax.disruptor.EventFactory; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class BytesEventFactory implements EventFactory> { + + public static final int BUFFER_SIZE = 256 * 1024; +// public static final int BUFFER_SIZE = 8 * 1024; + + @Override + public EventEntity newInstance() { + return new EventEntity<>(); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventProducer.java new file mode 100644 index 00000000..0421e425 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/BytesEventProducer.java @@ -0,0 +1,40 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventProducer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 上午10:50 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.jd.blockchain.consensus.event.EventProducer; +import com.lmax.disruptor.RingBuffer; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class BytesEventProducer implements EventProducer { + + private final RingBuffer> ringBuffer; + + public BytesEventProducer(RingBuffer> ringBuffer) { + this.ringBuffer = ringBuffer; + } + + @Override + public void publish(byte[] entity) { + long sequence = ringBuffer.next(); + try { + EventEntity event = ringBuffer.get(sequence); + event.setEntity(entity); + } finally { + this.ringBuffer.publish(sequence); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEntityFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEntityFactory.java new file mode 100644 index 00000000..39c7494a --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEntityFactory.java @@ -0,0 +1,31 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.ExchangeEntityFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午4:08 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class ExchangeEntityFactory { + + public static ExchangeEventInnerEntity newBlockInstance() { + return new ExchangeEventInnerEntity(ExchangeType.BLOCK); + } + + public static ExchangeEventInnerEntity newEmptyInstance() { + return new ExchangeEventInnerEntity(ExchangeType.EMPTY); + } + + public static ExchangeEventInnerEntity newTransactionInstance(byte[] content) { + return new ExchangeEventInnerEntity(ExchangeType.TRANSACTION, content); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventFactory.java new file mode 100644 index 00000000..6d16b8c7 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventFactory.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 上午10:48 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.lmax.disruptor.EventFactory; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class ExchangeEventFactory implements EventFactory> { + + public static final int BUFFER_SIZE = 256 * 1024; +// public static final int BUFFER_SIZE = 8 * 1024; + + @Override + public EventEntity newInstance() { + return new EventEntity<>(); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventInnerEntity.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventInnerEntity.java new file mode 100644 index 00000000..752c1414 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventInnerEntity.java @@ -0,0 +1,53 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.ExchangeEventInnerEntity + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午4:04 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class ExchangeEventInnerEntity { + + private ExchangeType type; + + private byte[] content; + + public ExchangeEventInnerEntity() { + } + + public ExchangeEventInnerEntity(ExchangeType type) { + this.type = type; + } + + public ExchangeEventInnerEntity(ExchangeType type, byte[] content) { + this.type = type; + this.content = content; + } + + + + public ExchangeType getType() { + return type; + } + + public void setType(ExchangeType type) { + this.type = type; + } + + public byte[] getContent() { + return content; + } + + public void setContent(byte[] content) { + this.content = content; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventProducer.java new file mode 100644 index 00000000..942fe167 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeEventProducer.java @@ -0,0 +1,40 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.rabbitmq.nats.consensus.disruptor.ExchangeEventProducer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 上午10:50 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.jd.blockchain.consensus.event.EventProducer; +import com.lmax.disruptor.RingBuffer; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class ExchangeEventProducer implements EventProducer { + + private final RingBuffer> ringBuffer; + + public ExchangeEventProducer(RingBuffer> ringBuffer) { + this.ringBuffer = ringBuffer; + } + + @Override + public void publish(ExchangeEventInnerEntity entity) { + long sequence = ringBuffer.next(); + try { + EventEntity event = ringBuffer.get(sequence); + event.setEntity(entity); + } finally { + this.ringBuffer.publish(sequence); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeType.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeType.java new file mode 100644 index 00000000..1920d89e --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/exchange/ExchangeType.java @@ -0,0 +1,23 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.nats.ExchangeType + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午3:34 + * Description: + */ +package com.jd.blockchain.consensus.mq.exchange; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public enum ExchangeType { + BLOCK, + EMPTY, + TRANSACTION, + ; +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueConfig.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueConfig.java new file mode 100644 index 00000000..1d4419a7 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueConfig.java @@ -0,0 +1,23 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.factory.MsgQueueConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午5:16 + * Description: + */ +package com.jd.blockchain.consensus.mq.factory; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public final class MsgQueueConfig { + + public static final String NATS_PREFIX = "nats"; + + public static final String RABBIT_PREFIX = "rabbit"; +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueFactory.java new file mode 100644 index 00000000..db2b0c1a --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/MsgQueueFactory.java @@ -0,0 +1,52 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: MsgQueueFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:13 + * Description: + */ +package com.jd.blockchain.consensus.mq.factory; + + +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; + +import static com.jd.blockchain.consensus.mq.factory.MsgQueueConfig.NATS_PREFIX; +import static com.jd.blockchain.consensus.mq.factory.MsgQueueConfig.RABBIT_PREFIX; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class MsgQueueFactory { + + public static MsgQueueProducer newProducer(String server, String topic) { + try { + if (server.startsWith(NATS_PREFIX)) { + return NatsFactory.newProducer(server, topic); + } else if (server.startsWith(RABBIT_PREFIX)) { + return RabbitFactory.newProducer(server, topic); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + return null; + } + + public static MsgQueueConsumer newConsumer(String server, String topic) { + try { + if (server.startsWith(NATS_PREFIX)) { + return NatsFactory.newConsumer(server, topic); + } else if (server.startsWith(RABBIT_PREFIX)) { + return RabbitFactory.newConsumer(server, topic); + } + return null; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/NatsFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/NatsFactory.java new file mode 100644 index 00000000..c5a4d8f0 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/NatsFactory.java @@ -0,0 +1,32 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: NatsFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:15 + * Description: + */ +package com.jd.blockchain.consensus.mq.factory; + +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.consumer.NatsConsumer; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.mq.producer.NatsProducer; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class NatsFactory { + + public static MsgQueueProducer newProducer(String server, String topic) throws Exception { + return new NatsProducer(server, topic); + } + + public static MsgQueueConsumer newConsumer(String server, String topic) throws Exception { + return new NatsConsumer(server, topic); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/RabbitFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/RabbitFactory.java new file mode 100644 index 00000000..b66cf3b6 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/factory/RabbitFactory.java @@ -0,0 +1,52 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.nats.RabbitFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:15 + * Description: + */ +package com.jd.blockchain.consensus.mq.factory; + +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.consumer.RabbitConsumer; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.mq.producer.RabbitProducer; +import com.rabbitmq.client.ConnectionFactory; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class RabbitFactory { + + public static MsgQueueProducer newProducer(String server, String topic) throws Exception { + return new RabbitProducer(server, topic); + } + + public static MsgQueueConsumer newConsumer(String server, String topic) throws Exception { + return new RabbitConsumer(server, topic); + } + + public static ConnectionFactory initConnectionFactory(String server) { + ConnectionFactory factory = new ConnectionFactory(); + // 解析server,生成host+port,默认格式:rabbit://localhost:5672 + try { + String[] hostAndPort = server.split("//")[1].split(":"); + if (hostAndPort == null || hostAndPort.length == 0) { + factory.setHost("localhost"); + } else if (hostAndPort.length == 1) { + factory.setHost(hostAndPort[0]); + } else { + factory.setHost(hostAndPort[0]); + factory.setPort(Integer.parseInt(hostAndPort[1])); + } + } catch (Exception e) { + factory.setHost("localhost"); + } + return factory; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/MsgQueueProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/MsgQueueProducer.java new file mode 100644 index 00000000..91cee801 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/MsgQueueProducer.java @@ -0,0 +1,36 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: MsgQueueProducer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:37 + * Description: + */ +package com.jd.blockchain.consensus.mq.producer; + +import java.io.Closeable; +import java.util.List; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public interface MsgQueueProducer extends Closeable { + + void connect() throws Exception; + + void publish(byte[] message) throws Exception; + + void publishString(String message) throws Exception; + + void publishStringList(List messages) throws Exception; + + void publishStringArray(String[] messages) throws Exception; + + void publishBytesArray(byte[][] message) throws Exception; + + void publishBytesList(List messages) throws Exception; +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/NatsProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/NatsProducer.java new file mode 100644 index 00000000..2f3b2357 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/NatsProducer.java @@ -0,0 +1,100 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: NatsProducer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:39 + * Description: + */ +package com.jd.blockchain.consensus.mq.producer; + +import io.nats.client.Connection; +import io.nats.client.Nats; +import io.nats.client.Options; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +import com.jd.blockchain.utils.ConsoleUtils; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class NatsProducer implements MsgQueueProducer { + + // 主要操作:发送MQ请求 + private Connection nc; + + private String server; + + // 主题 + private String topic; + + public NatsProducer() { + + } + + public NatsProducer(String server, String topic) { + this.topic = topic; + this.server = server; + } + + @Override + public void connect() throws Exception{ + Options o = new Options.Builder().server(server).noReconnect().build(); + this.nc = Nats.connect(o); + ConsoleUtils.info("[*] NatsProducer[%s, %s] connect success !!!", this.server, this.topic); + } + + @Override + public void publish(byte[] message) { + nc.publish(topic, message); + } + + @Override + public void publishString(String message) { + publish(message.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void publishStringList(List messages) { + for (String message : messages) { + publishString(message); + } + } + + @Override + public void publishStringArray(String[] messages) { + for (String message : messages) { + publishString(message); + } + } + + @Override + public void publishBytesArray(byte[][] message) { + for (byte[] bytes : message) { + publish(bytes); + } + } + + @Override + public void publishBytesList(List messages) { + for (byte[] message : messages) { + publish(message); + } + } + + @Override + public void close() throws IOException { + try { + nc.close(); + } catch (Exception e) { + throw new IOException(e); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/RabbitProducer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/RabbitProducer.java new file mode 100644 index 00000000..fcd6979b --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/producer/RabbitProducer.java @@ -0,0 +1,106 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.nats.RabbitProducer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/5 下午10:39 + * Description: + */ +package com.jd.blockchain.consensus.mq.producer; + +import com.jd.blockchain.consensus.mq.factory.RabbitFactory; +import com.jd.blockchain.utils.ConsoleUtils; +import com.rabbitmq.client.Channel; +import com.rabbitmq.client.Connection; +import com.rabbitmq.client.ConnectionFactory; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.List; + +/** + * + * @author shaozhuguang + * @create 2018/11/5 + * @since 1.0.0 + */ + +public class RabbitProducer implements MsgQueueProducer { + + // 主要操作时发送JMQ请求 + private Channel channel; + + private Connection connection; + + private String exchangeName; + + private String server; + + public RabbitProducer() { + + } + + public RabbitProducer(String server, String topic) throws Exception { + this.exchangeName = topic; + this.server = server; + } + + @Override + public void connect() throws Exception { + ConnectionFactory factory = RabbitFactory.initConnectionFactory(server); + connection = factory.newConnection(); + channel = connection.createChannel(); + channel.exchangeDeclare(this.exchangeName, "fanout"); + ConsoleUtils.info("[*] RabbitProducer[%s, %s] connect success !!!", this.server, this.exchangeName); + } + + @Override + public void publish(byte[] message) throws Exception { + channel.basicPublish(this.exchangeName, "", null, message); + } + + @Override + public void publishString(String message) throws Exception { + publish(message.getBytes(StandardCharsets.UTF_8)); + } + + @Override + public void publishStringList(List messages) throws Exception { + for (String message : messages) { + publishString(message); + } + } + + @Override + public void publishStringArray(String[] messages) throws Exception { + for (String message : messages) { + publishString(message); + } + } + + @Override + public void publishBytesArray(byte[][] message) throws Exception { + for (byte[] bytes : message) { + publish(bytes); + } + } + + @Override + public void publishBytesList(List messages) throws Exception { + for (byte[] message : messages) { + publish(message); + } + } + + @Override + public void close() throws IOException { + try { + channel.close(); + connection.close(); + } catch (Exception e) { + throw new IOException(e); + } + } + + +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/DefaultMsgQueueMessageDispatcher.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/DefaultMsgQueueMessageDispatcher.java new file mode 100644 index 00000000..63af3844 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/DefaultMsgQueueMessageDispatcher.java @@ -0,0 +1,297 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.server.DefaultMsgQueueMessageDispatcher + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 上午11:05 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.jd.blockchain.consensus.event.EventProducer; +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.exchange.ExchangeEntityFactory; +import com.jd.blockchain.consensus.mq.exchange.ExchangeEventFactory; +import com.jd.blockchain.consensus.mq.exchange.ExchangeEventInnerEntity; +import com.jd.blockchain.consensus.mq.exchange.ExchangeEventProducer; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.lmax.disruptor.BlockingWaitStrategy; +import com.lmax.disruptor.EventHandler; +import com.lmax.disruptor.RingBuffer; +import com.lmax.disruptor.dsl.Disruptor; +import com.lmax.disruptor.dsl.ProducerType; + +import java.io.IOException; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicLong; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public class DefaultMsgQueueMessageDispatcher implements MsgQueueMessageDispatcher, EventHandler> { + + private static final byte[] blockCommitBytes = new byte[]{0x00}; + + private final BlockingQueue dataQueue = new ArrayBlockingQueue<>(1024 * 16); + + private final ExecutorService dataExecutor = Executors.newSingleThreadExecutor(); + + private final ScheduledThreadPoolExecutor timeHandleExecutor = new ScheduledThreadPoolExecutor(2); + + private final AtomicLong blockIndex = new AtomicLong(); + + private long syncIndex = 0L; + + private MsgQueueProducer txProducer; + + private MsgQueueConsumer txConsumer; + + private EventProducer eventProducer; + + private EventHandler eventHandler; + + private final int TX_SIZE_PER_BLOCK; + + private final long MAX_DELAY_MILLISECONDS_PER_BLOCK; + + private boolean isRunning; + + private boolean isConnected; + + public DefaultMsgQueueMessageDispatcher(int txSizePerBlock, long maxDelayMilliSecondsPerBlock) { + this.TX_SIZE_PER_BLOCK = txSizePerBlock; + this.MAX_DELAY_MILLISECONDS_PER_BLOCK = maxDelayMilliSecondsPerBlock; + } + + public DefaultMsgQueueMessageDispatcher setTxProducer(MsgQueueProducer txProducer) { + this.txProducer = txProducer; + return this; + } + + public DefaultMsgQueueMessageDispatcher setTxConsumer(MsgQueueConsumer txConsumer) { + this.txConsumer = txConsumer; + return this; + } + + public DefaultMsgQueueMessageDispatcher setEventHandler(EventHandler eventHandler) { + this.eventHandler = eventHandler; + return this; + } + + public void init() { + handleDisruptor(eventHandler); + } + + private void handleDisruptor(EventHandler eventHandler) { + Disruptor> disruptor = + new Disruptor<>(new ExchangeEventFactory(), + ExchangeEventFactory.BUFFER_SIZE, r -> { + return new Thread(r); + }, ProducerType.SINGLE, new BlockingWaitStrategy()); + + disruptor.handleEventsWith(eventHandler); + disruptor.start(); + RingBuffer> ringBuffer = disruptor.getRingBuffer(); + + this.eventProducer = new ExchangeEventProducer(ringBuffer); + } + + public synchronized void connect() throws Exception { + if (!isConnected) { + txProducer.connect(); + txConsumer.connect(this); + isConnected = true; + } + } + + @Override + public synchronized void stop() throws Exception { + isRunning = false; + close(); + } + + @Override + public void run() { + this.isRunning = true; + try { + txConsumer.start(); + } catch (Exception e) { + + } +// handleData(); +// listen(); + } + +// private void listen() { +// while (isRunning) { +// try { +// byte[] data = this.txConsumer.start(); +// dataQueue.put(data); +// // 收到数据后由队列处理 +//// handleData(data); +// } catch (Exception e) { +// // 日志打印 +// ConsoleUtils.info("ERROR dispatcher start data exception {%s}", e.getMessage()); +// } +// } +// } + +// private void handleData() { +// dataExecutor.execute(() -> { +// byte[] data; +// for (;;) { +// try { +// data = dataQueue.take(); +// if (data.length == 1) { +// // 结块标识优先处理 +// syncIndex = 0L; +// this.blockIndex.getAndIncrement(); +// eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); +// } else { +// if (syncIndex == 0) { // 收到第一个交易 +// // 需要判断是否需要进行定时任务 +// if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { +// this.timeHandleExecutor.schedule( +// timeBlockTask(this.blockIndex.get()), +// MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); +// } +// } +// syncIndex++; +// eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); +// if (syncIndex == TX_SIZE_PER_BLOCK) { +// syncIndex = 0L; +// this.blockIndex.getAndIncrement(); +// eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); +// } +// } +// +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// }); +// for (;;) { +// try { +// final byte[] data = dataQueue.take(); +// dataExecutor.execute(() -> { +// if (data.length == 1) { +// // 结块标识优先处理 +// syncIndex = 0L; +// this.blockIndex.getAndIncrement(); +// eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); +// } else { +// if (syncIndex == 0) { // 收到第一个交易 +// // 需要判断是否需要进行定时任务 +// if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { +// this.timeHandleExecutor.schedule( +// timeBlockTask(this.blockIndex.get()), +// MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); +// } +// } +// syncIndex++; +// eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); +// if (syncIndex == TX_SIZE_PER_BLOCK) { +// syncIndex = 0L; +// this.blockIndex.getAndIncrement(); +// eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); +// } +// } +// } +// ); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// } + +// private void handleData(final byte[] data) { +// dataExecutor.execute(() -> { +// try { +// if (data.length == 1) { +// // 结块标识优先处理 +// syncIndex = 0L; +// this.blockIndex.getAndIncrement(); +// eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); +// } else { +// if (syncIndex == 0) { // 收到第一个交易 +// // 需要判断是否需要进行定时任务 +// if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { +// this.timeHandleExecutor.schedule( +// timeBlockTask(this.blockIndex.get()), +// MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); +// } +// } +// syncIndex++; +// eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); +// if (syncIndex == TX_SIZE_PER_BLOCK) { +// syncIndex = 0L; +// this.blockIndex.getAndIncrement(); +// eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); +// } +// } +// } catch (Exception e) { +// // 记录日志 +// ConsoleUtils.info("ERROR TransactionDispatcher process queue data exception {%s}", e.getMessage()); +// } +// }); +// +// } + + private Runnable timeBlockTask(final long currentBlockIndex) { + return () -> { + final boolean isEqualBlock = this.blockIndex.compareAndSet( + currentBlockIndex, currentBlockIndex + 1); + if (isEqualBlock) { + try { + txProducer.publish(blockCommitBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }; + } + + @Override + public void close() throws IOException { + this.txProducer.close(); + this.txConsumer.close(); + } + + @Override + public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception { + try { + byte[] data = event.getEntity(); +// System.out.printf("Thread [%s, $s] on event !!!\r\n", +// Thread.currentThread().getId(), Thread.currentThread().getName()); + if (data.length == 1) { + // 结块标识优先处理 + syncIndex = 0L; + this.blockIndex.getAndIncrement(); + eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); + } else { + if (syncIndex == 0) { // 收到第一个交易 + // 需要判断是否需要进行定时任务 + if (MAX_DELAY_MILLISECONDS_PER_BLOCK > 0) { + this.timeHandleExecutor.schedule( + timeBlockTask(this.blockIndex.get()), + MAX_DELAY_MILLISECONDS_PER_BLOCK, TimeUnit.MILLISECONDS); + } + } + syncIndex++; + eventProducer.publish(ExchangeEntityFactory.newTransactionInstance(data)); + if (syncIndex == TX_SIZE_PER_BLOCK) { + syncIndex = 0L; + this.blockIndex.getAndIncrement(); + eventProducer.publish(ExchangeEntityFactory.newBlockInstance()); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/ExtendMsgQueueMessageExecutor.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/ExtendMsgQueueMessageExecutor.java new file mode 100644 index 00000000..9a73ece4 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/ExtendMsgQueueMessageExecutor.java @@ -0,0 +1,126 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.server.DefaultMsgQueueMessageDispatcher + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 上午11:05 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.lmax.disruptor.EventHandler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public class ExtendMsgQueueMessageExecutor implements MsgQueueMessageDispatcher, EventHandler> { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExtendMsgQueueMessageExecutor.class); + + private final ExecutorService dataExecutor = Executors.newSingleThreadExecutor(); + + private MsgQueueProducer msgProducer; + + private MsgQueueConsumer msgConsumer; + + private MessageHandle messageHandle; + + private boolean isRunning; + + private boolean isConnected; + + public ExtendMsgQueueMessageExecutor setMsgProducer(MsgQueueProducer msgProducer) { + this.msgProducer = msgProducer; + return this; + } + + public ExtendMsgQueueMessageExecutor setMsgConsumer(MsgQueueConsumer msgConsumer) { + this.msgConsumer = msgConsumer; + return this; + } + + public ExtendMsgQueueMessageExecutor setMessageHandle(MessageHandle messageHandle) { + this.messageHandle = messageHandle; + return this; + } + + @Override + public void init() { + // do nothing + } + + public synchronized void connect() throws Exception { + if (!isConnected) { + msgProducer.connect(); + msgConsumer.connect(this); + msgConsumer.start(); + isConnected = true; + } + } + + @Override + public synchronized void stop() throws Exception { + isRunning = false; + close(); + } + + @Override + public void run() { + this.isRunning = true; +// this.msgConsumer.start(); +// listen(); + } + +// private void listen() { +// while (isRunning) { +// try { +// byte[] data = this.msgConsumer.start(); +// // 收到数据后由队列处理 +// handleData(data); +// } catch (Exception e) { +// // 日志打印 +// LOGGER.error("extend message handle exception {}", e.getMessage()); +// } +// } +// } + + private void handleData(byte[] data) { + dataExecutor.execute(() -> { + try { + AsyncFuture result = messageHandle.processUnordered(data); + msgProducer.publish(result.get()); + } catch (Exception e) { + LOGGER.error("process Unordered message exception {}", e.getMessage()); + } + }); + } + + @Override + public void close() throws IOException { + isConnected = false; + this.msgProducer.close(); + this.msgConsumer.close(); + } + + @Override + public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception { + byte[] data = event.getEntity(); + handleData(data); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueConsensusManageService.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueConsensusManageService.java new file mode 100644 index 00000000..d33c462e --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueConsensusManageService.java @@ -0,0 +1,73 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.server.MsgQueueConsensusManageService + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 下午1:46 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ConsensusManageService; +import com.jd.blockchain.consensus.ConsensusSecurityException; +import com.jd.blockchain.consensus.mq.client.MsgQueueClientIdentification; +import com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueConsensusConfig; +import com.jd.blockchain.consensus.mq.settings.MsgQueueClientIncomingSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; + +import java.lang.reflect.Proxy; +import java.util.Arrays; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueConsensusManageService implements ConsensusManageService { + + private MsgQueueConsensusSettings consensusSettings; + + public MsgQueueConsensusManageService setConsensusSettings(MsgQueueConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + return this; + } + + @Override + public MsgQueueClientIncomingSettings authClientIncoming(ClientIdentification authId) throws ConsensusSecurityException { + boolean isLegal = isLegal(authId); + if (isLegal) { + MsgQueueClientIncomingSettings mqcis = new MsgQueueClientIncomingConfig() + .setPubKey(authId.getPubKey()) + .setClientId(clientId(authId.getIdentityInfo())) + .setConsensusSettings(this.consensusSettings) + ; + return mqcis; + } + return null; + } + + private int clientId(byte[] identityInfo) { + // todo + + return 0; + } + + public boolean isLegal(ClientIdentification authId) { + boolean isLegal = false; + PubKey pubKey = authId.getPubKey(); + byte[] identityInfo = authId.getIdentityInfo(); + byte[] address = pubKey.toBytes(); // 使用公钥地址作为认证信息 + if (Arrays.equals(address, identityInfo)) { + SignatureFunction signatureFunction = CryptoUtils.sign(pubKey.getAlgorithm()); + isLegal = signatureFunction.verify(authId.getSignature(), pubKey, identityInfo); + } + return isLegal; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageDispatcher.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageDispatcher.java new file mode 100644 index 00000000..a3b6e286 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageDispatcher.java @@ -0,0 +1,28 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.server.MsgQueueMessageDispatcher + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:30 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + + +import java.io.Closeable; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public interface MsgQueueMessageDispatcher extends Runnable, Closeable { + + void init(); + + void connect() throws Exception; + + void stop() throws Exception; +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageExecutor.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageExecutor.java new file mode 100644 index 00000000..36454696 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueMessageExecutor.java @@ -0,0 +1,182 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.server.MsgQueueMessageExecutor + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午2:10 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + +import com.jd.blockchain.consensus.event.EventEntity; +import com.jd.blockchain.consensus.mq.event.MessageEvent; +import com.jd.blockchain.consensus.mq.event.TxBlockedEvent; +import com.jd.blockchain.consensus.mq.exchange.ExchangeEventInnerEntity; +import com.jd.blockchain.consensus.mq.exchange.ExchangeType; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.mq.util.MessageConvertUtil; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.consensus.service.StateMachineReplicate; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.lmax.disruptor.EventHandler; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public class MsgQueueMessageExecutor implements EventHandler> { + + private static final Logger LOGGER = LoggerFactory.getLogger(MsgQueueMessageExecutor.class); + + // todo 暂不处理队列溢出导致的OOM + private final ExecutorService blockEventExecutor = Executors.newFixedThreadPool(10); + + private MsgQueueProducer blProducer; + + private List exchangeEvents = new ArrayList<>(); + + private String realmName; + + private MessageHandle messageHandle; + + private final AtomicInteger messageId = new AtomicInteger(); + + private int txSizePerBlock = 1000; + + private StateMachineReplicate stateMachineReplicator; + + public MsgQueueMessageExecutor setRealmName(String realmName) { + this.realmName = realmName; + return this; + } + + public MsgQueueMessageExecutor setBlProducer(MsgQueueProducer blProducer) { + this.blProducer = blProducer; + return this; + } + + public MsgQueueMessageExecutor setTxSizePerBlock(int txSizePerBlock) { + this.txSizePerBlock = txSizePerBlock; + return this; + } + + public MsgQueueMessageExecutor setMessageHandle(MessageHandle messageHandle) { + this.messageHandle = messageHandle; + return this; + } + + public MsgQueueMessageExecutor setStateMachineReplicator(StateMachineReplicate stateMachineReplicator) { + this.stateMachineReplicator = stateMachineReplicator; + return this; + } + + public MsgQueueMessageExecutor init() { + try { + long latestStateId = stateMachineReplicator.getLatestStateID(realmName); + // 设置基础消息ID + messageId.set(((int)latestStateId + 1) * txSizePerBlock); + blProducer.connect(); + } catch (Exception e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception { + ExchangeEventInnerEntity entity = event.getEntity(); + if (entity != null) { + if (entity.getType() == ExchangeType.BLOCK || entity.getType() == ExchangeType.EMPTY) { + if (!exchangeEvents.isEmpty()) { + process(exchangeEvents); + exchangeEvents.clear(); + } + } else { + byte[] bytes = event.getEntity().getContent(); + String key = bytes2Key(bytes); + exchangeEvents.add(new MessageEvent(key, bytes)); + } + } + } + + private void process(List messageEvents) { + if (messageEvents != null && !messageEvents.isEmpty()) { + try { + Map> txResponseMap = execute(messageEvents); + if (txResponseMap != null && !txResponseMap.isEmpty()) { +// byte[] asyncFuture; + for (Map.Entry> entry : txResponseMap.entrySet()) { + final String txKey = entry.getKey(); + final AsyncFuture asyncFuture = entry.getValue(); +// asyncFuture = entry.getValue().get(); + + blockEventExecutor.execute(() -> { + TxBlockedEvent txBlockedEvent = new TxBlockedEvent(txKey, + MessageConvertUtil.base64Encode(asyncFuture.get())); + byte[] serializeBytes = MessageConvertUtil.serializeTxBlockedEvent(txBlockedEvent); + // 通过消息队列发送该消息 + try { + this.blProducer.publish(serializeBytes); + } catch (Exception e) { + LOGGER.error("publish block event message exception {}", e.getMessage()); + } + }); + } + } + } catch (Exception e) { + // 打印日志 + LOGGER.error("process message exception {}", e.getMessage()); + } + } + } + + private Map> execute(List messageEvents) { +// System.out.printf("Thread[%s %s] execute messageEvents !!! \r\n", +// Thread.currentThread().getId(), Thread.currentThread().getName()); + Map> asyncFutureMap = new HashMap<>(); + // 使用MessageHandle处理 +// long startTime = System.currentTimeMillis(); +// int txSize = messageEvents.size(); + String batchId = messageHandle.beginBatch(realmName); + try { + for (MessageEvent messageEvent : messageEvents) { + String txKey = messageEvent.getMessageKey(); + byte[] txContent = messageEvent.getMessage(); + AsyncFuture asyncFuture = messageHandle.processOrdered(messageId.getAndIncrement(), txContent, realmName, batchId); + asyncFutureMap.put(txKey, asyncFuture); + } + messageHandle.completeBatch(realmName, batchId); + messageHandle.commitBatch(realmName, batchId); +// long totalTime = System.currentTimeMillis() - startTime; +// String content = String.format("batch[%s] process, time = {%s}ms, TPS = %.2f \r\n", +// batchId, totalTime, txSize * 1000.0D / totalTime); +// System.out.println(content); +// logQueue.put(content); + // 提交之后需要获取对应的结果 + } catch (Exception e) { + // todo 需要处理应答码 404 + messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); + } + return asyncFutureMap; + } + + + private String bytes2Key(byte[] bytes) { + return MessageConvertUtil.messageKey(bytes); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServer.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServer.java new file mode 100644 index 00000000..3f1f0b82 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServer.java @@ -0,0 +1,196 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.server.MsgQueueNodeServer + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 上午11:20 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + +import com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider; +import com.jd.blockchain.consensus.mq.consumer.MsgQueueConsumer; +import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.consensus.mq.settings.MsgQueueBlockSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNetworkSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.consensus.service.StateMachineReplicate; + +import java.util.concurrent.Executors; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public class MsgQueueNodeServer implements NodeServer { + + private DefaultMsgQueueMessageDispatcher dispatcher; + + private ExtendMsgQueueMessageExecutor extendExecutor; + + private MessageHandle messageHandle; + + private StateMachineReplicate stateMachineReplicator; + + private MsgQueueMessageExecutor messageExecutor; + + private MsgQueueNetworkSettings networkSettings; + + private MsgQueueConsensusManageService manageService; + + private int txSizePerBlock = 1000; + + private long maxDelayMilliSecondsPerBlock = 1000; + + private MsgQueueServerSettings serverSettings; + + private boolean isRunning; + + public MsgQueueNodeServer setMessageHandle(MessageHandle messageHandle) { + this.messageHandle = messageHandle; + return this; + } + + public MsgQueueNodeServer setStateMachineReplicator(StateMachineReplicate stateMachineReplicator) { + this.stateMachineReplicator = stateMachineReplicator; + return this; + } + + public MsgQueueNodeServer setTxSizePerBlock(int txSizePerBlock) { + this.txSizePerBlock = txSizePerBlock; + return this; + } + + public MsgQueueNodeServer setMaxDelayMilliSecondsPerBlock(long maxDelayMilliSecondsPerBlock) { + this.maxDelayMilliSecondsPerBlock = maxDelayMilliSecondsPerBlock; + return this; + } + + public MsgQueueNodeServer setMsgQueueNetworkSettings(MsgQueueNetworkSettings networkSettings) { + this.networkSettings = networkSettings; + return this; + } + + public MsgQueueNodeServer setServerSettings(MsgQueueServerSettings serverSettings) { + this.serverSettings = serverSettings; + this.manageService = new MsgQueueConsensusManageService() + .setConsensusSettings(serverSettings.getConsensusSettings()); + return this; + } + + public MsgQueueNodeServer init() { + String realmName = this.serverSettings.getRealmName(); + MsgQueueBlockSettings blockSettings = this.serverSettings.getBlockSettings(); + MsgQueueConsensusSettings consensusSettings = this.serverSettings.getConsensusSettings(); + + this.setTxSizePerBlock(blockSettings.getTxSizePerBlock()) + .setMaxDelayMilliSecondsPerBlock(blockSettings.getMaxDelayMilliSecondsPerBlock()) + .setMsgQueueNetworkSettings(consensusSettings.getNetworkSettings()) + ; + + String server = networkSettings.getServer(), + txTopic = networkSettings.getTxTopic(), + blTopic = networkSettings.getBlTopic(), + msgTopic = networkSettings.getMsgTopic(); + + MsgQueueProducer blProducer = MsgQueueFactory.newProducer(server, blTopic), + txProducer = MsgQueueFactory.newProducer(server, txTopic), + msgProducer = MsgQueueFactory.newProducer(server, msgTopic); + + MsgQueueConsumer txConsumer = MsgQueueFactory.newConsumer(server, txTopic), + msgConsumer = MsgQueueFactory.newConsumer(server, msgTopic); + + initMessageExecutor(blProducer, realmName); + + initDispatcher(txProducer, txConsumer); + + initExtendExecutor(msgProducer, msgConsumer); + + return this; + } + + @Override + public String getProviderName() { + return MsgQueueConsensusProvider.NAME; + } + + @Override + public MsgQueueConsensusManageService getManageService() { + return this.manageService; + } + + @Override + public MsgQueueServerSettings getSettings() { + return serverSettings; + } + + @Override + public boolean isRunning() { + return isRunning; + } + + @Override + public synchronized void start() { + if (!isRunning) { + try { + dispatcher.connect(); + Executors.newSingleThreadExecutor().execute(dispatcher); + extendExecutor.connect(); + Executors.newSingleThreadExecutor().execute(extendExecutor); + isRunning = true; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + @Override + public synchronized void stop() { + if (isRunning) { + try { + dispatcher.stop(); + extendExecutor.stop(); + isRunning = false; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private void initMessageExecutor(MsgQueueProducer blProducer, final String realmName) { + messageExecutor = new MsgQueueMessageExecutor() + .setRealmName(realmName) + .setMessageHandle(messageHandle) + .setBlProducer(blProducer) + .setStateMachineReplicator(stateMachineReplicator) + .setTxSizePerBlock(txSizePerBlock) + .init() + ; + } + + private void initDispatcher(MsgQueueProducer txProducer, MsgQueueConsumer txConsumer) { + dispatcher = new DefaultMsgQueueMessageDispatcher(txSizePerBlock, maxDelayMilliSecondsPerBlock) + .setTxProducer(txProducer) + .setTxConsumer(txConsumer) + .setEventHandler(messageExecutor) + ; + dispatcher.init(); + } + + + private void initExtendExecutor(MsgQueueProducer msgProducer, MsgQueueConsumer msgConsumer) { + extendExecutor = new ExtendMsgQueueMessageExecutor() + .setMessageHandle(messageHandle) + .setMsgConsumer(msgConsumer) + .setMsgProducer(msgProducer) + ; + extendExecutor.init(); + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServerFactory.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServerFactory.java new file mode 100644 index 00000000..c8d8f337 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/server/MsgQueueNodeServerFactory.java @@ -0,0 +1,61 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.server.MsgQueueNodeServerFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:30 + * Description: + */ +package com.jd.blockchain.consensus.mq.server; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.mq.config.MsgQueueNodeConfig; +import com.jd.blockchain.consensus.mq.config.MsgQueueServerConfig; +import com.jd.blockchain.consensus.mq.settings.MsgQueueConsensusSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings; +import com.jd.blockchain.consensus.mq.settings.MsgQueueServerSettings; +import com.jd.blockchain.consensus.service.*; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ + +public class MsgQueueNodeServerFactory implements NodeServerFactory { + + @Override + public MsgQueueServerSettings buildServerSettings(String realmName, ConsensusSettings consensusSetting, String currentNodeAddress) { + + if (!(consensusSetting instanceof MsgQueueConsensusSettings)) { + throw new IllegalArgumentException("ConsensusSettings data isn't supported! Accept MsgQueueConsensusSettings only!"); + } + + MsgQueueNodeSettings nodeSettings = new MsgQueueNodeConfig().setAddress(currentNodeAddress); + + MsgQueueServerSettings serverSettings = new MsgQueueServerConfig() + .setRealmName(realmName) + .setNodeSettings(nodeSettings) + .setConsensusSettings((MsgQueueConsensusSettings) consensusSetting) + ; + return serverSettings; + + + } + + @Override + public MsgQueueNodeServer setupServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicator) { + if (!(serverSettings instanceof MsgQueueServerSettings)) { + throw new IllegalArgumentException("ServerSettings data isn't supported! Accept MsgQueueServerSettings only!"); + } + + MsgQueueNodeServer nodeServer = new MsgQueueNodeServer() + .setServerSettings((MsgQueueServerSettings) serverSettings) + .setMessageHandle(messageHandler) + .setStateMachineReplicator(stateMachineReplicator) + .init() + ; + return nodeServer; + } +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueBlockSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueBlockSettings.java new file mode 100644 index 00000000..95ba2000 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueBlockSettings.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueBlockSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午4:28 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ +@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_BLOCK_SETTINGS) +public interface MsgQueueBlockSettings { + + @DataField(order = 0, primitiveType = ValueType.INT32) + int getTxSizePerBlock(); + + @DataField(order = 1, primitiveType = ValueType.INT64) + long getMaxDelayMilliSecondsPerBlock(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientIncomingSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientIncomingSettings.java new file mode 100644 index 00000000..1eeffa91 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientIncomingSettings.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientIncomingSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午4:35 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.ValueType; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ +@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_CLI_INCOMING_SETTINGS) +public interface MsgQueueClientIncomingSettings extends ClientIncomingSettings { + + @DataField(order = 1, primitiveType=ValueType.BYTES) + PubKey getPubKey(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientSettings.java new file mode 100644 index 00000000..95f24516 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueClientSettings.java @@ -0,0 +1,23 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueClientSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午4:30 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.consensus.client.ClientSettings; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public interface MsgQueueClientSettings extends ClientSettings { + + MsgQueueNetworkSettings getMsgQueueNetworkSettings(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueConsensusSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueConsensusSettings.java new file mode 100644 index 00000000..d24c5422 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueConsensusSettings.java @@ -0,0 +1,33 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueConsensusSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午4:37 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.mq.config.MsgQueueBlockConfig; +import com.jd.blockchain.utils.Property; +import com.jd.blockchain.utils.ValueType; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ +@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_SETTINGS) +public interface MsgQueueConsensusSettings extends ConsensusSettings { + + @DataField(order = 0, refContract = true) + MsgQueueNetworkSettings getNetworkSettings(); + + @DataField(order = 1, refContract = true) + MsgQueueBlockSettings getBlockSettings(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNetworkSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNetworkSettings.java new file mode 100644 index 00000000..d512c32b --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNetworkSettings.java @@ -0,0 +1,36 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.mq.config.MsgQueueNetworkSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/12 上午11:43 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +/** + * + * @author shaozhuguang + * @create 2018/12/12 + * @since 1.0.0 + */ +@DataContract(code = TypeCodes.CONSENSUS_MSGQUEUE_NETWORK_SETTINGS) +public interface MsgQueueNetworkSettings { + + @DataField(order = 0, primitiveType = ValueType.TEXT) + String getServer(); + + @DataField(order = 1, primitiveType = ValueType.TEXT) + String getTxTopic(); + + @DataField(order = 2, primitiveType = ValueType.TEXT) + String getBlTopic(); + + @DataField(order = 3, primitiveType = ValueType.TEXT) + String getMsgTopic(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNodeSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNodeSettings.java new file mode 100644 index 00000000..784a1708 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueNodeSettings.java @@ -0,0 +1,25 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.settings.MsgQueueNodeSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午4:50 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.consensus.NodeSettings; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +@DataContract(code=TypeCodes.CONSENSUS_MSGQUEUE_NODE_SETTINGS) +public interface MsgQueueNodeSettings extends NodeSettings { + +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueServerSettings.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueServerSettings.java new file mode 100644 index 00000000..04ea74c2 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/settings/MsgQueueServerSettings.java @@ -0,0 +1,25 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.consensus.mq.config.MsgQueueServerSettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/13 下午4:39 + * Description: + */ +package com.jd.blockchain.consensus.mq.settings; + +import com.jd.blockchain.consensus.service.ServerSettings; + +/** + * + * @author shaozhuguang + * @create 2018/12/13 + * @since 1.0.0 + */ + +public interface MsgQueueServerSettings extends ServerSettings { + + MsgQueueBlockSettings getBlockSettings(); + + MsgQueueConsensusSettings getConsensusSettings(); +} \ No newline at end of file diff --git a/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/util/MessageConvertUtil.java b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/util/MessageConvertUtil.java new file mode 100644 index 00000000..c0b032a4 --- /dev/null +++ b/source/consensus/consensus-mq/src/main/java/com/jd/blockchain/consensus/mq/util/MessageConvertUtil.java @@ -0,0 +1,95 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.mq.event.MessageConvertUtil + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/21 下午7:28 + * Description: + */ +package com.jd.blockchain.consensus.mq.util; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.consensus.mq.event.BlockEvent; +import com.jd.blockchain.consensus.mq.event.TxBlockedEvent; +import com.jd.blockchain.utils.security.ShaUtils; + +import org.springframework.util.Base64Utils; + + +/** + * + * @author shaozhuguang + * @create 2018/11/21 + * @since 1.0.0 + */ + +public class MessageConvertUtil { + + public static final String defaultCharsetName = "UTF-8"; + + public static String base64Encode(byte[] src) { + return Base64Utils.encodeToString(src); + } + + public static byte[] base64Decode(String src) { + return Base64Utils.decodeFromString(src); + } + + public static String messageKey(byte[] src) { + return base64Encode(ShaUtils.hash_256(src)); + } + + public static BlockEvent convertBytes2BlockEvent(byte[] serializeBytes) { + String text; + try{ + text = new String(serializeBytes, defaultCharsetName); + } catch (Exception e) { + throw new RuntimeException(e); + } + return convertString2BlockEvent(text); + } + + public static BlockEvent convertString2BlockEvent(String serializeString) { + return JSON.parseObject(serializeString, BlockEvent.class); + } + + public static TxBlockedEvent convertBytes2TxBlockedEvent(byte[] serializeBytes) { + String text; + try{ + text = new String(serializeBytes, defaultCharsetName); + } catch (Exception e) { + throw new RuntimeException(e); + } + return convertString2TxBlockedEvent(text); + } + + public static TxBlockedEvent convertString2TxBlockedEvent(String serializeString) { + return JSON.parseObject(serializeString, TxBlockedEvent.class); + } + + public static byte[] serializeBlockEvent(BlockEvent blockEvent) { + String serializeString = serializeEvent(blockEvent); + byte[] serializeBytes; + try { + serializeBytes = serializeString.getBytes(defaultCharsetName); + } catch (Exception e) { + throw new RuntimeException(e); + } + return serializeBytes; + } + + public static byte[] serializeTxBlockedEvent(TxBlockedEvent txBlockedEvent) { + String serializeString = JSON.toJSONString(txBlockedEvent); + byte[] serializeBytes; + try { + serializeBytes = serializeString.getBytes(defaultCharsetName); + } catch (Exception e) { + throw new RuntimeException(e); + } + return serializeBytes; + } + + public static String serializeEvent(BlockEvent blockEvent) { + return JSON.toJSONString(blockEvent); + } +} \ No newline at end of file diff --git a/source/consensus/pom.xml b/source/consensus/pom.xml new file mode 100644 index 00000000..c7b8c41e --- /dev/null +++ b/source/consensus/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + consensus + pom + + + consensus-framework + consensus-bftsmart + consensus-mq + + \ No newline at end of file diff --git a/source/contract/README.MD b/source/contract/README.MD new file mode 100644 index 00000000..15b32c0d --- /dev/null +++ b/source/contract/README.MD @@ -0,0 +1,22 @@ +合约相关说明 + +1.编译合约入口:ContractCompilerCmdTest.java; +2.账本调用合约测试入口:ContractEventSendOperationHandleTest.java; + +使用ContractEventSendOperationHandleTest.java进行单元测试,注意事项: +1.设置合约使用PUB_CLASS_PATH、CORE_CLASS_PATH位置(sys-contract.properties); +PUB包在根目录中的contract-libs文件夹;core包需要编译来生成。具体如下: +1)进入contract-jar模块,执行maven命令:mvn clean assembly:assembly +2)生成的core包位于模块的target中的contract-jar-xxx所在的coreLib中; +3)将此coreLib目录作为CORE_CLASS_PATH指向的目录。 +2.编译生成合约压缩包,即执行:ContractCompilerCmdTest.java对应的mainTestOk(); +在编译之前,修改sys-contract.properties文件的变量CONTRACT_FROM_PATH(合约源文件位置)、CONTRACT_SAVE_TO_PATH(合约保存位置)。 +3.在合约保存位置中可看到生成的压缩包:xxx.contract;然后执行 ContractEventSendOperationHandleTest.java测试用例test1()即可。 + +20180910版本改造 +1.在contract-jar中添加了mvn assembly处理逻辑,将合约用到的lib包全部放置其target/xxx/pubLib文件夹中,执行:mvn clean assembly:assembly +2.修改了sys-contract.properties文件,新增了CONTRACT_CLASS_LIBS参数,将CONTRACT_CLASS_PATH专用于存储路径,CONTRACT_CLASS_LIBS来存放所有的jar包; +3.删减了contract-libs文件夹中需要动态生成的jar; + + + diff --git a/source/contract/contract-compile/pom.xml b/source/contract/contract-compile/pom.xml new file mode 100644 index 00000000..e6c67b88 --- /dev/null +++ b/source/contract/contract-compile/pom.xml @@ -0,0 +1,114 @@ + + + + + contract + com.jd.blockchain + 0.8.3.RELEASE + + 4.0.0 + + contract-compile + + + + com.jd.blockchain + contract-model + ${project.version} + provided + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + false + true + false + false + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.5 + + + + + true + + + + + maven-assembly-plugin + + contract + false + + + com.jd.blockchain.contract.AssetContract2 + + + + jar-with-dependencies + + + + + make-assembly + package + + single + + + + + + + com.jd.blockchain + contract-maven-plugin + ${project.version} + + + + + + + make-assembly + process-sources + + checkImports + + + + + + maven-jar-plugin + 3.0.2 + + + default-jar + none + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract1.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract1.java new file mode 100644 index 00000000..8fae2439 --- /dev/null +++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract1.java @@ -0,0 +1,217 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.contract.model.*; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.ByteArray; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * mock the smart contract; + */ +@Contract +public class AssetContract1 implements EventProcessingAwire { + // private static final Logger LOGGER = LoggerFactory.getLogger(AssetContract1.class); + // the address of asset manager; +// private static String ASSET_ADDRESS = "2njZBNbFQcmKd385DxVejwSjy4driRzf9Pk"; + private static String ASSET_ADDRESS = ""; + //account address; + private static final String ACCOUNT_ADDRESS = "accountAddress"; + String contractAddress = "2njZBNbFQcmKd385DxVejwSjy4driRzf9Pk"; + String userPubKeyVal = "this is user's pubKey"; + + // the key of save asset; + private static final String KEY_TOTAL = "TOTAL"; + + // contractEvent context; + private ContractEventContext eventContext; + private Object eventContextObj; + private byte[] eventContextBytes; + + @Override + public void beforeEvent(ContractEventContext contractEventContext) { + eventContext = contractEventContext; + System.out.println("in beforeEvent(),event is: "+contractEventContext.getEvent()); + } + + + @Override + public void postEvent(ContractEventContext eventContext, ContractException error) { + this.eventContext = null; + } + + @Override + public void postEvent(ContractException error) { + this.eventContextBytes = null; + } + + @Override + public void postEvent() { + this.eventContextBytes = null; + System.out.println("postEvent(),over."); + } + + /** + * issue-asset; + * @param contractEventContext + * @throws Exception + */ + @ContractEvent(name = "issue-asset") + public void issue(ContractEventContext contractEventContext) throws Exception { + // String strArgs = (String)BytesUtils.getObjectFromBytes(args_); + byte [] args_ = contractEventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + long amount = Long.parseLong(args[0]); + // 新发行的资产的持有账户; + String assetHolderAddress = args[1]; + String ASSET_ADDRESS = args[2]; + String previousBlockHash = args[3]; + String userAddress = args[4]; + String contractAddress = args[5]; + String txHash = args[6]; + String pubKeyVal = args[7]; + +// checkAllOwnersAgreementPermission(); + +// long amount = BytesUtils.toLong(args[0]); + + if (amount < 0) { + throw new ContractException("The amount is negative!"); + } + if (amount == 0) { + return; + } + +// BlockchainAccount holderAccount = eventContext.getLedger().getAccount(currentLedgerHash(), assetHolderAddress); +// if (holderAccount == null) { +// throw new ContractError("The holder is not exist!"); +// } + HashDigest hashDigest = eventContext.getCurrentLedgerHash(); + +// eventContext.getLedger().dataAccount(ACCOUNT_ADDRESS).set(KEY_TOTAL,"total new dataAccount".getBytes(),2); +// KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(hashDigest, ASSET_ADDRESS, KEY_TOTAL,assetHolderAddress); +// assert ByteArray.toHex("total new dataAccount".getBytes()).equals(kvEntries[0].getValue()) +// && ByteArray.toHex("abc new dataAccount".getBytes()).equals(kvEntries[1].getValue()) : +// "getDataEntries() test,expect!=actual;"; + + KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(hashDigest, ASSET_ADDRESS, + KEY_TOTAL,assetHolderAddress,"ledgerHash"); //,"latestBlockHash" + + assert ByteArray.toHex("total value,dataAccount".getBytes()).equals(kvEntries[0].getValue()) + && ByteArray.toHex("abc value,dataAccount".getBytes()).equals(kvEntries[1].getValue()) : + "getDataEntries() test,expect=actual;"; + + //get the latest block; + LedgerBlock ledgerBlock = eventContext.getLedger().getBlock(hashDigest, + eventContext.getLedger().getLedger(hashDigest).getLatestBlockHeight()); + + +// assert "zhaogw".equals(new String(ledgerBlock.getLedgerHash().getRawDigest())) && +// "lisi".equals(new String(ledgerBlock.getPreviousHash().getRawDigest())) : +// "getBlock(hash,long) test,expect!=actual;"; + assert ByteArray.toHex(eventContext.getCurrentLedgerHash().getRawDigest()).equals(kvEntries[2].getValue()) && + ledgerBlock.getPreviousHash().toBase58().equals(previousBlockHash) : + "getPreviousHash() test,expect!=acutal;"; + + //模拟:根据hash来获得区块; + LedgerBlock ledgerBlock1 = eventContext.getLedger().getBlock(hashDigest,ledgerBlock.getHash()); + + assert eventContext.getLedger().getTransactionCount(hashDigest,1) == 2 : + "getTransactionCount(),expect!=acutal"; + +// assert "zhaogw".equals(new String(ledgerBlock1.getLedgerHash().getRawDigest())) && +// "lisi".equals(new String(ledgerBlock1.getPreviousHash().getRawDigest())) : +// "getBlock(hash,blockHash) test,expect!=acutal;"; + assert ByteArray.toHex(eventContext.getCurrentLedgerHash().getRawDigest()).equals(kvEntries[2].getValue()) && + ledgerBlock1.getPreviousHash().toBase58().equals(previousBlockHash) : + "getBlock(hash,blockHash) test,expect!=acutal;"; + + assert ASSET_ADDRESS.equals(eventContext.getLedger().getDataAccount(hashDigest,ASSET_ADDRESS).getAddress()) : + "getDataAccount(hash,address), expect!=acutal"; + + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); + BlockchainIdentity contractID = new BlockchainIdentityData(pubKey); +// assert contractID == contractEventContext.getLedger().dataAccounts().register(contractID).getAccountID() : +// "dataAccounts(),expect!=acutal"; + contractEventContext.getLedger().dataAccounts().register(contractID); + contractEventContext.getLedger().dataAccount(contractID.getAddress()). + set(KEY_TOTAL,"hello".getBytes(),-1).getOperation(); + + assert userAddress.equals(eventContext.getLedger().getUser(hashDigest,userAddress).getAddress()) : + "getUser(hash,address), expect!=acutal"; + + assert contractAddress.equals(eventContext.getLedger().getContract(hashDigest,contractAddress).getAddress()) : + "getContract(hash,address), expect!=acutal"; + + PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, userPubKeyVal.getBytes()); + BlockchainIdentity userBlockId = new BlockchainIdentityData(userPubKey); + contractEventContext.getLedger().users().register(userBlockId); + +// txRootHash +// eventContext.getLedger().getTransactions(hashDigest,ledgerBlock1.getHash(),0,10); + + HashDigest txHashDigest = new HashDigest(Base58Utils.decode(txHash)); + LedgerTransaction ledgerTransactions = eventContext.getLedger().getTransactionByContentHash(hashDigest,txHashDigest); + assert ledgerTransactions != null : "getTransactionByContentHash(hashDigest,txHashDigest),expect!=acutal"; + + System.out.println("issue(),over."); + } + + @ContractEvent(name = "transfer-asset") + public void transfer(ContractEventContext contractEventContext) { + byte[] args = contractEventContext.getArgs(); + String[] argStr = new String(args).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + String fromAddress = argStr[0]; + String toAddress = argStr[1]; + long amount = Long.parseLong(argStr[2]); + + if (amount < 0) { + throw new ContractException("The amount is negative!"); + } + if (amount == 0) { + return; + } + checkSignerPermission(fromAddress); + System.out.println("transfer(),over."); + } + + private void checkAllOwnersAgreementPermission() { + Set owners = eventContext.getContracOwners(); + Set requestors = eventContext.getTxSigners(); + if (requestors.size() != owners.size()) { + throw new ContractException("Permission Error! -- The requestors is not exactlly being owners!"); + } + + Map ownerMap = new HashMap<>(); + for (BlockchainIdentity o : owners) { + ownerMap.put(o.getAddress().toBase58(), o); + } + for (BlockchainIdentity r : requestors) { + System.out.println("checkAllOwnersAgreementPermission(),r.getAddress:"+r.getAddress()); + if (!ownerMap.containsKey(r.getAddress())) { + throw new ContractException("Permission Error! -- No agreement of all owners!"); + } + } + } + + private void checkSignerPermission(String address) { + Set requestors = eventContext.getTxSigners(); + for (BlockchainIdentity r : requestors) { + if (r.getAddress().equals(address)) { + return; + } + } +// throw new ContractError("Permission Error! -- No signature !"); + } + +} diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract2.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract2.java new file mode 100644 index 00000000..247e36e8 --- /dev/null +++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract2.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.contract.model.*; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.utils.BaseConstant; + +/** + * mock the smart contract; + */ +@Contract +public class AssetContract2 implements EventProcessingAwire { + // private static final Logger LOGGER = LoggerFactory.getLogger(AssetContract.class); + private static final String KEY_TOTAL = "TOTAL"; + private static final String LEDGER_HASH = "ledgerHash"; + + @ContractEvent(name = "issue-asset") + public void test1(ContractEventContext eventContext) throws Exception{ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("in contract2,invoke test1(),amountAdd:"+(amount+amount1)+",contractDataAddress="+contractDataAddress); + + //test invoke; + HashDigest hashDigest = eventContext.getCurrentLedgerHash(); + KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(hashDigest, contractDataAddress, + KEY_TOTAL,LEDGER_HASH); //,"latestBlockHash" + + // +// assert ByteArray.toHex("total value,dataAccount".getBytes()).equals(kvEntries[0].getValue()) +// && ByteArray.toHex("abc value,dataAccount".getBytes()).equals(kvEntries[1].getValue()) : +// "getDataEntries() test,expect=actual;"; + System.out.println("in dataSet,KEY_TOTAL="+new String(kvEntries[0].getValue().toString())); + System.out.println("in dataSet,LEDGER_HASH="+new String(kvEntries[1].getValue().toString())); + } + + @Override + public void beforeEvent(ContractEventContext contractEventContext) { + + } + + @Override + public void postEvent() { + + } + + @Override + public void postEvent(ContractEventContext contractEventContext, ContractException contractError) { + + } + + @Override + public void postEvent(ContractException contractError) { + + } +} diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract3.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract3.java new file mode 100644 index 00000000..acae4716 --- /dev/null +++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract3.java @@ -0,0 +1,65 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.contract.model.*; +import com.jd.blockchain.utils.BaseConstant; + +/** + * mock the smart contract; + * Do only the simplest addition operation. + */ +@Contract +public class AssetContract3 implements EventProcessingAwire { + + @ContractEvent(name = "issue-asset") + public void test1(ContractEventContext eventContext){ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + +// KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), +// "", ""); + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("###@@@,in contract3,invoke test1(),amountAdd:"+(amount+amount1)+ + ",contractDataAddress= "+contractDataAddress); + } + + @ContractEvent(name = "multi") + public void test2(ContractEventContext eventContext) throws Exception{ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("###test,in contract3,invoke test2(),amount Multi:"+(amount*amount1)+ + ",contractDataAddress= "+contractDataAddress); + } + + @Override + public void beforeEvent(ContractEventContext contractEventContext) { + + } + + @Override + public void postEvent() { + + } + + @Override + public void postEvent(ContractEventContext contractEventContext, ContractException contractError) { + + } + + @Override + public void postEvent(ContractException contractError) { + + } +} diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract4.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract4.java new file mode 100644 index 00000000..0b2766b0 --- /dev/null +++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract4.java @@ -0,0 +1,83 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.contract.model.*; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.utils.io.ByteArray; + +/** + * mock the smart contract; + * The test takes data from the chain and compares it with the expected value; + * the value of param1Val should be consistent with that of Integration Test; + */ +@Contract +public class AssetContract4 implements EventProcessingAwire { + String param1 = "param1"; + String param1Val = "param1Val"; + + @ContractEvent(name = "issue-asset") + public void test1(ContractEventContext eventContext) throws Exception{ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("###@@@,in contract4,invoke test1(),amountAdd:"+(amount+amount1)+ + ",contractDataAddress= "+contractDataAddress); + + BlockchainKeyPair dataAccount = BlockchainKeyGenerator.getInstance().generate(); +// contractEventContext.getLedger().dataAccounts().register(dataAccount.getIdentity()); +// contractEventContext.getLedger().dataAccount(dataAccount.getAddress()). +// set(param1,param1Val.getBytes(),-1).getOperation(); + System.out.println("data address="+dataAccount.getAddress()); + } + + @ContractEvent(name = "event2") + public void test2(ContractEventContext eventContext) throws Exception{ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("###!!!!!!!!,in contract3,invoke test2(),amount Multi:"+(amount*amount1)+ + ",contractDataAddress= "+contractDataAddress); + + KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(eventContext.getCurrentLedgerHash(), + contractDataAddress, param1); + if (ByteArray.toHex(param1Val.getBytes()).equals(kvEntries[0].getValue())){ + System.out.println("getDataEntries() test,expect==actual;"); + } else { + System.out.println("getDataEntries() test,expect==actual;"); + } + } + + @Override + public void beforeEvent(ContractEventContext contractEventContext) { + + } + + @Override + public void postEvent() { + + } + + @Override + public void postEvent(ContractEventContext contractEventContext, ContractException contractError) { + + } + + @Override + public void postEvent(ContractException contractError) { + + } +} diff --git a/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract5.java b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract5.java new file mode 100644 index 00000000..bf3d8def --- /dev/null +++ b/source/contract/contract-compile/src/main/java/com/jd/blockchain/contract/AssetContract5.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.contract.model.*; +import com.jd.blockchain.utils.BaseConstant; + +/** + * mock the smart contract; + * Do only the simplest addition operation. + */ +@Contract +public class AssetContract5 implements EventProcessingAwire { + + @ContractEvent(name = "issue-asset") + public void test1(ContractEventContext eventContext) throws Exception{ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("###@@@,in contract5,invoke test1(),amountAdd:"+(amount+amount1)+ + ",contractDataAddress= "+contractDataAddress); + } + + @ContractEvent(name = "multi") + public void test2(ContractEventContext eventContext) throws Exception{ + byte [] args_ = eventContext.getArgs(); + if(args_ == null){ + return; + } + String[] args = new String(args_).split(BaseConstant.DELIMETER_DOUBLE_ALARM); + + long amount = Long.parseLong(args[0]); + long amount1 = Long.parseLong(args[1]); + String contractDataAddress = args[2]; + System.out.println("###test,in contract5,invoke test2(),amount Multi:"+(amount*amount1)+ + ",contractDataAddress= "+contractDataAddress); + } + + @Override + public void beforeEvent(ContractEventContext contractEventContext) { + + } + + @Override + public void postEvent() { + + } + + @Override + public void postEvent(ContractEventContext contractEventContext, ContractException contractError) { + + } + + @Override + public void postEvent(ContractException contractError) { + + } +} diff --git a/source/contract/contract-framework/pom.xml b/source/contract/contract-framework/pom.xml new file mode 100644 index 00000000..be2b88ab --- /dev/null +++ b/source/contract/contract-framework/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + com.jd.blockchain + contract + 0.8.2.RELEASE + + contract-framework + + + + com.jd.blockchain + contract-model + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractCode.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractCode.java new file mode 100644 index 00000000..5590cabb --- /dev/null +++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractCode.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.contract; + +import com.jd.blockchain.contract.model.ContractEventContext; + +public interface ContractCode { + + String getAddress(); + + long getVersion(); + + void processEvent(ContractEventContext eventContext); + +} diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractEngine.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractEngine.java new file mode 100644 index 00000000..fe6f6a7a --- /dev/null +++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractEngine.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.contract; + +/** + * contract engine. + * + * @author huanghaiquan + * + */ +public interface ContractEngine { + + /** + * Returns the contract code for the specified address;
+ * + * If not, return null; + * + * @param address + * @return + */ + ContractCode getContract(String address, long version); + + /** + * Load contract code;
+ * + * If it already exists, it returns the existing instance directly. + * + * @param address + * @param code + * @return + */ + ContractCode setupContract(String address, long version, byte[] code); + +} diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProvider.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProvider.java new file mode 100644 index 00000000..1cc94b5e --- /dev/null +++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProvider.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.contract; + +public interface ContractServiceProvider { + + String getName(); + + /** + * Return the contract code execution engine instance; + * + * @return + */ + ContractEngine getEngine(); + +} diff --git a/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProviders.java b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProviders.java new file mode 100644 index 00000000..f0a6ba37 --- /dev/null +++ b/source/contract/contract-framework/src/main/java/com/jd/blockchain/contract/ContractServiceProviders.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.contract; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.beans.BeanUtils; +import org.springframework.util.ClassUtils; + +public class ContractServiceProviders { + + private static final Object mutex = new Object(); + private static Map providers = new ConcurrentHashMap<>(); + + public static ContractServiceProvider getProvider(String className) { + ContractServiceProvider provider = providers.get(className); + if (provider == null) { + synchronized (mutex) { + provider = providers.get(className); + if (provider == null) { + provider = loadProvider(ContractServiceProvider.class, className); + providers.put(className, provider); + } + } + } + return provider; + } + + private static T loadProvider(Class assignableTo, String implementClassName) { + Class providerClass = ClassUtils.resolveClassName(implementClassName, + ContractServiceProvider.class.getClassLoader()); + if (!assignableTo.isAssignableFrom(providerClass)) { + throw new IllegalArgumentException( + String.format("%s is not implement %s!", implementClassName, assignableTo.getName())); + } + return BeanUtils.instantiateClass(providerClass, assignableTo); + } + + public static void registerProvider(ContractServiceProvider provider) { + providers.put(provider.getName(), provider); + } +} diff --git a/source/contract/contract-jvm/pom.xml b/source/contract/contract-jvm/pom.xml new file mode 100644 index 00000000..7c36748a --- /dev/null +++ b/source/contract/contract-jvm/pom.xml @@ -0,0 +1,43 @@ + + 4.0.0 + + com.jd.blockchain + contract + 0.8.2.RELEASE + + contract-jvm + + + + com.jd.blockchain + contract-framework + ${project.version} + + + com.jd.blockchain + runtime-context + ${project.version} + + + com.jd.blockchain + runtime-modular + ${project.version} + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + + \ No newline at end of file diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractEngine.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractEngine.java new file mode 100644 index 00000000..6769b618 --- /dev/null +++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractEngine.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.contract.jvm; + +import com.jd.blockchain.contract.ContractCode; +import com.jd.blockchain.contract.ContractEngine; +import com.jd.blockchain.runtime.Module; +import com.jd.blockchain.runtime.RuntimeContext; + +public class JVMContractEngine implements ContractEngine { + + private RuntimeContext runtimeContext = RuntimeContext.get(); +// private RuntimeContext runtimeContext = ModularRuntimeContext.setup(System.getProperty("user.dir")); + + private String getCodeName(String address, long version) { + return address + "_" + version; + } + + @Override + public ContractCode getContract(String address, long version) { + String codeName = getCodeName(address, version); + Module module = runtimeContext.getDynamicModule(codeName); + if (module == null) { + return null; + } + return new JavaContractCode(address, version, module); + } + + @Override + public ContractCode setupContract(String address, long version, byte[] code) { + //is there the contractCode before setup? if yes ,then return; + ContractCode contractCode = getContract(address,version); + if(contractCode != null){ + return contractCode; + } + String codeName = getCodeName(address, version); + Module module = runtimeContext.createDynamicModule(codeName,code); + if (module == null) { + return null; + } + return new JavaContractCode(address, version, module); + } +} diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractServiceProvider.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractServiceProvider.java new file mode 100644 index 00000000..859c25c7 --- /dev/null +++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JVMContractServiceProvider.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.contract.jvm; + +import com.jd.blockchain.contract.ContractEngine; +import com.jd.blockchain.contract.ContractServiceProvider; + +public class JVMContractServiceProvider implements ContractServiceProvider { + @Override + public String getName() { + return JVMContractServiceProvider.class.getName(); + } + + @Override + public ContractEngine getEngine() { + return InnerEngine.INSTANCE; + } + + private static class InnerEngine { + private static final ContractEngine INSTANCE = new JVMContractEngine(); + } +} diff --git a/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java new file mode 100644 index 00000000..282f06c6 --- /dev/null +++ b/source/contract/contract-jvm/src/main/java/com/jd/blockchain/contract/jvm/JavaContractCode.java @@ -0,0 +1,123 @@ +package com.jd.blockchain.contract.jvm; + +import com.jd.blockchain.contract.ContractCode; +import com.jd.blockchain.contract.model.ContractEvent; +import com.jd.blockchain.contract.model.ContractEventContext; +import com.jd.blockchain.runtime.Module; +import com.jd.blockchain.utils.BaseConstant; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.ReflectionUtils; + +import static com.jd.blockchain.utils.BaseConstant.CONTRACT_MAIN_CLASS_KEY; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * contract code based jvm + * @author zhaogw + */ +public class JavaContractCode implements ContractCode { + private static final Logger LOGGER = LoggerFactory.getLogger(JavaContractCode.class); + private Module codeModule; + private String address; + private long version; + private ContractEventContext contractEventContext; + + public JavaContractCode(String address, long version, Module codeModule) { + this.address = address; + this.version = version; + this.codeModule = codeModule; + } + + @Override + public String getAddress() { + return address; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public void processEvent(ContractEventContext eventContext) { + this.contractEventContext = eventContext; + codeModule.execute(new ContractThread()); + } + + class ContractThread implements Runnable{ + @Override + public void run(){ + LOGGER.info("ContractThread execute()."); + try { + //Perform pretreatment; + long startTime = System.currentTimeMillis(); + + String contractClassName = codeModule.getMainClass(); + Class myClass = codeModule.loadClass(contractClassName); + Object contractMainClassObj = myClass.newInstance(); + + Method beforeMth_ = myClass.getMethod("beforeEvent",codeModule.loadClass(ContractEventContext.class.getName())); + ReflectionUtils.invokeMethod(beforeMth_,contractMainClassObj,contractEventContext); + LOGGER.info("beforeEvent,spend time:"+(System.currentTimeMillis()-startTime)); + + Method eventMethod = this.getMethodByAnno(contractMainClassObj,contractEventContext.getEvent()); + startTime = System.currentTimeMillis(); + + ReflectionUtils.invokeMethod(eventMethod,contractMainClassObj,contractEventContext); + + LOGGER.info("execute contract,spend time:"+(System.currentTimeMillis()-startTime)); + + Method mth2 = myClass.getMethod("postEvent"); + startTime = System.currentTimeMillis(); + ReflectionUtils.invokeMethod(mth2,contractMainClassObj); + LOGGER.info("postEvent,spend time:"+(System.currentTimeMillis()-startTime)); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + //get the relation between the methods and annotations + Method getMethodByAnno(Object classObj, String eventName){ + Class c = classObj.getClass(); + Class contractEventClass = null; + try { + contractEventClass = (Class )c.getClassLoader().loadClass(ContractEvent.class.getName()); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + Method[] classMethods = c.getMethods(); + Map methodAnnoMap = new HashMap(); + Map annoMethodMap = new HashMap(); + for(int i = 0;i + 4.0.0 + + com.jd.blockchain + contract + 0.8.2.RELEASE + + contract-maven-plugin + maven-plugin + + ${parent.version} + ${parent.version} + 4.12 + + + 0.8.2.RELEASE + + + junit + junit + test + + + + com.jd.blockchain + contract-model + ${contract.version} + + + + com.jd.blockchain + utils-common + ${utils.version} + + + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + provided + + + + org.apache.maven + maven-plugin-api + 2.0 + + + org.apache.maven + maven-project + 2.0.6 + + + + com.github.javaparser + javaparser-core + ${javaparser.version} + + + + + + + + org.apache.maven.plugins + maven-plugin-plugin + 3.5 + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + false + true + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + + + + + + + + + + + + + + + + + diff --git a/source/contract/contract-maven-plugin/readme.txt b/source/contract/contract-maven-plugin/readme.txt new file mode 100644 index 00000000..46697cce --- /dev/null +++ b/source/contract/contract-maven-plugin/readme.txt @@ -0,0 +1,19 @@ +说明 +1.编译:mvn clean install + +快速自测: +1.ContractRemoteAutoMojoTest类用于快速自测发布和执行,快速自测是在测试链的环境中发布和执行合约; +2.修改sys-contract.properties文件中的相关信息; +3.合约发布之后,会在控制台生成合约地址,待5秒钟之后,会执行此合约。sys-contract.properties的contractArgs参数可修改,查看其不同效果; +### +contract's address=5SmEqUsnLY4APVfS32xYDpRPuz55Rsuupdt1 +execute the contract,result=true +exeContract(),SUCCESS +### +4.在peer节点的控制台可以看到输出的结果信息。 + +通过maven插件中通过ContractAllAutoMojo做简单的编译、发布和执行测试,对应单元测试类ContractAllAutoMojoTest; + + + + diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java new file mode 100644 index 00000000..86747f9d --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/CheckImportsMojo.java @@ -0,0 +1,60 @@ +package com.jd.blockchain; + +import com.github.javaparser.JavaParser; +import com.github.javaparser.ast.CompilationUnit; +import com.github.javaparser.ast.ImportDeclaration; +import com.github.javaparser.ast.NodeList; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.apache.maven.project.MavenProject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.Properties; +import java.util.stream.Collectors; + + +@Mojo(name = "checkImports") +public class CheckImportsMojo extends AbstractMojo { + Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class); + + @Parameter(defaultValue = "${project}", required = true, readonly = true) + private MavenProject project; + + @Override + public void execute() throws MojoFailureException { + List sources; + try { + InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("config.properties"); + Properties properties = new Properties(); + properties.load(inputStream); + String[] packageBlackList = properties.getProperty("blacklist").split(","); + Path baseDirPath = project.getBasedir().toPath(); + sources = Files.find(baseDirPath, Integer.MAX_VALUE, (file, attrs) -> (file.toString().endsWith(".java"))).collect(Collectors.toList()); + for (Path path : sources) { + CompilationUnit compilationUnit = JavaParser.parse(path); + NodeList imports = compilationUnit.getImports(); + for (ImportDeclaration imp : imports) { + String importName = imp.getName().asString(); + for (String item : packageBlackList) { + if (importName.startsWith(item)) { + throw new MojoFailureException("Inclusion of this import package is not allowed in source code:" + importName); + } + } + } + } + } catch (IOException exception) { + logger.error(exception.getMessage()); + throw new MojoFailureException("IO ERROR"); + } catch (NullPointerException e) { + logger.error(e.getMessage()); + } + } +} diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractDeployMojo.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractDeployMojo.java new file mode 100644 index 00000000..fa51d3ef --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/ContractDeployMojo.java @@ -0,0 +1,119 @@ +package com.jd.blockchain; + +import com.jd.blockchain.contract.model.ContractDeployExeUtil; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.FileUtils; +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * for contract remote deploy; + * @phase compile + * @author zhaogw + * date 2018/10/18 10:12 + */ + +@Mojo(name = "contractDeploy") +public class ContractDeployMojo extends AbstractMojo { + Logger logger = LoggerFactory.getLogger(ContractDeployMojo.class); + + @Parameter + private File config; + + @Override + public void execute()throws MojoFailureException { + Properties prop = new Properties(); + InputStream input = null; + + try { + input = new FileInputStream(config); + prop.load(input); + + } catch (IOException ex) { + logger.error(ex.getMessage()); + throw new MojoFailureException("io error"); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + logger.error(e.getMessage()); + } + } + } + int port; + try { + port = Integer.parseInt(prop.getProperty("port")); + }catch (NumberFormatException e){ + logger.error(e.getMessage()); + throw new MojoFailureException("invalid port"); + } + String host = prop.getProperty("host"); + String ledger = prop.getProperty("ledgerHash"); + String ownerPubPath = prop.getProperty("ownerPubPath"); + String ownerPrvPath = prop.getProperty("ownerPrvPath"); + String ownerPassword = FileUtils.readText(prop.getProperty("ownerPassword")); + String chainCodePath = prop.getProperty("chainCodePath"); + + if(StringUtils.isEmpty(host)){ + logger.info("host can not be empty"); + return; + } + + if(StringUtils.isEmpty(ledger)){ + logger.info("ledger can not be empty."); + return; + } + if(StringUtils.isEmpty(ownerPubPath)){ + logger.info("pubKey can not be empty."); + return; + } + if(StringUtils.isEmpty(ownerPrvPath)){ + logger.info("prvKey can not be empty."); + return; + } + if(StringUtils.isEmpty(chainCodePath)){ + logger.info("contractPath can not be empty."); + return; + } + + File contract = new File(chainCodePath); + if (!contract.isFile()){ + logger.info("file:"+chainCodePath+" is not exist"); + return; + } + byte[] contractBytes = FileUtils.readBytes(chainCodePath); + + +// PrivKey prv = KeyGenCommand.decodePrivKeyWithRawPassword(prvKey, password); +// PubKey pub = KeyGenCommand.decodePubKey(pubKey); +// BlockchainKeyPair blockchainKeyPair = new BlockchainKeyPair(pub, prv); + BlockchainKeyPair ownerKey = ContractDeployExeUtil.instance.getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword); + HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger)); + + StringBuffer sb = new StringBuffer(); + sb.append("host:"+ host).append(",port:"+port).append(",ledgerHash:"+ledgerHash.toBase58()). + append(",pubKey:"+ownerKey.getPubKey()).append(",prvKey:"+ownerKey.getPrivKey()).append(",contractPath:"+chainCodePath); + logger.info(sb.toString()); + if(ContractDeployExeUtil.instance.deploy(host,port,ledgerHash, ownerKey, contractBytes)){ + logger.info("deploy is OK."); + } + } +} + + diff --git a/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/StringUtils.java b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/StringUtils.java new file mode 100644 index 00000000..f8fe1867 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/java/com/jd/blockchain/StringUtils.java @@ -0,0 +1,10 @@ +package com.jd.blockchain; +/** + * @Author zhaogw + * @Date 2018/11/26 20:46 + */ +public abstract class StringUtils { + public static boolean isEmpty(Object str) { + return str == null || "".equals(str); + } +} \ No newline at end of file diff --git a/source/contract/contract-maven-plugin/src/main/resources/config.properties b/source/contract/contract-maven-plugin/src/main/resources/config.properties new file mode 100644 index 00000000..fdfca23e --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/resources/config.properties @@ -0,0 +1 @@ +blacklist=java.io,java.net,java.util.Random \ No newline at end of file diff --git a/source/contract/contract-maven-plugin/src/main/resources/sys-contract.properties b/source/contract/contract-maven-plugin/src/main/resources/sys-contract.properties new file mode 100644 index 00000000..6dd66e5e --- /dev/null +++ b/source/contract/contract-maven-plugin/src/main/resources/sys-contract.properties @@ -0,0 +1,19 @@ +#ĿԴļŵλ; +PROJECT_BASE_DIR=E:\\gitCode\\block\\prototype\\ +#ͬʹõŵλãܲĿУʲȫµĵַ; +LEDGER_BASE_CLASS_PATH=E:\\gitCode\\block\\prototype\\libs\\ + +#Ϊ˲ԣʱӵı; +cParam=com.jd.blockchain.contract.AssetContract3 +sParam=E:\\gitCode\\block\\prototype\\source\\sdk\\contract-sample\\src\\main\\java\\ +eParam=utf-8 +oParam=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\src\\test\\resources\\ +host=127.0.0.1 +port=8081 +event = issue-asset +ownerPassword=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\conf\\ownerPassword.txt +ownerPubPath=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\conf\\jd-com.pub +ownerPrvPath=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\conf\\jd-com.priv +chainCodePath=E:\\gitCode\\block\\prototype\\source\\contract\\contract-maven-plugin\\src\\test\\resources\\AssetContract3.contract +ledgerHash=6FEQDTQMnBGANpfX4haXuWHKHw5cZ6P1h2ocqwckYBxp4 +contractArgs=101##999##abc \ No newline at end of file diff --git a/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java new file mode 100644 index 00000000..9a91e4f6 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/CheckImportsMojoTest.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.CheckImportsMojo; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Arrays; +import java.util.Properties; + +/** + * @Author zhaogw + * @Date 2019/3/1 21:27 + */ +public class CheckImportsMojoTest { + Logger logger = LoggerFactory.getLogger(CheckImportsMojo.class); + + @Test + public void test1() { + try { + InputStream inputStream = CheckImportsMojo.class.getClassLoader().getResourceAsStream("config.properties"); + Properties properties = new Properties(); + properties.load(inputStream); + String result[] = properties.getProperty("blacklist").split(","); + logger.info(Arrays.toString(result).toString()); + } catch (IOException e) { + logger.error(e.getMessage()); + } + } +} diff --git a/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractDeployMojoTest.java b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractDeployMojoTest.java new file mode 100644 index 00000000..85c02021 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/test/java/com/jd/blockchain/ledger/ContractDeployMojoTest.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.ContractDeployMojo; +import org.junit.Test; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; + +/** + * for contract deploy and exe; + * @Author zhaogw + * @Date 2018/11/02 09:06 + */ +public class ContractDeployMojoTest { + private ContractDeployMojo contractDeployMojo = new ContractDeployMojo(); + + private void fieldHandle(String fieldName,Object objValue) throws NoSuchFieldException, + IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException { + Field field = contractDeployMojo.getClass().getDeclaredField(fieldName);//name为类Instance中的private属性 + field.setAccessible(true);//=true,可访问私有变量。 + Class typeClass = field.getType(); + field.set(contractDeployMojo, objValue); + } +} diff --git a/source/contract/contract-maven-plugin/src/test/resources/AssetContract3.contract b/source/contract/contract-maven-plugin/src/test/resources/AssetContract3.contract new file mode 100644 index 00000000..0874a68f Binary files /dev/null and b/source/contract/contract-maven-plugin/src/test/resources/AssetContract3.contract differ diff --git a/source/contract/contract-maven-plugin/src/test/resources/contract.properties b/source/contract/contract-maven-plugin/src/test/resources/contract.properties new file mode 100644 index 00000000..b0f816f9 --- /dev/null +++ b/source/contract/contract-maven-plugin/src/test/resources/contract.properties @@ -0,0 +1,3 @@ +#\u5408\u7EA6\u76F8\u5173\u5C5E\u6027 +#Fri Nov 02 17:13:02 CST 2018 +contract=com.jd.blockchain.contract.AssetContract3 diff --git a/source/contract/contract-model/pom.xml b/source/contract/contract-model/pom.xml new file mode 100644 index 00000000..880c446e --- /dev/null +++ b/source/contract/contract-model/pom.xml @@ -0,0 +1,37 @@ + + 4.0.0 + + com.jd.blockchain + contract + 0.8.2.RELEASE + + contract-model + + + + com.jd.blockchain + sdk-client + ${project.version} + + + + com.jd.blockchain + tools-keygen + ${project.version} + + + crypto-framework + com.jd.blockchain + + + + + + + + + + + \ No newline at end of file diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/Contract.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/Contract.java new file mode 100644 index 00000000..3e79827c --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/Contract.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.contract.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface Contract { + + String name() default ""; + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractAppLifecycleAwire.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractAppLifecycleAwire.java new file mode 100644 index 00000000..e3fd66ae --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractAppLifecycleAwire.java @@ -0,0 +1,16 @@ +package com.jd.blockchain.contract.model; + +/** + * The contract implements this interface to monitor the life cycle events of the contract application. + * + * + * @author huanghaiquan + * + */ +public interface ContractAppLifecycleAwire extends ContractRuntimeAwire { + + void postConstruct(); + + void beforeDestroy(); + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractDeployExeUtil.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractDeployExeUtil.java new file mode 100644 index 00000000..988703c3 --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractDeployExeUtil.java @@ -0,0 +1,190 @@ +package com.jd.blockchain.contract.model; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.BlockchainIdentityData; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; +import com.jd.blockchain.ledger.EndpointRequest; +import com.jd.blockchain.ledger.NodeRequest; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionContentBody; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * @Author zhaogw + * @Date 2018/11/2 10:18 + */ +public enum ContractDeployExeUtil { + instance; + private BlockchainService bcsrv; + private Bytes contractAddress; + + public BlockchainKeyPair getKeyPair(String pubPath, String prvPath, String rawPassword){ + PubKey pub = null; + PrivKey prv = null; + try { + prv = KeyGenCommand.readPrivKey(prvPath, KeyGenCommand.encodePassword(rawPassword)); + pub = KeyGenCommand.readPubKey(pubPath); + + } catch (Exception e) { + e.printStackTrace(); + } + + return new BlockchainKeyPair(pub, prv); + } + + public PubKey getPubKey(String pubPath){ + PubKey pub = null; + try { + if(pubPath == null){ + BlockchainKeyPair contractKeyPair = BlockchainKeyGenerator.getInstance().generate(); + pub = contractKeyPair.getPubKey(); + }else { + pub = KeyGenCommand.readPubKey(pubPath); + } + + } catch (Exception e) { + e.printStackTrace(); + } + + return pub; + } + public byte[] getChainCode(String path){ + byte[] chainCode = null; + File file = null; + InputStream input = null; + try { + file = new File(path); + input = new FileInputStream(file); + chainCode = new byte[input.available()]; + input.read(chainCode); + + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if(input!=null){ + input.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return chainCode; + } + + private void register(){ + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(ContractCodeDeployOperation.class); + DataContractRegistry.register(ContractEventSendOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + } + + public BlockchainService initBcsrv(String host, int port) { + if(bcsrv!=null){ + return bcsrv; + } + NetworkAddress addr = new NetworkAddress(host, port); + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(addr); + bcsrv = gwsrvFact.getBlockchainService(); + return bcsrv; + } + + public boolean deploy(HashDigest ledgerHash, BlockchainIdentity contractIdentity, BlockchainKeyPair ownerKey, byte[] chainCode){ + register(); + + TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash); + txTpl.contracts().deploy(contractIdentity, chainCode); + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(ownerKey); + // Submit and wait for consensus to return; + TransactionResponse txResp = ptx.commit(); + + // Verification results; + contractAddress = contractIdentity.getAddress(); + this.setContractAddress(contractAddress); + System.out.println("contract's address="+contractAddress); + return txResp.isSuccess(); + } + public boolean deploy(String host, int port, HashDigest ledgerHash, BlockchainKeyPair ownerKey, byte[] chainCode){ + register(); + + BlockchainIdentity contractIdentity = BlockchainKeyGenerator.getInstance().generate().getIdentity(); + initBcsrv(host,port); + return deploy(ledgerHash, contractIdentity, ownerKey, chainCode); + } + + // Generate contract addresses based on user-specified public keys + public boolean deploy(String host, int port, String ledger,String ownerPubPath, String ownerPrvPath, + String ownerPassword, String chainCodePath,String pubPath){ + PubKey pubKey = getPubKey(pubPath); + BlockchainIdentity contractIdentity = new BlockchainIdentityData(pubKey); + byte[] chainCode = getChainCode(chainCodePath); + + BlockchainKeyPair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword); + HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger)); + initBcsrv(host,port); + return deploy(ledgerHash, contractIdentity, ownerKey, chainCode); + } + + public boolean exeContract(String ledger,String ownerPubPath, String ownerPrvPath, + String ownerPassword,String event,String contractArgs){ + BlockchainKeyPair ownerKey = getKeyPair(ownerPubPath, ownerPrvPath, ownerPassword); + HashDigest ledgerHash = new HashDigest(Base58Utils.decode(ledger)); + + // Define the transaction, transfer the simplest numbers, strings, extract the address in the contract; + TransactionTemplate txTpl = bcsrv.newTransaction(ledgerHash); + txTpl.contractEvents().send(getContractAddress(),event,contractArgs.getBytes()); + + // sign; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(ownerKey); + + // Submit and wait for consensus to return; + TransactionResponse txResp = ptx.commit(); + + // Verification results; + return txResp.isSuccess(); + } + + public Bytes getContractAddress() { + return contractAddress; + } + + public void setContractAddress(Bytes contractAddress) { + this.contractAddress = contractAddress; + } +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEvent.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEvent.java new file mode 100644 index 00000000..00f24e76 --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEvent.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.contract.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface ContractEvent { + + String name() default ""; + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEventContext.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEventContext.java new file mode 100644 index 00000000..6104a1e6 --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractEventContext.java @@ -0,0 +1,64 @@ +package com.jd.blockchain.contract.model; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.TransactionRequest; + +import java.util.Set; + + +public interface ContractEventContext { + + /** + * current ledger hash; + * + * @return + */ + HashDigest getCurrentLedgerHash(); + + /** + * Transaction requests for execution of contract events; + * + * @return + */ + TransactionRequest getTransactionRequest(); + + /** + * Collection of signatories of the transaction; + * + * @return + */ + Set getTxSigners(); + + /** + * event name; + * + * @return + */ + String getEvent(); + + /** + * param list; + * + * @return + */ + byte[] getArgs(); + + /** + * ledger operation context; + * + * @return + */ + LedgerContext getLedger(); + + /** + * Collection of Contracts Owners; + * + *
+ * The owner of the contract is the signer when the contract is deployed. + * + * @return + */ + Set getContracOwners(); + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractException.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractException.java new file mode 100644 index 00000000..0e13d2c5 --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractException.java @@ -0,0 +1,16 @@ +package com.jd.blockchain.contract.model; + +public class ContractException extends RuntimeException { + + public ContractException(String message) { + super(message); + } + + public ContractException(String message,ErrorCodeEnum errorCodeEnum) { + super(message+","+errorCodeEnum.toString()); + } + + public ContractException(ErrorCodeEnum errorCodeEnum) { + super(errorCodeEnum.toString()); + } +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractRuntimeAwire.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractRuntimeAwire.java new file mode 100644 index 00000000..f60eff3b --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ContractRuntimeAwire.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.contract.model; + +/** + * The sub-interface of contract implementation {@link ContractRuntimeAwire} can listen for life cycle events at runtime. + * @author huanghaiquan + * + */ +public interface ContractRuntimeAwire { + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ErrorCodeEnum.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ErrorCodeEnum.java new file mode 100644 index 00000000..c507af4d --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/ErrorCodeEnum.java @@ -0,0 +1,52 @@ +package com.jd.blockchain.contract.model; + +/** + * ErrorCodeEnum + * @author zhaogw + * date 2018/11/8 15:32 + */ +public enum ErrorCodeEnum { + //<100为fatal error; + GATEWAY_CONNECT_ERROR(1,ErrorType.ERROR,"GatewayServiceFactory connect error.!"), + CONTRACT_CLASSPATH_NOT_SET(2,ErrorType.ERROR,"in private contract classLoader,no jar in the contract folder!"), + //Other errors are counted from 101. + AMOUNT_NEGATIVE(101,ErrorType.ALARM,"The amount is negative!"); + + + private int code; + private int type; + private String message; + + ErrorCodeEnum(int code, int type, String message) { + this.code = code; + this.type = type; + this.message = message; + } + + public int getCode() { + return code; + } + public int getType() { + return type; + } + public String getMessage() { + return message; + } + + @Override + public String toString(){ + return "code:"+code+", type:"+type+", message:"+message; + } +} + +/** + * Classify the errors so that they can be summarized easily. + */ +class ErrorType { + public static final int ALARM = 0; + public static final int ERROR = 1; + public static final int CONTRACT_EXE = 2; + public static final int CONTRACT_COMPILE = 3; +} + + diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventHandle.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventHandle.java new file mode 100644 index 00000000..3467d031 --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventHandle.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.contract.model; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * HTTP service method; + * + * @author haiq + * + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface EventHandle { + + + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventProcessingAwire.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventProcessingAwire.java new file mode 100644 index 00000000..7e96bb96 --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/EventProcessingAwire.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.contract.model; + +/** + * @author huanghaiquan + * + */ +public interface EventProcessingAwire extends ContractRuntimeAwire { + + /** + * Called before the event handling method is executed; + * + * @param eventContext + */ + void beforeEvent(ContractEventContext eventContext); + + /** + * Called after the event handling method is successfully executed; + * + * @param eventContext + * evenet; + * @param error + * Error; if the event processing ends normally, this parameter is null; + * if an event processing error occurs, this parameter is not empty; + */ + void postEvent(ContractEventContext eventContext, ContractException error); + + + /** + * Called after the event handling method is successfully executed; + * + * @param error + * Error; if the event processing ends normally, this parameter is null; + * if an event processing error occurs, this parameter is not empty; + */ + void postEvent(ContractException error); + + /** + * Called after the event handling method is successfully executed; + */ + void postEvent(); +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LedgerContext.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LedgerContext.java new file mode 100644 index 00000000..5f2fbcbc --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LedgerContext.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.contract.model; + +import com.jd.blockchain.ledger.data.DataAccountOperator; +import com.jd.blockchain.ledger.data.UserOperator; +import com.jd.blockchain.sdk.BlockchainQueryService; + +public interface LedgerContext extends BlockchainQueryService, UserOperator, DataAccountOperator{ + + + +} diff --git a/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LocalContractEventContext.java b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LocalContractEventContext.java new file mode 100644 index 00000000..4da536cd --- /dev/null +++ b/source/contract/contract-model/src/main/java/com/jd/blockchain/contract/model/LocalContractEventContext.java @@ -0,0 +1,108 @@ +package com.jd.blockchain.contract.model; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.utils.io.ByteArray; + +import java.util.Set; + +/** + * @Author zhaogw + * @Date 2018/9/5 17:43 + */ +public class LocalContractEventContext implements ContractEventContext,Cloneable { + private HashDigest ledgeHash; + private String event; + private byte[] chainCode; + private byte[] args; + private TransactionRequest transactionRequest; + private Set txSigners; + private Set contractOwners; + private LedgerContext ledgerContext; + + public LocalContractEventContext(HashDigest ledgeHash, byte[] chainCode, String event){ + this.ledgeHash = ledgeHash; + this.event = event; + this.chainCode = chainCode; + } + + @Override + public HashDigest getCurrentLedgerHash() { + return ledgeHash; + } + + @Override + public TransactionRequest getTransactionRequest() { + return transactionRequest; + } + + @Override + public Set getTxSigners() { + return txSigners; + } + + @Override + public String getEvent() { + return event; + } + + @Override + public byte[] getArgs() { + return args; + } + + @Override + public LedgerContext getLedger() { + return ledgerContext; + } + + @Override + public Set getContracOwners() { + return null; + } + + public LocalContractEventContext setLedgeHash(HashDigest ledgeHash) { + this.ledgeHash = ledgeHash; + return this; + } + + public LocalContractEventContext setEvent(String event) { + this.event = event; + return this; + } + + public LocalContractEventContext setTransactionRequest(TransactionRequest transactionRequest) { + this.transactionRequest = transactionRequest; + return this; + } + + public LocalContractEventContext setTxSigners(Set txSigners) { + this.txSigners = txSigners; + return this; + } + + public LocalContractEventContext setContractOwners(Set contractOwners) { + this.contractOwners = contractOwners; + return this; + } + + public LocalContractEventContext setLedgerContext(LedgerContext ledgerContext) { + this.ledgerContext = ledgerContext; + return this; + } + + public byte[] getChainCode() { + return chainCode; + } + + public LocalContractEventContext setChainCode(byte[] chainCode) { + this.chainCode = chainCode; + return this; + } + + public LocalContractEventContext setArgs(byte[] args) { + this.args = args; + return this; + } +} diff --git a/source/contract/contract-model/src/main/resources/sys-contract.properties b/source/contract/contract-model/src/main/resources/sys-contract.properties new file mode 100644 index 00000000..e8aee795 --- /dev/null +++ b/source/contract/contract-model/src/main/resources/sys-contract.properties @@ -0,0 +1,7 @@ +#项目源文件存放的位置; +PROJECT_BASE_DIR=e:\\gitCode\\block\\prototype\\ +#合同使用的类库存放的位置,可能不在项目中,故采用全新的地址; +LEDGER_BASE_CLASS_PATH=e:\\gitCode\\block\\prototype\\libs\\ +BLACKLIST=java.io;java.net;java.util.Random +#type:public,private +CONTRACT_CLASSLOADER_TYPE=private \ No newline at end of file diff --git a/source/contract/pom.xml b/source/contract/pom.xml new file mode 100644 index 00000000..ba9ac007 --- /dev/null +++ b/source/contract/pom.xml @@ -0,0 +1,22 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + contract + pom + + + contract-model + contract-framework + contract-jvm + contract-maven-plugin + contract-compile + + + + + \ No newline at end of file diff --git a/source/crypto/crypto-adv/pom.xml b/source/crypto/crypto-adv/pom.xml new file mode 100644 index 00000000..ac213acd --- /dev/null +++ b/source/crypto/crypto-adv/pom.xml @@ -0,0 +1,75 @@ + + 4.0.0 + + com.jd.blockchain + crypto + 0.8.2.RELEASE + + crypto-adv + + + + + + org.bouncycastle + bcprov-jdk15on + + + com.jd.blockchain + binary-proto + ${project.version} + + + + org.powermock + powermock-module-junit4 + 1.6.2 + test + + + + org.powermock + powermock-api-mockito + 1.6.2 + test + + + + net.i2p.crypto + eddsa + 0.1.0 + + + net.java.dev.jna + jna + 5.1.0 + + + + org.mockito + mockito-core + 1.10.19 + test + + + com.jd.blockchain + crypto-framework + ${project.version} + compile + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/ecvrf/VRF.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/ecvrf/VRF.java new file mode 100644 index 00000000..0fd713ef --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/ecvrf/VRF.java @@ -0,0 +1,102 @@ +package com.jd.blockchain.crypto.ecvrf; + + +import com.sun.jna.Library; +import com.sun.jna.Native; + +import java.security.SecureRandom; +import java.util.Objects; + + +public class VRF { + + public static String getLib() throws IllegalArgumentException{ + + String lib; + String path; + String osName = System.getProperty("os.name").toLowerCase(); + + // Mac OS + if (osName.startsWith("mac")){ + lib = "libsodium.23.dylib"; + } + + // Linux OS + else if (osName.contains("linux")){ + lib = "libsodium.so.23.1.0"; + } + + // unsupported OS + else throw new IllegalArgumentException("The VRF implementation is not supported in this Operation System!"); + + path = Objects.requireNonNull(VRF.class.getClassLoader().getResource(lib)).getPath(); + return path; + } + + public interface CLibrary extends Library { + + CLibrary INSTANCE = (CLibrary) + Native.load((getLib()), CLibrary.class); + + int crypto_vrf_is_valid_key(byte[] pk); + int crypto_vrf_keypair_from_seed(byte[] pk, byte[] sk, byte[] seed); + int crypto_vrf_prove(byte[] proof, byte[] sk, byte[] m, long mlen); + int crypto_vrf_verify(byte[] output, byte[] pk, byte[] proof, byte[] m, long mlen); + int crypto_vrf_proof_to_hash(byte[] hash, byte[] proof); + void crypto_vrf_ietfdraft03_sk_to_pk(byte[] pk, byte[] sk); + } + + public static byte[] genSecretKey(){ + byte [] seed = new byte[32]; + byte [] sk = new byte[64]; + byte [] pk = new byte[32]; + try{ + SecureRandom.getInstanceStrong().nextBytes(seed); + CLibrary.INSTANCE.crypto_vrf_keypair_from_seed(pk, sk, seed); + }catch (Exception e){ + e.printStackTrace(); + } + return sk; + } + + public static byte[] sk2pk(byte[] sk){ + if (sk.length != 64){ + return null; + } + byte [] pk = new byte[32]; + CLibrary.INSTANCE.crypto_vrf_ietfdraft03_sk_to_pk(pk, sk); + return pk; + } + + public static boolean IsValidPk(byte[] pk){ + if (pk.length != 32){ + return false; + } + return CLibrary.INSTANCE.crypto_vrf_is_valid_key(pk) == 1; + } + + public static byte[] prove(byte[] sk, byte[] msg){ + byte[] proof = new byte[80]; + if (CLibrary.INSTANCE.crypto_vrf_prove(proof, sk, msg, msg.length)==0){ + return proof; + } + return null; + } + + public static byte[] proof2hash(byte[] proof){ + byte[] hash = new byte[64]; + if (proof.length != 80){ + return null; + } + CLibrary.INSTANCE.crypto_vrf_proof_to_hash(hash, proof); + return hash; + } + + public static boolean verify(byte[] pk, byte[] proof, byte[]msg){ + byte[] output = new byte[64]; + if (proof.length != 80 || pk.length != 32){ + return false; + } + return CLibrary.INSTANCE.crypto_vrf_verify(output, pk, proof, msg, msg.length) == 0; + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java new file mode 100644 index 00000000..4a5dcc9c --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/mpc/MultiSum.java @@ -0,0 +1,98 @@ +package com.jd.blockchain.crypto.mpc; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.PaillierUtils; +import com.jd.blockchain.crypto.paillier.PublicKey; +import com.jd.blockchain.crypto.smutils.asymmetric.SM2Utils; +import com.jd.blockchain.crypto.smutils.hash.SM3Utils; +import com.jd.blockchain.utils.io.BytesUtils; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.agreement.ECDHBasicAgreement; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; + +import java.math.BigInteger; + +public class MultiSum { + + private ECPrivateKeyParameters ePrivKey; + private ECPublicKeyParameters ePubKey; + private ECCurve curve; + private ECDomainParameters domainParams; + + public void generateEphemeralKeyPair(){ + AsymmetricCipherKeyPair eKeyPair = SM2Utils.generateKeyPair(); + this.ePrivKey = (ECPrivateKeyParameters) eKeyPair.getPrivate(); + this.ePubKey = (ECPublicKeyParameters) eKeyPair.getPublic(); + this.curve = SM2Utils.getCurve(); + this.domainParams = SM2Utils.getDomainParams(); + + } + + public BigInteger calculateAgreement(CipherParameters otherEPubKey){ + ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement(); + basicAgreement.init(ePrivKey); + return basicAgreement.calculateAgreement(otherEPubKey); + } + + public static BigInteger deriveShares(byte[] frontID, byte[] rearID, BigInteger agreement){ + byte[] agreementBytes = agreement.toByteArray(); + byte[] inputBytes = BytesUtils.concat(frontID,rearID,agreementBytes); + return new BigInteger(1,SM3Utils.hash(inputBytes)); + } + + public static BigInteger encryptBlindedMsg(PublicKey encKey, BigInteger msg, BigInteger frontShare, BigInteger rearShare){ + return encKey.encrypt(msg.add(frontShare).subtract(rearShare).mod(encKey.getN())); + } + + public static BigInteger aggregateCiphertexts(PublicKey encKey, BigInteger... bigIntegersList){ + BigInteger aggregatedCiphertext = BigInteger.ONE; + for (BigInteger entry : bigIntegersList) { + aggregatedCiphertext = aggregatedCiphertext.multiply(entry).mod(encKey.getnSquared()); + } + return aggregatedCiphertext; + } + + public static BigInteger decrypt(KeyPair keyPair, BigInteger ciphertext){ + return keyPair.decrypt(ciphertext); + } + + public ECPublicKeyParameters getEPubKey(){return ePubKey;} + + public ECPrivateKeyParameters getEPrivKey(){return ePrivKey;} + + + public byte[] getEPubKeyBytes(){ + byte[] ePubKeyBytes = new byte[65]; + byte[] ePubKeyBytesX = ePubKey.getQ().getAffineXCoord().getEncoded(); + byte[] ePubKeyBytesY = ePubKey.getQ().getAffineYCoord().getEncoded(); + System.arraycopy(Hex.decode("04"),0,ePubKeyBytes,0,1); + System.arraycopy(ePubKeyBytesX,0,ePubKeyBytes,1,32); + System.arraycopy(ePubKeyBytesY,0,ePubKeyBytes,1+32,32); + return ePubKeyBytes; + } + + public byte[] getEPrivKeyBytes(){ + return PaillierUtils.BigIntegerToLBytes(ePrivKey.getD(),32); + } + + public ECPublicKeyParameters resolveEPubKey(byte[] ePubKeyBytes){ + byte[] ePubKeyX = new byte[32]; + byte[] ePubKeyY = new byte[32]; + System.arraycopy(ePubKeyBytes,1,ePubKeyX,0,32); + System.arraycopy(ePubKeyBytes,1+32,ePubKeyY,0,32); + ECPoint ePubKeyPoint = curve.createPoint(new BigInteger(1,ePubKeyX), new BigInteger(1,ePubKeyY)); + return new ECPublicKeyParameters(ePubKeyPoint,domainParams); + } + + public ECPrivateKeyParameters resolveEPrivKey(byte[] ePrivKeyBytes){ + return new ECPrivateKeyParameters(new BigInteger(1,ePrivKeyBytes),domainParams); + } + +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPair.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPair.java new file mode 100644 index 00000000..68fd6970 --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPair.java @@ -0,0 +1,98 @@ +package com.jd.blockchain.crypto.paillier; + +import java.math.BigInteger; +import java.util.List; + +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Hendrik Kunert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A class that holds a pair of associated public and private keys. + */ +public class KeyPair { + private final PrivateKey privateKey; + private final PublicKey publicKey; + private final BigInteger upperBound; + + public KeyPair(PrivateKey privateKey, PublicKey publicKey, BigInteger upperBound) { + this.privateKey = privateKey; + this.publicKey = publicKey; + this.upperBound = upperBound; + } + + public KeyPair(byte[] privKeyBytes,byte[] pubKeyBytes,byte[] upperBoundBytes){ + this.privateKey = new PrivateKey(privKeyBytes); + this.publicKey = new PublicKey(pubKeyBytes); + this.upperBound = new BigInteger(upperBoundBytes); + } + + public KeyPair(byte[] keyPairBytes){ + List list = PaillierUtils.split(keyPairBytes, "##KeyPair##".getBytes()); + this.privateKey = new PrivateKey(list.get(0)); + this.publicKey = new PublicKey(list.get(1)); + this.upperBound = new BigInteger(list.get(2)); + } + + public PrivateKey getPrivateKey() { + return privateKey; + } + + public PublicKey getPublicKey() { + return publicKey; + } + + public BigInteger getUpperBound() { return upperBound; } + + /** + * Decrypts the given ciphertext. + * + * @param c The ciphertext that should be decrypted. + * @return The corresponding plaintext. If an upper bound was given to {@link KeyPairBuilder}, + * the result can also be negative. See {@link KeyPairBuilder#upperBound(BigInteger)} for details. + */ + public final BigInteger decrypt(BigInteger c) { + + BigInteger n = publicKey.getN(); + BigInteger nSquare = publicKey.getnSquared(); + BigInteger lambda = privateKey.getLambda(); + + BigInteger u = privateKey.getPreCalculatedDenominator(); + + BigInteger p = c.modPow(lambda, nSquare).subtract(BigInteger.ONE).divide(n).multiply(u).mod(n); + + if (upperBound != null && p.compareTo(upperBound) > 0) { + p = p.subtract(n); + } + + return p; + } + + public byte[] getUpperBoundBytes(){ return upperBound.toByteArray(); } + + public byte[] getKeyPairBytes(){ + return BytesUtils.concat(privateKey.getPrivKeyBytes(),"##KeyPair##".getBytes(),publicKey.getPubKeyBytes(),"##KeyPair##".getBytes(),upperBound.toByteArray()); + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPairBuilder.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPairBuilder.java new file mode 100644 index 00000000..c4234cae --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/KeyPairBuilder.java @@ -0,0 +1,165 @@ +package com.jd.blockchain.crypto.paillier; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Random; +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Hendrik Kunert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +/** + * A class that is used for generating a pair of associated public and private + * keys. + * + * @see KeyPair + */ +public class KeyPairBuilder { + + private int bits = 1024; + + private int certainty = 0; + + private Random rng; + + private BigInteger upperBound; + + /** + * Sets the size of the key to be created. + *

+ * The default size is 1024 bits. + * + * @param bits The size of the key in bits. + * @return This instance of KeyPairBuilder for method chaining. + */ + public KeyPairBuilder bits(int bits) { + this.bits = bits; + return this; + } + + /** + * See {@link BigInteger#BigInteger(int, int, Random)} for more details. + *

+ * The default value is 0. + * + * @return This instance of KeyPairBuilder for method chaining. + */ + public KeyPairBuilder certainty(int certainty) { + this.certainty = certainty; + return this; + } + + /** + * Sets the random number generator that is used for the generation of + * internally needed prime numbers. + *

+ * The default is {@link SecureRandom}. + *

+ * Warning: + * The change of this value affects the security of the whole cryptographic + * system. + * + * @param rng The random number generator that should be used instead of + * {@link SecureRandom}. + * @return This instance of KeyPairBuilder for method chaining. + */ + public KeyPairBuilder randomNumberGenerator(Random rng) { + this.rng = rng; + return this; + } + + /** + * Sets an upper bound that is used for decrypting ciphertexts representing a negative value. + *

+ * In most cases the upper bound should be the same as of the underlying number system - + * for example {@link Integer#MAX_VALUE}. + * + * @param b The upper bound. + * @return This instance of KeyPairBuilder for method chaining. + */ + public KeyPairBuilder upperBound(BigInteger b) { + this.upperBound = b; + return this; + } + + /** + * Creates a pair of associated public and private keys. + * + * @return The pair of associated public and private keys. + */ + public KeyPair generateKeyPair() { + if (rng == null) { + rng = new SecureRandom(); + } + + BigInteger p, q; + int length = bits / 2; + if (certainty > 0) { + p = new BigInteger(length, certainty, rng); + q = new BigInteger(length, certainty, rng); + } else { + p = BigInteger.probablePrime(length, rng); + q = BigInteger.probablePrime(length, rng); + } + + BigInteger n = p.multiply(q); + BigInteger nSquared = n.multiply(n); + + BigInteger pMinusOne = p.subtract(BigInteger.ONE); + BigInteger qMinusOne = q.subtract(BigInteger.ONE); + + BigInteger lambda = this.lcm(pMinusOne, qMinusOne); + + BigInteger g; + BigInteger helper; + + do { + g = new BigInteger(bits, rng); + helper = calculateL(g.modPow(lambda, nSquared), n); + + } while (!helper.gcd(n).equals(BigInteger.ONE)); + + PublicKey publicKey = new PublicKey(n, nSquared, g, bits); + PrivateKey privateKey = new PrivateKey(lambda, helper.modInverse(n)); + + return new KeyPair(privateKey, publicKey, upperBound); + + } + + // TODO separate this somewhere + private BigInteger calculateL(BigInteger u, BigInteger n) { + BigInteger result = u.subtract(BigInteger.ONE); + result = result.divide(n); + return result; + } + + // TODO add to own BigInteger extended class + private BigInteger lcm(BigInteger a, BigInteger b) { + BigInteger result; + BigInteger gcd = a.gcd(b); + + result = a.abs().divide(gcd); + result = result.multiply(b.abs()); + + return result; + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java new file mode 100644 index 00000000..ac14f22b --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PaillierUtils.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.crypto.paillier; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +public class PaillierUtils { + + // To convert BigInteger to byte[] whose length is l + public static byte[] BigIntegerToLBytes(BigInteger b, int l){ + byte[] tmp = b.toByteArray(); + byte[] result = new byte[l]; + if (tmp.length > result.length) + System.arraycopy(tmp, tmp.length-result.length, result, 0, result.length); + else System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length); + return result; + } + + public static byte[] intToBytes(int i){ + byte[] result = new byte[4]; + result[0] = (byte) (i >> 24); + result[1] = (byte) (i >> 16); + result[2] = (byte) (i >> 8); + result[3] = (byte) (i); + return result; + } + + public static int bytesToInt(byte[] array){ + int result = 0; + result |= ((array[0] & 0xFF) << 24); + result |= ((array[1] & 0xFF) << 16); + result |= ((array[2] & 0xFF) << 8); + result |= ((array[3] & 0xFF)); + return result; + } + + public static List split(byte[] array, byte[] delimiter) { + List byteArrays = new LinkedList<>(); + if (delimiter.length == 0) { + return byteArrays; + } + int begin = 0; + + outer: + for (int i = 0; i < array.length - delimiter.length + 1; i++) { + for (int j = 0; j < delimiter.length; j++) { + if (array[i + j] != delimiter[j]) { + continue outer; + } + } + byteArrays.add(Arrays.copyOfRange(array, begin, i)); + begin = i + delimiter.length; + } + byteArrays.add(Arrays.copyOfRange(array, begin, array.length)); + return byteArrays; + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PrivateKey.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PrivateKey.java new file mode 100644 index 00000000..ac4b656a --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PrivateKey.java @@ -0,0 +1,75 @@ +package com.jd.blockchain.crypto.paillier; + +import java.math.BigInteger; +import java.util.List; +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Hendrik Kunert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * A class that represents the private part of the Paillier key pair. + */ +public class PrivateKey { + + private final BigInteger lambda; + private final BigInteger preCalculatedDenominator; + + public PrivateKey(BigInteger lambda, BigInteger preCalculatedDenominator) { + this.lambda = lambda; + + this.preCalculatedDenominator = preCalculatedDenominator; + } + + public PrivateKey(byte[] lambdaBytes, byte[] preCalculatedDenominatorBytes){ + this.lambda = new BigInteger(lambdaBytes); + this.preCalculatedDenominator = new BigInteger(preCalculatedDenominatorBytes); + } + + public PrivateKey(byte[] privKeyBytes){ + List list = PaillierUtils.split(privKeyBytes, "##PrivateKey##".getBytes()); + this.lambda = new BigInteger(list.get(0)); + this.preCalculatedDenominator = new BigInteger(list.get(1)); + } + + public BigInteger getLambda() { + return lambda; + } + + public BigInteger getPreCalculatedDenominator() { + return preCalculatedDenominator; + } + + public byte[] getLambdaBytes(){ + return lambda.toByteArray(); + } + + public byte[] getPreCalculatedDenominatorBytes(){ + return preCalculatedDenominator.toByteArray(); + } + + public byte[] getPrivKeyBytes(){ + return BytesUtils.concat(getLambdaBytes(),"##PrivateKey##".getBytes(),getPreCalculatedDenominatorBytes()); + } +} diff --git a/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PublicKey.java b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PublicKey.java new file mode 100644 index 00000000..aa1d3f51 --- /dev/null +++ b/source/crypto/crypto-adv/src/main/java/com/jd/blockchain/crypto/paillier/PublicKey.java @@ -0,0 +1,129 @@ +package com.jd.blockchain.crypto.paillier; + +import java.math.BigInteger; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +/** + * The MIT License (MIT) + * + * Copyright (c) 2014 Hendrik Kunert + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * A class that represents the public part of the Paillier key pair. + *

+ * As in all asymmetric cryptographic systems it is responsible for the + * encryption. + *

+ * Additional instructions for the decryption can be found on {@link KeyPair}. + * + * @see KeyPair + */ +public class PublicKey { + private final int bits; + private final BigInteger n; + private final BigInteger nSquared; + private final BigInteger g; + + public PublicKey(BigInteger n, BigInteger nSquared, BigInteger g, int bits) { + this.n = n; + this.nSquared = nSquared; + this.bits = bits; + this.g = g; + } + + public PublicKey(byte[] nBytes, byte[] nSquaredBytes, byte[] gBytes, byte[] bitsBytes) { + this.n = new BigInteger(nBytes); + this.nSquared = new BigInteger(nSquaredBytes); + this.g = new BigInteger(gBytes); + this.bits = PaillierUtils.bytesToInt(bitsBytes); + } + + public PublicKey(byte[] pubKeyBytes){ + List list = PaillierUtils.split(pubKeyBytes, "##PublicKey##".getBytes()); + this.n = new BigInteger(list.get(0)); + this.nSquared = new BigInteger(list.get(1)); + this.g = new BigInteger(list.get(2)); + this.bits = PaillierUtils.bytesToInt(list.get(3)); + } + + + public int getBits() { + return bits; + } + + public BigInteger getN() { + return n; + } + + public BigInteger getnSquared() { + return nSquared; + } + + public BigInteger getG() { + return g; + } + + /** + * Encrypts the given plaintext. + * + * @param m The plaintext that should be encrypted. + * @return The corresponding ciphertext. + */ + public final BigInteger encrypt(BigInteger m) { + + BigInteger r; + do { + r = new BigInteger(bits, new Random()); + } while (r.compareTo(n) >= 0); + + BigInteger result = g.modPow(m, nSquared); + BigInteger x = r.modPow(n, nSquared); + + result = result.multiply(x); + result = result.mod(nSquared); + + return result; + } + + public byte[] getBitsBytes(){ + return PaillierUtils.intToBytes(bits); + } + + public byte[] getNBytes(){ return n.toByteArray(); } + + public byte[] getNSquaredBytes(){ + return nSquared.toByteArray(); + } + + public byte[] getGBytes(){ + return g.toByteArray(); + } + + public byte[] getPubKeyBytes(){ + return BytesUtils.concat(getNBytes(),"##PublicKey##".getBytes(),getNSquaredBytes(),"##PublicKey##".getBytes(),getGBytes(),"##PublicKey##".getBytes(),getBitsBytes()); + } +} + diff --git a/source/crypto/crypto-adv/src/main/resources/libsodium.23.dylib b/source/crypto/crypto-adv/src/main/resources/libsodium.23.dylib new file mode 100755 index 00000000..bce89d3f Binary files /dev/null and b/source/crypto/crypto-adv/src/main/resources/libsodium.23.dylib differ diff --git a/source/crypto/crypto-adv/src/main/resources/libsodium.so.23.1.0 b/source/crypto/crypto-adv/src/main/resources/libsodium.so.23.1.0 new file mode 100755 index 00000000..44c1ef1a Binary files /dev/null and b/source/crypto/crypto-adv/src/main/resources/libsodium.so.23.1.0 differ diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/ecvrf/VRFTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/ecvrf/VRFTest.java new file mode 100644 index 00000000..5c109651 --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/ecvrf/VRFTest.java @@ -0,0 +1,73 @@ +package test.com.jd.blockchain.crypto.ecvrf; + +import com.jd.blockchain.crypto.ecvrf.VRF; +import org.junit.Test; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +public class VRFTest { + + @Test + public void testVRF() { + + byte[] msg = "你好".getBytes(); + + //初始化一个异常 + Exception actualEx = null; + + try{ + VRF.getLib(); + long skGenStartTime = System.currentTimeMillis(); + byte[] sk = VRF.genSecretKey(); + long skGenTime = System.currentTimeMillis() - skGenStartTime; + System.out.println(String.format("VRF sk generation time = %s ms", skGenTime)); + + long pkGenStartTime = System.currentTimeMillis(); + byte[] pk = VRF.sk2pk(sk); + long pkGenTime = System.currentTimeMillis() - pkGenStartTime; + System.out.println(String.format("VRF pk generation time = %s ms", pkGenTime)); + + long proofGenStartTime = System.currentTimeMillis(); + byte[] proof = VRF.prove(sk, msg); + long proofGenTime = System.currentTimeMillis() - proofGenStartTime; + System.out.println(String.format("VRF proof generation time = %s ms", proofGenTime)); + + long pkValidationStartTime = System.currentTimeMillis(); + assertTrue(VRF.IsValidPk(pk)); + long pkValidationTime = System.currentTimeMillis() - pkValidationStartTime; + System.out.println(String.format("VRF pk validation time = %s ms", pkValidationTime)); + + assertNotNull(proof); + + long proof2hashStartTime = System.currentTimeMillis(); + byte[] output = VRF.proof2hash(proof); + long proof2hashTime = System.currentTimeMillis() - proof2hashStartTime; + System.out.println(String.format("VRF proof2hash time = %s ms", proof2hashTime)); + + long verificationStartTime = System.currentTimeMillis(); + boolean isValid = VRF.verify(pk, proof, msg); + long verificationTime = System.currentTimeMillis() - verificationStartTime; + System.out.println(String.format("VRF verification time = %s ms", verificationTime)); + + assertTrue(isValid); + assertNotNull(output); + + }catch (Exception e){ + actualEx = e; + } + + String osName = System.getProperty("os.name").toLowerCase(); + + if (osName.startsWith("mac") + || osName.contains("linux")) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + Class expectedException = IllegalArgumentException.class; + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java new file mode 100644 index 00000000..4dff44cc --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/mpc/MultiSumTest.java @@ -0,0 +1,105 @@ +package test.com.jd.blockchain.crypto.mpc; + +import com.jd.blockchain.crypto.mpc.MultiSum; +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PublicKey; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.*; + +public class MultiSumTest { + + private KeyPair keyPair; + private PublicKey encKey; + + @Before + public void init() { + KeyPairBuilder keygen = new KeyPairBuilder(); + keyPair = keygen.generateKeyPair(); + encKey = keyPair.getPublicKey(); + } + + @Test + public void testMultiSum() { + + MultiSum instance1 = new MultiSum(); + MultiSum instance2 = new MultiSum(); + MultiSum instance3 = new MultiSum(); + + BigInteger value1 = BigInteger.valueOf(6); + BigInteger value2 = BigInteger.valueOf(60); + BigInteger value3 = BigInteger.valueOf(600); + BigInteger expectedSum = BigInteger.valueOf(666); + + byte[] id1 = "1".getBytes(); + byte[] id2 = "2".getBytes(); + byte[] id3 = "3".getBytes(); + + instance1.generateEphemeralKeyPair(); + instance2.generateEphemeralKeyPair(); + instance3.generateEphemeralKeyPair(); + + ECPublicKeyParameters ePubKey1 = instance1.getEPubKey(); + ECPublicKeyParameters ePubKey2 = instance2.getEPubKey(); + ECPublicKeyParameters ePubKey3 = instance3.getEPubKey(); + + BigInteger sk12 = instance1.calculateAgreement(ePubKey2); + BigInteger sk23 = instance2.calculateAgreement(ePubKey3); + BigInteger sk31 = instance1.calculateAgreement(ePubKey3); + + assertEquals(sk12,instance2.calculateAgreement(ePubKey1)); + assertEquals(sk23,instance3.calculateAgreement(ePubKey2)); + assertEquals(sk31,instance3.calculateAgreement(ePubKey1)); + + BigInteger s12 = MultiSum.deriveShares(id1,id2,sk12); + BigInteger s23 = MultiSum.deriveShares(id2,id3,sk23); + BigInteger s31 = MultiSum.deriveShares(id3,id1,sk31); + + assertEquals(s12, MultiSum.deriveShares(id1,id2,sk12)); + assertEquals(s23, MultiSum.deriveShares(id2,id3,sk23)); + assertEquals(s31, MultiSum.deriveShares(id3,id1,sk31)); + + BigInteger c1 = MultiSum.encryptBlindedMsg(encKey,value1,s12,s31); + BigInteger c2 = MultiSum.encryptBlindedMsg(encKey,value2,s23,s12); + BigInteger c3 = MultiSum.encryptBlindedMsg(encKey,value3,s31,s23); + + BigInteger aggregatedCiphertext = MultiSum.aggregateCiphertexts(encKey,c1,c2,c3); + + BigInteger decryptedValue = MultiSum.decrypt(keyPair,aggregatedCiphertext); + + assertEquals(expectedSum,decryptedValue); + } + + @Test + public void testResolveEPrivKey(){ + + MultiSum instance = new MultiSum(); + instance.generateEphemeralKeyPair(); + + ECPrivateKeyParameters expectedEPrivKey = instance.getEPrivKey(); + byte[] ePrivKeyBytes = instance.getEPrivKeyBytes(); + ECPrivateKeyParameters ePrivKey = instance.resolveEPrivKey(ePrivKeyBytes); + assertEquals(expectedEPrivKey.getD(),ePrivKey.getD()); + } + + @Test + public void testResolveEPubKey(){ + + MultiSum instance = new MultiSum(); + instance.generateEphemeralKeyPair(); + + ECPublicKeyParameters expectedEPubKey = instance.getEPubKey(); + byte[] ePubKeyBytes = instance.getEPubKeyBytes(); + ECPublicKeyParameters ePubKey = instance.resolveEPubKey(ePubKeyBytes); + + assertEquals(Hex.toHexString(expectedEPubKey.getQ().getAffineXCoord().getEncoded()),Hex.toHexString(ePubKey.getQ().getAffineXCoord().getEncoded())); + assertEquals(Hex.toHexString(expectedEPubKey.getQ().getAffineYCoord().getEncoded()),Hex.toHexString(ePubKey.getQ().getAffineYCoord().getEncoded())); + } +} \ No newline at end of file diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/DecryptionTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/DecryptionTest.java new file mode 100644 index 00000000..8994d9dc --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/DecryptionTest.java @@ -0,0 +1,58 @@ +package test.com.jd.blockchain.crypto.paillier; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PublicKey; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Collection; + +import static org.junit.Assert.assertEquals; + +/** + * Created by kunerd on 22.09.15. + */ +@RunWith(value = Parameterized.class) +public class DecryptionTest { + + @Parameterized.Parameters + public static Collection data() { + return Arrays.asList(createTestParameter(Long.MIN_VALUE), + createTestParameter(Integer.MIN_VALUE), + createTestParameter(Short.MIN_VALUE), + createTestParameter(0), + createTestParameter(Short.MAX_VALUE), + createTestParameter(Integer.MAX_VALUE), + createTestParameter(Long.MAX_VALUE)); + } + + private BigInteger input; + private BigInteger expected; + + public DecryptionTest(BigInteger input, BigInteger expected) { + this.input = input; + this.expected = expected; + } + + @Test + public void test() { + KeyPair keyPair = new KeyPairBuilder().upperBound(BigInteger.valueOf(Long.MAX_VALUE)) + .generateKeyPair(); + PublicKey publicKey = keyPair.getPublicKey(); + + BigInteger encryptedData = publicKey.encrypt(input); + + assertEquals(expected, keyPair.decrypt(encryptedData)); + } + + private static Object[] createTestParameter(long plaintext) { + BigInteger p = BigInteger.valueOf(plaintext); + return new Object[]{p, p}; + } + +} + diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/HomomorphicPropertiesTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/HomomorphicPropertiesTest.java new file mode 100644 index 00000000..2b156386 --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/HomomorphicPropertiesTest.java @@ -0,0 +1,90 @@ +package test.com.jd.blockchain.crypto.paillier; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PublicKey; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.assertEquals; + +public class HomomorphicPropertiesTest { + private KeyPair keypair; + private PublicKey publicKey; + + @Before + public void init() { + KeyPairBuilder keygen = new KeyPairBuilder(); + this.keypair = keygen.generateKeyPair(); + this.publicKey = keypair.getPublicKey(); + } + + @Test + public void testHomomorphicAddition() { + BigInteger plainA = BigInteger.valueOf(102); + BigInteger plainB = BigInteger.valueOf(203); + + BigInteger encryptedA = publicKey.encrypt(plainA); + BigInteger encryptedB = publicKey.encrypt(plainB); + + BigInteger decryptedProduct = keypair.decrypt(encryptedA.multiply( + encryptedB).mod(publicKey.getnSquared())); + BigInteger plainSum = plainA.add(plainB).mod(publicKey.getN()); + + assertEquals(decryptedProduct, plainSum); + } + + @Test + public void testHomomorphicConstantMultiplication() { + BigInteger plainA = BigInteger.valueOf(14); + BigInteger plainB = BigInteger.valueOf(203); + + BigInteger encryptedA = publicKey.encrypt(plainA); + + BigInteger decryptedPow = keypair.decrypt(encryptedA.modPow(plainB, + publicKey.getnSquared())); + BigInteger plainSum = plainA.multiply(plainB).mod(publicKey.getN()); + + assertEquals(decryptedPow, plainSum); + } + + @Test + public void testHomomorphicMultiplication() { + BigInteger plainA = BigInteger.valueOf(23); + BigInteger plainB = BigInteger.valueOf(234); + + BigInteger encryptedA = publicKey.encrypt(plainA); + BigInteger decryptedPowA = keypair.decrypt(encryptedA.modPow( + plainB, publicKey.getnSquared())); + BigInteger plainSumA = plainA.multiply(plainB).mod(publicKey.getN()); + + assertEquals(decryptedPowA, plainSumA); + + BigInteger encryptedB = publicKey.encrypt(plainB); + BigInteger decryptedPowB = keypair.decrypt(encryptedB.modPow( + plainA, publicKey.getnSquared())); + BigInteger plainSumB = plainA.multiply(plainB).mod(publicKey.getN()); + + assertEquals(decryptedPowB, plainSumB); + + assertEquals(decryptedPowA, decryptedPowB); + } + + @Test + public void testHomomorphicMultiplicationPowG() { + BigInteger plainA = BigInteger.valueOf(230); + BigInteger plainB = BigInteger.valueOf(100); + + BigInteger g = publicKey.getG(); + + BigInteger encryptedA = publicKey.encrypt(plainA); + BigInteger decryptedPow = keypair.decrypt(encryptedA.multiply(g.modPow( + plainB, publicKey.getnSquared()).mod(publicKey.getnSquared()))); + + BigInteger plainSumA = plainA.add(plainB).mod(publicKey.getN()); + + assertEquals(decryptedPow, plainSumA); + } +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/JPaillierTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/JPaillierTest.java new file mode 100644 index 00000000..12e0258d --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/JPaillierTest.java @@ -0,0 +1,43 @@ +package test.com.jd.blockchain.crypto.paillier; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PublicKey; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; + +public class JPaillierTest { + private KeyPair keyPair; + private PublicKey publicKey; + + @Before + public void init() { + KeyPairBuilder keygen = new KeyPairBuilder(); + keyPair = keygen.generateKeyPair(); + publicKey = keyPair.getPublicKey(); + } + + @Test + public void testEncryption() { + BigInteger plainData = BigInteger.valueOf(10); + + BigInteger encryptedData = publicKey.encrypt(plainData); + + assertNotEquals(plainData, encryptedData); + } + + @Test + public void testDecyption() { + BigInteger plainData = BigInteger.valueOf(10); + + BigInteger encryptedData = publicKey.encrypt(plainData); + BigInteger decryptedData = keyPair.decrypt(encryptedData); + + assertEquals(plainData, decryptedData); + } +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPrivateKeyTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPrivateKeyTest.java new file mode 100644 index 00000000..ac64efda --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPrivateKeyTest.java @@ -0,0 +1,44 @@ +package test.com.jd.blockchain.crypto.paillier; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PrivateKey; +import com.jd.blockchain.crypto.paillier.PublicKey; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.assertEquals; + +public class KeyPairBuilderPrivateKeyTest { + private KeyPairBuilder keygen; + private KeyPair keypair; + private PrivateKey privateKey; + + @Before + public void init() { + this.keygen = new KeyPairBuilder(); + this.keypair = keygen.generateKeyPair(); + this.privateKey = keypair.getPrivateKey(); + } + + @Test + public void testPreCalculatedDenominator() { + PublicKey publicKey = keypair.getPublicKey(); + + BigInteger preCalculatedDenominator = privateKey.getPreCalculatedDenominator(); + + BigInteger g = publicKey.getG(); + BigInteger n = publicKey.getN(); + BigInteger nSquared = publicKey.getnSquared(); + BigInteger lambda = privateKey.getLambda(); + + BigInteger expected = g.modPow(lambda, nSquared); + expected = expected.subtract(BigInteger.ONE); + expected = expected.divide(n); + expected = expected.modInverse(n); + + assertEquals(expected, preCalculatedDenominator); + } +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPublicKeyTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPublicKeyTest.java new file mode 100644 index 00000000..5517c97f --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderPublicKeyTest.java @@ -0,0 +1,57 @@ +package test.com.jd.blockchain.crypto.paillier; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PrivateKey; +import com.jd.blockchain.crypto.paillier.PublicKey; +import org.junit.Before; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.assertEquals; + +public class KeyPairBuilderPublicKeyTest { + + private KeyPair keypair; + private PublicKey publicKey; + + @Before + public void init() { + KeyPairBuilder keygen = new KeyPairBuilder(); + this.keypair = keygen.generateKeyPair(); + this.publicKey = keypair.getPublicKey(); + } + + @Test + public void testBitsSetup() { + int BITS = 1024; + assertEquals(BITS, publicKey.getBits()); + } + + @Test + public void testCalculationOfNSquared() { + + BigInteger n = publicKey.getN(); + BigInteger nSquared = n.multiply(n); + + assertEquals(nSquared, publicKey.getnSquared()); + } + + @Test + public void testCalculationOfGOfG() { + PrivateKey privateKey = keypair.getPrivateKey(); + + BigInteger n = publicKey.getN(); + BigInteger nSquared = publicKey.getnSquared(); + BigInteger g = publicKey.getG(); + BigInteger lambda = privateKey.getLambda(); + + BigInteger l = g.modPow(lambda, nSquared); + l = l.subtract(BigInteger.ONE); + l = l.divide(n); + + assertEquals(BigInteger.ONE, l.gcd(n)); + } + +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderTest.java new file mode 100644 index 00000000..02efb028 --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/KeyPairBuilderTest.java @@ -0,0 +1,113 @@ +package test.com.jd.blockchain.crypto.paillier; + +import static org.junit.Assert.assertEquals; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Random; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +import com.jd.blockchain.crypto.paillier.KeyPair; +import com.jd.blockchain.crypto.paillier.KeyPairBuilder; +import com.jd.blockchain.crypto.paillier.PrivateKey; +import com.jd.blockchain.crypto.paillier.PublicKey; + +@RunWith(PowerMockRunner.class) +@PrepareForTest(KeyPairBuilder.class) +public class KeyPairBuilderTest { + + private static final int BITS = 128; + + private KeyPairBuilder keygen; + private PublicKey publicKey; + private PrivateKey privateKey; + + private BigInteger p = BigInteger.valueOf(5); + private BigInteger q = BigInteger.valueOf(7); + private BigInteger g1 = BigInteger.valueOf(35); + private BigInteger g2 = BigInteger.valueOf(36); + + private Random rng; + + @Before + public void beforeEach() { + rng = PowerMockito.mock(SecureRandom.class); + + keygen = new KeyPairBuilder() + .bits(BITS) + .randomNumberGenerator(rng); + + PowerMockito.mockStatic(BigInteger.class); + } + + private void prepareTest() throws Exception { + + PowerMockito.when(BigInteger.probablePrime(BITS / 2, rng)).thenReturn(p, q); + + PowerMockito.whenNew(BigInteger.class).withArguments(BITS, rng).thenReturn(g1, g2); + + KeyPair keypair = keygen.generateKeyPair(); + + publicKey = keypair.getPublicKey(); + privateKey = keypair.getPrivateKey(); + } + + @Test + public void computationOfN() throws Exception { + prepareTest(); + + BigInteger e = p.multiply(q); + BigInteger a = publicKey.getN(); + + assertEquals(e, a); + } + + + @Test + public void computationOfLambda() throws Exception { + BigInteger e = new BigInteger("12"); + + prepareTest(); + + BigInteger a = privateKey.getLambda(); + + assertEquals(e, a); + } + + @Test + public void computationOfG() throws Exception { + prepareTest(); + + PowerMockito.verifyNew(BigInteger.class, Mockito.times(2)).withArguments(Mockito.eq(128), Mockito.any(Random.class)); + } + + @Test + public void withoutCertainty() throws Exception { + prepareTest(); + + PowerMockito.verifyStatic(Mockito.times(2)); + BigInteger.probablePrime(BITS / 2, rng); + + } + + @Test + public void withCertainty() throws Exception { + int certainty = 6; + + keygen.certainty(certainty); + + PowerMockito.whenNew(BigInteger.class).withArguments(BITS / 2, certainty, rng).thenReturn(p, q); + + prepareTest(); + + PowerMockito.verifyNew(BigInteger.class, Mockito.times(2)).withArguments(BITS / 2, certainty, rng); + } + +} diff --git a/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/ResolveTest.java b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/ResolveTest.java new file mode 100644 index 00000000..05022dad --- /dev/null +++ b/source/crypto/crypto-adv/src/test/java/test/com/jd/blockchain/crypto/paillier/ResolveTest.java @@ -0,0 +1,124 @@ +package test.com.jd.blockchain.crypto.paillier; + +import com.jd.blockchain.crypto.paillier.*; +import org.junit.Test; + +import java.math.BigInteger; + +import static org.junit.Assert.assertEquals; + +public class ResolveTest { + + @Test + public void testResolvePrivateKey() { + KeyPairBuilder keygen = new KeyPairBuilder(); + KeyPair keyPair = keygen.generateKeyPair(); + + PrivateKey privKey = keyPair.getPrivateKey(); + BigInteger lambda = privKey.getLambda(); + BigInteger preCalculatedDenominator = privKey.getPreCalculatedDenominator(); + + byte[] privKeyBytes = privKey.getPrivKeyBytes(); + byte[] lambdaBytes = privKey.getLambdaBytes(); + byte[] preCalculatedDenominatorBytes = privKey.getPreCalculatedDenominatorBytes(); + + assertEquals(lambda,new BigInteger(lambdaBytes)); + assertEquals(preCalculatedDenominator,new BigInteger(preCalculatedDenominatorBytes)); + + assertEquals(lambda,new PrivateKey(lambda,preCalculatedDenominator).getLambda()); + assertEquals(preCalculatedDenominator,(new PrivateKey(lambda,preCalculatedDenominator)).getPreCalculatedDenominator()); + + assertEquals(lambda,(new PrivateKey(lambdaBytes,preCalculatedDenominatorBytes)).getLambda()); + assertEquals(preCalculatedDenominator,(new PrivateKey(lambdaBytes,preCalculatedDenominatorBytes)).getPreCalculatedDenominator()); + + assertEquals(lambda,(new PrivateKey(privKeyBytes)).getLambda()); + assertEquals(preCalculatedDenominator,(new PrivateKey(privKeyBytes)).getPreCalculatedDenominator()); + } + + @Test + public void testResolvePublicKey() { + KeyPairBuilder keygen = new KeyPairBuilder(); + KeyPair keyPair = keygen.generateKeyPair(); + + PublicKey pubKey = keyPair.getPublicKey(); + int bits = pubKey.getBits(); + BigInteger n = pubKey.getN(); + BigInteger nSquared = pubKey.getnSquared(); + BigInteger g = pubKey.getG(); + + byte[] pubKeyBytes = pubKey.getPubKeyBytes(); + byte[] bitsBytes = pubKey.getBitsBytes(); + byte[] nBytes = pubKey.getNBytes(); + byte[] nSquaredBytes = pubKey.getNSquaredBytes(); + byte[] gBytes = pubKey.getGBytes(); + + assertEquals(bits,PaillierUtils.bytesToInt(bitsBytes)); + assertEquals(n,new BigInteger(nBytes)); + assertEquals(nSquared,new BigInteger(nSquaredBytes)); + assertEquals(g,new BigInteger(gBytes)); + + assertEquals(bits,(new PublicKey(n,nSquared,g,bits)).getBits()); + assertEquals(n,(new PublicKey(n,nSquared,g,bits)).getN()); + assertEquals(nSquared,(new PublicKey(n,nSquared,g,bits)).getnSquared()); + assertEquals(g,(new PublicKey(n,nSquared,g,bits)).getG()); + + assertEquals(bits,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getBits()); + assertEquals(n,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getN()); + assertEquals(nSquared,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getnSquared()); + assertEquals(g,(new PublicKey(nBytes,nSquaredBytes,gBytes,bitsBytes)).getG()); + + assertEquals(bits,(new PublicKey(pubKeyBytes)).getBits()); + assertEquals(n,(new PublicKey(pubKeyBytes)).getN()); + assertEquals(nSquared,(new PublicKey(pubKeyBytes)).getnSquared()); + assertEquals(g,(new PublicKey(pubKeyBytes)).getG()); + } + + @Test + public void testResolveKeyPair() { + KeyPairBuilder keygen = new KeyPairBuilder(); + keygen.upperBound(new BigInteger(PaillierUtils.intToBytes(Integer.MAX_VALUE))); + KeyPair keyPair = keygen.generateKeyPair(); + + PrivateKey privKey = keyPair.getPrivateKey(); + PublicKey pubKey = keyPair.getPublicKey(); + BigInteger upperBound = keyPair.getUpperBound(); + + byte[] keyPairBytes = keyPair.getKeyPairBytes(); + byte[] privKeyBytes = privKey.getPrivKeyBytes(); + byte[] pubKeyBytes = pubKey.getPubKeyBytes(); + byte[] upperBoundBytes = keyPair.getUpperBoundBytes(); + + assertEquals(upperBound,keyPair.getUpperBound()); + assertEquals(privKey.getLambda(),keyPair.getPrivateKey().getLambda()); + assertEquals(privKey.getPreCalculatedDenominator(),keyPair.getPrivateKey().getPreCalculatedDenominator()); + assertEquals(pubKey.getBits(),keyPair.getPublicKey().getBits()); + assertEquals(pubKey.getN(),keyPair.getPublicKey().getN()); + assertEquals(pubKey.getnSquared(),keyPair.getPublicKey().getnSquared()); + assertEquals(pubKey.getG(),keyPair.getPublicKey().getG()); + + assertEquals(upperBound,(new KeyPair(privKey,pubKey,upperBound).getUpperBound())); + assertEquals(privKey.getLambda(),(new KeyPair(privKey,pubKey,upperBound).getPrivateKey().getLambda())); + assertEquals(privKey.getPreCalculatedDenominator(),(new KeyPair(privKey,pubKey,upperBound).getPrivateKey().getPreCalculatedDenominator())); + assertEquals(pubKey.getBits(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getBits())); + assertEquals(pubKey.getN(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getN())); + assertEquals(pubKey.getnSquared(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getnSquared())); + assertEquals(pubKey.getG(),(new KeyPair(privKey,pubKey,upperBound).getPublicKey().getG())); + + assertEquals(upperBound,(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getUpperBound())); + assertEquals(privKey.getLambda(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPrivateKey().getLambda())); + assertEquals(privKey.getPreCalculatedDenominator(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPrivateKey().getPreCalculatedDenominator())); + assertEquals(pubKey.getBits(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getBits())); + assertEquals(pubKey.getN(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getN())); + assertEquals(pubKey.getnSquared(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getnSquared())); + assertEquals(pubKey.getG(),(new KeyPair(privKeyBytes,pubKeyBytes,upperBoundBytes).getPublicKey().getG())); + + assertEquals(upperBound,(new KeyPair(keyPairBytes).getUpperBound())); + assertEquals(privKey.getLambda(),(new KeyPair(keyPairBytes).getPrivateKey().getLambda())); + assertEquals(privKey.getPreCalculatedDenominator(),(new KeyPair(keyPairBytes).getPrivateKey().getPreCalculatedDenominator())); + assertEquals(pubKey.getBits(),(new KeyPair(keyPairBytes).getPublicKey().getBits())); + assertEquals(pubKey.getN(),(new KeyPair(keyPairBytes).getPublicKey().getN())); + assertEquals(pubKey.getnSquared(),(new KeyPair(keyPairBytes).getPublicKey().getnSquared())); + assertEquals(pubKey.getG(),(new KeyPair(keyPairBytes).getPublicKey().getG())); + + } +} diff --git a/source/crypto/crypto-framework/pom.xml b/source/crypto/crypto-framework/pom.xml new file mode 100644 index 00000000..7816a2b9 --- /dev/null +++ b/source/crypto/crypto-framework/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + + com.jd.blockchain + crypto + 0.8.2.RELEASE + + crypto-framework + + + + com.jd.blockchain + base + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + com.jd.blockchain + utils-serialize + ${project.version} + + + com.jd.blockchain + binary-proto + ${project.version} + + + \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressEncoding.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressEncoding.java new file mode 100644 index 00000000..bc4a08a8 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressEncoding.java @@ -0,0 +1,66 @@ +package com.jd.blockchain.crypto; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.NumberMask; +import com.jd.blockchain.utils.security.RipeMD160Utils; +import com.jd.blockchain.utils.security.ShaUtils; + +public class AddressEncoding { + + /** + * 将区块链地址写入到输出流;
+ * + * 现将地址按 Base58 解码为字节数组,并将字节数组以 {@link BytesEncoding} 的方式写入输出流;
+ * + * 如果指定的地址为 null,则仅写入空字节数组;注:此种情况下,输出流并不是完全没有写入,而是实际上会被写入一个表示内容长度为 0 的头部字节;
+ * + * @param address + * 要写入的区块链地址; + * @param out + * 输出流; + * @return 写入的地址的字节数;如果指定地址为 null,则返回值为写入的头部字节数;; + */ + public static int writeAddress(Bytes address, OutputStream out) { + return address.writeTo(out); + } + + /** + * 从流中读取区块链地址; + * + * @param in + * @return + * @throws IOException + */ + public static Bytes readAddress(InputStream in) throws IOException { + byte[] bytesAddress = BytesEncoding.read(NumberMask.TINY, in); + if (bytesAddress.length == 0) { + return null; + } + return new Bytes(bytesAddress); + } + + /** + * 从公钥生成地址; + * + * @param pubKey + * @return + */ + public static Bytes generateAddress(PubKey pubKey) { + byte[] h1Bytes = ShaUtils.hash_256(pubKey.getRawKeyBytes()); + byte[] h2Bytes = RipeMD160Utils.hash(h1Bytes); + byte[] xBytes = BytesUtils.concat(new byte[]{AddressVersion.V1.CODE, pubKey.getAlgorithm().CODE}, h2Bytes); + byte[] checksum = Arrays.copyOf(ShaUtils.hash_256(ShaUtils.hash_256(xBytes)), 4); + byte[] addressBytes = BytesUtils.concat(xBytes, checksum); + + return new Bytes(addressBytes); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressVersion.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressVersion.java new file mode 100644 index 00000000..dff8877b --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/AddressVersion.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.crypto; + +public enum AddressVersion { + + V1((byte) 0x91); + + public final byte CODE; + + AddressVersion(byte code) { + CODE = code; + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/Ciphertext.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/Ciphertext.java new file mode 100644 index 00000000..98b136dc --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/Ciphertext.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.crypto; + +/** + * 密文; + * + * @author huanghaiquan + * + */ +public interface Ciphertext extends CryptoBytes { + + /** + * 原始的密文数据; + * + * @return + */ + byte[] getRawCiphertext(); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithm.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithm.java new file mode 100644 index 00000000..33a9c562 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithm.java @@ -0,0 +1,150 @@ +package com.jd.blockchain.crypto; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.EnumContract; +import com.jd.blockchain.binaryproto.EnumField; +import com.jd.blockchain.utils.ValueType; + + +@EnumContract(code= TypeCodes.ENUM_TYPE_CRYPTO_ALGORITHM) +public enum CryptoAlgorithm { + + SHA256(CryptoAlgorithmType.HASH, (byte) 0x01, false, false), + + RIPEMD160(CryptoAlgorithmType.HASH, (byte) 0x02, false, false), + + SM3(CryptoAlgorithmType.HASH, (byte) 0x03, false, false), + + JNISHA256(CryptoAlgorithmType.HASH, (byte) 0x04, false, false), + + JNIRIPEMD160(CryptoAlgorithmType.HASH, (byte) 0x05, false, false), + + // 非对称签名/加密算法; + + /** + * RSA 签名算法;可签名,可加密; + */ + RSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x01, true, true), + + /** + * ED25519 签名算法;只用于签名,没有加密特性; + */ + ED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x02, true, false), + + /** + * ECDSA 签名算法;只用于签名,没有加密特性; + */ + ECDSA(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x03, true, false), + + /** + * 国密 SM2 算法;可签名,可加密; + */ + SM2(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x04, true, true), + + /** + * JNIED25519 签名算法;只用于签名,没有加密特性; + */ + JNIED25519(CryptoAlgorithmType.ASYMMETRIC, (byte) 0x05, true, false), + + // 对称加密; + /** + * AES 算法;可加密; + */ + AES(CryptoAlgorithmType.SYMMETRIC, (byte) 0x01, false, true), + + SM4(CryptoAlgorithmType.SYMMETRIC, (byte) 0x02, false, true), + + // 随机性; + /** + * 随机数算法,待定; + */ + JAVA_SECURE(CryptoAlgorithmType.RANDOM, (byte) 0x01, false, false); + + /** + * 密码算法的代号;
+ * 注:只占16位; + */ + @EnumField(type= ValueType.INT8) + public final byte CODE; + + private final boolean signable; + + private final boolean encryptable; + + private CryptoAlgorithm(byte algType, byte algId, boolean signable, boolean encryptable) { + this.CODE = (byte) (algType | algId); + this.signable = signable; + this.encryptable = encryptable; + } + + /** + * 是否属于摘要算法; + * + * @return + */ + public boolean isHash() { + return (CODE & CryptoAlgorithmType.HASH) == CryptoAlgorithmType.HASH; + } + + /** + * 是否属于非对称密码算法; + * + * @return + */ + public boolean isAsymmetric() { + return (CODE & CryptoAlgorithmType.ASYMMETRIC) == CryptoAlgorithmType.ASYMMETRIC; + } + + /** + * 是否属于对称密码算法; + * + * @return + */ + public boolean isSymmetric() { + return (CODE & CryptoAlgorithmType.SYMMETRIC) == CryptoAlgorithmType.SYMMETRIC; + } + + /** + * 是否属于随机数算法; + * + * @return + */ + public boolean isRandom() { + return (CODE & CryptoAlgorithmType.RANDOM) == CryptoAlgorithmType.RANDOM; + } + + /** + * 是否支持签名操作; + * + * @return + */ + public boolean isSignable() { + return signable; + } + + /** + * 是否支持加密操作; + * + * @return + */ + public boolean isEncryptable() { + return encryptable; + } + + /** + * 返回指定编码对应的枚举实例;
+ * + * 如果不存在,则返回 null; + * + * @param code + * @return + */ + public static CryptoAlgorithm valueOf(byte code) { + for (CryptoAlgorithm alg : CryptoAlgorithm.values()) { + if (alg.CODE == code) { + return alg; + } + } + throw new IllegalArgumentException("CryptoAlgorithm doesn't support enum code[" + code + "]!"); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithmType.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithmType.java new file mode 100644 index 00000000..10e38f77 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoAlgorithmType.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.crypto; + +public class CryptoAlgorithmType { + /** + * Hash 类算法的掩码; + */ + public static final byte HASH = 0x10; + + /** + * 非对称加密类算法的掩码; + */ + public static final byte ASYMMETRIC = 0x20; + + /** + * 对称加密类算法的掩码; + */ + public static final byte SYMMETRIC = 0x30; + + /** + * 随机数类算法的掩码; + */ + public static final byte RANDOM = 0x40; + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoBytes.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoBytes.java new file mode 100644 index 00000000..2da141ef --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoBytes.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.crypto; + +import com.jd.blockchain.utils.io.BytesSerializable; + +/** + * {@link CryptoBytes} 表示与特定密码算法相关的编码数据; + * + * @author huanghaiquan + * + */ +public interface CryptoBytes extends BytesSerializable { + + /** + * 算法标识符的长度; + */ + int ALGORYTHM_BYTES = 1; + + /** + * 算法; + * + * @return + */ + CryptoAlgorithm getAlgorithm(); + + /** + * 返回编码后的摘要信息;
+ * + * 这是算法标识 {@link #getAlgorithm()} 与原始的摘要数据 {@link #getRawDigest()} + * 按照特定的编码方式合并后的结果; + */ + @Override + byte[] toBytes(); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoDigest.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoDigest.java new file mode 100644 index 00000000..da7e9e07 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoDigest.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.crypto; + +/** + * 摘要; + * + * @author huanghaiquan + * + */ +public interface CryptoDigest extends CryptoBytes { + + /** + * 原始的摘要数据; + * + * @return + */ + byte[] getRawDigest(); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoException.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoException.java new file mode 100644 index 00000000..f4daf036 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoException.java @@ -0,0 +1,16 @@ +package com.jd.blockchain.crypto; + +public class CryptoException extends RuntimeException { + + private static final long serialVersionUID = 1044893802336696205L; + + + public CryptoException(String message) { + super(message); + } + + public CryptoException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFactory.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFactory.java new file mode 100644 index 00000000..5cf15bbf --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFactory.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.crypto; + +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.hash.HashCryptography; +import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; + +public interface CryptoFactory { + + HashCryptography hashCryptography(); + + AsymmetricCryptography asymmetricCryptography(); + + SymmetricCryptography symmetricCryptography(); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFunction.java new file mode 100644 index 00000000..7c781eea --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoFunction.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.crypto; + +public interface CryptoFunction { + + CryptoAlgorithm getAlgorithm(); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKey.java new file mode 100644 index 00000000..ea73ef25 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKey.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.crypto; + +/** + * 密钥; + * + * @author huanghaiquan + * + */ +public interface CryptoKey extends CryptoBytes { + + /** + * 密钥的类型; + * @return + */ + CryptoKeyType getKeyType(); + + /** + * 原始的密钥数据; + * + * @return + */ + byte[] getRawKeyBytes(); + + +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyPairGenerator.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyPairGenerator.java new file mode 100644 index 00000000..2e3c9da9 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyPairGenerator.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.crypto; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; + +public interface CryptoKeyPairGenerator { + + /** + * 返回密钥对; + */ + CryptoKeyPair generateKeyPair(); + + + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyType.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyType.java new file mode 100644 index 00000000..7c94d2df --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoKeyType.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.crypto; + +public enum CryptoKeyType { + + /** + * 非对称密码算法的公钥 + */ + PUB_KEY((byte)0x01), + + /** + * 非对称密码算法的私钥; + */ + PRIV_KEY((byte)0x02), + + /** + * 对称密码算法的密钥; + */ + SYMMETRIC_KEY((byte)0x03); + + public final byte CODE; + + CryptoKeyType(byte code) { + CODE = code; + } + + public static CryptoKeyType valueOf(byte code) { + for (CryptoKeyType alg : CryptoKeyType.values()) { + if (alg.CODE == code) { + return alg; + } + } + throw new IllegalArgumentException("CryptoKeyType doesn't support enum code[" + code + "]!"); + } + + public byte getCODE() { + return CODE; + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoSymmetricKeyGenerator.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoSymmetricKeyGenerator.java new file mode 100644 index 00000000..baafb599 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoSymmetricKeyGenerator.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.crypto; + +public interface CryptoSymmetricKeyGenerator { + + /** + * 返回对称密钥; + */ + CryptoKey generateSymmetricKey(); +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoUtils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoUtils.java new file mode 100644 index 00000000..4493a681 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/CryptoUtils.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.crypto; + +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.AsymmetricEncryptionFunction; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashCryptography; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.crypto.impl.CryptoFactoryImpl; +import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; +import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction; + +public class CryptoUtils { + + private static CryptoFactory CRYPTO_FACTORY = new CryptoFactoryImpl(); + + public static CryptoFactory crypto() { + return CRYPTO_FACTORY; + } + + + public static HashCryptography hashCrypto() { + return crypto().hashCryptography(); + } + + public static HashFunction hash(CryptoAlgorithm alg) { + return hashCrypto().getFunction(alg); + } + + + public static AsymmetricCryptography asymmCrypto() { + return crypto().asymmetricCryptography(); + } + + public static SignatureFunction sign(CryptoAlgorithm alg) { + return asymmCrypto().getSignatureFunction(alg); + } + + public static AsymmetricEncryptionFunction asymmEncrypt(CryptoAlgorithm alg) { + return asymmCrypto().getAsymmetricEncryptionFunction(alg); + } + + + public static SymmetricCryptography symmCrypto() { + return crypto().symmetricCryptography(); + } + + public static SymmetricEncryptionFunction symmEncrypt(CryptoAlgorithm alg) { + return symmCrypto().getSymmetricEncryptionFunction(alg); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCiphertext.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCiphertext.java new file mode 100644 index 00000000..f28d3d83 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCiphertext.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.base.BaseCryptoBytes; + +public class AsymmetricCiphertext extends BaseCryptoBytes implements Ciphertext { + + public AsymmetricCiphertext(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + super(algorithm, rawCryptoBytes); + } + + public AsymmetricCiphertext(byte[] cryptoBytes) { + super(cryptoBytes); + } + + @Override + protected boolean support(CryptoAlgorithm algorithm) { + return algorithm.isAsymmetric() && algorithm.isEncryptable(); + } + + @Override + public byte[] getRawCiphertext() { + return getRawCryptoBytes().getBytesCopy(); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCryptography.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCryptography.java new file mode 100644 index 00000000..475990f9 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricCryptography.java @@ -0,0 +1,72 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; + +public interface AsymmetricCryptography { + + /** + * 生成密钥对; + * + * @param algorithm + * @return + */ + CryptoKeyPair generateKeyPair(CryptoAlgorithm algorithm); + + /** + * 获取签名方法; + * + * @param algorithm + * @return + */ + SignatureFunction getSignatureFunction(CryptoAlgorithm algorithm); + + /** + * 校验签名摘要和数据是否一致; + * + * @param digestBytes 签名摘要数据 + * @param pubKeyBytes 公钥数据 + * @param data 被签名数据 + * @return + */ + boolean verify(byte[] digestBytes, byte[] pubKeyBytes, byte[] data); + + /** + * 获取非对称加密方法; + * + * @param algorithm + * @return + */ + AsymmetricEncryptionFunction getAsymmetricEncryptionFunction(CryptoAlgorithm algorithm); + + /** + * 解密; + * + * @param privKeyBytes + * @param ciphertextBytes + * @return + */ + byte[] decrypt(byte[] privKeyBytes, byte[] ciphertextBytes); + + + Ciphertext resolveCiphertext(byte[] ciphertextBytes); + + Ciphertext tryResolveCiphertext(byte[] ciphertextBytes); + + /** + * @param digestBytes 待解析签名摘要 + * @return + */ + SignatureDigest resolveSignatureDigest(byte[] digestBytes); + + SignatureDigest tryResolveSignatureDigest(byte[] digestBytes); + + PubKey resolvePubKey(byte[] pubKeyBytes); + + PubKey tryResolvePubKey(byte[] pubKeyBytes); + + PrivKey resolvePrivKey(byte[] privKeyBytes); + + PrivKey tryResolvePrivKey(byte[] privKeyBytes); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricEncryptionFunction.java new file mode 100644 index 00000000..8357d54e --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/AsymmetricEncryptionFunction.java @@ -0,0 +1,74 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoFunction; +import com.jd.blockchain.crypto.CryptoKeyPairGenerator; + +public interface AsymmetricEncryptionFunction extends CryptoKeyPairGenerator, CryptoFunction { + + /** + * 加密; + * + * @param data + * @return + */ + Ciphertext encrypt(PubKey pubKey, byte[] data); + + /** + * 解密; + * + * @param privKey + * @param ciphertext + * @return + */ + byte[] decrypt(PrivKey privKey, Ciphertext ciphertext); + + /** + * 校验私钥格式是否满足要求; + * + * @param privKeyBytes 包含算法标识、密钥掩码和私钥的字节数组 + * @return 是否满足指定算法的私钥格式 + */ + boolean supportPrivKey(byte[] privKeyBytes); + + /** + * 将字节数组形式的私钥转换成PrivKey格式; + * + * @param privKeyBytes 包含算法标识和私钥的字节数组 + * @return PrivKey形式的私钥 + */ + PrivKey resolvePrivKey(byte[] privKeyBytes); + + /** + * 校验公钥格式是否满足要求; + * + * @param pubKeyBytes 包含算法标识、密钥掩码和公钥的字节数组 + * @return 是否满足指定算法的公钥格式 + */ + boolean supportPubKey(byte[] pubKeyBytes); + + /** + * 将字节数组形式的密钥转换成PubKey格式; + * + * @param pubKeyBytes 包含算法标识和公钥的字节数组 + * @return PubKey形式的公钥 + */ + PubKey resolvePubKey(byte[] pubKeyBytes); + + /** + * 校验密文格式是否满足要求; + * + * @param ciphertextBytes 包含算法标识和密文的字节数组 + * @return 是否满足指定算法的密文格式 + */ + boolean supportCiphertext(byte[] ciphertextBytes); + + /** + * 将字节数组形式的密文转换成AsymmetricCiphertext格式; + * + * @param ciphertextBytes 包含算法标识和密文的字节数组 + * @return AsymmetricCiphertext形式的签名摘要 + */ + AsymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/CryptoKeyPair.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/CryptoKeyPair.java new file mode 100644 index 00000000..1dd023b2 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/CryptoKeyPair.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.crypto.asymmetric; + +public class CryptoKeyPair { + + private PubKey pubKey; + + private PrivKey privKey; + + public PubKey getPubKey() { + return pubKey; + } + + public PrivKey getPrivKey() { + return privKey; + } + + public CryptoKeyPair(PubKey pubKey, PrivKey privKey) { + this.pubKey = pubKey; + this.privKey = privKey; + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PrivKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PrivKey.java new file mode 100644 index 00000000..b756b9f2 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PrivKey.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKeyType; +import com.jd.blockchain.crypto.base.BaseCryptoKey; + +/** + * 私钥; + * + * @author huanghaiquan + * + */ +public class PrivKey extends BaseCryptoKey { + private static final long serialVersionUID = 6265440395252295646L; + + public PrivKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + super(algorithm, rawCryptoBytes, CryptoKeyType.PRIV_KEY); + } + + public PrivKey(byte[] cryptoBytes) { + super(cryptoBytes); + } + + @Override + protected boolean support(CryptoKeyType keyType) { + return CryptoKeyType.PRIV_KEY == keyType; + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PubKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PubKey.java new file mode 100644 index 00000000..328b0751 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/PubKey.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKeyType; +import com.jd.blockchain.crypto.base.BaseCryptoKey; + +/** + * 公钥; + * + * @author huanghaiquan + * + */ +public class PubKey extends BaseCryptoKey { + + private static final long serialVersionUID = -2055071197736385328L; + + public PubKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + super(algorithm, rawCryptoBytes, CryptoKeyType.PUB_KEY); + } + public PubKey() { + super(); + } + + public PubKey(byte[] cryptoBytes) { + super(cryptoBytes); + } + + @Override + protected boolean support(CryptoKeyType keyType) { + return CryptoKeyType.PUB_KEY == keyType; + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureDigest.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureDigest.java new file mode 100644 index 00000000..9e6e17de --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureDigest.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoDigest; +import com.jd.blockchain.crypto.base.BaseCryptoBytes; + +public class SignatureDigest extends BaseCryptoBytes implements CryptoDigest { + public SignatureDigest() { + super(); + } + + public SignatureDigest(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + super(algorithm, rawCryptoBytes); + } + + public SignatureDigest(byte[] cryptoBytes) { + super(cryptoBytes); + } + + @Override + protected boolean support(CryptoAlgorithm algorithm) { + return algorithm.isAsymmetric() && algorithm.isSignable(); + } + + /** + * 返回原始签名摘要; + * + * @return + */ + @Override + public byte[] getRawDigest() { + return getRawCryptoBytes().getBytesCopy(); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureFunction.java new file mode 100644 index 00000000..cdfdc9bd --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/asymmetric/SignatureFunction.java @@ -0,0 +1,73 @@ +package com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.CryptoFunction; +import com.jd.blockchain.crypto.CryptoKeyPairGenerator; + +public interface SignatureFunction extends CryptoKeyPairGenerator, CryptoFunction { + + /** + * 计算指定数据的 hash; + * + * @param data 被签名消息 + * @return SignatureDigest形式的签名摘要 + */ + SignatureDigest sign(PrivKey privKey, byte[] data); + + /** + * 校验签名摘要和数据是否一致; + * + * @param digest 待验证的签名摘要 + * @param data 被签名信息 + * @return 是否验证通过 + */ + boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data); + + /** + * 校验私钥格式是否满足要求; + * + * @param privKeyBytes 包含算法标识、密钥掩码和私钥的字节数组 + * @return 是否满足指定算法的私钥格式 + */ + boolean supportPrivKey(byte[] privKeyBytes); + + /** + * 将字节数组形式的私钥转换成PrivKey格式; + * + * @param privKeyBytes 包含算法标识、密钥掩码和私钥的字节数组 + * @return PrivKey形式的私钥 + */ + PrivKey resolvePrivKey(byte[] privKeyBytes); + + /** + * 校验公钥格式是否满足要求; + * + * @param pubKeyBytes 包含算法标识、密钥掩码和公钥的字节数组 + * @return 是否满足指定算法的公钥格式 + */ + boolean supportPubKey(byte[] pubKeyBytes); + + /** + * 将字节数组形式的密钥转换成PubKey格式; + * + * @param pubKeyBytes 包含算法标识、密钥掩码和公钥的字节数组 + * @return PubKey形式的公钥 + */ + PubKey resolvePubKey(byte[] pubKeyBytes); + + /** + * 校验字节数组形式的签名摘要的格式是否满足要求; + * + * @param digestBytes 包含算法标识和签名摘要的字节数组 + * @return 是否满足指定算法的签名摘要格式 + */ + + boolean supportDigest(byte[] digestBytes); + + /** + * 将字节数组形式的签名摘要转换成SignatureDigest格式; + * + * @param digestBytes 包含算法标识和签名摘要的字节数组 + * @return SignatureDigest形式的签名摘要 + */ + SignatureDigest resolveDigest(byte[] digestBytes); +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoBytes.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoBytes.java new file mode 100644 index 00000000..3fc2ac70 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoBytes.java @@ -0,0 +1,56 @@ +package com.jd.blockchain.crypto.base; + +import java.util.Arrays; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoBytes; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public abstract class BaseCryptoBytes extends Bytes implements CryptoBytes { + + private CryptoAlgorithm algorithm; + + public BaseCryptoBytes() { + super(); + } + + public BaseCryptoBytes(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + super(encodeBytes(algorithm, rawCryptoBytes)); + this.algorithm = algorithm; + } + + public BaseCryptoBytes(byte[] cryptoBytes) { + super(cryptoBytes); + CryptoAlgorithm algorithm = decodeAlgorithm(cryptoBytes); + if (!support(algorithm)) { + throw new IllegalArgumentException("Not supported algorithm[" + algorithm.toString() + "]!"); + } + this.algorithm = algorithm; + } + + static byte[] encodeBytes(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + return BytesUtils.concat(new byte[] { algorithm.CODE }, rawCryptoBytes); + } + + static CryptoAlgorithm decodeAlgorithm(byte[] cryptoBytes) { + return CryptoAlgorithm.valueOf(cryptoBytes[0]); + } + + protected abstract boolean support(CryptoAlgorithm algorithm); + + protected byte[] resolveRawCryptoBytes(byte[] cryptoBytes) { + return Arrays.copyOfRange(cryptoBytes, 1, cryptoBytes.length); + } + + public CryptoAlgorithm getAlgorithm() { + // return resolveAlgorithm(encodedBytes); + return algorithm; + } + + protected BytesSlice getRawCryptoBytes() { + // return resolveRawCryptoBytes(encodedBytes); + return new BytesSlice(getDirectBytes(), 1); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoKey.java new file mode 100644 index 00000000..de6eeabf --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/base/BaseCryptoKey.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.crypto.base; + +import java.io.Serializable; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoException; +import com.jd.blockchain.crypto.CryptoKey; +import com.jd.blockchain.crypto.CryptoKeyType; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.io.BytesUtils; + +public abstract class BaseCryptoKey extends BaseCryptoBytes implements CryptoKey,Serializable { + + public static final int KEY_TYPE_BYTES = 1; + private static final long serialVersionUID = 4543074827807908363L; + + private CryptoKeyType keyType; + + public BaseCryptoKey() { + super(); + } + + public BaseCryptoKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes, CryptoKeyType keyType) { + super(algorithm, encodeKeyBytes(rawCryptoBytes, keyType)); + this.keyType = keyType; + } + + public BaseCryptoKey(byte[] cryptoBytes) { + super(cryptoBytes); + CryptoKeyType keyType = decodeKeyType(getRawCryptoBytes()); + if (!support(keyType)) { + throw new CryptoException("CryptoKey doesn't support keyType[" + keyType + "]!"); + } + this.keyType = keyType; + } + + @Override + public CryptoKeyType getKeyType() { + return keyType; + } + + private static byte[] encodeKeyBytes(byte[] rawCryptoBytes, CryptoKeyType keyType ) { + return BytesUtils.concat(new byte[] {keyType.CODE }, rawCryptoBytes); + } + + private static CryptoKeyType decodeKeyType(BytesSlice cryptoBytes) { + return CryptoKeyType.valueOf(cryptoBytes.getByte()); + } + + protected abstract boolean support(CryptoKeyType keyType); + + @Override + protected boolean support(CryptoAlgorithm algorithm) { + return algorithm.isSymmetric() || algorithm.isAsymmetric(); + } + + + @Override + public byte[] getRawKeyBytes() { + return getRawCryptoBytes().getBytesCopy(1); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashCryptography.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashCryptography.java new file mode 100644 index 00000000..52c8240d --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashCryptography.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.crypto.hash; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoException; + +public interface HashCryptography { + + /** + * return HashFunction instance of the specified hash alg; + * + * + * if alg out of hash alg,then throws {@link IllegalArgumentException} + * + * @param algorithm + * @return + */ + HashFunction getFunction(CryptoAlgorithm algorithm); + + /** + * 校验 hash 摘要与指定的数据是否匹配; + * + * @param digestBytes + * @param data + * @return + */ + boolean verify(byte[] digestBytes, byte[] data); + + boolean verify(HashDigest digest, byte[] data); + + /** + * 解析指定的 hash 摘要;
+ * + * 如果不符合哈希摘要的编码格式,则引发 {@link CryptoException} 异常; + * + * @param digestBytes + * @return + */ + HashDigest resolveHashDigest(byte[] digestBytes); + + /** + * 解析指定的 hash 摘要;
+ * + * 如果不符合哈希摘要的编码格式,则返回 null; + * + * @param digestBytes + * @return + */ + HashDigest tryResolveHashDigest(byte[] digestBytes); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashDigest.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashDigest.java new file mode 100644 index 00000000..e19ff3fb --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashDigest.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.crypto.hash; + +import java.io.Serializable; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoDigest; +import com.jd.blockchain.crypto.base.BaseCryptoBytes; + +public class HashDigest extends BaseCryptoBytes implements CryptoDigest,Serializable { + + private static final long serialVersionUID = 693895170514236428L; + + public HashDigest(CryptoAlgorithm algorithm, byte[] rawDigestBytes) { + super(algorithm, rawDigestBytes); + } + + public HashDigest() { + super(); + } + + public HashDigest(byte[] encodedDigestBytes) { + super(encodedDigestBytes); + } + + @Override + protected boolean support(CryptoAlgorithm algorithm) { + return algorithm.isHash(); + } + + @Override + public byte[] getRawDigest() { + return getRawCryptoBytes().getBytesCopy(); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashFunction.java new file mode 100644 index 00000000..c5bec189 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/hash/HashFunction.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.crypto.hash; + +import com.jd.blockchain.crypto.CryptoFunction; + +public interface HashFunction extends CryptoFunction { + + /** + * 计算指定数据的 hash; + * + * @param data + * @return + */ + HashDigest hash(byte[] data); + + + /** + * 校验 hash 摘要与指定的数据是否匹配; + * + * @param digest + * @param data + * @return + */ + boolean verify(HashDigest digest, byte[] data); + + /** + * 校验字节数组形式的hash摘要的格式是否满足要求; + * + * @param digestBytes 包含算法标识和hash摘要的字节数组 + * @return 是否满足指定算法的hash摘要格式 + */ + boolean supportHashDigest(byte[] digestBytes); + + /** + * 将字节数组形式的hash摘要转换成HashDigest格式; + * + * @param digestBytes 包含算法标识和hash摘要的字节数组 + * @return HashDigest形式的hash摘要 + */ + HashDigest resolveHashDigest(byte[] digestBytes); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/AsymmtricCryptographyImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/AsymmtricCryptographyImpl.java new file mode 100644 index 00000000..2d18626c --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/AsymmtricCryptographyImpl.java @@ -0,0 +1,194 @@ +package com.jd.blockchain.crypto.impl; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.crypto.impl.def.asymmetric.ED25519SignatureFunction; +import com.jd.blockchain.crypto.impl.jni.asymmetric.JNIED25519SignatureFunction; +import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction; + +public class AsymmtricCryptographyImpl implements AsymmetricCryptography { + + private static final SignatureFunction ED25519_SIGF = new ED25519SignatureFunction(); + + private static final SignatureFunction SM2_SIGF = new SM2CryptoFunction(); + + private static final SignatureFunction JNIED25519_SIGF = new JNIED25519SignatureFunction(); + + private static final AsymmetricEncryptionFunction SM2_ENCF = new SM2CryptoFunction(); + + /** + * 封装了非对称密码算法对应的密钥生成算法 + */ + @Override + public CryptoKeyPair generateKeyPair(CryptoAlgorithm algorithm) { + + //判断算法是签名算法还是非对称加密算法,并根据算法生成密钥对,否则抛出异常 + if (algorithm.isSignable() && algorithm.isAsymmetric()){ + return getSignatureFunction(algorithm).generateKeyPair(); + } + else if (algorithm.isEncryptable() && algorithm.isAsymmetric()){ + return getAsymmetricEncryptionFunction(algorithm).generateKeyPair(); + } + else throw new IllegalArgumentException("The specified algorithm is not signature or asymmetric encryption algorithm!"); + } + + @Override + public SignatureFunction getSignatureFunction(CryptoAlgorithm algorithm) { + //遍历签名算法,如果满足,则返回实例 + switch (algorithm) { + case ED25519: + return ED25519_SIGF; + case SM2: + return SM2_SIGF; + case JNIED25519: + return JNIED25519_SIGF; + default: + break; + } + throw new IllegalArgumentException("The specified algorithm is not signature algorithm!"); + } + + @Override + public boolean verify(byte[] digestBytes, byte[] pubKeyBytes, byte[] data) { + + //得到SignatureDigest类型的签名摘要,并得到算法标识 + SignatureDigest signatureDigest = resolveSignatureDigest(digestBytes); + CryptoAlgorithm algorithm = signatureDigest.getAlgorithm(); + PubKey pubKey = resolvePubKey(pubKeyBytes); + + //验证两个输入中算法标识一致,否则抛出异常 + if (algorithm != signatureDigest.getAlgorithm()) + throw new IllegalArgumentException("Digest's algorithm and key's are not matching!"); + + //根据算法标识,调用对应算法实例来验证签名摘要 + return getSignatureFunction(algorithm).verify(signatureDigest,pubKey,data); + } + + @Override + public AsymmetricEncryptionFunction getAsymmetricEncryptionFunction(CryptoAlgorithm algorithm) { + //遍历非对称加密算法,如果满足,则返回实例 + switch (algorithm) { + case SM2: + return SM2_ENCF; + default: + break; + } + throw new IllegalArgumentException("The specified algorithm is not asymmetric encryption algorithm!"); + } + + @Override + public byte[] decrypt(byte[] privKeyBytes, byte[] ciphertextBytes) { + + //分别得到PrivKey和Ciphertext类型的密钥和密文,以及privKey对应的算法 + PrivKey privKey = resolvePrivKey(privKeyBytes); + Ciphertext ciphertext = resolveCiphertext(ciphertextBytes); + CryptoAlgorithm algorithm = privKey.getAlgorithm(); + + //验证两个输入中算法标识一致,否则抛出异常 + if (algorithm != ciphertext.getAlgorithm()) + throw new IllegalArgumentException("Ciphertext's algorithm and key's are not matching!"); + + //根据算法标识,调用对应算法实例来计算返回明文 + return getAsymmetricEncryptionFunction(algorithm).decrypt(privKey,ciphertext); + } + + @Override + public Ciphertext resolveCiphertext(byte[] ciphertextBytes) { + Ciphertext ciphertext = tryResolveCiphertext(ciphertextBytes); + if (ciphertext == null) + throw new IllegalArgumentException("This ciphertextBytes cannot be resolved!"); + else return ciphertext; + } + + @Override + public Ciphertext tryResolveCiphertext(byte[] ciphertextBytes) { + //遍历非对称加密算法,如果满足,则返回解析结果 + if (SM2_ENCF.supportCiphertext(ciphertextBytes)){ + return SM2_ENCF.resolveCiphertext(ciphertextBytes); + } + //否则返回null + return null; + } + + @Override + public SignatureDigest resolveSignatureDigest(byte[] digestBytes) { + SignatureDigest signatureDigest = tryResolveSignatureDigest(digestBytes); + if (signatureDigest == null) + throw new IllegalArgumentException("This digestBytes cannot be resolved!"); + else return signatureDigest; + } + + @Override + public SignatureDigest tryResolveSignatureDigest(byte[] digestBytes) { + //遍历签名算法,如果满足,则返回解析结果 + if (ED25519_SIGF.supportDigest(digestBytes)){ + return ED25519_SIGF.resolveDigest(digestBytes); + } + if (SM2_SIGF.supportDigest(digestBytes)){ + return SM2_SIGF.resolveDigest(digestBytes); + } + if (JNIED25519_SIGF.supportDigest(digestBytes)){ + return JNIED25519_SIGF.resolveDigest(digestBytes); + } + //否则返回null + return null; + } + + @Override + public PubKey resolvePubKey(byte[] pubKeyBytes) { + PubKey pubKey = tryResolvePubKey(pubKeyBytes); + if (pubKey == null) + throw new IllegalArgumentException("This pubKeyBytes cannot be resolved!"); + else return pubKey; + + } + + @Override + public PubKey tryResolvePubKey(byte[] pubKeyBytes) { + //遍历签名算法,如果满足,则返回解析结果 + if (ED25519_SIGF.supportPubKey(pubKeyBytes)){ + return ED25519_SIGF.resolvePubKey(pubKeyBytes); + } + if (SM2_SIGF.supportPubKey(pubKeyBytes)){ + return SM2_SIGF.resolvePubKey(pubKeyBytes); + } + if (JNIED25519_SIGF.supportPubKey(pubKeyBytes)){ + return JNIED25519_SIGF.resolvePubKey(pubKeyBytes); + } + //遍历非对称加密算法,如果满足,则返回解析结果 + if (SM2_ENCF.supportPubKey(pubKeyBytes)){ + return SM2_ENCF.resolvePubKey(pubKeyBytes); + } + //否则返回null + return null; + } + + @Override + public PrivKey resolvePrivKey(byte[] privKeyBytes) { + PrivKey privKey = tryResolvePrivKey(privKeyBytes); + if (privKey == null) + throw new IllegalArgumentException("This privKeyBytes cannot be resolved!"); + else return privKey; + } + + @Override + public PrivKey tryResolvePrivKey(byte[] privKeyBytes) { + //遍历签名算法,如果满足,则返回解析结果 + if (ED25519_SIGF.supportPrivKey(privKeyBytes)){ + return ED25519_SIGF.resolvePrivKey(privKeyBytes); + } + if (SM2_SIGF.supportPrivKey(privKeyBytes)){ + return SM2_SIGF.resolvePrivKey(privKeyBytes); + } + if (JNIED25519_SIGF.supportPrivKey(privKeyBytes)){ + return JNIED25519_SIGF.resolvePrivKey(privKeyBytes); + } + //遍历非对称加密算法,如果满足,则返回解析结果 + if (SM2_ENCF.supportPrivKey(privKeyBytes)){ + return SM2_ENCF.resolvePrivKey(privKeyBytes); + } + //否则返回null + return null; + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/CryptoFactoryImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/CryptoFactoryImpl.java new file mode 100644 index 00000000..90a6d719 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/CryptoFactoryImpl.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.crypto.impl; + +import com.jd.blockchain.crypto.CryptoFactory; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.hash.HashCryptography; +import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; + +public class CryptoFactoryImpl implements CryptoFactory { + + //Field; + private static HashCryptography hashCryptography = new HashCryptographyImpl(); + private static AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + private static SymmetricCryptography symmetricCryptography = new SymmetricCryptographyImpl(); + + @Override + public HashCryptography hashCryptography() { + return hashCryptography; + } + + @Override + public AsymmetricCryptography asymmetricCryptography() { + return asymmetricCryptography; + } + + @Override + public SymmetricCryptography symmetricCryptography() { + return symmetricCryptography; + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/HashCryptographyImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/HashCryptographyImpl.java new file mode 100644 index 00000000..ae1772c5 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/HashCryptographyImpl.java @@ -0,0 +1,84 @@ +package com.jd.blockchain.crypto.impl; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashCryptography; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.crypto.impl.def.hash.RIPEMD160HashFunction; +import com.jd.blockchain.crypto.impl.def.hash.SHA256HashFunction; +import com.jd.blockchain.crypto.impl.jni.hash.JNIRIPEMD160HashFunction; +import com.jd.blockchain.crypto.impl.jni.hash.JNISHA256HashFunction; +import com.jd.blockchain.crypto.impl.sm.hash.SM3HashFunction; + +public class HashCryptographyImpl implements HashCryptography { + + private static final HashFunction SHA256_FUNC = new SHA256HashFunction(); + private static final HashFunction RIPEMD160_FUNC = new RIPEMD160HashFunction(); + private static final HashFunction SM3_FUNC = new SM3HashFunction(); + + private static final HashFunction JNISHA256_FUNC = new JNISHA256HashFunction(); + private static final HashFunction JNIRIPEMD160_FUNC = new JNIRIPEMD160HashFunction(); + + @Override + public HashFunction getFunction(CryptoAlgorithm algorithm) { + + // 遍历哈希算法,如果满足,则返回实例 + switch (algorithm) { + case SHA256: + return SHA256_FUNC; + case RIPEMD160: + return RIPEMD160_FUNC; + case SM3: + return SM3_FUNC; + case JNISHA256: + return JNISHA256_FUNC; + case JNIRIPEMD160: + return JNIRIPEMD160_FUNC; + default: + break; + } + throw new IllegalArgumentException("The specified algorithm is not hash algorithm!"); + } + + @Override + public boolean verify(byte[] digestBytes, byte[] data) { + HashDigest hashDigest = resolveHashDigest(digestBytes); + return verify(hashDigest,data); + } + + @Override + public boolean verify(HashDigest digest, byte[] data) { + CryptoAlgorithm algorithm = digest.getAlgorithm(); + return getFunction(algorithm).verify(digest, data); + } + + @Override + public HashDigest resolveHashDigest(byte[] digestBytes) { + HashDigest hashDigest = tryResolveHashDigest(digestBytes); + if (hashDigest == null) + throw new IllegalArgumentException("This digestBytes cannot be resolved!"); + else return hashDigest; + } + + @Override + public HashDigest tryResolveHashDigest(byte[] digestBytes) { + //遍历哈希函数,如果满足,则返回解析结果 + if (SHA256_FUNC.supportHashDigest(digestBytes)) { + return SHA256_FUNC.resolveHashDigest(digestBytes); + } + if (RIPEMD160_FUNC.supportHashDigest(digestBytes)) { + return RIPEMD160_FUNC.resolveHashDigest(digestBytes); + } + if (SM3_FUNC.supportHashDigest(digestBytes)) { + return SM3_FUNC.resolveHashDigest(digestBytes); + } + if (JNISHA256_FUNC.supportHashDigest(digestBytes)) { + return JNISHA256_FUNC.resolveHashDigest(digestBytes); + } + if (JNIRIPEMD160_FUNC.supportHashDigest(digestBytes)) { + return JNIRIPEMD160_FUNC.resolveHashDigest(digestBytes); + } + //否则返回null + return null; + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/SymmetricCryptographyImpl.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/SymmetricCryptographyImpl.java new file mode 100644 index 00000000..ce787511 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/SymmetricCryptographyImpl.java @@ -0,0 +1,101 @@ +package com.jd.blockchain.crypto.impl; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.impl.def.symmetric.AESSymmetricEncryptionFunction; +import com.jd.blockchain.crypto.impl.sm.symmetric.SM4SymmetricEncryptionFunction; +import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; +import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction; +import com.jd.blockchain.crypto.symmetric.SymmetricKey; + +public class SymmetricCryptographyImpl implements SymmetricCryptography { + + private static final SymmetricEncryptionFunction AES_ENCF = new AESSymmetricEncryptionFunction(); + private static final SymmetricEncryptionFunction SM4_ENCF = new SM4SymmetricEncryptionFunction(); + + /** + * 封装了对称密码算法对应的密钥生成算法 + */ + @Override + public SymmetricKey generateKey(CryptoAlgorithm algorithm) { + + //验证算法标识是对称加密算法,并根据算法生成对称密钥,否则抛出异常 + if (algorithm.isEncryptable() && algorithm.isSymmetric() ){ + return (SymmetricKey) getSymmetricEncryptionFunction(algorithm).generateSymmetricKey(); + } + else throw new IllegalArgumentException("The specified algorithm is not symmetric encryption algorithm!"); + } + + @Override + public SymmetricEncryptionFunction getSymmetricEncryptionFunction(CryptoAlgorithm algorithm) { + + // 遍历对称加密算法,如果满足,则返回实例 + switch (algorithm) { + case AES: + return AES_ENCF; + case SM4: + return SM4_ENCF; + default: + break; + } + throw new IllegalArgumentException("The specified algorithm is not symmetric encryption algorithm!"); + } + + @Override + public byte[] decrypt(byte[] symmetricKeyBytes, byte[] ciphertextBytes) { + + //分别得到SymmetricKey和Ciphertext类型的密钥和密文,以及symmetricKey对应的算法 + SymmetricKey symmetricKey = resolveSymmetricKey(symmetricKeyBytes); + Ciphertext ciphertext = resolveCiphertext(ciphertextBytes); + CryptoAlgorithm algorithm = symmetricKey.getAlgorithm(); + + //验证两个输入中算法标识一致,否则抛出异常 + if (algorithm != ciphertext.getAlgorithm()) + throw new IllegalArgumentException("Ciphertext's algorithm and key's are not matching!"); + + //根据算法标识,调用对应算法实例来计算返回明文 + return getSymmetricEncryptionFunction(algorithm).decrypt(symmetricKey,ciphertext); + } + + @Override + public Ciphertext resolveCiphertext(byte[] ciphertextBytes) { + Ciphertext ciphertext = tryResolveCiphertext(ciphertextBytes); + if (ciphertext == null) + throw new IllegalArgumentException("This ciphertextBytes cannot be resolved!"); + else return ciphertext; + } + + @Override + public Ciphertext tryResolveCiphertext(byte[] ciphertextBytes) { + //遍历对称加密算法,如果满足,则返回解析结果 + if (AES_ENCF.supportCiphertext(ciphertextBytes)) { + return AES_ENCF.resolveCiphertext(ciphertextBytes); + } + if (SM4_ENCF.supportCiphertext(ciphertextBytes)) { + return SM4_ENCF.resolveCiphertext(ciphertextBytes); + } + //否则返回null + return null; + } + + @Override + public SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes) { + SymmetricKey symmetricKey = tryResolveSymmetricKey(symmetricKeyBytes); + if (symmetricKey == null) + throw new IllegalArgumentException("This symmetricKeyBytes cannot be resolved!"); + else return symmetricKey; + } + + @Override + public SymmetricKey tryResolveSymmetricKey(byte[] symmetricKeyBytes) { + //遍历对称加密算法,如果满足,则返回解析结果 + if(AES_ENCF.supportSymmetricKey(symmetricKeyBytes)) { + return AES_ENCF.resolveSymmetricKey(symmetricKeyBytes); + } + if(SM4_ENCF.supportSymmetricKey(symmetricKeyBytes)) { + return SM4_ENCF.resolveSymmetricKey(symmetricKeyBytes); + } + //否则返回null + return null; + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ECDSASignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ECDSASignatureFunction.java new file mode 100644 index 00000000..7cacb1fe --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ECDSASignatureFunction.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.crypto.impl.def.asymmetric; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.*; + +public class ECDSASignatureFunction implements SignatureFunction { + + @Override + public SignatureDigest sign(PrivKey privKey, byte[] data) { + return null; + } + + @Override + public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) { + return false; + } + + @Override + public boolean supportPrivKey(byte[] privKeyBytes) { + return false; + } + + @Override + public PrivKey resolvePrivKey(byte[] privKeyBytes) { + return null; + } + + @Override + public boolean supportPubKey(byte[] pubKeyBytes) { + return false; + } + + @Override + public PubKey resolvePubKey(byte[] pubKeyBytes) { + return null; + } + + @Override + public boolean supportDigest(byte[] digestBytes) { + return false; + } + + @Override + public SignatureDigest resolveDigest(byte[] digestBytes) { + return null; + } + + @Override + public CryptoAlgorithm getAlgorithm() { + return CryptoAlgorithm.ECDSA; + } + + @Override + public CryptoKeyPair generateKeyPair() { + return null; + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ED25519SignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ED25519SignatureFunction.java new file mode 100644 index 00000000..0a28b3fc --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/asymmetric/ED25519SignatureFunction.java @@ -0,0 +1,123 @@ +package com.jd.blockchain.crypto.impl.def.asymmetric; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.utils.security.Ed25519Utils; + +import net.i2p.crypto.eddsa.EdDSAPrivateKey; +import net.i2p.crypto.eddsa.EdDSAPublicKey; +import net.i2p.crypto.eddsa.KeyPairGenerator; + +import java.security.KeyPair; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.ED25519; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; +import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY; +import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY; +import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES; + +public class ED25519SignatureFunction implements SignatureFunction { + + private static final int PUBKEY_SIZE = 32; + private static final int PRIVKEY_SIZE = 32; + private static final int DIGEST_SIZE = 64; + + private static final int PUBKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PUBKEY_SIZE; + private static final int PRIVKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PRIVKEY_SIZE; + private static final int SIGNATUREDIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_SIZE; + + public ED25519SignatureFunction() { + } + + @Override + public SignatureDigest sign(PrivKey privKey, byte[] data) { + + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + + // 验证原始私钥长度为256比特,即32字节 + if (rawPrivKeyBytes.length != PRIVKEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应ED25519签名算法 + if (privKey.getAlgorithm() != ED25519) + throw new IllegalArgumentException("This key is not ED25519 private key!"); + + // 调用ED25519签名算法计算签名结果 + return new SignatureDigest(ED25519, Ed25519Utils.sign_512(data, rawPrivKeyBytes)); + } + + @Override + public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) { + + byte[] rawPubKeyBytes = pubKey.getRawKeyBytes(); + byte[] rawDigestBytes = digest.getRawDigest(); + + // 验证原始公钥长度为256比特,即32字节 + if (rawPubKeyBytes.length != PUBKEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应ED25519签名算法 + if (pubKey.getAlgorithm() != ED25519) + throw new IllegalArgumentException("This key is not ED25519 public key!"); + + // 验证密文数据的算法标识对应ED25519签名算法,并且原始摘要长度为64字节 + if (digest.getAlgorithm() != ED25519 || rawDigestBytes.length != DIGEST_SIZE) + throw new IllegalArgumentException("This is not ED25519 signature digest!"); + + // 调用ED25519验签算法验证签名结果 + return Ed25519Utils.verify(data, rawPubKeyBytes, rawDigestBytes); + } + + @Override + public boolean supportPrivKey(byte[] privKeyBytes) { + // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应ED25519签名算法,并且密钥类型是私钥 + return privKeyBytes.length == PRIVKEY_LENGTH && privKeyBytes[0] == ED25519.CODE && privKeyBytes[1] == PRIV_KEY.CODE; + } + + @Override + public PrivKey resolvePrivKey(byte[] privKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new PrivKey(privKeyBytes); + } + + @Override + public boolean supportPubKey(byte[] pubKeyBytes) { + // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应ED25519签名算法,并且密钥类型是公钥 + return pubKeyBytes.length == PUBKEY_LENGTH && pubKeyBytes[0] == ED25519.CODE && pubKeyBytes[1] == PUB_KEY.CODE; + + } + + @Override + public PubKey resolvePubKey(byte[] pubKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new PubKey(pubKeyBytes); + } + + @Override + public boolean supportDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应ED25519算法 + return digestBytes.length == SIGNATUREDIGEST_LENGTH && digestBytes[0] == ED25519.CODE; + } + + @Override + public SignatureDigest resolveDigest(byte[] digestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SignatureDigest(digestBytes); + } + + @Override + public CryptoAlgorithm getAlgorithm() { + return ED25519; + } + + @Override + public CryptoKeyPair generateKeyPair() { + // 调用ED25519算法的密钥生成算法生成公私钥对priKey和pubKey,返回密钥对 + KeyPairGenerator keyPairGenerator = new KeyPairGenerator(); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + EdDSAPrivateKey privKey = (EdDSAPrivateKey) keyPair.getPrivate(); + EdDSAPublicKey pubKey = (EdDSAPublicKey) keyPair.getPublic(); + return new CryptoKeyPair(new PubKey(ED25519, pubKey.getAbyte()), new PrivKey(ED25519, privKey.getSeed())); + + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/RIPEMD160HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/RIPEMD160HashFunction.java new file mode 100644 index 00000000..7933021b --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/RIPEMD160HashFunction.java @@ -0,0 +1,47 @@ +package com.jd.blockchain.crypto.impl.def.hash; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.RIPEMD160; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; + +import java.util.Arrays; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.utils.security.RipeMD160Utils; + +public class RIPEMD160HashFunction implements HashFunction { + + private static final int DIGEST_BYTES = 160 / 8; + + private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES; + + @Override + public CryptoAlgorithm getAlgorithm() { + return RIPEMD160; + } + + @Override + public HashDigest hash(byte[] data) { + byte[] digestBytes = RipeMD160Utils.hash(data); + return new HashDigest(RIPEMD160, digestBytes); + } + + @Override + public boolean verify(HashDigest digest, byte[] data) { + HashDigest hashDigest = hash(data); + return Arrays.equals(hashDigest.toBytes(), digest.toBytes()); + } + + @Override + public boolean supportHashDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,以及算法标识; + return RIPEMD160.CODE == digestBytes[0] && DIGEST_LENGTH == digestBytes.length; + } + + @Override + public HashDigest resolveHashDigest(byte[] digestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new HashDigest(digestBytes); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/SHA256HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/SHA256HashFunction.java new file mode 100644 index 00000000..b7aebeff --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/hash/SHA256HashFunction.java @@ -0,0 +1,49 @@ +package com.jd.blockchain.crypto.impl.def.hash; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.SHA256; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; + +import java.util.Arrays; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.utils.security.ShaUtils; + +public class SHA256HashFunction implements HashFunction { + + private static final int DIGEST_BYTES = 256 / 8; + + private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES; + + @Override + public CryptoAlgorithm getAlgorithm() { + return SHA256; + } + + @Override + public HashDigest hash(byte[] data) { + byte[] digestBytes = ShaUtils.hash_256(data); + return new HashDigest(SHA256, digestBytes); + } + + @Override + public boolean verify(HashDigest digest, byte[] data) { + HashDigest hashDigest = hash(data); + return Arrays.equals(hashDigest.toBytes(), digest.toBytes()); + } + + @Override + public boolean supportHashDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,以及算法标识; + return SHA256.CODE == digestBytes[0] && DIGEST_LENGTH == digestBytes.length; + } + + @Override + public HashDigest resolveHashDigest(byte[] hashDigestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new HashDigest(hashDigestBytes); + } + +} + diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/symmetric/AESSymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/symmetric/AESSymmetricEncryptionFunction.java new file mode 100644 index 00000000..64b7cf82 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/def/symmetric/AESSymmetricEncryptionFunction.java @@ -0,0 +1,142 @@ +package com.jd.blockchain.crypto.impl.def.symmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKey; +import com.jd.blockchain.crypto.symmetric.SymmetricCiphertext; +import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction; +import com.jd.blockchain.crypto.symmetric.SymmetricKey; +import com.jd.blockchain.utils.security.AESUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.AES; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; +import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY; +import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES; + +public class AESSymmetricEncryptionFunction implements SymmetricEncryptionFunction { + + private static final int KEY_SIZE = 16; + private static final int BLOCK_SIZE = 16; + + private static final int SYMMETRICKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + KEY_SIZE; + + public AESSymmetricEncryptionFunction() { + } + + @Override + public Ciphertext encrypt(SymmetricKey key, byte[] data) { + + byte[] rawKeyBytes = key.getRawKeyBytes(); + + // 验证原始密钥长度为128比特,即16字节 + if (rawKeyBytes.length != KEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应AES算法 + if (key.getAlgorithm() != AES) + throw new IllegalArgumentException("The is not AES symmetric key!"); + + // 调用底层AES128算法并计算密文数据 + return new SymmetricCiphertext(AES, AESUtils.encrypt(data, rawKeyBytes)); + } + + @Override + public void encrypt(SymmetricKey key, InputStream in, OutputStream out) { + + // 读输入流得到明文,加密,密文数据写入输出流 + try { + byte[] aesData = new byte[in.available()]; + in.read(aesData); + in.close(); + + out.write(encrypt(key, aesData).toBytes()); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public byte[] decrypt(SymmetricKey key, Ciphertext ciphertext) { + + byte[] rawKeyBytes = key.getRawKeyBytes(); + byte[] rawCiphertextBytes = ciphertext.getRawCiphertext(); + + // 验证原始密钥长度为128比特,即16字节 + if (rawKeyBytes.length != KEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应AES算法 + if (key.getAlgorithm().CODE != AES.CODE) + throw new IllegalArgumentException("The is not AES symmetric key!"); + + // 验证原始密文长度为分组长度的整数倍 + if (rawCiphertextBytes.length % BLOCK_SIZE != 0) + throw new IllegalArgumentException("This ciphertext has wrong format!"); + + // 验证密文数据算法标识对应AES算法 + if (ciphertext.getAlgorithm() != AES) + throw new IllegalArgumentException("This is not AES ciphertext!"); + + // 调用底层AES128算法解密,得到明文 + return AESUtils.decrypt(rawCiphertextBytes, rawKeyBytes); + } + + @Override + public void decrypt(SymmetricKey key, InputStream in, OutputStream out) { + + // 读输入流得到密文数据,解密,明文写入输出流 + try { + byte[] aesData = new byte[in.available()]; + in.read(aesData); + in.close(); + + if (!supportCiphertext(aesData)) + throw new IllegalArgumentException("InputStream is not valid AES ciphertext!"); + + out.write(decrypt(key, resolveCiphertext(aesData))); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public boolean supportSymmetricKey(byte[] symmetricKeyBytes) { + //验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,字节数组的算法标识对应AES算法且密钥密钥类型是对称密钥 + return symmetricKeyBytes.length == SYMMETRICKEY_LENGTH && symmetricKeyBytes[0] == AES.CODE && symmetricKeyBytes[1] == SYMMETRIC_KEY.CODE; + } + + @Override + public SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SymmetricKey(symmetricKeyBytes); + } + + @Override + public boolean supportCiphertext(byte[] ciphertextBytes) { + // 验证(输入字节数组长度-算法标识长度)是分组长度的整数倍,字节数组的算法标识对应AES算法 + return (ciphertextBytes.length - ALGORYTHM_BYTES) % BLOCK_SIZE == 0 && ciphertextBytes[0] == AES.CODE; + } + + @Override + public SymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SymmetricCiphertext(ciphertextBytes); + } + + @Override + public CryptoAlgorithm getAlgorithm() { + return AES; + } + + @Override + public CryptoKey generateSymmetricKey() { + // 根据对应的标识和原始密钥生成相应的密钥数据 + return new SymmetricKey(AES, AESUtils.generateKey128_Bytes()); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/asymmetric/JNIED25519SignatureFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/asymmetric/JNIED25519SignatureFunction.java new file mode 100644 index 00000000..7aa6b80f --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/asymmetric/JNIED25519SignatureFunction.java @@ -0,0 +1,130 @@ +package com.jd.blockchain.crypto.impl.jni.asymmetric; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.crypto.jniutils.asymmetric.JNIED25519Utils; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.JNIED25519; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; +import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY; +import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY; +import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES; + +public class JNIED25519SignatureFunction implements SignatureFunction { + + private static final int PUBKEY_SIZE = 32; + private static final int PRIVKEY_SIZE = 32; + private static final int DIGEST_SIZE = 64; + + private static final int PUBKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PUBKEY_SIZE; + private static final int PRIVKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PRIVKEY_SIZE; + private static final int SIGNATUREDIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_SIZE; + + public JNIED25519SignatureFunction() { + } + + @Override + public SignatureDigest sign(PrivKey privKey, byte[] data) { + + if (data == null) + throw new IllegalArgumentException("This data is null!"); + + JNIED25519Utils ed25519 = new JNIED25519Utils(); + + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + byte[] rawPubKeyBytes = ed25519.getPubKey(rawPrivKeyBytes); + + // 验证原始私钥长度为256比特,即32字节 + if (rawPrivKeyBytes.length != PRIVKEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应JNIED25519签名算法 + if (privKey.getAlgorithm() != JNIED25519) + throw new IllegalArgumentException("This key is not ED25519 private key!"); + + // 调用JNIED25519签名算法计算签名结果 + return new SignatureDigest(JNIED25519, ed25519.sign(data, rawPrivKeyBytes, rawPubKeyBytes)); + } + + @Override + public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) { + + JNIED25519Utils ed25519 = new JNIED25519Utils(); + + byte[] rawPubKeyBytes = pubKey.getRawKeyBytes(); + byte[] rawDigestBytes = digest.getRawDigest(); + + // 验证原始公钥长度为256比特,即32字节 + if (rawPubKeyBytes.length != PUBKEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应JNIED25519签名算法 + if (pubKey.getAlgorithm() != JNIED25519) + throw new IllegalArgumentException("This key is not ED25519 public key!"); + + // 验证密文数据的算法标识对应JNIED25519签名算法,并且原始摘要长度为64字节 + if (digest.getAlgorithm() != JNIED25519 || rawDigestBytes.length != DIGEST_SIZE) + throw new IllegalArgumentException("This is not ED25519 signature digest!"); + + // 调用JNIED25519验签算法验证签名结果 + return ed25519.verify(data, rawPubKeyBytes, rawDigestBytes); + } + + @Override + public boolean supportPrivKey(byte[] privKeyBytes) { + // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应JNIED25519签名算法,并且密钥类型是私钥 + return privKeyBytes.length == PRIVKEY_LENGTH + && privKeyBytes[0] == JNIED25519.CODE && privKeyBytes[1] == PRIV_KEY.CODE; + } + + @Override + public PrivKey resolvePrivKey(byte[] privKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new PrivKey(privKeyBytes); + } + + @Override + public boolean supportPubKey(byte[] pubKeyBytes) { + // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应JNIED25519签名算法,并且密钥类型是公钥 + return pubKeyBytes.length == PUBKEY_LENGTH && + pubKeyBytes[0] == JNIED25519.CODE && pubKeyBytes[1] == PUB_KEY.CODE; + + } + + @Override + public PubKey resolvePubKey(byte[] pubKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new PubKey(pubKeyBytes); + } + + @Override + public boolean supportDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应JNIED25519算法 + return digestBytes.length == SIGNATUREDIGEST_LENGTH && digestBytes[0] == JNIED25519.CODE; + } + + @Override + public SignatureDigest resolveDigest(byte[] digestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SignatureDigest(digestBytes); + } + + @Override + public CryptoAlgorithm getAlgorithm() { + return JNIED25519; + } + + @Override + public CryptoKeyPair generateKeyPair() { + + JNIED25519Utils ed25519 = new JNIED25519Utils(); + byte[] rawPrivKeyBytes = new byte[PRIVKEY_SIZE]; + byte[] rawPubKeyBytes = new byte[PUBKEY_SIZE]; + + // 调用JNIED25519算法的密钥生成算法生成公私钥对 + ed25519.generateKeyPair(rawPrivKeyBytes, rawPubKeyBytes); + + return new CryptoKeyPair(new PubKey(JNIED25519, rawPubKeyBytes), new PrivKey(JNIED25519, rawPrivKeyBytes)); + + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNIRIPEMD160HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNIRIPEMD160HashFunction.java new file mode 100644 index 00000000..72d0777e --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNIRIPEMD160HashFunction.java @@ -0,0 +1,53 @@ +package com.jd.blockchain.crypto.impl.jni.hash; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.crypto.jniutils.hash.JNIRIPEMD160Utils; + +import java.util.Arrays; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.JNIRIPEMD160; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; + +public class JNIRIPEMD160HashFunction implements HashFunction { + + private static final int DIGEST_BYTES = 160 / 8; + + private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES; + + @Override + public CryptoAlgorithm getAlgorithm() { + return JNIRIPEMD160; + } + + @Override + public HashDigest hash(byte[] data) { + + if (data == null) + throw new IllegalArgumentException("This data is null!"); + + JNIRIPEMD160Utils ripemd160 = new JNIRIPEMD160Utils(); + byte[] digestBytes = ripemd160.hash(data); + return new HashDigest(JNIRIPEMD160, digestBytes); + } + + @Override + public boolean verify(HashDigest digest, byte[] data) { + HashDigest hashDigest = hash(data); + return Arrays.equals(hashDigest.toBytes(), digest.toBytes()); + } + + @Override + public boolean supportHashDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应JNIRIPEMD160算法 + return digestBytes.length == DIGEST_LENGTH && JNIRIPEMD160.CODE == digestBytes[0]; + } + + @Override + public HashDigest resolveHashDigest(byte[] digestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new HashDigest(digestBytes); + } +} + diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNISHA256HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNISHA256HashFunction.java new file mode 100644 index 00000000..bb6e84f0 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/jni/hash/JNISHA256HashFunction.java @@ -0,0 +1,53 @@ +package com.jd.blockchain.crypto.impl.jni.hash; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.crypto.jniutils.hash.JNISHA256Utils; + +import java.util.Arrays; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.JNISHA256; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; + +public class JNISHA256HashFunction implements HashFunction { + + private static final int DIGEST_BYTES = 256/8; + + private static final int DIGEST_LENGTH = ALGORYTHM_BYTES + DIGEST_BYTES; + + @Override + public CryptoAlgorithm getAlgorithm() { + return JNISHA256; + } + + @Override + public HashDigest hash(byte[] data) { + + if (data == null) + throw new IllegalArgumentException("This data is null!"); + + JNISHA256Utils sha256 = new JNISHA256Utils(); + byte[] digestBytes = sha256.hash(data); + return new HashDigest(JNISHA256,digestBytes); + } + + @Override + public boolean verify(HashDigest digest, byte[] data) { + HashDigest hashDigest=hash(data); + return Arrays.equals(hashDigest.toBytes(),digest.toBytes()); + } + + @Override + public boolean supportHashDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,字节数组的算法标识对应JNISHA256算法 + return digestBytes.length == DIGEST_LENGTH && JNISHA256.CODE == digestBytes[0]; + } + + @Override + public HashDigest resolveHashDigest(byte[] hashDigestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new HashDigest(hashDigestBytes); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/asymmetric/SM2CryptoFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/asymmetric/SM2CryptoFunction.java new file mode 100644 index 00000000..070bae5d --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/asymmetric/SM2CryptoFunction.java @@ -0,0 +1,184 @@ +package com.jd.blockchain.crypto.impl.sm.asymmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.crypto.smutils.asymmetric.SM2Utils; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.util.encoders.Hex; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.SM2; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; +import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY; +import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY; +import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES; + +public class SM2CryptoFunction implements AsymmetricEncryptionFunction, SignatureFunction { + + private static final int ECPOINT_SIZE = 65; + private static final int PRIVKEY_SIZE = 32; + private static final int SIGNATUREDIGEST_SIZE = 64; + private static final int HASHDIGEST_SIZE = 32; + + private static final int PUBKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + ECPOINT_SIZE; + private static final int PRIVKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + PRIVKEY_SIZE; + private static final int SIGNATUREDIGEST_LENGTH = ALGORYTHM_BYTES + SIGNATUREDIGEST_SIZE; + + @Override + public Ciphertext encrypt(PubKey pubKey, byte[] data) { + + byte[] rawPubKeyBytes = pubKey.getRawKeyBytes(); + + // 验证原始公钥长度为65字节 + if (rawPubKeyBytes.length != ECPOINT_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应SM2算法 + if (pubKey.getAlgorithm() != SM2) + throw new IllegalArgumentException("The is not sm2 public key!"); + + // 调用SM2加密算法计算密文 + return new AsymmetricCiphertext(SM2, SM2Utils.encrypt(data, rawPubKeyBytes)); + } + + @Override + public byte[] decrypt(PrivKey privKey, Ciphertext ciphertext) { + + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + byte[] rawCiphertextBytes = ciphertext.getRawCiphertext(); + + // 验证原始私钥长度为32字节 + if (rawPrivKeyBytes.length != PRIVKEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应SM2算法 + if (privKey.getAlgorithm() != SM2) + throw new IllegalArgumentException("This key is not SM2 private key!"); + + // 验证密文数据的算法标识对应SM2签名算法,并且原始摘要长度为64字节 + if (ciphertext.getAlgorithm() != SM2 || rawCiphertextBytes.length < ECPOINT_SIZE + HASHDIGEST_SIZE) + throw new IllegalArgumentException("This is not SM2 ciphertext!"); + + // 调用SM2解密算法得到明文结果 + return SM2Utils.decrypt(rawCiphertextBytes,rawPrivKeyBytes); + } + + @Override + public boolean supportPrivKey(byte[] privKeyBytes) { + // 验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,密钥数据的算法标识对应SM2算法,并且密钥类型是私钥 + return privKeyBytes.length == PRIVKEY_LENGTH && privKeyBytes[0] == SM2.CODE && privKeyBytes[1] == PRIV_KEY.CODE; + } + + @Override + public PrivKey resolvePrivKey(byte[] privKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new PrivKey(privKeyBytes); + } + + @Override + public boolean supportPubKey(byte[] pubKeyBytes) { + // 验证输入字节数组长度=算法标识长度+密钥类型长度+椭圆曲线点长度,密钥数据的算法标识对应SM2算法,并且密钥类型是公钥 + return pubKeyBytes.length == PUBKEY_LENGTH && pubKeyBytes[0] == SM2.CODE && pubKeyBytes[1] == PUB_KEY.CODE; + } + + @Override + public PubKey resolvePubKey(byte[] pubKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new PubKey(pubKeyBytes); + } + + @Override + public boolean supportCiphertext(byte[] ciphertextBytes) { + // 验证输入字节数组长度>=算法标识长度+椭圆曲线点长度+哈希长度,字节数组的算法标识对应SM2算法 + return ciphertextBytes.length >= ALGORYTHM_BYTES + ECPOINT_SIZE + HASHDIGEST_SIZE && ciphertextBytes[0] == SM2.CODE; + } + + @Override + public AsymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new AsymmetricCiphertext(ciphertextBytes); + } + + @Override + public SignatureDigest sign(PrivKey privKey, byte[] data) { + + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + + // 验证原始私钥长度为256比特,即32字节 + if (rawPrivKeyBytes.length != PRIVKEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应SM2签名算法 + if (privKey.getAlgorithm() != SM2) + throw new IllegalArgumentException("This key is not SM2 private key!"); + + // 调用SM2签名算法计算签名结果 + return new SignatureDigest(SM2, SM2Utils.sign(data,rawPrivKeyBytes)); + } + + @Override + public boolean verify(SignatureDigest digest, PubKey pubKey, byte[] data) { + + byte[] rawPubKeyBytes = pubKey.getRawKeyBytes(); + byte[] rawDigestBytes = digest.getRawDigest(); + + // 验证原始公钥长度为520比特,即65字节 + if (rawPubKeyBytes.length != ECPOINT_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应SM2签名算法 + if (pubKey.getAlgorithm() != SM2) + throw new IllegalArgumentException("This key is not SM2 public key!"); + + // 验证签名数据的算法标识对应SM2签名算法,并且原始签名长度为64字节 + if (digest.getAlgorithm() != SM2 || rawDigestBytes.length != SIGNATUREDIGEST_SIZE) + throw new IllegalArgumentException("This is not SM2 signature digest!"); + + // 调用SM2验签算法验证签名结果 + return SM2Utils.verify(data, rawPubKeyBytes, rawDigestBytes); + } + + @Override + public boolean supportDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+签名长度,字节数组的算法标识对应SM2算法 + return digestBytes.length == SIGNATUREDIGEST_LENGTH && digestBytes[0] == SM2.CODE; + } + + @Override + public SignatureDigest resolveDigest(byte[] digestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SignatureDigest(digestBytes); + } + + @Override + public CryptoAlgorithm getAlgorithm() { + return SM2; + } + + @Override + public CryptoKeyPair generateKeyPair() { + + // 调用SM2算法的密钥生成算法生成公私钥对priKey和pubKey,返回密钥对 + AsymmetricCipherKeyPair keyPair = SM2Utils.generateKeyPair(); + ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters) keyPair.getPrivate(); + ECPublicKeyParameters ecPub = (ECPublicKeyParameters) keyPair.getPublic(); + + byte[] privKeyBytesD = ecPriv.getD().toByteArray(); + byte[] privKeyBytes = new byte[PRIVKEY_SIZE]; + if (privKeyBytesD.length > PRIVKEY_SIZE) + System.arraycopy(privKeyBytesD,privKeyBytesD.length-PRIVKEY_SIZE,privKeyBytes,0,PRIVKEY_SIZE); + else System.arraycopy(privKeyBytesD,0,privKeyBytes,PRIVKEY_SIZE-privKeyBytesD.length,privKeyBytesD.length); + + byte[] pubKeyBytesX = ecPub.getQ().getAffineXCoord().getEncoded(); + byte[] pubKeyBytesY = ecPub.getQ().getAffineYCoord().getEncoded(); + byte[] pubKeyBytes = new byte[ECPOINT_SIZE]; + System.arraycopy(Hex.decode("04"),0,pubKeyBytes,0,1); + System.arraycopy(pubKeyBytesX,0,pubKeyBytes,1,32); + System.arraycopy(pubKeyBytesY,0,pubKeyBytes,1+32,32); + + return new CryptoKeyPair(new PubKey(SM2,pubKeyBytes),new PrivKey(SM2,privKeyBytes)); + } +} + diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/hash/SM3HashFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/hash/SM3HashFunction.java new file mode 100644 index 00000000..98f9a487 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/hash/SM3HashFunction.java @@ -0,0 +1,47 @@ +package com.jd.blockchain.crypto.impl.sm.hash; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoBytes; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.crypto.smutils.hash.SM3Utils; + +import java.util.Arrays; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.SM3; + +public class SM3HashFunction implements HashFunction { + + private static final int DIGEST_BYTES = 256/8; + + private static final int DIGEST_LENGTH = CryptoBytes.ALGORYTHM_BYTES + DIGEST_BYTES; + + @Override + public CryptoAlgorithm getAlgorithm() { + return SM3; + } + + @Override + public HashDigest hash(byte[] data) { + byte[] digestBytes = SM3Utils.hash(data); + return new HashDigest(SM3,digestBytes); + } + + @Override + public boolean verify(HashDigest digest, byte[] data) { + HashDigest hashDigest = hash(data); + return Arrays.equals(hashDigest.toBytes(), digest.toBytes()); + } + + @Override + public boolean supportHashDigest(byte[] digestBytes) { + // 验证输入字节数组长度=算法标识长度+摘要长度,以及算法标识; + return SM3.CODE == digestBytes[0] && DIGEST_LENGTH == digestBytes.length; + } + + @Override + public HashDigest resolveHashDigest(byte[] hashDigestBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new HashDigest(hashDigestBytes); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/symmetric/SM4SymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/symmetric/SM4SymmetricEncryptionFunction.java new file mode 100644 index 00000000..3bba6efa --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/impl/sm/symmetric/SM4SymmetricEncryptionFunction.java @@ -0,0 +1,145 @@ +package com.jd.blockchain.crypto.impl.sm.symmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKey; +import com.jd.blockchain.crypto.smutils.symmetric.SM4Utils; +import com.jd.blockchain.crypto.symmetric.SymmetricCiphertext; +import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction; +import com.jd.blockchain.crypto.symmetric.SymmetricKey; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import static com.jd.blockchain.crypto.CryptoAlgorithm.SM4; +import static com.jd.blockchain.crypto.CryptoBytes.ALGORYTHM_BYTES; +import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY; +import static com.jd.blockchain.crypto.base.BaseCryptoKey.KEY_TYPE_BYTES; + +public class SM4SymmetricEncryptionFunction implements SymmetricEncryptionFunction { + + private static final int KEY_SIZE = 16; + private static final int BLOCK_SIZE = 16; + + private static final int SYMMETRICKEY_LENGTH = ALGORYTHM_BYTES + KEY_TYPE_BYTES + KEY_SIZE; + + @Override + public Ciphertext encrypt(SymmetricKey key, byte[] data) { + + byte[] rawKeyBytes = key.getRawKeyBytes(); + + // 验证原始密钥长度为128比特,即16字节 + if (rawKeyBytes.length != KEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应SM4算法 + if (key.getAlgorithm() != SM4) + throw new IllegalArgumentException("The is not SM4 symmetric key!"); + + // 调用底层SM4算法并计算密文数据 + return new SymmetricCiphertext(SM4, SM4Utils.encrypt(data, rawKeyBytes)); + } + + @Override + public void encrypt(SymmetricKey key, InputStream in, OutputStream out) { + + // 读输入流得到明文,加密,密文数据写入输出流 + try { + byte[] sm4Data = new byte[in.available()]; + in.read(sm4Data); + in.close(); + + out.write(encrypt(key, sm4Data).toBytes()); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public byte[] decrypt(SymmetricKey key, Ciphertext ciphertext) { + + byte[] rawKeyBytes = key.getRawKeyBytes(); + byte[] rawCiphertextBytes = ciphertext.getRawCiphertext(); + + // 验证原始密钥长度为128比特,即16字节 + if (rawKeyBytes.length != KEY_SIZE) + throw new IllegalArgumentException("This key has wrong format!"); + + // 验证密钥数据的算法标识对应SM4算法 + if (key.getAlgorithm().CODE != SM4.CODE) + throw new IllegalArgumentException("The is not SM4 symmetric key!"); + + // 验证原始密文长度为分组长度的整数倍 + if (rawCiphertextBytes.length % BLOCK_SIZE != 0) + throw new IllegalArgumentException("This ciphertext has wrong format!"); + + // 验证密文数据算法标识对应SM4算法 + if (ciphertext.getAlgorithm() != SM4) + throw new IllegalArgumentException("This is not SM4 ciphertext!"); + + // 调用底层SM4算法解密,得到明文 + try { + return SM4Utils.decrypt(rawCiphertextBytes, rawKeyBytes); + } catch (Exception e) { + e.printStackTrace(); + } + + throw new IllegalArgumentException("Decrypting process fails!"); + } + + @Override + public void decrypt(SymmetricKey key, InputStream in, OutputStream out) { + + // 读输入流得到密文数据,解密,明文写入输出流 + try { + byte[] sm4Data = new byte[in.available()]; + in.read(sm4Data); + in.close(); + + if (!supportCiphertext(sm4Data)) + throw new IllegalArgumentException("InputStream is not valid SM4 ciphertext!"); + + out.write(decrypt(key, resolveCiphertext(sm4Data))); + out.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + @Override + public boolean supportSymmetricKey(byte[] symmetricKeyBytes) { + //验证输入字节数组长度=算法标识长度+密钥类型长度+密钥长度,字节数组的算法标识对应SM4算法且密钥密钥类型是对称密钥 + return symmetricKeyBytes.length == SYMMETRICKEY_LENGTH && symmetricKeyBytes[0] == SM4.CODE && symmetricKeyBytes[1] == SYMMETRIC_KEY.CODE; + } + + @Override + public SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SymmetricKey(symmetricKeyBytes); + } + + @Override + public boolean supportCiphertext(byte[] ciphertextBytes) { + // 验证(输入字节数组长度-算法标识长度)是分组长度的整数倍,字节数组的算法标识对应SM4算法 + return (ciphertextBytes.length - ALGORYTHM_BYTES) % BLOCK_SIZE == 0 && ciphertextBytes[0] == SM4.CODE; + } + + @Override + public SymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes) { + // 由框架调用 support 方法检查有效性,在此不做重复检查; + return new SymmetricCiphertext(ciphertextBytes); + } + + @Override + public CryptoAlgorithm getAlgorithm() { + return SM4; + } + + @Override + public CryptoKey generateSymmetricKey() { + // 根据对应的标识和原始密钥生成相应的密钥数据 + return new SymmetricKey(SM4, SM4Utils.generateKey()); + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/asymmetric/JNIED25519Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/asymmetric/JNIED25519Utils.java new file mode 100644 index 00000000..e1b4bb3b --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/asymmetric/JNIED25519Utils.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.crypto.jniutils.asymmetric; + + +import com.jd.blockchain.crypto.jniutils.hash.JNISHA256Utils; + +import java.util.Objects; + +public class JNIED25519Utils { + + /* load c library */ + static { + //differentiate OS + String osName = System.getProperty("os.name").toLowerCase(); + String path=""; + // Windows OS + if (osName.startsWith("windows")){ + path = Objects.requireNonNull(JNIED25519Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/asymmetric/c_ed25519.dll")).getPath(); + } + // Linux OS + else if (osName.contains("linux")){ + path = Objects.requireNonNull(JNIED25519Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/asymmetric/libc_ed25519.so")).getPath(); + } + // Mac OS + else if (osName.contains("mac")){ + path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/asymmetric/libc_ed25519.jnilib")).getPath(); + } + + System.load(path); + } + + /* define java native method */ + public native void generateKeyPair(byte[] privKey, byte[] pubKey); + public native byte[] getPubKey(byte[] privKey); + public native byte[] sign(byte[] msg, byte[] privKey, byte[] pubKey); + public native boolean verify(byte[] msg, byte[] pubKey, byte[] signature); + } + diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIMBSHA256Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIMBSHA256Utils.java new file mode 100644 index 00000000..5518117d --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIMBSHA256Utils.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.crypto.jniutils.hash; + +import java.util.Objects; + +public class JNIMBSHA256Utils { + /* load c library */ + static{ + //differentiate OS + String osName = System.getProperty("os.name").toLowerCase(); + String pathOfSo; + String pathOfSo2; + if (osName.contains("linux")){ + pathOfSo = Objects.requireNonNull(JNIMBSHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_mbsha256.so")).getPath(); + pathOfSo2 = Objects.requireNonNull(JNIMBSHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libisal_crypto.so.2")).getPath(); + } + else throw new IllegalArgumentException("The JNIMBSHA256 implementation is not supported in this Operation System!"); + + System.load(pathOfSo2); + System.load(pathOfSo); + } + + /* define java native method */ + public native byte[][] multiBufferHash(byte[][] multiMsgs); +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIRIPEMD160Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIRIPEMD160Utils.java new file mode 100644 index 00000000..d9e0ac7a --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNIRIPEMD160Utils.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.crypto.jniutils.hash; + +import java.util.Objects; + +public class JNIRIPEMD160Utils { + + /* load c library */ + static { + //differentiate OS + String osName = System.getProperty("os.name").toLowerCase(); + String path=""; + // Windows OS + if (osName.startsWith("windows")){ + path = Objects.requireNonNull(JNIRIPEMD160Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/c_ripemd160.dll")).getPath(); + } + // Linux OS + else if (osName.contains("linux")){ + path = Objects.requireNonNull(JNIRIPEMD160Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_ripemd160.so")).getPath(); + } + // Mac OS + else if (osName.contains("mac")){ + path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_ripemd160.jnilib")).getPath(); + } + + System.load(path); + } + + /* define java native method */ + public native byte[] hash(byte[] msg); +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNISHA256Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNISHA256Utils.java new file mode 100644 index 00000000..a26ef912 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/jniutils/hash/JNISHA256Utils.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.crypto.jniutils.hash; + +import java.util.Objects; + +public class JNISHA256Utils { + + /* load c library */ + static { + //differentiate OS + String osName = System.getProperty("os.name").toLowerCase(); + String path=""; + // Windows OS + if (osName.startsWith("windows")){ + path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/c_sha256.dll")).getPath(); + } + // Linux OS + else if (osName.contains("linux")){ + path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_sha256.so")).getPath(); + } + // Mac OS + else if (osName.contains("mac")){ + path = Objects.requireNonNull(JNISHA256Utils.class.getClassLoader().getResource("com/jd/blockchain/crypto/jniutils/hash/libc_sha256.jnilib")).getPath(); + } + + System.load(path); + } + + /* define java native method */ + public native byte[] hash(byte[] msg); +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/asymmetric/SM2Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/asymmetric/SM2Utils.java new file mode 100644 index 00000000..8dfa598f --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/asymmetric/SM2Utils.java @@ -0,0 +1,281 @@ +package com.jd.blockchain.crypto.smutils.asymmetric; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1Integer; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.CryptoException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.engines.SM2Engine; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.*; +import org.bouncycastle.crypto.signers.SM2Signer; +import org.bouncycastle.math.ec.ECCurve; +import org.bouncycastle.math.ec.ECPoint; + +import java.io.IOException; +import java.math.BigInteger; +import java.security.SecureRandom; + +public class SM2Utils { + + private static final int COORDS_SIZE = 32; + private static final int POINT_SIZE = COORDS_SIZE * 2 + 1; + private static final int R_SIZE =32; + private static final int S_SIZE =32; + + // The length of sm3 output is 32 bytes + private static final int SM3DIGEST_LENGTH = 32; + + private static final BigInteger SM2_ECC_P = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16); + private static final BigInteger SM2_ECC_A = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16); + private static final BigInteger SM2_ECC_B = new BigInteger("28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16); + private static final BigInteger SM2_ECC_N = new BigInteger("FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16); + private static final BigInteger SM2_ECC_GX = new BigInteger("32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); + private static final BigInteger SM2_ECC_GY = new BigInteger("BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); + + // To get the curve from the equation y^2=x^3+ax+b according the coefficient a and b, + // with the big prime p, and obtain the generator g and the domain's parameters + private static final ECCurve curve = new ECCurve.Fp(SM2_ECC_P, SM2_ECC_A, SM2_ECC_B); + private static final ECPoint g = curve.createPoint(SM2_ECC_GX, SM2_ECC_GY); + private static final ECDomainParameters domainParams = new ECDomainParameters(curve, g, SM2_ECC_N); + + + //-----------------Key Pair Generation Algorithm----------------- + + /** + * key generation + * + * @return key pair + */ + public static AsymmetricCipherKeyPair generateKeyPair(){ + + SecureRandom random = new SecureRandom(); + return generateKeyPair(random); + } + + public static AsymmetricCipherKeyPair generateKeyPair(SecureRandom random){ + + ECKeyGenerationParameters keyGenerationParams = new ECKeyGenerationParameters(domainParams,random); + ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); + + // To generate the key pair + keyPairGenerator.init(keyGenerationParams); + return keyPairGenerator.generateKeyPair(); + } + + + //-----------------Digital Signature Algorithm----------------- + + + /** + * signature generation + * + * @param data data to be signed + * @param privateKey private key + * @return signature + */ + public static byte[] sign(byte[] data, byte[] privateKey){ + + SecureRandom random = new SecureRandom(); + ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey),domainParams); + CipherParameters param = new ParametersWithRandom(privKey,random); + + return sign(data,param); + } + + public static byte[] sign(byte[] data, byte[] privateKey, SecureRandom random, String ID){ + + ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey),domainParams); + CipherParameters param = new ParametersWithID(new ParametersWithRandom(privKey,random),ID.getBytes()); + + return sign(data,param); + } + + private static byte[] sign(byte[] data, CipherParameters param){ + + SM2Signer signer = new SM2Signer(); + + // To get Z_A and prepare parameters + signer.init(true,param); + // To fill the whole message to be signed + signer.update(data,0,data.length); + // To get and return the signature result; + + byte[] encodedSignature; + try { + encodedSignature = signer.generateSignature(); + } catch (CryptoException e) { + throw new com.jd.blockchain.crypto.CryptoException(e.getMessage(), e); + } + + // To decode the signature + ASN1Sequence sig = ASN1Sequence.getInstance(encodedSignature); + byte[] rBytes = BigIntegerTo32Bytes(ASN1Integer.getInstance(sig.getObjectAt(0)).getValue()); + byte[] sBytes = BigIntegerTo32Bytes(ASN1Integer.getInstance(sig.getObjectAt(1)).getValue()); + + byte[] signature = new byte[R_SIZE + S_SIZE]; + System.arraycopy(rBytes,0,signature,0,R_SIZE); + System.arraycopy(sBytes,0,signature,R_SIZE,S_SIZE); + + return signature; + } + + /** + * verification + * + * @param data data to be signed + * @param publicKey public key + * @return true or false + */ + public static boolean verify(byte[] data, byte[] publicKey, byte[] signature){ + + ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); + ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint,domainParams); + + return verify(data,pubKey,signature); + } + + public static boolean verify(byte[] data, byte[] publicKey, byte[] signature, String ID){ + + ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); + ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint,domainParams); + ParametersWithID param = new ParametersWithID(pubKey,ID.getBytes()); + + return verify(data,param,signature); + } + + private static boolean verify(byte[] data, CipherParameters param, byte[] signature){ + + + SM2Signer verifier = new SM2Signer(); + + // To get Z_A and prepare parameters + verifier.init(false,param); + // To fill the whole message + verifier.update(data,0,data.length); + // To verify the signature + + byte[] rBytes = new byte[R_SIZE]; + byte[] sBytes = new byte[S_SIZE]; + System.arraycopy(signature,0,rBytes,0,R_SIZE); + System.arraycopy(signature,R_SIZE,sBytes,0,S_SIZE); + + BigInteger r = new BigInteger(1,rBytes); + BigInteger s = new BigInteger(1,sBytes); + byte[] encodedSignature = new byte[0]; + try { + encodedSignature = new DERSequence(new ASN1Encodable[] { new ASN1Integer(r), new ASN1Integer(s)}).getEncoded(); + } catch (IOException e) { + e.printStackTrace(); + } + + return verifier.verifySignature(encodedSignature); + } + + + + + //-----------------Public Key Encryption Algorithm----------------- + + /** + * encryption + * + * @param plainBytes plaintext + * @param publicKey public key + * @return ciphertext + */ + public static byte[] encrypt(byte[] plainBytes, byte[] publicKey){ + + SecureRandom random = new SecureRandom(); + return encrypt(plainBytes,publicKey,random); + } + + public static byte[] encrypt(byte[] plainBytes, byte[] publicKey, SecureRandom random){ + + ECPoint pubKeyPoint = resolvePubKeyBytes(publicKey); + ECPublicKeyParameters pubKey = new ECPublicKeyParameters(pubKeyPoint,domainParams); + ParametersWithRandom param = new ParametersWithRandom(pubKey,random); + + SM2Engine encryptor = new SM2Engine(); + + // To prepare parameters + encryptor.init(true,param); + + // To generate the twisted ciphertext c1c2c3. + // The latest standard specification indicates that the correct ordering is c1c3c2 + byte[] c1c2c3 = new byte[0]; + try { + c1c2c3 = encryptor.processBlock(plainBytes,0,plainBytes.length); + } catch (InvalidCipherTextException e) { + e.printStackTrace(); + } + + // get correct output c1c3c2 from c1c2c3 + byte[] c1c3c2 = new byte[c1c2c3.length]; + System.arraycopy(c1c2c3,0,c1c3c2,0,POINT_SIZE); + System.arraycopy(c1c2c3,POINT_SIZE,c1c3c2,POINT_SIZE + SM3DIGEST_LENGTH, plainBytes.length); + System.arraycopy(c1c2c3,POINT_SIZE + plainBytes.length, c1c3c2,POINT_SIZE,SM3DIGEST_LENGTH); + + return c1c3c2; + } + + /** + * decryption + * + * @param cipherBytes ciphertext + * @param privateKey private key + * @return plaintext + */ + public static byte[] decrypt(byte[] cipherBytes, byte[] privateKey){ + + + // To get c1c2c3 from ciphertext whose ordering is c1c3c2 + byte[] c1c2c3 = new byte[cipherBytes.length]; + System.arraycopy(cipherBytes,0,c1c2c3,0,POINT_SIZE); + System.arraycopy(cipherBytes,POINT_SIZE,c1c2c3,c1c2c3.length-SM3DIGEST_LENGTH, SM3DIGEST_LENGTH); + System.arraycopy(cipherBytes,SM3DIGEST_LENGTH + POINT_SIZE,c1c2c3,POINT_SIZE,c1c2c3.length-SM3DIGEST_LENGTH-POINT_SIZE); + + ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(new BigInteger(1,privateKey),domainParams); + + SM2Engine decryptor = new SM2Engine(); + + // To prepare parameters + decryptor.init(false,privKey); + + // To output the plaintext + try { + return decryptor.processBlock(c1c2c3,0,c1c2c3.length); + } catch (InvalidCipherTextException e) { + throw new com.jd.blockchain.crypto.CryptoException(e.getMessage(), e); + } + } + + // To convert BigInteger to byte[] whose length is 32 + private static byte[] BigIntegerTo32Bytes(BigInteger b){ + byte[] tmp = b.toByteArray(); + byte[] result = new byte[32]; + if (tmp.length > result.length) + System.arraycopy(tmp, tmp.length-result.length, result, 0, result.length); + else System.arraycopy(tmp,0,result,result.length-tmp.length,tmp.length); + return result; + } + + // To retrieve the public key point from publicKey in byte array mode + private static ECPoint resolvePubKeyBytes(byte[] publicKey){ + + byte[] pubKeyX = new byte[COORDS_SIZE]; + byte[] pubKeyY = new byte[COORDS_SIZE]; + System.arraycopy(publicKey,1,pubKeyX,0,COORDS_SIZE); + System.arraycopy(publicKey,1+COORDS_SIZE,pubKeyY,0,COORDS_SIZE); + + return curve.createPoint(new BigInteger(1,pubKeyX), new BigInteger(1,pubKeyY)); + } + public static ECCurve getCurve(){return curve;} + public static ECDomainParameters getDomainParams(){return domainParams;} +} + + + diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/hash/SM3Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/hash/SM3Utils.java new file mode 100644 index 00000000..788247f3 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/hash/SM3Utils.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.crypto.smutils.hash; + +import org.bouncycastle.crypto.digests.SM3Digest; + +public class SM3Utils { + + + // The length of sm3 output is 32 bytes + private static final int SM3DIGEST_LENGTH = 32; + + public static byte[] hash(byte[] data) { + + byte[] result = new byte[SM3DIGEST_LENGTH]; + + SM3Digest sm3digest = new SM3Digest(); + + sm3digest.update(data, 0, data.length); + sm3digest.doFinal(result, 0); + + return result; + + } + +} + diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/symmetric/SM4Utils.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/symmetric/SM4Utils.java new file mode 100644 index 00000000..b1855a19 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/smutils/symmetric/SM4Utils.java @@ -0,0 +1,143 @@ +package com.jd.blockchain.crypto.smutils.symmetric; + +import com.jd.blockchain.crypto.CryptoException; +import org.bouncycastle.crypto.CipherKeyGenerator; +import org.bouncycastle.crypto.KeyGenerationParameters; +import org.bouncycastle.crypto.engines.SM4Engine; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +import org.bouncycastle.crypto.paddings.PKCS7Padding; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + + +import java.security.SecureRandom; + +public class SM4Utils { + + // SM4 supports 128-bit secret key + private static final int KEY_Length = 128; + // One block contains 16 bytes + private static final int BLOCK_SIZE = 16; + // Initial vector's size is 16 bytes + private static final int IV_SIZE = 16; + + + /** + * key generation + * + * @return secret key + */ + public static byte[] generateKey(){ + + CipherKeyGenerator keyGenerator = new CipherKeyGenerator(); + + // To provide secure randomness and key length as input + // to prepare generate private key + keyGenerator.init(new KeyGenerationParameters(new SecureRandom(),KEY_Length)); + + // To generate key + return keyGenerator.generateKey(); + } + + + /** + * encryption + * + * @param plainBytes plaintext + * @param secretKey symmetric key + * @param iv initial vector + * @return ciphertext + */ + public static byte[] encrypt(byte[] plainBytes, byte[] secretKey, byte[] iv){ + + // To ensure that plaintext is not null + if (plainBytes == null) + { + throw new IllegalArgumentException("plaintext is null!"); + } + + // To get the value padded into input + int padding = 16 - plainBytes.length % BLOCK_SIZE; + // The plaintext with padding value + byte[] plainBytesWithPadding = new byte[plainBytes.length + padding]; + System.arraycopy(plainBytes,0,plainBytesWithPadding,0,plainBytes.length); + // The padder adds PKCS7 padding to the input, which makes its length to + // become an integral multiple of 16 bytes + PKCS7Padding padder = new PKCS7Padding(); + // To add padding + padder.addPadding(plainBytesWithPadding, plainBytes.length); + + CBCBlockCipher encryptor = new CBCBlockCipher(new SM4Engine()); + // To provide key and initialisation vector as input + encryptor.init(true,new ParametersWithIV(new KeyParameter(secretKey),iv)); + byte[] output = new byte[plainBytesWithPadding.length + IV_SIZE]; + // To encrypt the input_p in CBC mode + for(int i = 0 ; i < plainBytesWithPadding.length/BLOCK_SIZE; i++) + encryptor.processBlock(plainBytesWithPadding, i * BLOCK_SIZE, output, (i+1) * BLOCK_SIZE); + + // The IV locates on the first block of ciphertext + System.arraycopy(iv,0,output,0,BLOCK_SIZE); + return output; + } + + public static byte[] encrypt(byte[] plainBytes, byte[] secretKey){ + + byte[] iv = new byte[IV_SIZE]; + SecureRandom random = new SecureRandom(); + random.nextBytes(iv); + return encrypt(plainBytes,secretKey,iv); + } + + /** + * decryption + * + * @param cipherBytes ciphertext + * @param secretKey symmetric key + * @return plaintext + */ + public static byte[] decrypt(byte[] cipherBytes, byte[] secretKey){ + + // To ensure that the ciphertext is not null + if (cipherBytes == null) + { + throw new IllegalArgumentException("ciphertext is null!"); + } + + // To ensure that the ciphertext's length is integral multiples of 16 bytes + if ( cipherBytes.length % BLOCK_SIZE != 0 ) + { + throw new IllegalArgumentException("ciphertext's length is wrong!"); + } + + byte[] iv = new byte[IV_SIZE]; + System.arraycopy(cipherBytes,0,iv,0,BLOCK_SIZE); + + CBCBlockCipher decryptor = new CBCBlockCipher(new SM4Engine()); + // To prepare the decryption + decryptor.init(false,new ParametersWithIV(new KeyParameter(secretKey),iv)); + byte[] outputWithPadding = new byte[cipherBytes.length-BLOCK_SIZE]; + // To decrypt the input in CBC mode + for(int i = 1 ; i < cipherBytes.length/BLOCK_SIZE ; i++) + decryptor.processBlock(cipherBytes, i * BLOCK_SIZE, outputWithPadding, (i-1) * BLOCK_SIZE); + + int p = outputWithPadding[outputWithPadding.length-1]; + // To ensure that the padding of output_p is valid + if(p > BLOCK_SIZE || p < 0x01) + { + throw new CryptoException("There no exists such padding!"); + + } + for(int i = 0 ; i < p ; i++) + { + if(outputWithPadding[outputWithPadding.length-i-1] != p) + { + throw new CryptoException("Padding is invalid!"); + } + } + + // To remove the padding from output and obtain plaintext + byte[] output = new byte[outputWithPadding.length-p]; + System.arraycopy(outputWithPadding, 0, output, 0, output.length); + return output; + } +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCiphertext.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCiphertext.java new file mode 100644 index 00000000..39878e38 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCiphertext.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.crypto.symmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.base.BaseCryptoBytes; + +public class SymmetricCiphertext extends BaseCryptoBytes implements Ciphertext { + + public SymmetricCiphertext(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { + super(algorithm, rawCryptoBytes); + } + + public SymmetricCiphertext(byte[] cryptoBytes) { + super(cryptoBytes); + } + +// @Override +// protected void support(CryptoAlgorithm algorithm) { +// if (!algorithm.isSymmetric()) { +// throw new CryptoException("SymmetricCiphertext doesn't support algorithm[" + algorithm + "]!"); +// } +// } + + @Override + protected boolean support(CryptoAlgorithm algorithm) { + return algorithm.isSymmetric(); + } + + @Override + public byte[] getRawCiphertext() { + return getRawCryptoBytes().getBytesCopy(); + } + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCryptography.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCryptography.java new file mode 100644 index 00000000..4de371a2 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricCryptography.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.crypto.symmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; + +public interface SymmetricCryptography { + + /** + * 生成秘钥对; + * + * @param algorithm + * @return + */ + SymmetricKey generateKey(CryptoAlgorithm algorithm); + + /** + * 获取签名方法; + * + * @param algorithm + * @return + */ + SymmetricEncryptionFunction getSymmetricEncryptionFunction(CryptoAlgorithm algorithm); + + byte[] decrypt(byte[] symmetricKeyBytes,byte[] ciphertextBytes); + + Ciphertext resolveCiphertext(byte[] ciphertextBytes); + + Ciphertext tryResolveCiphertext(byte[] ciphertextBytes); + + SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes); + + SymmetricKey tryResolveSymmetricKey(byte[] symmetricKeyBytes); + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricEncryptionFunction.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricEncryptionFunction.java new file mode 100644 index 00000000..5e97958c --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricEncryptionFunction.java @@ -0,0 +1,82 @@ +package com.jd.blockchain.crypto.symmetric; + +import java.io.InputStream; +import java.io.OutputStream; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoFunction; +import com.jd.blockchain.crypto.CryptoSymmetricKeyGenerator; + +public interface SymmetricEncryptionFunction extends CryptoSymmetricKeyGenerator, CryptoFunction { + + /** + * 加密; + * + * @param key 密钥; + * @param data 明文; + * @return + */ + Ciphertext encrypt(SymmetricKey key, byte[] data); + + /** + * 加密明文的输入流,把密文写入输出流; + * + * @param key 密钥; + * @param in 明文的输入流; + * @param out 密文的输出流; + */ + void encrypt(SymmetricKey key, InputStream in, OutputStream out); + + /** + * 解密; + * + * @param key 密钥; + * @param ciphertext 密文; + * @return + */ + byte[] decrypt(SymmetricKey key, Ciphertext ciphertext); + + /** + * 解密密文的输入流,把明文写入输出流; + * + * @param key 密钥; + * @param in 密文的输入流; + * @param out 明文的输出流; + */ + void decrypt(SymmetricKey key, InputStream in, OutputStream out); + + + /** + * 校验对称密钥格式是否满足要求; + * + * @param symmetricKeyBytes 包含算法标识、密钥掩码和对称密钥的字节数组 + * @return 是否满足指定算法的对称密钥格式 + */ + boolean supportSymmetricKey(byte[] symmetricKeyBytes); + + /** + * 将字节数组形式的密钥转换成SymmetricKey格式; + * + * @param symmetricKeyBytes 包含算法标识、密钥掩码和对称密钥的字节数组 + * @return SymmetricKey形式的对称密钥 + */ + SymmetricKey resolveSymmetricKey(byte[] symmetricKeyBytes); + + /** + * 校验密文格式是否满足要求; + * + * @param ciphertextBytes 包含算法标识和密文的字节数组 + * @return 是否满足指定算法的密文格式 + */ + boolean supportCiphertext(byte[] ciphertextBytes); + + /** + * 将字节数组形式的密文转换成SymmetricCiphertext格式; + * + * @param ciphertextBytes 包含算法标识和密文的字节数组 + * @return SymmetricCiphertext形式的签名摘要 + */ + SymmetricCiphertext resolveCiphertext(byte[] ciphertextBytes); + + +} diff --git a/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricKey.java b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricKey.java new file mode 100644 index 00000000..c226dfd3 --- /dev/null +++ b/source/crypto/crypto-framework/src/main/java/com/jd/blockchain/crypto/symmetric/SymmetricKey.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.crypto.symmetric; + +import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKeyType; +import com.jd.blockchain.crypto.base.BaseCryptoKey; + +public class SymmetricKey extends BaseCryptoKey { + + private static final long serialVersionUID = 5055547663903904933L; + + public SymmetricKey(CryptoAlgorithm algorithm, byte[] rawKeyBytes) { + super(algorithm, rawKeyBytes, SYMMETRIC_KEY); + } + + public SymmetricKey(byte[] keyBytes) { + super(keyBytes); + } + + @Override + protected boolean support(CryptoKeyType keyType) { + return SYMMETRIC_KEY == keyType; + } + + @Override + public CryptoKeyType getKeyType() { + return SYMMETRIC_KEY; + } + +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/asymmetric/AsymmtricCryptographyImplTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/asymmetric/AsymmtricCryptographyImplTest.java new file mode 100644 index 00000000..2970635b --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/asymmetric/AsymmtricCryptographyImplTest.java @@ -0,0 +1,900 @@ +package test.com.jd.blockchain.crypto.asymmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoException; +import com.jd.blockchain.crypto.CryptoKeyType; +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.utils.io.BytesUtils; + +import org.junit.Test; + +import java.util.Random; + +import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY; +import static com.jd.blockchain.crypto.CryptoKeyType.PUB_KEY; +import static org.junit.Assert.*; +import static org.junit.Assert.assertNotNull; + +public class AsymmtricCryptographyImplTest { + + @Test + public void testGenerateKeyPair() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + + //test ED25519 + CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519; + CryptoKeyPair keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + assertNotNull(keyPair); + + PubKey pubKey = keyPair.getPubKey(); + PrivKey privKey = keyPair.getPrivKey(); + + assertNotNull(pubKey); + assertNotNull(privKey); + + assertEquals(algorithm,pubKey.getAlgorithm()); + assertEquals(algorithm,privKey.getAlgorithm()); + + assertEquals(32,pubKey.getRawKeyBytes().length); + assertEquals(32,privKey.getRawKeyBytes().length); + + byte[] pubKeyBytes = pubKey.toBytes(); + byte[] privKeyBytes = privKey.toBytes(); + + assertEquals(32+1+1,pubKeyBytes.length); + assertEquals(32+1+1,privKeyBytes.length); + + assertEquals(CryptoAlgorithm.ED25519.CODE,pubKeyBytes[0]); + assertEquals(CryptoAlgorithm.ED25519.CODE,privKeyBytes[0]); + assertEquals(CryptoAlgorithm.ED25519, CryptoAlgorithm.valueOf(pubKey.getAlgorithm().CODE)); + assertEquals(CryptoAlgorithm.ED25519, CryptoAlgorithm.valueOf(privKey.getAlgorithm().CODE)); + + assertEquals(pubKey.getKeyType().CODE,pubKeyBytes[1]); + assertEquals(privKey.getKeyType().CODE,privKeyBytes[1]); + + + //test SM2 + algorithm = CryptoAlgorithm.SM2; + keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + assertNotNull(keyPair); + + pubKey = keyPair.getPubKey(); + privKey = keyPair.getPrivKey(); + + assertNotNull(pubKey); + assertNotNull(privKey); + + assertEquals(algorithm,pubKey.getAlgorithm()); + assertEquals(algorithm,privKey.getAlgorithm()); + + assertEquals(65,pubKey.getRawKeyBytes().length); + assertEquals(32,privKey.getRawKeyBytes().length); + + pubKeyBytes = pubKey.toBytes(); + privKeyBytes = privKey.toBytes(); + + assertEquals(32+1+1,privKeyBytes.length); + assertEquals(65+1+1,pubKeyBytes.length); + + assertEquals(CryptoAlgorithm.SM2.CODE,pubKeyBytes[0]); + assertEquals(CryptoAlgorithm.SM2.CODE,privKeyBytes[0]); + assertEquals(CryptoAlgorithm.SM2, CryptoAlgorithm.valueOf(pubKey.getAlgorithm().CODE)); + assertEquals(CryptoAlgorithm.SM2, CryptoAlgorithm.valueOf(privKey.getAlgorithm().CODE)); + + assertEquals(pubKey.getKeyType().CODE,pubKeyBytes[1]); + assertEquals(privKey.getKeyType().CODE,privKeyBytes[1]); + + + //test JNIED25519 + algorithm = CryptoAlgorithm.JNIED25519; + keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + assertNotNull(keyPair); + + pubKey = keyPair.getPubKey(); + privKey = keyPair.getPrivKey(); + + assertNotNull(pubKey); + assertNotNull(privKey); + + assertEquals(algorithm,pubKey.getAlgorithm()); + assertEquals(algorithm,privKey.getAlgorithm()); + + assertEquals(32,pubKey.getRawKeyBytes().length); + assertEquals(32,privKey.getRawKeyBytes().length); + + pubKeyBytes = pubKey.toBytes(); + privKeyBytes = privKey.toBytes(); + + assertEquals(32+1+1,pubKeyBytes.length); + assertEquals(32+1+1,privKeyBytes.length); + + assertEquals(CryptoAlgorithm.JNIED25519.CODE,pubKeyBytes[0]); + assertEquals(CryptoAlgorithm.JNIED25519.CODE,privKeyBytes[0]); + assertEquals(CryptoAlgorithm.JNIED25519, CryptoAlgorithm.valueOf(pubKey.getAlgorithm().CODE)); + assertEquals(CryptoAlgorithm.JNIED25519, CryptoAlgorithm.valueOf(privKey.getAlgorithm().CODE)); + + assertEquals(pubKey.getKeyType().CODE,pubKeyBytes[1]); + assertEquals(privKey.getKeyType().CODE,privKeyBytes[1]); + } + + @Test + public void testGetSignatureFunction() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + Random random = new Random(); + + + //test ED25519 + CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519; + + // 测试256字节的消息进行签名 + byte[] data = new byte[256]; + random.nextBytes(data); + verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,null); + + //错误的算法标识 + verifyGetSignatureFunction(asymmetricCrypto,CryptoAlgorithm.AES,data,32,32,64,IllegalArgumentException.class); + + data = null; + verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,NullPointerException.class); + + + //test SM2 + algorithm = CryptoAlgorithm.SM2; + + // 测试256字节的消息进行签名 + data = new byte[256]; + random.nextBytes(data); + verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,65,32,64,null); + + //错误的算法标识 + verifyGetSignatureFunction(asymmetricCrypto,CryptoAlgorithm.AES,data,65,32,64,IllegalArgumentException.class); + + data = null; + verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,65,32,64,NullPointerException.class); + + + //test JNNIED25519 + algorithm = CryptoAlgorithm.JNIED25519; + + // 测试256字节的消息进行签名 + data = new byte[256]; + random.nextBytes(data); + verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,null); + + //错误的算法标识 + verifyGetSignatureFunction(asymmetricCrypto,CryptoAlgorithm.AES,data,32,32,64,IllegalArgumentException.class); + + data = null; + verifyGetSignatureFunction(asymmetricCrypto,algorithm,data,32,32,64,IllegalArgumentException.class); + } + + private void verifyGetSignatureFunction(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, byte[] data, + int expectedPubKeyLength, int expectedPrivKeyLength, + int expectedSignatureDigestLength, Class expectedException){ + + //初始化一个异常 + Exception actualEx = null; + + try { + SignatureFunction sf = asymmetricCrypto.getSignatureFunction(algorithm); + + assertNotNull(sf); + + CryptoKeyPair keyPair = sf.generateKeyPair(); + PubKey pubKey = keyPair.getPubKey(); + PrivKey privKey = keyPair.getPrivKey(); + byte[] rawPubKeyBytes = pubKey.getRawKeyBytes(); + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + byte[] pubKeyBytes = pubKey.toBytes(); + byte[] privKeyBytes = privKey.toBytes(); + + assertEquals(algorithm, pubKey.getAlgorithm()); + assertEquals(algorithm, privKey.getAlgorithm()); + assertEquals(expectedPubKeyLength,rawPubKeyBytes.length); + assertEquals(expectedPrivKeyLength,rawPrivKeyBytes.length); + + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PUB_KEY.CODE},rawPubKeyBytes), pubKeyBytes); + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PRIV_KEY.CODE},rawPrivKeyBytes), privKeyBytes); + + SignatureDigest signatureDigest = sf.sign(privKey,data); + byte[] rawDigest = signatureDigest.getRawDigest(); + + assertEquals(algorithm,signatureDigest.getAlgorithm()); + assertEquals(expectedSignatureDigestLength,rawDigest.length); + byte[] signatureDigestBytes = signatureDigest.toBytes(); + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},rawDigest),signatureDigestBytes); + + assertTrue(signatureDigest.equals(signatureDigest)); + assertEquals(signatureDigest.hashCode(),signatureDigest.hashCode()); + + assertTrue(sf.verify(signatureDigest,pubKey,data)); + + assertTrue(sf.supportPubKey(pubKeyBytes)); + assertTrue(sf.supportPrivKey(privKeyBytes)); + assertTrue(sf.supportDigest(signatureDigestBytes)); + + assertEquals(pubKey,sf.resolvePubKey(pubKeyBytes)); + assertEquals(privKey,sf.resolvePrivKey(privKeyBytes)); + assertEquals(signatureDigest,sf.resolveDigest(signatureDigestBytes)); + + assertEquals(algorithm,sf.getAlgorithm()); + + } catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testVerify() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + Random randomData = new Random(); + + //test ED25519 + CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519; + + // 测试256字节的消息进行签名 + byte[] data = new byte[256]; + randomData.nextBytes(data); + SignatureFunction sf = asymmetricCrypto.getSignatureFunction(algorithm); + CryptoKeyPair keyPair = sf.generateKeyPair(); + byte[] pubKeyBytes = keyPair.getPubKey().toBytes(); + + byte[] signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes(); + verifyVerify(asymmetricCrypto,true,data,pubKeyBytes,signatureDigestBytes,null); + + //签名数据末尾两个字节丢失情况下,抛出异常 + byte[] truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2]; + System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length); + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,truncatedSignatureDigestBytes,IllegalArgumentException.class); + + byte[] signatureDigestBytesWithWrongAlgCode = signatureDigestBytes; + signatureDigestBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytesWithWrongAlgCode,IllegalArgumentException.class); + + signatureDigestBytes = null; + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytes,NullPointerException.class); + + + //test SM2 + algorithm = CryptoAlgorithm.SM2; + + // 测试256字节的消息进行签名 + data = new byte[256]; + randomData.nextBytes(data); + sf = asymmetricCrypto.getSignatureFunction(algorithm); + keyPair = sf.generateKeyPair(); + pubKeyBytes = keyPair.getPubKey().toBytes(); + + signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes(); + verifyVerify(asymmetricCrypto,true,data,pubKeyBytes,signatureDigestBytes,null); + + //签名数据末尾两个字节丢失情况下,抛出异常 + truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2]; + System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length); + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,truncatedSignatureDigestBytes,IllegalArgumentException.class); + + signatureDigestBytesWithWrongAlgCode = signatureDigestBytes; + signatureDigestBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytesWithWrongAlgCode,IllegalArgumentException.class); + + signatureDigestBytes = null; + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytes,NullPointerException.class); + + //test JNIED25519 + algorithm = CryptoAlgorithm.JNIED25519; + + // 测试256字节的消息进行签名 + data = new byte[256]; + randomData.nextBytes(data); + sf = asymmetricCrypto.getSignatureFunction(algorithm); + keyPair = sf.generateKeyPair(); + pubKeyBytes = keyPair.getPubKey().toBytes(); + + signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes(); + verifyVerify(asymmetricCrypto,true,data,pubKeyBytes,signatureDigestBytes,null); + + //签名数据末尾两个字节丢失情况下,抛出异常 + truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2]; + System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length); + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,truncatedSignatureDigestBytes,IllegalArgumentException.class); + + signatureDigestBytesWithWrongAlgCode = signatureDigestBytes; + signatureDigestBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytesWithWrongAlgCode,IllegalArgumentException.class); + + signatureDigestBytes = null; + verifyVerify(asymmetricCrypto,false,data,pubKeyBytes,signatureDigestBytes,NullPointerException.class); + } + + private void verifyVerify(AsymmetricCryptography asymmetricCrypto,boolean expectedResult,byte[] data, + byte[] pubKeyBytes, byte[] signatureDigestBytes, Class expectedException){ + + //初始化一个异常 + Exception actualEx = null; + boolean pass = false; + + try { + + pass = asymmetricCrypto.verify(signatureDigestBytes,pubKeyBytes,data); + + } + catch (Exception e){ + actualEx = e; + } + + assertEquals(expectedResult, pass); + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testGetAsymmetricEncryptionFunction() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + Random random = new Random(); + + + //test SM2 + CryptoAlgorithm algorithm = CryptoAlgorithm.SM2; + + //Case 1: SM2Encryption with 16 bytes data + byte[] data = new byte[16]; + random.nextBytes(data); + verifyGetAsymmetricEncryptionFunction(asymmetricCrypto, algorithm,65,32,65+16+32,data,null); + + //Case 2: SM2Encryption with 256 bytes data + data = new byte[256]; + random.nextBytes(data); + verifyGetAsymmetricEncryptionFunction(asymmetricCrypto, algorithm,65,32,65+256+32,data,null); + + //Case 3: SM2Encryption with 1 bytes data + data = new byte[3]; + random.nextBytes(data); + verifyGetAsymmetricEncryptionFunction(asymmetricCrypto, algorithm,65,32,65+3+32,data,null); + + //Case 4: SM2Encryption with wrong algorithm + verifyGetAsymmetricEncryptionFunction(asymmetricCrypto,CryptoAlgorithm.AES,65,32,65+3+32,data,IllegalArgumentException.class); + + //Case 5: SM2Encryption with null data + data = null; + verifyGetAsymmetricEncryptionFunction(asymmetricCrypto,algorithm,65,32,65+32,data,NullPointerException.class); + } + + private void verifyGetAsymmetricEncryptionFunction(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, + int expectedPubKeyLength, int expectedPrivKeyLength, + int expectedCiphertextLength, byte[] data, Class expectedException){ + + //初始化一个异常 + Exception actualEx = null; + + try { + AsymmetricEncryptionFunction aef = asymmetricCrypto.getAsymmetricEncryptionFunction(algorithm); + //验证获取的算法实例非空 + assertNotNull(aef); + + CryptoKeyPair keyPair = aef.generateKeyPair(); + PubKey pubKey = keyPair.getPubKey(); + PrivKey privKey = keyPair.getPrivKey(); + byte[] rawPubKeyBytes = pubKey.getRawKeyBytes(); + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + byte[] pubKeyBytes = pubKey.toBytes(); + byte[] privKeyBytes = privKey.toBytes(); + + assertEquals(algorithm, pubKey.getAlgorithm()); + assertEquals(algorithm, privKey.getAlgorithm()); + assertEquals(expectedPubKeyLength,rawPubKeyBytes.length); + assertEquals(expectedPrivKeyLength,rawPrivKeyBytes.length); + + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PUB_KEY.CODE},rawPubKeyBytes), pubKeyBytes); + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{CryptoKeyType.PRIV_KEY.CODE},rawPrivKeyBytes), privKeyBytes); + + Ciphertext ciphertext = aef.encrypt(pubKey,data); + byte[] rawCiphertextBytes = ciphertext.getRawCiphertext(); + + assertEquals(algorithm,ciphertext.getAlgorithm()); + assertEquals(expectedCiphertextLength,rawCiphertextBytes.length); + byte[] ciphertextBytes = ciphertext.toBytes(); + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},rawCiphertextBytes),ciphertextBytes); + + assertArrayEquals(data,aef.decrypt(privKey,ciphertext)); + + assertTrue(aef.supportPubKey(pubKeyBytes)); + assertTrue(aef.supportPrivKey(privKeyBytes)); + assertTrue(aef.supportCiphertext(ciphertextBytes)); + + assertEquals(pubKey,aef.resolvePubKey(pubKeyBytes)); + assertEquals(privKey,aef.resolvePrivKey(privKeyBytes)); + assertEquals(ciphertext,aef.resolveCiphertext(ciphertextBytes)); + + assertEquals(algorithm,aef.getAlgorithm()); + + + }catch (Exception e){ + actualEx = e; + } + + if(expectedException == null){ + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testDecrypt() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + Random random = new Random(); + + byte[] data = new byte[16]; + random.nextBytes(data); + + //test SM2 + CryptoAlgorithm algorithm = CryptoAlgorithm.SM2; + AsymmetricEncryptionFunction aef = asymmetricCrypto.getAsymmetricEncryptionFunction(algorithm); + CryptoKeyPair keyPair = aef.generateKeyPair(); + PubKey pubKey = keyPair.getPubKey(); + PrivKey privKey = keyPair.getPrivKey(); + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + Ciphertext ciphertext = aef.encrypt(pubKey,data); + byte[] ciphertextBytes = ciphertext.toBytes(); + + verifyDecrypt(asymmetricCrypto, algorithm, rawPrivKeyBytes, data, ciphertextBytes, null); + + //密钥的算法标识与密文的算法标识不一致情况 + verifyDecrypt(asymmetricCrypto, CryptoAlgorithm.AES, rawPrivKeyBytes, data, ciphertextBytes, IllegalArgumentException.class); + + //密文末尾两个字节丢失情况下,抛出异常 + byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2]; + System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length); + verifyDecrypt(asymmetricCrypto, algorithm, rawPrivKeyBytes, data, truncatedCiphertextBytes, com.jd.blockchain.crypto.CryptoException.class); + + byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes; + ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyDecrypt(asymmetricCrypto,algorithm,rawPrivKeyBytes,data,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class); + + ciphertextBytes = null; + verifyDecrypt(asymmetricCrypto,algorithm,rawPrivKeyBytes,data,ciphertextBytes,NullPointerException.class); + } + + private void verifyDecrypt(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, + byte[] key, byte[] data, byte[] ciphertextBytes, Class expectedException){ + Exception actualEx = null; + + try { + PrivKey privKey = new PrivKey(algorithm,key); + + byte[] plaintext = asymmetricCrypto.decrypt(privKey.toBytes(), ciphertextBytes); + + //解密后的明文与初始的明文一致 + assertArrayEquals(data,plaintext); + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testResolveCiphertext() { + + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + Random random = new Random(); + + byte[] data = new byte[16]; + random.nextBytes(data); + + //test SM2 + CryptoAlgorithm algorithm = CryptoAlgorithm.SM2; + AsymmetricEncryptionFunction aef = asymmetricCrypto.getAsymmetricEncryptionFunction(algorithm); + CryptoKeyPair keyPair = aef.generateKeyPair(); + PubKey pubKey = keyPair.getPubKey(); + PrivKey privKey = keyPair.getPrivKey(); + byte[] rawPrivKeyBytes = privKey.getRawKeyBytes(); + Ciphertext ciphertext = aef.encrypt(pubKey,data); + byte[] ciphertextBytes = ciphertext.toBytes(); + + verifyResolveCiphertext(asymmetricCrypto, algorithm, ciphertextBytes, null); + + + //密文末尾两个字节丢失情况下,抛出异常 + byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2]; + System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length); + verifyDecrypt(asymmetricCrypto, algorithm, rawPrivKeyBytes, data, truncatedCiphertextBytes, CryptoException.class); + + byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes; + ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolveCiphertext(asymmetricCrypto,algorithm,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class); + + ciphertextBytes = null; + verifyResolveCiphertext(asymmetricCrypto,algorithm,ciphertextBytes,NullPointerException.class); + } + + private void verifyResolveCiphertext(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, byte[] ciphertextBytes, + Class expectedException){ + Exception actualEx = null; + + try { + + Ciphertext ciphertext = asymmetricCrypto.resolveCiphertext(ciphertextBytes); + + assertNotNull(ciphertext); + + assertEquals(algorithm, ciphertext.getAlgorithm()); + + assertArrayEquals(ciphertextBytes, ciphertext.toBytes()); + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testTryResolveCiphertext() { + } + + @Test + public void testResolveSignatureDigest() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + Random randomData = new Random(); + + //test ED25519 + CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519; + + // 测试256字节的消息进行签名 + byte[] data = new byte[256]; + randomData.nextBytes(data); + SignatureFunction sf = asymmetricCrypto.getSignatureFunction(algorithm); + CryptoKeyPair keyPair = sf.generateKeyPair(); + + byte[] signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes(); + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,null); + + //签名数据末尾两个字节丢失情况下,抛出异常 + byte[] truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2]; + System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length); + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,truncatedSignatureDigestBytes,IllegalArgumentException.class); + + signatureDigestBytes = null; + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,NullPointerException.class); + + + //test SM2 + algorithm = CryptoAlgorithm.SM2; + + // 测试256字节的消息进行签名 + data = new byte[256]; + randomData.nextBytes(data); + sf = asymmetricCrypto.getSignatureFunction(algorithm); + keyPair = sf.generateKeyPair(); + + signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes(); + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,null); + + //签名数据末尾两个字节丢失情况下,抛出异常 + truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2]; + System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length); + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,truncatedSignatureDigestBytes,IllegalArgumentException.class); + + signatureDigestBytes = null; + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,NullPointerException.class); + + //test JNIED25519 + algorithm = CryptoAlgorithm.JNIED25519; + + // 测试256字节的消息进行签名 + data = new byte[256]; + randomData.nextBytes(data); + sf = asymmetricCrypto.getSignatureFunction(algorithm); + keyPair = sf.generateKeyPair(); + + signatureDigestBytes = sf.sign(keyPair.getPrivKey(),data).toBytes(); + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,null); + + //签名数据末尾两个字节丢失情况下,抛出异常 + truncatedSignatureDigestBytes = new byte[signatureDigestBytes.length-2]; + System.arraycopy(signatureDigestBytes,0,truncatedSignatureDigestBytes,0,truncatedSignatureDigestBytes.length); + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,truncatedSignatureDigestBytes,IllegalArgumentException.class); + + signatureDigestBytes = null; + verifyResolveSignatureDigest(asymmetricCrypto,algorithm,64,signatureDigestBytes,NullPointerException.class); + } + + private void verifyResolveSignatureDigest(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, + int expectedSignatureDigestLength, + byte[] signatureDigestBytes, Class expectedException){ + + //初始化一个异常 + Exception actualEx = null; + + try { + + SignatureDigest signatureDigest = asymmetricCrypto.resolveSignatureDigest(signatureDigestBytes); + + assertNotNull(signatureDigest); + + assertEquals(algorithm,signatureDigest.getAlgorithm()); + + assertEquals(expectedSignatureDigestLength,signatureDigest.getRawDigest().length); + + assertArrayEquals(signatureDigestBytes,signatureDigest.toBytes()); + + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testTryResolveSignatureDigest() { + } + + @Test + public void testResolvePubKey() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + + //test ED25519 + CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519; + + CryptoKeyPair keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + byte[] pubKeyBytes = keyPair.getPubKey().toBytes(); + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,null); + + byte[] truncatedPubKeyBytes = new byte[pubKeyBytes.length-2]; + System.arraycopy(pubKeyBytes,0,truncatedPubKeyBytes,0,truncatedPubKeyBytes.length); + verifyResolvePubKey(asymmetricCrypto,algorithm,32,truncatedPubKeyBytes,IllegalArgumentException.class); + + byte[] pubKeyBytesWithWrongAlgCode = pubKeyBytes; + pubKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + byte[] pubKeyBytesWithWrongKeyType= pubKeyBytes; + pubKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE; + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + pubKeyBytes = null; + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,NullPointerException.class); + + + //test SM2 + algorithm = CryptoAlgorithm.SM2; + + keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + pubKeyBytes = keyPair.getPubKey().toBytes(); + verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytes,null); + + truncatedPubKeyBytes = new byte[pubKeyBytes.length-2]; + System.arraycopy(pubKeyBytes,0,truncatedPubKeyBytes,0,truncatedPubKeyBytes.length); + verifyResolvePubKey(asymmetricCrypto,algorithm,65,truncatedPubKeyBytes,IllegalArgumentException.class); + + pubKeyBytesWithWrongAlgCode = pubKeyBytes; + pubKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + pubKeyBytesWithWrongKeyType= pubKeyBytes; + pubKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE; + verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + pubKeyBytes = null; + verifyResolvePubKey(asymmetricCrypto,algorithm,65,pubKeyBytes,NullPointerException.class); + + //test JNIED25519 + algorithm = CryptoAlgorithm.JNIED25519; + + keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + pubKeyBytes = keyPair.getPubKey().toBytes(); + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,null); + + truncatedPubKeyBytes = new byte[pubKeyBytes.length-2]; + System.arraycopy(pubKeyBytes,0,truncatedPubKeyBytes,0,truncatedPubKeyBytes.length); + verifyResolvePubKey(asymmetricCrypto,algorithm,32,truncatedPubKeyBytes,IllegalArgumentException.class); + + pubKeyBytesWithWrongAlgCode = pubKeyBytes; + pubKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + pubKeyBytesWithWrongKeyType= pubKeyBytes; + pubKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE; + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + pubKeyBytes = null; + verifyResolvePubKey(asymmetricCrypto,algorithm,32,pubKeyBytes,NullPointerException.class); + } + + private void verifyResolvePubKey(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, + int expectedPubKeyLength, byte[] pubKeyBytes,Class expectedException){ + + Exception actualEx = null; + + try { + PubKey pubKey = asymmetricCrypto.resolvePubKey(pubKeyBytes); + + assertNotNull(pubKey); + + assertEquals(algorithm, pubKey.getAlgorithm()); + + assertEquals(expectedPubKeyLength, pubKey.getRawKeyBytes().length); + + assertArrayEquals(pubKeyBytes, pubKey.toBytes()); + + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testTryResolvePubKey() { + } + + @Test + public void testResolvePrivKey() { + + AsymmetricCryptography asymmetricCrypto = new AsymmtricCryptographyImpl(); + + //test ED25519 + CryptoAlgorithm algorithm = CryptoAlgorithm.ED25519; + + CryptoKeyPair keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + byte[] privKeyBytes = keyPair.getPrivKey().toBytes(); + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,null); + + byte[] truncatedPrivKeyBytes = new byte[privKeyBytes.length-2]; + System.arraycopy(privKeyBytes,0,truncatedPrivKeyBytes,0,truncatedPrivKeyBytes.length); + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,truncatedPrivKeyBytes,IllegalArgumentException.class); + + byte[] privKeyBytesWithWrongAlgCode = privKeyBytes; + privKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + byte[] privKeyBytesWithWrongKeyType = privKeyBytes; + privKeyBytesWithWrongKeyType[1] = PUB_KEY.CODE; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + privKeyBytes = null; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,NullPointerException.class); + + + //test SM2 + algorithm = CryptoAlgorithm.SM2; + + keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + privKeyBytes = keyPair.getPrivKey().toBytes(); + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,null); + + truncatedPrivKeyBytes = new byte[privKeyBytes.length-2]; + System.arraycopy(privKeyBytes,0,truncatedPrivKeyBytes,0,truncatedPrivKeyBytes.length); + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,truncatedPrivKeyBytes,IllegalArgumentException.class); + + privKeyBytesWithWrongAlgCode = privKeyBytes; + privKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + privKeyBytesWithWrongKeyType = privKeyBytes; + privKeyBytesWithWrongKeyType[1] = PUB_KEY.CODE; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + privKeyBytes = null; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,NullPointerException.class); + + //test JNIED25519 + algorithm = CryptoAlgorithm.JNIED25519; + + keyPair = asymmetricCrypto.generateKeyPair(algorithm); + + privKeyBytes = keyPair.getPrivKey().toBytes(); + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,null); + + truncatedPrivKeyBytes = new byte[privKeyBytes.length-2]; + System.arraycopy(privKeyBytes,0,truncatedPrivKeyBytes,0,truncatedPrivKeyBytes.length); + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,truncatedPrivKeyBytes,IllegalArgumentException.class); + + privKeyBytesWithWrongAlgCode = privKeyBytes; + privKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + privKeyBytesWithWrongKeyType = privKeyBytes; + privKeyBytesWithWrongKeyType[1] = PUB_KEY.CODE; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + privKeyBytes = null; + verifyResolvePrivKey(asymmetricCrypto,algorithm,32,privKeyBytes,NullPointerException.class); + } + + private void verifyResolvePrivKey(AsymmetricCryptography asymmetricCrypto, CryptoAlgorithm algorithm, + int expectedPrivKeyLength, byte[] privKeyBytes,Class expectedException){ + + Exception actualEx = null; + + try { + PrivKey privKey = asymmetricCrypto.resolvePrivKey(privKeyBytes); + + assertNotNull(privKey); + + assertEquals(algorithm, privKey.getAlgorithm()); + + assertEquals(expectedPrivKeyLength, privKey.getRawKeyBytes().length); + + assertArrayEquals(privKeyBytes, privKey.toBytes()); + + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testTryResolvePrivKey() { + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/hash/HashCryptographyImplTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/hash/HashCryptographyImplTest.java new file mode 100644 index 00000000..513cb577 --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/hash/HashCryptographyImplTest.java @@ -0,0 +1,333 @@ +package test.com.jd.blockchain.crypto.hash; + +import static org.junit.Assert.*; + +import java.util.Random; + +import com.jd.blockchain.crypto.smutils.hash.SM3Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.security.RipeMD160Utils; +import com.jd.blockchain.utils.security.ShaUtils; + +import org.junit.Test; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashCryptography; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.hash.HashFunction; +import com.jd.blockchain.crypto.impl.HashCryptographyImpl; + +public class HashCryptographyImplTest { + + @Test + public void testGetFunction() { + HashCryptography hashCrypto = new HashCryptographyImpl(); + Random rand = new Random(); + // test SHA256 + CryptoAlgorithm algorithm = CryptoAlgorithm.SHA256; + byte[] data = new byte[256]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = new byte[0]; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = new byte[1056]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = null; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,NullPointerException.class); + + + // test RIPEMD160 + algorithm = CryptoAlgorithm.RIPEMD160; + data=new byte[256]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null); + + data = new byte[0]; + verifyGetFunction(hashCrypto, algorithm, data, 160/ 8,null); + + data = new byte[1056]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null); + + data = null; + verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,NullPointerException.class); + + // test SM3 + algorithm = CryptoAlgorithm.SM3; + data = new byte[256]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = new byte[0]; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = new byte[1056]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = null; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,NullPointerException.class); + + // test AES + data = new byte[0]; + algorithm = CryptoAlgorithm.AES; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,IllegalArgumentException.class); + + // test JNISHA256 + algorithm = CryptoAlgorithm.JNISHA256; + data = new byte[256]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = new byte[0]; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = new byte[1056]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,null); + + data = null; + verifyGetFunction(hashCrypto, algorithm, data, 256 / 8,IllegalArgumentException.class); + + // test JNIRIPEMD160 + algorithm = CryptoAlgorithm.JNIRIPEMD160; + data=new byte[256]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null); + + data = new byte[0]; + verifyGetFunction(hashCrypto, algorithm, data, 160/ 8,null); + + data = new byte[1056]; + rand.nextBytes(data); + verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,null); + + data = null; + verifyGetFunction(hashCrypto, algorithm, data, 160 / 8,IllegalArgumentException.class); + } + + private void verifyGetFunction(HashCryptography hashCrypto, CryptoAlgorithm algorithm, byte[] data, + int expectedRawBytes,Class expectedException) { + Exception actualEx = null; + try { + HashFunction hf = hashCrypto.getFunction(algorithm); + assertNotNull(hf); + + HashDigest hd = hf.hash(data); + + assertEquals(algorithm, hd.getAlgorithm()); + + assertEquals(expectedRawBytes, hd.getRawDigest().length); + + // verify encoding; + byte[] encodedHash = hd.toBytes(); + assertEquals(expectedRawBytes + 1, encodedHash.length); + + + assertEquals(algorithm.CODE, encodedHash[0]); + + //verify equals + assertEquals(true, hd.equals(hf.hash(data))); + + //verify verify + assertTrue( hf.verify(hd, data)); + + } catch (Exception e) { + actualEx = e; + } + + if(expectedException==null){ + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testVerifyHashDigestByteArray() { + HashCryptography hashCrypto = new HashCryptographyImpl(); + //test SHA256 + byte[] data=new byte[256]; + Random rand = new Random(); + rand.nextBytes(data); + CryptoAlgorithm algorithm=CryptoAlgorithm.SHA256; + verifyHashDigestByteArray(hashCrypto,algorithm,data,null); + data=null; + verifyHashDigestByteArray(hashCrypto,algorithm,data,NullPointerException.class); + + //test RIPEMD160 + algorithm=CryptoAlgorithm.RIPEMD160; + data=new byte[896]; + rand.nextBytes(data); + verifyHashDigestByteArray(hashCrypto,algorithm,data,null); + data=null; + verifyHashDigestByteArray(hashCrypto,algorithm,data,NullPointerException.class); + + //test SM3 + algorithm=CryptoAlgorithm.SM3; + data=new byte[896]; + rand.nextBytes(data); + verifyHashDigestByteArray(hashCrypto,algorithm,data,null); + data=null; + verifyHashDigestByteArray(hashCrypto,algorithm,data,NullPointerException.class); + + + //test AES + algorithm=CryptoAlgorithm.AES; + data=new byte[277]; + rand.nextBytes(data); + verifyHashDigestByteArray(hashCrypto,algorithm,data,IllegalArgumentException.class); + + //test JNISHA256 + data=new byte[256]; + rand = new Random(); + rand.nextBytes(data); + algorithm=CryptoAlgorithm.JNISHA256; + verifyHashDigestByteArray(hashCrypto,algorithm,data,null); + data=null; + verifyHashDigestByteArray(hashCrypto,algorithm,data,IllegalArgumentException.class); + + //test JNIRIPEMD160 + algorithm=CryptoAlgorithm.JNIRIPEMD160; + data=new byte[896]; + rand.nextBytes(data); + verifyHashDigestByteArray(hashCrypto,algorithm,data,null); + data=null; + verifyHashDigestByteArray(hashCrypto,algorithm,data,IllegalArgumentException.class); + } + + private void verifyHashDigestByteArray(HashCryptography hashCrypto,CryptoAlgorithm algorithm,byte[] data,Class expectedException){ + Exception actualEx=null; + try { + HashFunction hf = hashCrypto.getFunction(algorithm); + assertNotNull(hf); + HashDigest hd = hf.hash(data); + hashCrypto.verify(hd,data); + }catch (Exception e) + { + actualEx=e; + } + if (expectedException==null) + { + assertNull(actualEx); + } + else{ + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testResolveHashDigest() { + Random rand = new Random(); + HashCryptography hashCrypto = new HashCryptographyImpl(); + + //test SHA256 + CryptoAlgorithm algorithm = CryptoAlgorithm.SHA256; + byte[] data = new byte[256]; + rand.nextBytes(data); + byte[] hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes(); + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,null); + + byte[] truncatedHashDigestBytes = new byte[hashDigestBytes.length-2]; + System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length); + verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,32+1,IllegalArgumentException.class); + + hashDigestBytes = null; + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,NullPointerException.class); + + + //test RIPEMD160 + algorithm = CryptoAlgorithm.RIPEMD160; + data = new byte[256]; + rand.nextBytes(data); + hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes(); + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,null); + + truncatedHashDigestBytes = new byte[hashDigestBytes.length-2]; + System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length); + verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,20+1,IllegalArgumentException.class); + + hashDigestBytes = null; + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,NullPointerException.class); + + + //test SM3 + algorithm = CryptoAlgorithm.SM3; + data = new byte[256]; + rand.nextBytes(data); + hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes(); + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,null); + + truncatedHashDigestBytes = new byte[hashDigestBytes.length-2]; + System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length); + verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,32+1,IllegalArgumentException.class); + + hashDigestBytes = null; + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,NullPointerException.class); + + + //test JNISHA256 + algorithm = CryptoAlgorithm.JNISHA256; + data = new byte[256]; + rand.nextBytes(data); + hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes(); + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,null); + + truncatedHashDigestBytes = new byte[hashDigestBytes.length-2]; + System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length); + verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,32+1,IllegalArgumentException.class); + + hashDigestBytes = null; + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,32+1,NullPointerException.class); + + //test JNIRIPEMD160 + algorithm = CryptoAlgorithm.JNIRIPEMD160; + data = new byte[256]; + rand.nextBytes(data); + hashDigestBytes = hashCrypto.getFunction(algorithm).hash(data).toBytes(); + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,null); + + truncatedHashDigestBytes = new byte[hashDigestBytes.length-2]; + System.arraycopy(hashDigestBytes,0,truncatedHashDigestBytes,0,truncatedHashDigestBytes.length); + verifyResolveHashDigest(algorithm, hashCrypto,truncatedHashDigestBytes,20+1,IllegalArgumentException.class); + + hashDigestBytes = null; + verifyResolveHashDigest(algorithm, hashCrypto,hashDigestBytes,20+1,NullPointerException.class); + } + + private void verifyResolveHashDigest(CryptoAlgorithm algorithm,HashCryptography + hashCrypto,byte[] hashDigestBytes,int expectedLength,ClassexpectedException){ + + Exception actualEx=null; + + try { + + HashDigest hashDigest=hashCrypto.resolveHashDigest(hashDigestBytes); + assertNotNull(hashDigest); + assertEquals(algorithm,hashDigest.getAlgorithm()); + byte[] algBytes = new byte[1]; + algBytes[0] = algorithm.CODE; + assertArrayEquals(hashDigestBytes,BytesUtils.concat(algBytes,hashDigest.getRawDigest())); + assertEquals(expectedLength,hashDigestBytes.length); + + }catch (Exception e) + { + actualEx = e; + } + if (expectedException==null) + { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + } diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIED25519UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIED25519UtilsTest.java new file mode 100644 index 00000000..8cbce2bb --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIED25519UtilsTest.java @@ -0,0 +1,123 @@ +package test.com.jd.blockchain.crypto.jniutils; + +import com.jd.blockchain.crypto.jniutils.asymmetric.JNIED25519Utils; + + +public class JNIED25519UtilsTest { + + /* Program entry function */ + public static void main(String args[]) { + + byte[] msg = "abc".getBytes(); + int i; + int j; + int count = 10000; + + long startTS; + long elapsedTS; + + byte[] privKey = new byte[32]; + byte[] pubKey = new byte[32]; + byte[] signature; + + + JNIED25519Utils ed25519 = new JNIED25519Utils(); + + System.out.println("=================== Key Generation test ==================="); + ed25519.generateKeyPair(privKey,pubKey); + System.out.println("Private Key: "); + for(i = 0; i < privKey.length; i++) { + System.out.print(privKey[i] + " "); + if((i+1)%8 == 0) + System.out.println(); + } + System.out.println(); + System.out.println("Public Key: "); + for(i = 0; i < pubKey.length; i++) { + System.out.print(pubKey[i] + " "); + if((i+1)%8 == 0) + System.out.println(); + } + System.out.println(); + + System.out.println("=================== Public Key Retrieval test ==================="); + byte[] pk; + pk = ed25519.getPubKey(privKey); + System.out.println("Retrieved Public Key: "); + for(i = 0; i < pk.length; i++) { + System.out.print(pk[i] + " "); + if((i+1)%8 == 0) + System.out.println(); + } + System.out.println(); + + System.out.println("=================== Signing test ==================="); + signature = ed25519.sign(msg,privKey,pubKey); + System.out.println("Signature: "); + for(i = 0; i < signature.length; i++) { + System.out.print(signature[i] + " "); + if((i+1)%8 == 0) + System.out.println(); + } + System.out.println(); + + System.out.println("=================== Verifying test ==================="); + if (ed25519.verify(msg,pubKey,signature)) + System.out.println("valid signature"); + else System.out.println("invalid signature"); + + System.out.println("=================== Do ED25519 Key Pair Generation Test ==================="); + + + for (j = 0; j < 5; j++) { + System.out.println("------------- round[" + j + "] --------------"); + startTS = System.currentTimeMillis(); + for (i = 0; i < count; i++) { + ed25519.generateKeyPair(privKey,pubKey); + } + elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("ED25519 Key Pair Generation: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + System.out.println(); + + System.out.println("=================== Do ED25519 Public Key Retrieval Test ==================="); + for (j = 0; j < 5; j++) { + System.out.println("------------- round[" + j + "] --------------"); + startTS = System.currentTimeMillis(); + for (i = 0; i < count; i++) { + ed25519.getPubKey(privKey); + } + elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("ED25519 Public Key Retrieval: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + System.out.println(); + + System.out.println("=================== Do ED25519 Signing Test ==================="); + for (j = 0; j < 5; j++) { + System.out.println("------------- round[" + j + "] --------------"); + startTS = System.currentTimeMillis(); + for (i = 0; i < count; i++) { + ed25519.sign(msg,privKey,pubKey); + } + elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("ED25519 Signing: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + System.out.println(); + + System.out.println("=================== Do ED25519 Verifying Test ==================="); + for (j = 0; j < 5; j++) { + System.out.println("------------- round[" + j + "] --------------"); + startTS = System.currentTimeMillis(); + for (i = 0; i < count; i++) { + ed25519.verify(msg,pubKey,signature); + } + elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("ED25519 Verifying: Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + System.out.println(); + } +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIMBSHA256UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIMBSHA256UtilsTest.java new file mode 100644 index 00000000..e5b5ef2f --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIMBSHA256UtilsTest.java @@ -0,0 +1,111 @@ +package test.com.jd.blockchain.crypto.jniutils; + +import com.jd.blockchain.crypto.jniutils.hash.JNIMBSHA256Utils; + +public class JNIMBSHA256UtilsTest { + /* Program entry function */ + public static void main(String args[]) { + + String osName = System.getProperty("os.name").toLowerCase(); + + if (! osName.contains("linux")) { + return ; + } + + byte[] array1 = "abc".getBytes(); + byte[] array2 = "abcd".getBytes(); + byte[] array3 = "abcde".getBytes(); + byte[] array4 = "abcdef".getBytes(); + + byte[][] arrays = {array1,array2,array3,array4}; + JNIMBSHA256Utils mbsha256 = new JNIMBSHA256Utils(); + byte[][] results = mbsha256.multiBufferHash(arrays); + + System.out.println("JAVA to C : "); + for (int i = 0; i < arrays.length; i++) { + for (int j = 0; j < arrays[i].length; j++) { + System.out.print(arrays[i][j] + " "); + } + System.out.println(); + } + + System.out.println(); + + System.out.println("C to JAVA : "); + for (int i = 0; i < results.length; i++) { + for (int j = 0; j < results[i].length; j++) { + System.out.print(results[i][j] + " "); + } + System.out.println(); + } + + System.out.println(); + + String str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + byte[] array = str.getBytes(); + + + int count = 1000000; + + + byte[][] arraysx4 = {array,array,array,array}; + byte[][] arraysx8 = {array,array,array,array,array,array,array,array}; + byte[][] arraysx16 = {array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array}; + byte[][] arraysx32 = {array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array,array}; + + + System.out.println("=================== do MBSHA256 hash test in x4==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + mbsha256.multiBufferHash(arraysx4); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*4)); + } + System.out.println(); + System.out.println(); + + System.out.println("=================== do MBSHA256 hash test in x8==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + mbsha256.multiBufferHash(arraysx8); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*8)); + } + System.out.println(); + System.out.println(); + + System.out.println("=================== do MBSHA256 hash test in x16==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + mbsha256.multiBufferHash(arraysx16); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*16)); + } + System.out.println(); + System.out.println(); + + System.out.println("=================== do MBSHA256 hash test in x32==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + mbsha256.multiBufferHash(arraysx32); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f; Total KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS, (count * 1000.00D) / elapsedTS*32)); + } + } +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIRIPEMD160UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIRIPEMD160UtilsTest.java new file mode 100644 index 00000000..47bb7bda --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNIRIPEMD160UtilsTest.java @@ -0,0 +1,41 @@ +package test.com.jd.blockchain.crypto.jniutils; + +import com.jd.blockchain.crypto.jniutils.hash.JNIRIPEMD160Utils; + +public class JNIRIPEMD160UtilsTest { + + /* Program entry function */ + public static void main(String args[]) { + byte[] array1 = "abc".getBytes(); + byte[] array2; + JNIRIPEMD160Utils ripemd160 = new JNIRIPEMD160Utils(); + array2 = ripemd160.hash(array1); + + System.out.print("JAVA to C : "); + for (byte anArray1 : array1) { + System.out.print(anArray1 + " "); + } + System.out.println(); + System.out.print("C to JAVA : "); + for (byte anArray2 : array2) { + System.out.print(anArray2 + " "); + } + System.out.println(); + + String str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + byte[] array = str.getBytes(); + int count = 1000000; + + System.out.println("=================== do RIPEMD160 hash test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + ripemd160.hash(array); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("RIPEMD160 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNISHA256UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNISHA256UtilsTest.java new file mode 100644 index 00000000..10e040f8 --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/jniutils/JNISHA256UtilsTest.java @@ -0,0 +1,41 @@ +package test.com.jd.blockchain.crypto.jniutils; + +import com.jd.blockchain.crypto.jniutils.hash.JNISHA256Utils; + +public class JNISHA256UtilsTest { + + /* Program entry function */ + public static void main(String args[]) { + byte[] array1 = "abc".getBytes(); + byte[] array2; + JNISHA256Utils sha256 = new JNISHA256Utils(); + array2 = sha256.hash(array1); + System.out.print("JAVA to C : "); + for (byte anArray1 : array1) { + System.out.print(anArray1 + " "); + } + System.out.println(); + System.out.print("C to JAVA : "); + for (byte anArray2 : array2) { + System.out.print(anArray2 + " "); + } + System.out.println(); + + + String str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; + byte[] array = str.getBytes(); + int count = 1000000; + + System.out.println("=================== do SHA256 hash test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sha256.hash(array); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + } +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyAsymmetricEncryptionTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyAsymmetricEncryptionTest.java new file mode 100644 index 00000000..df33e94c --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyAsymmetricEncryptionTest.java @@ -0,0 +1,55 @@ +package test.com.jd.blockchain.crypto.performance; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction; +import org.bouncycastle.util.encoders.Hex; + +public class MyAsymmetricEncryptionTest { + + public static void main(String[] args) { + + String string1K = "0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210"; + String string1M = ""; + for (int i = 0; i < 1024 ; i++) + { + string1M = string1M + string1K; + } + + byte[] data1K = Hex.decode(string1K); + byte[] data1M = Hex.decode(string1M); + int count = 10000; + + SM2CryptoFunction sm2 = new SM2CryptoFunction(); + CryptoKeyPair keyPairSM2 = sm2.generateKeyPair(); + PrivKey privKeySM2 = keyPairSM2.getPrivKey(); + PubKey pubKeySM2 = keyPairSM2.getPubKey(); + + System.out.println("=================== do SM2 encrypt test ==================="); + Ciphertext ciphertextSM2 = null; + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + ciphertextSM2 = sm2.encrypt(pubKeySM2,data1K); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM2 Encrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + System.out.println("=================== do SM2 decrypt test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sm2.decrypt(privKeySM2,ciphertextSM2); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM2 Decrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + } +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyHashTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyHashTest.java new file mode 100644 index 00000000..0c2733b0 --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MyHashTest.java @@ -0,0 +1,62 @@ +package test.com.jd.blockchain.crypto.performance; + +import com.jd.blockchain.crypto.impl.def.hash.RIPEMD160HashFunction; +import com.jd.blockchain.crypto.impl.def.hash.SHA256HashFunction; +import com.jd.blockchain.crypto.impl.sm.hash.SM3HashFunction; + +import java.util.Random; + +public class MyHashTest { + + public static void main(String[] args) { + + Random rand = new Random(); + byte[] data1K = new byte[1024]; + rand.nextBytes(data1K); + int count = 1000000; + + SHA256HashFunction sha256hf = new SHA256HashFunction(); + + System.out.println("=================== do SHA256 hash test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sha256hf.hash(data1K); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SHA256 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + RIPEMD160HashFunction ripemd160hf = new RIPEMD160HashFunction(); + + System.out.println("=================== do RIPEMD160 hash test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + ripemd160hf.hash(data1K); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("RIPEMD160 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + SM3HashFunction sm3hf = new SM3HashFunction(); + + System.out.println("=================== do SM3 hash test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sm3hf.hash(data1K); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM3 hashing Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + } +} + diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySignatureTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySignatureTest.java new file mode 100644 index 00000000..189d3011 --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySignatureTest.java @@ -0,0 +1,83 @@ +package test.com.jd.blockchain.crypto.performance; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.impl.def.asymmetric.ED25519SignatureFunction; +import com.jd.blockchain.crypto.impl.sm.asymmetric.SM2CryptoFunction; + +import java.util.Random; + +public class MySignatureTest { + + public static void main(String[] args) { + + Random rand = new Random(); + byte[] data = new byte[64]; + rand.nextBytes(data); + int count = 10000; + + ED25519SignatureFunction ed25519sf = new ED25519SignatureFunction(); + CryptoKeyPair keyPairED25519 = ed25519sf.generateKeyPair(); + PrivKey privKeyED25519 = keyPairED25519.getPrivKey(); + PubKey pubKeyED25519 = keyPairED25519.getPubKey(); + + System.out.println("=================== do ED25519 sign test ==================="); + SignatureDigest signatureDigestED25519 = null; + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + signatureDigestED25519 = ed25519sf.sign(privKeyED25519,data); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("ED25519 Signing Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + System.out.println("=================== do ED25519 verify test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + ed25519sf.verify(signatureDigestED25519,pubKeyED25519,data); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("ED25519 Verifying Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + SM2CryptoFunction sm2 = new SM2CryptoFunction(); + CryptoKeyPair keyPairSM2 = sm2.generateKeyPair(); + PrivKey privKeySM2 = keyPairSM2.getPrivKey(); + PubKey pubKeySM2 = keyPairSM2.getPubKey(); + + + System.out.println("=================== do SM2 sign test ==================="); + SignatureDigest signatureDigestSM2 = null; + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + signatureDigestSM2 = sm2.sign(privKeySM2,data); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM2 Signing Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + System.out.println("=================== do SM2 verify test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sm2.verify(signatureDigestSM2,pubKeySM2,data); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM2 Verifying Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + } +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySymmetricEncryptionTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySymmetricEncryptionTest.java new file mode 100644 index 00000000..82ea11c0 --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/performance/MySymmetricEncryptionTest.java @@ -0,0 +1,91 @@ +package test.com.jd.blockchain.crypto.performance; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.impl.def.symmetric.AESSymmetricEncryptionFunction; +import com.jd.blockchain.crypto.impl.sm.symmetric.SM4SymmetricEncryptionFunction; +import com.jd.blockchain.crypto.symmetric.SymmetricKey; +import org.bouncycastle.util.encoders.Hex; + +public class MySymmetricEncryptionTest { + + public static void main(String[] args) { + + String string1K = "0123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba98765432100123456789abcdeffedcba9876543210"; + +// String string1M = ""; +// for (int i = 0; i < 1024 ; i++) +// { +// string1M = string1M + string1K; +// } + + byte[] data1K = Hex.decode(string1K); +// byte[] data1M = Hex.decode(string1M); + + int count = 100000; + + + AESSymmetricEncryptionFunction aes = new AESSymmetricEncryptionFunction(); + SymmetricKey keyAES = (SymmetricKey) aes.generateSymmetricKey(); + Ciphertext ciphertext1KAES = null; + Ciphertext ciphertext1MAES = null; + + System.out.println("=================== do AES encrypt test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + ciphertext1KAES = aes.encrypt(keyAES,data1K); +// ciphertext1MAES = aes.encrypt(keyAES,data1M); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("AES Encrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + + + System.out.println("=================== do AES decrypt test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + aes.decrypt(keyAES,ciphertext1KAES); +// aes.decrypt(keyAES,ciphertext1MAES); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("AES Decrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + SM4SymmetricEncryptionFunction sm4 = new SM4SymmetricEncryptionFunction(); + SymmetricKey keySM4 = (SymmetricKey) sm4.generateSymmetricKey(); + Ciphertext ciphertext1KSM4 = null; + Ciphertext ciphertext1MSM4 = null; + + System.out.println("=================== do SM4 encrypt test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + ciphertext1KSM4 = sm4.encrypt(keySM4,data1K); +// ciphertext1MSM4 =sm4.encrypt(keySM4,data1M); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM4 Encrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + System.out.println("=================== do SM4 decrypt test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sm4.decrypt(keySM4,ciphertext1KSM4); +// sm4.decrypt(keySM4,ciphertext1MSM4); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("SM4 Decrypting Count=%s; Elapsed Times=%s; KBPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + } +} diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM2UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM2UtilsTest.java new file mode 100644 index 00000000..40111b3e --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM2UtilsTest.java @@ -0,0 +1,105 @@ +package test.com.jd.blockchain.crypto.smutils; + +import com.jd.blockchain.crypto.smutils.asymmetric.SM2Utils; +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.test.TestRandomBigInteger; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SM2UtilsTest { + + @Test + public void testGenerateKeyPair() { + + String expectedPrivateKey = "3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8"; + String expectedPublicKey = "04"+"09f9df311e5421a150dd7d161e4bc5c672179fad1833fc076bb08ff356f35020"+"ccea490ce26775a52dc6ea718cc1aa600aed05fbf35e084a6632f6072da9ad13"; + + AsymmetricCipherKeyPair keyPair = SM2Utils.generateKeyPair(new TestRandomBigInteger(expectedPrivateKey, 16)); + ECPublicKeyParameters ecPub = (ECPublicKeyParameters) keyPair.getPublic(); + ECPrivateKeyParameters ecPriv = (ECPrivateKeyParameters) keyPair.getPrivate(); + + byte[] expectedPrivateKeyBytes = Hex.decode(expectedPrivateKey); + byte[] privKeyBytes = ecPriv.getD().toByteArray(); + assertArrayEquals(expectedPrivateKeyBytes,privKeyBytes); + + byte[] pubKeyBytesX = ecPub.getQ().getAffineXCoord().getEncoded(); + byte[] pubKeyBytesY = ecPub.getQ().getAffineYCoord().getEncoded(); + assertEquals(expectedPublicKey,"04"+Hex.toHexString(pubKeyBytesX)+Hex.toHexString(pubKeyBytesY)); + + byte[] pubKeyBytes = ecPub.getQ().getEncoded(false); + assertArrayEquals(Hex.decode(expectedPublicKey),pubKeyBytes); + } + + @Test + public void testSign() { + + String expectedPrivateKey = "3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8"; + String expectedRandomness = "59276E27D506861A16680F3AD9C02DCCEF3CC1FA3CDBE4CE6D54B80DEAC1BC21"; + String expectedMessage = "message digest"; + String expectedIdentifier = "ALICE123@YAHOO.COM"; + String expectedR = "b0e3e7d4ac2178f833ad73fa9d1191e41c76c8bfedb5ad89040ba2e5184bde58"; + String expectedS = "cc8d096578f7dd2669ac1ac42f7e722bcfa42b9e0be0b1b5df7ca0b53fdd5750"; + + byte[] privKeyBytes = Hex.decode(expectedPrivateKey); + byte[] messageBytes = expectedMessage.getBytes(); + + byte[] signature = SM2Utils.sign(messageBytes,privKeyBytes,new TestRandomBigInteger(expectedRandomness, 16),expectedIdentifier); + assertArrayEquals(Hex.decode(expectedR+expectedS),signature); + } + + @Test + public void testVerify() { + + String expectedPublicKey = "04"+"09f9df311e5421a150dd7d161e4bc5c672179fad1833fc076bb08ff356f35020"+"ccea490ce26775a52dc6ea718cc1aa600aed05fbf35e084a6632f6072da9ad13"; + String expectedMessage = "message digest"; + String expectedIdentifier = "ALICE123@YAHOO.COM"; + String expectedR = "b0e3e7d4ac2178f833ad73fa9d1191e41c76c8bfedb5ad89040ba2e5184bde58"; + String expectedS = "cc8d096578f7dd2669ac1ac42f7e722bcfa42b9e0be0b1b5df7ca0b53fdd5750"; + + byte[] pubKeyBytes = Hex.decode(expectedPublicKey); + byte[] messageBytes = expectedMessage.getBytes(); + byte[] signatureBytes = Hex.decode(expectedR + expectedS); + + boolean isVerified = SM2Utils.verify(messageBytes,pubKeyBytes,signatureBytes,expectedIdentifier); + assertTrue(isVerified); + } + + @Test + public void testEncrypt() { + + String expectedPublicKey = "04"+"09f9df311e5421a150dd7d161e4bc5c672179fad1833fc076bb08ff356f35020"+"ccea490ce26775a52dc6ea718cc1aa600aed05fbf35e084a6632f6072da9ad13"; + String expectedMessage = "encryption standard"; + String expectedRandomness = "59276E27D506861A16680F3AD9C02DCCEF3CC1FA3CDBE4CE6D54B80DEAC1BC21"; + String expectedC1 = "0404ebfc718e8d1798620432268e77feb6415e2ede0e073c0f4f640ecd2e149a73e858f9d81e5430a57b36daab8f950a3c64e6ee6a63094d99283aff767e124df0"; + String expectedC2 = "21886ca989ca9c7d58087307ca93092d651efa"; + String expectedC3 = "59983c18f809e262923c53aec295d30383b54e39d609d160afcb1908d0bd8766"; + + byte[] pubKeyBytes = Hex.decode(expectedPublicKey); + byte[] messageBytes = expectedMessage.getBytes(); + + byte[] ciphertext = SM2Utils.encrypt(messageBytes,pubKeyBytes,new TestRandomBigInteger(expectedRandomness, 16)); + assertArrayEquals(Hex.decode(expectedC1 + expectedC3 + expectedC2),ciphertext); + + + } + + @Test + public void testDecrypt() { + + String expectedPrivateKey = "3945208f7b2144b13f36e38ac6d39f95889393692860b51a42fb81ef4df7c5b8"; + String expectedMessage = "encryption standard"; + String expectedC1 = "0404ebfc718e8d1798620432268e77feb6415e2ede0e073c0f4f640ecd2e149a73e858f9d81e5430a57b36daab8f950a3c64e6ee6a63094d99283aff767e124df0"; + String expectedC2 = "21886ca989ca9c7d58087307ca93092d651efa"; + String expectedC3 = "59983c18f809e262923c53aec295d30383b54e39d609d160afcb1908d0bd8766"; + + byte[] privKeyBytes = Hex.decode(expectedPrivateKey); + byte[] ciphertext = Hex.decode(expectedC1 + expectedC3 + expectedC2); + + byte[] plaintext = SM2Utils.decrypt(ciphertext,privKeyBytes); + assertArrayEquals(expectedMessage.getBytes(),plaintext); + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM3UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM3UtilsTest.java new file mode 100644 index 00000000..5d2ff04d --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM3UtilsTest.java @@ -0,0 +1,33 @@ +package test.com.jd.blockchain.crypto.smutils; + +import com.jd.blockchain.crypto.smutils.hash.SM3Utils; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SM3UtilsTest { + + private static final int SM3DIGEST_LENGTH = 32; + + @Test + public void testHash() { + + String testString1 = "abc"; + String testString2 = "abcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"; + String expectedResult1="66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" ; + String expectedResult2="debe9ff92275b8a138604889c18e5a4d6fdb70e5387e5765293dcba39c0c5732"; + + byte[] testString1Bytes = testString1.getBytes(); + byte[] testString2Bytes = testString2.getBytes(); + byte[] hash1 = SM3Utils.hash(testString1Bytes); + byte[] hash2 = SM3Utils.hash(testString2Bytes); + byte[] expectedResult1Bytes = expectedResult1.getBytes(); + byte[] expectedResult2Bytes = expectedResult2.getBytes(); + assertEquals(hash1.length, SM3DIGEST_LENGTH); + assertEquals(hash2.length, SM3DIGEST_LENGTH); + assertArrayEquals(hash1, Hex.decode(expectedResult1Bytes)); + assertArrayEquals(hash2, Hex.decode(expectedResult2Bytes)); + } + +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM4UtilsTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM4UtilsTest.java new file mode 100644 index 00000000..2137eec4 --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/smutils/SM4UtilsTest.java @@ -0,0 +1,66 @@ +package test.com.jd.blockchain.crypto.smutils; + +import com.jd.blockchain.crypto.smutils.symmetric.SM4Utils; +import org.bouncycastle.util.encoders.Hex; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class SM4UtilsTest { + + private static final int KEY_SIZE = 16; + private static final int BLOCK_SIZE = 16; + + @Test + public void testGenerateKey() { + byte[] key = SM4Utils.generateKey(); + assertEquals(KEY_SIZE,key.length); + } + + @Test + public void testEncrypt() { + + String plaintext = "0123456789abcdeffedcba9876543210"; + String key = "0123456789abcdeffedcba9876543210"; + String iv = "00000000000000000000000000000000"; + String expectedCiphertextIn2ndBlock = "681edf34d206965e86b3e94f536e4246"; + + byte[] plaintextBytes = Hex.decode(plaintext); + byte[] keyBytes = Hex.decode(key); + byte[] ivBytes = Hex.decode(iv); + byte[] expectedCiphertextIn2ndBlockBytes = Hex.decode(expectedCiphertextIn2ndBlock); + + + byte[] ciphertextbytes = SM4Utils.encrypt(plaintextBytes,keyBytes,ivBytes); + + assertEquals(BLOCK_SIZE*3,ciphertextbytes.length); + + byte[] ciphertextIn1stBlockBytes = new byte[BLOCK_SIZE]; + System.arraycopy(ciphertextbytes,0,ciphertextIn1stBlockBytes,0,BLOCK_SIZE); + assertArrayEquals(ivBytes,ciphertextIn1stBlockBytes); + + byte[] ciphertextIn2ndBlockBytes = new byte[BLOCK_SIZE]; + System.arraycopy(ciphertextbytes,BLOCK_SIZE,ciphertextIn2ndBlockBytes,0,BLOCK_SIZE); + assertArrayEquals(expectedCiphertextIn2ndBlockBytes,ciphertextIn2ndBlockBytes); + + + } + + @Test + public void testDecrypt() { + + String plaintext = "0123456789abcdeffedcba987654321000112233445566778899"; + String key = "0123456789abcdeffedcba9876543210"; + String iv = "0123456789abcdeffedcba9876543210"; + + byte[] plaintextBytes = Hex.decode(plaintext); + byte[] keyBytes = Hex.decode(key); + byte[] ivBytes = Hex.decode(iv); + + + byte[] ciphertext = SM4Utils.encrypt(plaintextBytes,keyBytes,ivBytes); + byte[] decryptedData = SM4Utils.decrypt(ciphertext,keyBytes); + assertArrayEquals(plaintextBytes,decryptedData); + + } +} \ No newline at end of file diff --git a/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/symmetric/SymmetricCryptographyImplTest.java b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/symmetric/SymmetricCryptographyImplTest.java new file mode 100644 index 00000000..37a3ceaf --- /dev/null +++ b/source/crypto/crypto-framework/src/test/java/test/com/jd/blockchain/crypto/symmetric/SymmetricCryptographyImplTest.java @@ -0,0 +1,471 @@ +package test.com.jd.blockchain.crypto.symmetric; + +import com.jd.blockchain.crypto.Ciphertext; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.impl.SymmetricCryptographyImpl; +import com.jd.blockchain.crypto.symmetric.SymmetricCryptography; +import com.jd.blockchain.crypto.symmetric.SymmetricEncryptionFunction; +import com.jd.blockchain.crypto.symmetric.SymmetricKey; +import com.jd.blockchain.utils.io.BytesUtils; + +import org.junit.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +import static com.jd.blockchain.crypto.CryptoKeyType.PRIV_KEY; +import static com.jd.blockchain.crypto.CryptoKeyType.SYMMETRIC_KEY; +import static org.junit.Assert.*; + +public class SymmetricCryptographyImplTest { + + @Test + public void testGenerateKey() { + + SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl(); + + //test AES + CryptoAlgorithm algorithm = CryptoAlgorithm.AES; + verifyGenerateKey(symmetricCrypto,algorithm); + + //test SM4 + algorithm = CryptoAlgorithm.SM4; + verifyGenerateKey(symmetricCrypto,algorithm); + } + + private void verifyGenerateKey(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm){ + + SymmetricKey symmetricKey= symmetricCrypto.generateKey(algorithm); + + assertNotNull(symmetricKey); + assertEquals(algorithm, symmetricKey.getAlgorithm()); + assertEquals(128/8,symmetricKey.getRawKeyBytes().length); + + byte[] symmetricKeyBytes = symmetricKey.toBytes(); + //判断密钥数据长度=算法标识长度+密钥掩码长度+原始密钥长度 + assertEquals(1 + 1 + 128 / 8, symmetricKeyBytes.length); + + assertEquals(algorithm.CODE,symmetricKeyBytes[0]); + assertEquals(algorithm,CryptoAlgorithm.valueOf(symmetricKeyBytes[0])); + } + + @Test + public void testGetSymmetricEncryptionFunction() { + + SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl(); + Random random = new Random(); + + + //test AES + CryptoAlgorithm algorithm = CryptoAlgorithm.AES; + + //Case 1: AES with 16 bytes data + //刚好一个分组长度,随机生成明文数据 + byte[] data = new byte[16]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 2*16, null); + + //Case 2: AES with 33 bytes data + //明文长度大于两倍分组长度,生成的密文是三倍分组长度 + data = new byte[33]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 3*16,null); + + //Case 3: AES with 3 bytes data + //明文长度小于分组长度,生成的密文是一倍分组长度 + data = new byte[3]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,null); + + //Case 4: AES with 0 bytes data + //明文长度小于分组长度,生成的密文是一倍分组长度 + data = new byte[0]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,null); + + //Case 5 AES with null + //明文为空,可以捕获到异常异常 + data = null; + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,IllegalArgumentException.class); + + + //test ED25519 + algorithm = CryptoAlgorithm.ED25519; + data = new byte[16]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,IllegalArgumentException.class); + + + //test SM4 + algorithm = CryptoAlgorithm.SM4; + + //Case 1: SM4 with 16 bytes data + data = new byte[16]; + random.nextBytes(data); + //密文长度 = IV长度 + 真实密文长度 + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 3*16, null); + + //Case 2: SM4 with 33 bytes data + data = new byte[33]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 4*16,null); + + //Case 3: SM4 with 3 bytes data + data = new byte[3]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 2*16,null); + + //Case 4: SM4 with 0 bytes data + data = new byte[0]; + random.nextBytes(data); + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 2*16,null); + + //Case 5 SM4 with null + data = null; + verifyGetSymmetricEncryptionFunction(symmetricCrypto, algorithm, data, 16,IllegalArgumentException.class); + } + + //不同明文输入下,用来简化加解密过程的method + private void verifyGetSymmetricEncryptionFunction(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm, + byte[] data, int expectedCiphertextLength, Class expectedException){ + + //初始化一个异常 + Exception actualEx = null; + + try { + SymmetricEncryptionFunction sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm); + //验证获取的算法实例非空 + assertNotNull(sef); + + SymmetricKey symmetricKey = (SymmetricKey) sef.generateSymmetricKey(); + + //验证SymmetricKey的getAlgorithm方法 + assertEquals(algorithm, symmetricKey.getAlgorithm()); + //验证SymmetricKey的getRawKeyBytes方法 + assertEquals(16, symmetricKey.getRawKeyBytes().length); + //验证SymmetricKey的toBytes方法 + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{SYMMETRIC_KEY.CODE},symmetricKey.getRawKeyBytes()), symmetricKey.toBytes()); + + + Ciphertext ciphertext = sef.encrypt(symmetricKey,data); + + //Ciphertext中算法标识与入参算法一致 + assertEquals(algorithm, ciphertext.getAlgorithm()); + //验证原始密文长度与预期长度一致 + assertEquals(expectedCiphertextLength, ciphertext.getRawCiphertext().length); + //验证密文数据长度=算法标识长度+预期长度 + byte[] ciphertextBytes = ciphertext.toBytes(); + assertArrayEquals(BytesUtils.concat(new byte[]{algorithm.CODE},ciphertext.getRawCiphertext()), ciphertextBytes); + + + //验证equal + assertTrue(ciphertext.equals(ciphertext)); + assertEquals(ciphertext.hashCode(),ciphertext.hashCode()); + + //验证SymmetricEncryptionFunction的decrypt + assertArrayEquals(data, sef.decrypt(symmetricKey,ciphertext)); + + //测试SymmetricEncryptionFunction的输入输出流的加解密方法 + InputStream inPlaintext = new ByteArrayInputStream(data); + //16字节的明文输入,将会产生32字节的密文 + OutputStream outCiphertext = new ByteArrayOutputStream(ciphertext.toBytes().length); + InputStream inCiphertext = new ByteArrayInputStream(ciphertext.toBytes()); + OutputStream outPlaintext = new ByteArrayOutputStream(data.length); + sef.encrypt(symmetricKey, inPlaintext, outCiphertext); + sef.decrypt(symmetricKey, inCiphertext, outPlaintext); + + //验证SymmetricEncryptionFunction的supportCiphertext方法 + assertTrue(sef.supportCiphertext(ciphertextBytes)); + + //验证SymmetricEncryptionFunction的resolveCiphertext方法 + assertEquals(ciphertext, sef.resolveCiphertext(ciphertextBytes)); + + //验证SymmetricEncryptionFunction的supportSymmetricKey方法 + assertTrue(sef.supportSymmetricKey(symmetricKey.toBytes())); + + //验证SymmetricEncryptionFunction的resolveSymmetricKey方法 + assertEquals(symmetricKey, sef.resolveSymmetricKey(symmetricKey.toBytes())); + + //验证SymmetricEncryptionFunction的getAlgorithm + assertEquals(algorithm, sef.getAlgorithm()); + + } catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testDecrypt() { + + SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl(); + Random randomData = new Random(); + Random randomKey = new Random(); + + + //test AES + CryptoAlgorithm algorithm = CryptoAlgorithm.AES; + SymmetricEncryptionFunction sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm); + + byte[] data = new byte[16]; + randomData.nextBytes(data); + byte[] key = new byte[16]; + randomKey.nextBytes(key); + + SymmetricKey symmetricKey = new SymmetricKey(algorithm, key); + byte[] ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes(); + + verifyDecrypt(symmetricCrypto, algorithm, key, data, ciphertextBytes, null); + + //密钥的算法标识与密文的算法标识不一致情况 + verifyDecrypt(symmetricCrypto, CryptoAlgorithm.SM4, key, data, ciphertextBytes, IllegalArgumentException.class); + + //密文末尾两个字节丢失情况下,抛出异常 + byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2]; + System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length); + verifyDecrypt(symmetricCrypto, algorithm, key, data, truncatedCiphertextBytes, IllegalArgumentException.class); + + byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes; + ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class); + + ciphertextBytes = null; + verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytes,NullPointerException.class); + + + //test SM4 + algorithm = CryptoAlgorithm.SM4; + sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm); + symmetricKey = new SymmetricKey(algorithm, key); + ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes(); + + verifyDecrypt(symmetricCrypto, algorithm, key, data, ciphertextBytes, null); + + //密钥的算法标识与密文的算法标识不一致情况 + verifyDecrypt(symmetricCrypto, CryptoAlgorithm.AES, key, data, ciphertextBytes, IllegalArgumentException.class); + + //密文末尾两个字节丢失情况下,抛出异常 + truncatedCiphertextBytes = new byte[ciphertextBytes.length-2]; + System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length); + verifyDecrypt(symmetricCrypto, algorithm, key, data, truncatedCiphertextBytes, IllegalArgumentException.class); + + ciphertextBytesWithWrongAlgCode = ciphertextBytes; + ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class); + + ciphertextBytes = null; + verifyDecrypt(symmetricCrypto,algorithm,key,data,ciphertextBytes,NullPointerException.class); + } + + private void verifyDecrypt(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm, + byte[] key, byte[] data, byte[] ciphertextBytes, Class expectedException) { + + Exception actualEx = null; + + try { + SymmetricKey symmetricKey = new SymmetricKey(algorithm,key); + + byte[] plaintext = symmetricCrypto.decrypt(symmetricKey.toBytes(), ciphertextBytes); + + //解密后的明文与初始的明文一致 + assertArrayEquals(data,plaintext); + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testResolveCiphertext() { + + SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl(); + Random randomData = new Random(); + Random randomKey = new Random(); + + //test AES + CryptoAlgorithm algorithm = CryptoAlgorithm.AES; + SymmetricEncryptionFunction sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm); + + byte[] data = new byte[16]; + randomData.nextBytes(data); + byte[] key = new byte[16]; + randomKey.nextBytes(key); + + SymmetricKey symmetricKey = new SymmetricKey(algorithm, key); + byte[] ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes(); + verifyResolveCiphertext(symmetricCrypto, algorithm, ciphertextBytes, null); + + byte[] truncatedCiphertextBytes = new byte[ciphertextBytes.length-2]; + System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length); + verifyResolveCiphertext(symmetricCrypto,algorithm,truncatedCiphertextBytes,IllegalArgumentException.class); + + byte[] ciphertextBytesWithWrongAlgCode = ciphertextBytes; + ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class); + + ciphertextBytes = null; + verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytes,NullPointerException.class); + + + //test SM4 + algorithm = CryptoAlgorithm.SM4; + sef = symmetricCrypto.getSymmetricEncryptionFunction(algorithm); + + symmetricKey = new SymmetricKey(algorithm, key); + ciphertextBytes = sef.encrypt(symmetricKey,data).toBytes(); + + verifyResolveCiphertext(symmetricCrypto, algorithm, ciphertextBytes, null); + + truncatedCiphertextBytes = new byte[ciphertextBytes.length-2]; + System.arraycopy(ciphertextBytes,0,truncatedCiphertextBytes,0,truncatedCiphertextBytes.length); + verifyResolveCiphertext(symmetricCrypto,algorithm,truncatedCiphertextBytes,IllegalArgumentException.class); + + ciphertextBytesWithWrongAlgCode = ciphertextBytes; + ciphertextBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytesWithWrongAlgCode,IllegalArgumentException.class); + + ciphertextBytes = null; + verifyResolveCiphertext(symmetricCrypto,algorithm,ciphertextBytes,NullPointerException.class); + } + + private void verifyResolveCiphertext(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm, byte[] ciphertextBytes, + Class expectedException) { + + Exception actualEx = null; + + try { + Ciphertext ciphertext = symmetricCrypto.resolveCiphertext(ciphertextBytes); + + assertNotNull(ciphertext); + + assertEquals(algorithm, ciphertext.getAlgorithm()); + + assertEquals(0, ciphertext.getRawCiphertext().length % 16); + + assertArrayEquals(ciphertextBytes, ciphertext.toBytes()); + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testTryResolveCiphertext() { + } + + + + @Test + public void testResolveSymmetricKey() { + + SymmetricCryptography symmetricCrypto = new SymmetricCryptographyImpl(); + + //test AES + CryptoAlgorithm algorithm = CryptoAlgorithm.AES; + + Random randomKey = new Random(); + byte[] key = new byte[16]; + randomKey.nextBytes(key); + + byte[] symmetricKeyBytes = BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{SYMMETRIC_KEY.CODE},key); + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,null); + + byte[] truncatedSymmetricKeyBytes = new byte[symmetricKeyBytes.length-2]; + System.arraycopy(symmetricKeyBytes,0,truncatedSymmetricKeyBytes,0,truncatedSymmetricKeyBytes.length); + verifyResolveSymmetricKey(symmetricCrypto,algorithm,truncatedSymmetricKeyBytes,IllegalArgumentException.class); + + byte[] symmetricKeyBytesWithWrongAlgCode = symmetricKeyBytes; + symmetricKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + byte[] symmetricKeyBytesWithWrongKeyType= symmetricKeyBytes; + System.arraycopy(symmetricKeyBytes,0,symmetricKeyBytesWithWrongKeyType,0,symmetricKeyBytesWithWrongKeyType.length); + symmetricKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE; + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + symmetricKeyBytes = null; + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,NullPointerException.class); + + + //test SM4 + algorithm = CryptoAlgorithm.SM4; + symmetricKeyBytes = BytesUtils.concat(new byte[]{algorithm.CODE},new byte[]{SYMMETRIC_KEY.CODE},key); + + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,null); + + truncatedSymmetricKeyBytes = new byte[symmetricKeyBytes.length-2]; + System.arraycopy(symmetricKeyBytes,0,truncatedSymmetricKeyBytes,0,truncatedSymmetricKeyBytes.length); + verifyResolveSymmetricKey(symmetricCrypto,algorithm,truncatedSymmetricKeyBytes,IllegalArgumentException.class); + + symmetricKeyBytesWithWrongAlgCode = symmetricKeyBytes; + symmetricKeyBytesWithWrongAlgCode[0] = CryptoAlgorithm.SHA256.CODE; + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongAlgCode,IllegalArgumentException.class); + + symmetricKeyBytesWithWrongKeyType= symmetricKeyBytes; + System.arraycopy(symmetricKeyBytes,0,symmetricKeyBytesWithWrongKeyType,0,symmetricKeyBytesWithWrongKeyType.length); + symmetricKeyBytesWithWrongKeyType[1] = PRIV_KEY.CODE; + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytesWithWrongKeyType,IllegalArgumentException.class); + + symmetricKeyBytes = null; + verifyResolveSymmetricKey(symmetricCrypto,algorithm,symmetricKeyBytes,NullPointerException.class); + } + + private void verifyResolveSymmetricKey(SymmetricCryptography symmetricCrypto, CryptoAlgorithm algorithm, byte[] symmetricKeyBytes, + Class expectedException) { + + Exception actualEx = null; + + try { + SymmetricKey symmetricKey = symmetricCrypto.resolveSymmetricKey(symmetricKeyBytes); + + assertNotNull(symmetricKey); + + assertEquals(algorithm, symmetricKey.getAlgorithm()); + + assertEquals(16, symmetricKey.getRawKeyBytes().length); + + assertArrayEquals(symmetricKeyBytes, symmetricKey.toBytes()); + } + catch (Exception e){ + actualEx = e; + } + + if (expectedException == null) { + assertNull(actualEx); + } + else { + assertNotNull(actualEx); + assertTrue(expectedException.isAssignableFrom(actualEx.getClass())); + } + } + + @Test + public void testTryResolveSymmetricKey() { + } +} diff --git a/source/crypto/pom.xml b/source/crypto/pom.xml new file mode 100644 index 00000000..15e92fd9 --- /dev/null +++ b/source/crypto/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + crypto + pom + + + crypto-framework + crypto-adv + + + \ No newline at end of file diff --git a/source/deployment/deployment-gateway/pom.xml b/source/deployment/deployment-gateway/pom.xml new file mode 100644 index 00000000..7dda468f --- /dev/null +++ b/source/deployment/deployment-gateway/pom.xml @@ -0,0 +1,60 @@ + + 4.0.0 + + com.jd.blockchain + deployment + 0.8.2.RELEASE + + deployment-gateway + + + + com.jd.blockchain + gateway + ${project.version} + + + com.jd.blockchain + consensus-bftsmart + ${project.version} + + + + com.jd.blockchain + consensus-mq + ${project.version} + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-assembly-plugin + + + make-assembly + package + + single + + + jdchain-gateway + + src/main/resources/assembly.xml + + + + + + + + \ No newline at end of file diff --git a/source/deployment/deployment-gateway/src/main/java/com/jd/blockchain/gateway/boot/GatewayBooter.java b/source/deployment/deployment-gateway/src/main/java/com/jd/blockchain/gateway/boot/GatewayBooter.java new file mode 100644 index 00000000..2cec1a5f --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/java/com/jd/blockchain/gateway/boot/GatewayBooter.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.gateway.boot; + +import com.jd.blockchain.gateway.GatewayServerBooter; + +import java.io.File; +import java.io.FileOutputStream; +import java.lang.management.ManagementFactory; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class GatewayBooter { + + public static void main(String[] args) { + try { + writePID(); + GatewayServerBooter.main(args); + } catch (Exception e) { + System.err.println("Error!!! --[" + e.getClass().getName() + "] " + e.getMessage()); + } + } + + private static final void writePID() throws Exception { + URL url = GatewayBooter.class + .getProtectionDomain() + .getCodeSource() + .getLocation(); + String currPath = java.net.URLDecoder.decode(url.getPath(), "UTF-8"); + if (currPath.contains("!/")) { + currPath = currPath.substring(5, currPath.indexOf("!/")); + } + if (currPath.endsWith(".jar")) { + currPath = currPath.substring(0, currPath.lastIndexOf("/") + 1); + } + System.out.printf("currentPath = %s \r\n", currPath); + File file = new File(currPath); + String homeDir = file.getParent(); + String pidFilePath = homeDir + File.separator + "bin" + File.separator + "PID.log"; + File pidFile = new File(pidFilePath); + if (!pidFile.exists()) { + pidFile.createNewFile(); + } + String name = ManagementFactory.getRuntimeMXBean().getName(); + String pid = name.split("@")[0]; + List bootInfos = new ArrayList<>(); + bootInfos.add("JDChain gateway starts to boot ......\r\n"); + bootInfos.add(String.format("GW_BOOT_TIME = [%s] \r\n", new Date().toString())); + bootInfos.add(String.format("GW_BOOT_PID = [%s] \r\n", pid)); + try (FileOutputStream outputStream = new FileOutputStream(pidFile)) { + for (String bootInfo : bootInfos) { + outputStream.write(bootInfo.getBytes()); + } + outputStream.flush(); + } + } +} diff --git a/source/deployment/deployment-gateway/src/main/resources/assembly.xml b/source/deployment/deployment-gateway/src/main/resources/assembly.xml new file mode 100644 index 00000000..39811c14 --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/resources/assembly.xml @@ -0,0 +1,35 @@ + + + ${project.version} + + zip + + false + + + src/main/resources/scripts + bin + + + src/main/resources/config + config + + + src/main/resources/docs + docs + + + + + false + true + lib + + com.jd.blockchain:deployment-gateway + + + + \ No newline at end of file diff --git a/source/deployment/deployment-gateway/src/main/resources/config/gateway.conf b/source/deployment/deployment-gateway/src/main/resources/config/gateway.conf new file mode 100644 index 00000000..79902cc6 --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/resources/config/gateway.conf @@ -0,0 +1,28 @@ +#网关的HTTP服务地址; +http.host=127.0.0.1 +#网关的HTTP服务端口; +http.port=8081 +#网关的HTTP服务上下文路径,可选; +#http.context-path= + +#共识节点的服务地址; +peer.host=127.0.0.1 +#共识节点的服务端口; +peer.port=7080 +#共识节点的服务是否启用安全证书; +peer.secure=false +#共识节点的服务提供解析器 +peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider + +#数据检索服务对应URL +#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 +data.retrieval.url=http://192.168.1.1:10001 + +#默认公钥的内容(Base58编码数据); +keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey-path= +#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +#默认私钥的解码密码; +keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY \ No newline at end of file diff --git a/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.MD b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.MD new file mode 100644 index 00000000..f9e315e0 --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.MD @@ -0,0 +1,1794 @@ +# 京东区块链浏览器API文档参考 V_1.3 + +## 1 API调用说明 + +该文档内的所有api的调用成功和失败均按照以下规则 + +### 1.1 成功 + +```json +{ + "data": ..., + "success": true +} +``` + +说明 + + - success 值为 true 表明api调用成功 + - data 为返回的数据,具体数据类型参考具体的api说明 + +### 1.2 失败 + +```json +{ + "error": { + "errorCode": 5000, + "errorMessage": "未预期的异常! --Unsupported access ledger[6Gw3cK4uazegy4HjoaM81ck9NgYLNoKyBMb7a1TK1jt3d] !" + }, + "success": false +} +``` + +说明 + + - success 值为 false 表明api调用成功 + - errorCode 为异常代码 + - errorMessage 为错误提示 + +## 2 账本 + +### 2.1 获取账本总数 + +```http +GET /ledgers/count +``` + +#### 参数 +无 + + +#### 请求实例 +```http +http://localhost/ledgers/count +``` + +#### 返回实例 + +```json +{ + "data": 2, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|账本总数| + + +### 2.2 获取账本列表 + +```http +GET /ledgers?fromIndex={start_index}&count={count} +``` + +#### 参数 +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|query|start_index|否|查询账本的起始序号,默认为0|数字 +|query|count|否|查询返回账本的数量限制,默认最大限制为100,小于0或大于100均返回最大可返回结果集|数字 + + +#### 请求实例 +```http +http://localhost/ledgers?fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "data": [ + { + "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs" + } + ], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|账本哈希列表| +|value|账户哈希| + +### 2.3 获取账本详细信息 + +```http +GET /ledgers/{ledger} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs +``` + +#### 返回实例 + +```json +{ + "data": { + "hash": { + "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs" + }, + "latestBlockHash": { + "value": "67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL" + }, + "latestBlockHeight": 66 + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|账本信息| +|hash.value|账本哈希| +|latestBlockHash.value|最新区块哈希 +|latestBlockHeight|账本高度 + + +### 2.4 获取账本成员总数 + +```http +GET /ledgers/{ledger}/participants/count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants/count +``` + +#### 返回实例 + +```json +{ + "data": 4, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|账本成员总数| + + +### 2.5 获取账本成员列表 + +```http +GET /ledgers/{ledger}/participants?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|query|start_index|否|查询成员起始序号,默认为0|数字 +|query|count|否|查询成员返回数量,默认最大返回100,小于0或大于100均返回最大可返回结果集|数字 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants?fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "data": [ + { + "address": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522", + "name": "jd.com", + "id": 0, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + } + }, + { + "address": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha", + "name": "at.com", + "id": 1, + "pubKey": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + }, + { + "address": "5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG", + "name": "bt.com", + "id": 2, + "pubKey": { + "value": "mb4AtiGAH7vtPufMDuap2oca2Ww9X6KTkp59Eh5nZjXA5H" + } + }, + { + "address": "5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds", + "name": "xt.com", + "id": 3, + "pubKey": { + "value": "mb7pGhmmjqYUhxrJJ57C1YxXr9h1AWXv8QVosETyuLhVvH" + } + } + ], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|id|成员唯一标识| +|name|成员名称| +|address|成员地址| +|pubKey.value|成员公钥| + +## 3 区块 + +### 3.1 获取最新区块 + +```http +GET /ledgers/{ledger}/blocks/latest +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/latest +``` + +#### 返回实例 + +```json +{ + "data": { + "ledgerHash": { + "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs" + }, + "previousHash": { + "value": "6EJZnMc9464DCSU2kgi96RyngEv8YeEfVoJNhH3yZ2v5T" + }, + "transactionSetHash": { + "value": "6LmZtDpMM7xE8FPChACEmLj1PLhfaoVM2rEHRsrV3ohPN" + }, + "userAccountSetHash": { + "value": "67jx7SctrwdSczxxuYjwBocA8fER7V8qcRZUzWamSav5p" + }, + "contractAccountSetHash": { + "value": "67ftaBhPDez24NEB9wiiTM3SNcn1XFz5rb7boYhpbbLXN" + }, + "adminAccountHash": { + "value": "69KEFp9m5iFyAiyGmJ2qPcVxuT79gMChMf9JkStBZe8aa" + }, + "dataAccountSetHash": { + "value": "6LB9gosVWEPG3uvWXkxTcWq22mcwMHVehbiXkavFtr5fZ" + }, + "hash": { + "value": "67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL" + }, + "height": 66 + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|hash|区块哈希| +|ledgerHash|账本哈希| +|previousHash|前置区块哈希| +|transactionSetHash|交易集哈希| +|userAccountSetHash|用户集哈希| +|contractAccountSetHash|合约集哈希| +|adminAccountHash|管理员集哈希| +|dataAccountSetHash|数据账户集哈希| + +### 3.2 根据区块哈希获取区块详细信息 + +```http +GET /ledgers/{ledger}/blocks/hash/{block_hash} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串| +|path|block_hash|是|区块哈希|字符串| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL +``` + +#### 返回实例 + +[参考](#block-detail) + + +### 3.3 根据区块高度获取区块详细信息 + +```http +GET /ledgers/{ledger}/blocks/height/{block_height} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串| +|path|block_height|是|区块高度|数字| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66 +``` + +#### 返回实例 + +[参考](#block-detail) + + +### 3.4 根据哈希查询区块总数 + +```http + GET /ledgers/{ledger}/blocks/count/search?keyword={keyword} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**query**|**keyword**|是| 区块哈希的全部或者一部分|string| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/count/search?keyword=6D5M +``` + +#### 返回实例 + +```json +{ + "data": 26, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|查询到的区块总数| + +### 3.5 根据哈希查询区块 + +```http + GET /ledgers/{ledger}/blocks/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**query**|**keyword**|是| 区块哈希的全部或者一部分|string| +|**query**|**start_index**|否| 查询区块结果起始序号,默认为0|string| +|**query**|**count**|否| 查询区块结果返回数量,默认最大值为100,小于0或大于100均返回最大可返回结果集|string| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/search?keyword=6D5M&fromIndex=0&count=-1 +``` + + +#### 返回实例 + +```json +{ + "data": { + "blocks": [ + { + "hash": "6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb" + } + ] + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|blocks|查询到的区块列表| +|hash|区块哈希值| +|height|区块高度| +|txCount|区块内交易数量| + +## 4 交易 + +### 4.1 获取账本交易总数 + +```http +GET /ledgers/{ledger}/txs/count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/count +``` + +##### 返回实例 + +```json +{ + "data": 688, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|交易数量| + +### 4.2 根据区块高度查询区块内的交易数量 + +```http +GET /ledgers/{ledger}/blocks/height/{block_height}/txs/additional-count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串| +|path|block_height|是|区块高度|数字| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs/additional-count + +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count +``` + +#### 返回实例 + +```json +{ + "data": 86, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|交易数量| + + +### 4.3 根据区块哈希查询区块内的交易数量 + +```http +GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs/additional-count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串| +|path|block_hash|是|区块哈希|字符串| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count +``` + +#### 返回实例 + +```json +{ + "data": 86, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|交易数量| + + +### 4.4 获取指定高度的区块交易列表 + +```http +GET /ledgers/{ledger}/blocks/height/{height}/txs?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|height|是|区块高度|数字| +|query|start_index|否|查询交易的起始序号,默认为0|数字| +|query|count|否|查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs?fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "data": [ + { + "blockHeight": 1, + "executionState": "SUCCESS", + "transactionContent": { + "ledgerHash": { + "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs" + }, + "operations": [ + { + "userID": { + "address": { + "value": "5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v" + }, + "pubKey": { + "value": "mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ" + } + } + }, + { + "accountID": { + "address": { + "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha" + }, + "pubKey": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + } + }, + { + "contractID": { + "address": { + "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha" + }, + "pubKey": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + }, + "chainCode": "----------" + }, + { + "contractAddress": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + }, + "event": "----------", + "args": "----------" + }, + { + "writeSet": [{ + "key": "jdchain", + "value": { + "type": "TEXT", + "value": { + "value": "----------" + } + }, + "expectedVersion": 0 + }], + "accountAddress": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + } + ], + "hash": { + "value": "6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA" + } + }, + "endpointSignatures": [ + { + "digest": { + "value": "42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F" + }, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + } + } + ], + "nodeSignatures": [ + { + "digest": { + "value": "66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U" + }, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + } + } + ] + } + ], + "success": true +} +``` + + +说明 + +|名称|说明| +|---|---| +|executionState|交易执行结果| +|transactionContent.hash|交易的哈希| +|transactionContent.operations|交易的操作列表| +|endpointSignatures|终端签名列表| +|nodeSignatures|节点的签名列表| + +### 4.5 获取指定哈希的区块的交易列表 + +```http +GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|block_hash|是|区块哈希|字符串| +|query|start_index|否|查询交易的起始序号,默认为0|数字| +|query|count|否|查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs?fromIndex=0&count=-1 +``` + +#### 返回实例 + +[参考](#tx-list) + + +### 4.6 获取交易详细信息 + +```http +GET /ledgers/{ledger}/txs/hash/{tx_hash} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|tx_hash|是|交易哈希|字符串| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/hash/6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA +``` + +#### 返回实例 + +```json +{ + "data": { + "blockHeight": 1, + "executionState": "SUCCESS", + "transactionContent": { + "ledgerHash": { + "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs" + }, + "operations": [ + { + "userID": { + "address": { + "value": "5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v" + }, + "pubKey": { + "value": "mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ" + } + } + }, + { + "accountID": { + "address": { + "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha" + }, + "pubKey": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + } + }, + { + "contractID": { + "address": { + "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha" + }, + "pubKey": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + }, + "chainCode": "----------" + }, + { + "contractAddress": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + }, + "event": "----------", + "args": "----------" + }, + { + "writeSet": [{ + "key": "jdchain", + "value": { + "type": "TEXT", + "value": { + "value": "----------" + } + }, + "expectedVersion": 0 + }], + "accountAddress": { + "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR" + } + } + ], + "hash": { + "value": "6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA" + } + }, + "endpointSignatures": [ + { + "digest": { + "value": "42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F" + }, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + } + } + ], + "nodeSignatures": [ + { + "digest": { + "value": "66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U" + }, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + } + } + ] + }, + "success": true +} +``` + +说明 + +[参考](#tx-keyword) + + +### 4.7 根据哈希查询交易总数 + +```http + GET /ledgers/{ledgers}/txs/count/search?keyword={keyword} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledgers**|是|所要搜索的账本范围,需要完整的账本哈希|string| +|**query**|**keyword**|是|交易哈希,签名者公钥,或者节点公钥的全部或者部分的|string| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt +``` + +#### 返回实例 + +```json +{ + "data": 36, + "success": true +} +``` + + +说明 + +|名称|说明| +|---|---| +|data|指定交易数量| + + +### 4.8 根据哈希查询交易 + +```http + GET /ledgers/{ledgers}/txs/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledgers**|是|所要搜索的账本范围,需要完整的账本哈希|string| +|**query**|**keyword**|是|交易哈希,签名者公钥,或者节点公钥的全部或者部分的|string| +|**query**|**start_index**|否|查询交易的起始序号,默认为0|数字| +|**query**|**count**|否|查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt +``` + + +#### 返回 + +```json +{ + "data": { + "txs": [ + { + "hash": "6L3ehswCmC1jqBfvGJP9vaPx8qxkLsieu2aRgYepmkiw3" + } + ] + }, + "success": true +} +``` + +## 5 用户 + +### 5.1 获取用户总数 + +```http +GET /ledgers/{ledger}/users/count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count +``` + +#### 返回实例 + +```json +{ + "data": 4, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|用户总数| + + +### 5.2 获取用户列表 + +```http +GET /ledgers/{ledger}/users?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|query|start_index|否|查询用户的起始序号,默认为0|数字| +|query|count|否|查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users?fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "data":[{ + "address": { + "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522" + }, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + }, + "rootHash": { + "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522" + } + }], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|用户地址| +|pubKey.value|用户公钥| + + +### 5.3 获取用户详细信息 + +```http +GET /ledgers/{ledger}/users/address/{address} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|address|是|用户地址|字符串 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/address/55SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 +``` + +#### 返回实例 + +```json +{ + "data": { + "address": { + "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522" + }, + "pubKey": { + "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS" + }, + "rootHash": { + "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522" + } + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|用户地址| +|pubKey.value|用户公钥| +|rootHash.value|用户根Hash| + + +### 5.4 用户查询数量 + +```http + GET /ledgers/{ledger}/users/count/search?keyword={keyword} +``` + +#### 说明 + +用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户数量,也可以返回全部用户的数量 + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**query**|**keyword**|是| 用户的公钥或者地址的全部或者部分|string| +|**query**|**start_index**|否|查询用户的起始序号,默认为0|数字| +|**query**|**count**|否|查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count/search?keyword=5Sm +``` + +#### 返回实例 + +```json +{ + "data": 4, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|用户数量| + +### 5.5 用户查询 + +```http + GET /ledgers/{ledger}/users/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 说明 + +用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户,也可以返回全部用户的列表 + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**query**|**keyword**|是| 用户的公钥或者地址的全部或者部分|string| +|**query**|**start_index**|否|查询用户的起始序号,默认为0|数字| +|**query**|**count**|否|查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/search?keyword=5Sm&fromIndex=0&count=-1 +``` + + +#### 返回实例 + +```json +{ + "data": { + "users": [ + { + "address": { + "value": "5SmAGKgmXyj5VsVvJgHbYCJ67iTizwSkNpw1" + }, + "pubKey": { + "value": "mb97eG4bba2EjrgjXYiD9chAstjg4HaNuV5xgCtSHc5TeB" + } + } + ] + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|用户地址| +|pubKey.value|用户公钥| +|rootHash.value|用户根Hash| + +## 6 数据账户 + +### 6.1 获取账户列表 + +```http +GET /ledgers/{ledger}/accounts?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|query|start_index|否|查询数据账户的起始序号,默认为0|数字| +|query|count|否|查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts?fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "data":[{ + "address": { + "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa" + }, + "rootHash": { + "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM" + }, + "pubKey": { + "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs" + } + }], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|账户地址| +|pubKey.value|账户公钥| +|rootHash.value|默克尔树根哈希| + + +### 6.2 获取账户详细信息 + +```http +GET /ledgers/{ledger}/accounts/address/{address} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|address|是|账户地址|字符串 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa +``` + +#### 返回实例 + +```json +{ + "data": { + "address": { + "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa" + }, + "rootHash": { + "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM" + }, + "pubKey": { + "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs" + } + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|账户地址| +|pubKey.value|账户公钥| +|rootHash.value|默克尔树根哈希| + + +### 6.3 获取账户总数 + +```http +GET /ledgers/{ledger}/accounts/count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count +``` + +#### 返回实例 + +```json +{ + "data": 18, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|账户数量| + + +### 6.4 查询数据账户匹配的数量 + +```http +GET /ledgers/{ledger}/accounts/count/search?keyword={keyword} +``` + +#### 说明 + +通过账户的公钥和地址的全部或者部分查询特定账户的总数量,也可以通过KV值的Key来查询含有该Key的账户的总数量 + + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|字符串 +|**query**|**keyword**|是|数据账户的公钥或者地址的全部或者部分,或者是KV值的key|字符串 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count/search?keyword=jd +``` + +#### 返回实例 + +```json +{ + "data": 2, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|账户数量| + + +### 6.5 查询数据账户 + +```http + GET /ledgers/{ledger}/accounts/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 说明 + +通过账户的公钥和地址的全部或者部分查询特定账户,也可以通过KV值的Key来查询含有该Key的账户 + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**query**|**keyword**|是| 数据账户的公钥或者地址的全部或者部分,或者是KV值的key|string| +|**query**|**start_index**|否|查询数据账户的起始序号,默认为0|数字| +|**query**|**count**|否|查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/search?keyword=5Sm5V&fromIndex=0&count=-1 +``` + + +#### 返回实例 + +```json +{ + "data": { + "accounts": [ + { + "address": { + "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa" + }, + "pubKey": { + "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs" + } + } + ] + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|账户地址| +|pubKey.value|账户公钥| +|rootHash.value|数据账户根Hash| + + +### 6.6 获取某数据账户KV总数 + +```http + GET /ledgers/{ledger}/accounts/address/{address}/entries/count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|address|是|账户地址|字符串 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries/count +``` + +#### 返回实例 + +```json +{ + "data": 66, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|KV总数| + + +### 6.7 获取某数据账户KV详情 + +```http + GET/POST /ledgers/{ledger}/accounts/address/{address}/entries?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|address|是|账户地址|字符串 +|form|keys|是|key详细内容列表|字符串 +|query|start_index|否|查询数据账户对应KV的起始序号,默认为0|数字| +|query|count|否|查询返回数据账户对应KV的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +> keys说明: + 1)keys使用表单方式提交,且keys为需要查询Key的列表,列表中每个Key都需要为完整Key + 2)Key提交方式使用GET或POST均可 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries +``` + +说明:表单提交参数为keys={"jd", "jdchain"} + + +#### 返回实例 + +```json +{ + "data": [ + { + "key": "jd", + "version": 0, + "type": "TEXT", + "value": "www.jd.com" + }, + { + "key": "jdchain", + "version": 0, + "type": "TEXT", + "value": "www.blockchain.com" + }], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|key|键| +|version|版本号| +|type|value类型| +|value|值| + +### 6.8 查询某数据账户键数量 + +```http + GET /ledgers/{ledger}/accounts/address/{address}/keys/count/search?keyword={keyword} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**path**|**address**|是|所要搜索的数据账户地址,需要完整的数据账户地址|string| +|**query**|**keyword**|否|键的部分字符,空表示全部|string| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/count/search?keyword=j +``` + + +#### 返回实例 + +```json +{ + "data": 66, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|条件查询键总数| + +### 6.9 查询某数据账户键 + +```http + GET /ledgers/{ledger}/accounts/address/{address}/keys/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**path**|**address**|是|所要搜索的数据账户地址,需要完整的数据账户地址|string| +|**query**|**keyword**|否|键的部分字符,空表示全部|string| +|**query**|**start_index**|否|查询数据账户对应Key的起始序号,默认为0|数字| +|**query**|**count**|否|查询返回数据账户对应Key的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/search?keyword=j&fromIndex=0&count=-1 +``` + + +#### 返回实例 + +```json +{ + "data": [ + { + "key": "jd" + }, + { + "key": "jdchain" + }], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|key|键| + +## 7 搜索 + +### 7.1 搜索区块链 + +```http + GET /ledgers/{ledger}/all/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 说明 + +通过关键字搜索区块数据,支持区块哈希,交易哈希,用户公钥和地址,合约公钥和地址,数据账户哈希和地址的搜索 + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本,需要完整的账本哈希|string| +|**query**|**keyword**|是|关键字|string| +|**query**|**start_index**|否|查询匹配结果的起始序号,默认为0|数字| +|**query**|**count**|否|查询匹配结果的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/all/search?keyword=5Sm5V&fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "message": "OK", + "code": 0, + "data": { + "blocks": ..., + "txs": ..., + "users": ..., + "accounts": ..., + "contracts": ..., + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|blocks|[参考](#query-blocks-result)| +|txs|[参考](#query-txs-result)| +|users|[参考](#query-users-result)| +|accounts|[参考](#query-accounts-result)| +|contracts|[参考](#query-contracts-result)| + +## 8 合约 + +### 8.1 获取合约列表 + +```http +GET /ledgers/{ledger}/contracts?fromIndex={start_index}&count={count} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|query|start_index|否|查询合约的起始序号,默认为0|数字| +|query|count|否|查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts?fromIndex=0&count=-1 +``` + +#### 返回实例 + +```json +{ + "data": [{ + "address": { + "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa" + }, + "rootHash": { + "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM" + }, + "pubKey": { + "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs" + } + }], + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|账户地址| +|pubKey.value|账户公钥| +|rootHash.value|默克尔树根哈希| + + +### 8.2 获取合约详细信息 + +```http +GET /ledgers/{ledger}/contracts/address/{address} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 +|path|address|是|合约地址|字符串 + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa +``` + +#### 返回实例 + +```json +{ + "data": { + "address": { + "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa" + }, + "rootHash": { + "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM" + }, + "pubKey": { + "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs" + } + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|账户地址| +|pubKey.value|账户公钥| +|rootHash.value|默克尔树根哈希| + +### 8.3 获取合约总数 + +```http +GET /ledgers/{ledger}/contracts/count +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型 +|---|---|---|---|---| +|path|ledger|是|账本哈希|字符串 + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count +``` + +#### 返回实例 + +```json +{ + "data": 27, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|合约数量| + + +### 8.4 查询指定合约数量 + +```http +GET /ledgers/{ledger}/contracts/count/search?keyword={keyword} +``` + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本范围,需要完整的账本哈希|string| +|**query**|**keyword**|是| 合约的公钥或者地址的全部或者一部分|string| + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count/search?keyword=5Sm2 +``` + +#### 返回实例 + +```json +{ + "data": 2, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|data|合约数量| + +### 8.5 合约查询 + +```http + GET /ledgers/{ledger}/contracts/search?keyword={keyword}&fromIndex={start_index}&count={count} +``` + +#### 说明 + +合约有公钥和地址两个属性,可以通过合约的这两个属性查询特定合约,也可以返回一个当前所有合约的列表 + +#### 参数 + +|请求类型|名称|是否必需|说明|数据类型| +|---|---|---|---|---| +|**path**|**ledger**|是|所要搜索的账本范围,需要完整的账本哈希|string| +|**query**|**keyword**|是| 合约的公钥或者地址的全部或者一部分|string| +|**query**|**start_index**|否|查询合约的起始序号,默认为0|数字| +|**query**|**count**|否|查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| + + +#### 请求实例 +```http +http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/earch?keyword=5Sm2&fromIndex=0&count=-1 +``` + + +#### 返回 + +```json +{ + "data": { + "contracts": [ + { + "address": { + "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa" + }, + "rootHash": { + "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM" + }, + "pubKey": { + "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs" + } + } + ] + }, + "success": true +} +``` + +说明 + +|名称|说明| +|---|---| +|address.value|合约地址| +|pubKey.value|合约公钥| +|rootHash|合约根Hash| diff --git a/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.html b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.html new file mode 100644 index 00000000..169ef8b7 --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/resources/docs/api_doc_cn_1.3.html @@ -0,0 +1,2665 @@ + + + + + api_doc_cn_1.3 + + +

京东区块链浏览器API文档参考 V_1.3

+

1 API调用说明

+

该文档内的所有api的调用成功和失败均按照以下规则

+

1.1 成功

+
{
  "data": ...,
  "success": true
}
+

说明

+
- success 值为 true 表明api调用成功
- data 为返回的数据,具体数据类型参考具体的api说明

1.2 失败

+
{
  "error": {
    "errorCode": 5000,
    "errorMessage": "未预期的异常! --Unsupported access ledger[6Gw3cK4uazegy4HjoaM81ck9NgYLNoKyBMb7a1TK1jt3d] !"
  },
  "success": false
}
+

说明

+
- success 值为 false 表明api调用成功
- errorCode 为异常代码
- errorMessage 为错误提示

2 账本

+

2.1 获取账本总数

+
GET /ledgers/count
+

参数

+

+

请求实例

+
http://localhost/ledgers/count
+

返回实例

+
{
  "data": 2,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data账本总数
+

2.2 获取账本列表

+
GET /ledgers?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
querystart_index查询账本的起始序号,默认为0数字
querycount查询返回账本的数量限制,默认最大限制为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers?fromIndex=0&count=-1
+

返回实例

+
{
  "data": [
    {
      "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
    }
  ],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + +
名称说明
data账本哈希列表
value账户哈希
+

2.3 获取账本详细信息

+
GET /ledgers/{ledger}
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs
+

返回实例

+
{
  "data": {
    "hash": {
      "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
    },
    "latestBlockHash": {
      "value": "67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL"
    },
    "latestBlockHeight": 66
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
data账本信息
hash.value账本哈希
latestBlockHash.value最新区块哈希
latestBlockHeight账本高度
+

2.4 获取账本成员总数

+
GET /ledgers/{ledger}/participants/count
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants/count
+

返回实例

+
{
  "data": 4,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data账本成员总数
+

2.5 获取账本成员列表

+
GET /ledgers/{ledger}/participants?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
querystart_index查询成员起始序号,默认为0数字
querycount查询成员返回数量,默认最大返回100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/participants?fromIndex=0&count=-1
+

返回实例

+
{
  "data": [
    {
      "address": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522",
      "name": "jd.com",
      "id": 0,
      "pubKey": {
        "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
      }
    },
    {
      "address": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha",
      "name": "at.com",
      "id": 1,
      "pubKey": {
        "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
      }
    },
    {
      "address": "5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG",
      "name": "bt.com",
      "id": 2,
      "pubKey": {
        "value": "mb4AtiGAH7vtPufMDuap2oca2Ww9X6KTkp59Eh5nZjXA5H"
      }
    },
    {
      "address": "5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds",
      "name": "xt.com",
      "id": 3,
      "pubKey": {
        "value": "mb7pGhmmjqYUhxrJJ57C1YxXr9h1AWXv8QVosETyuLhVvH"
      }
    }
  ],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
id成员唯一标识
name成员名称
address成员地址
pubKey.value成员公钥
+

3 区块

+

3.1 获取最新区块

+
GET /ledgers/{ledger}/blocks/latest
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/latest
+

返回实例

+
{
  "data": {
    "ledgerHash": {
      "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
    },
    "previousHash": {
      "value": "6EJZnMc9464DCSU2kgi96RyngEv8YeEfVoJNhH3yZ2v5T"
    },
    "transactionSetHash": {
      "value": "6LmZtDpMM7xE8FPChACEmLj1PLhfaoVM2rEHRsrV3ohPN"
    },
    "userAccountSetHash": {
      "value": "67jx7SctrwdSczxxuYjwBocA8fER7V8qcRZUzWamSav5p"
    },
    "contractAccountSetHash": {
      "value": "67ftaBhPDez24NEB9wiiTM3SNcn1XFz5rb7boYhpbbLXN"
    },
    "adminAccountHash": {
      "value": "69KEFp9m5iFyAiyGmJ2qPcVxuT79gMChMf9JkStBZe8aa"
    },
    "dataAccountSetHash": {
      "value": "6LB9gosVWEPG3uvWXkxTcWq22mcwMHVehbiXkavFtr5fZ"
    },
    "hash": {
      "value": "67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL"
    },
    "height": 66
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
hash区块哈希
ledgerHash账本哈希
previousHash前置区块哈希
transactionSetHash交易集哈希
userAccountSetHash用户集哈希
contractAccountSetHash合约集哈希
adminAccountHash管理员集哈希
dataAccountSetHash数据账户集哈希
+

3.2 根据区块哈希获取区块详细信息

+
GET /ledgers/{ledger}/blocks/hash/{block_hash}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathblock_hash区块哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/67XsKWgqZTBz1NsytKGpyNWHMbMRENWcBj8PEDYQnWiDL
+

返回实例

+

参考

+

3.3 根据区块高度获取区块详细信息

+
GET /ledgers/{ledger}/blocks/height/{block_height}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathblock_height区块高度数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66
+

返回实例

+

参考

+

3.4 根据哈希查询区块总数

+
  GET /ledgers/{ledger}/blocks/count/search?keyword={keyword}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
querykeyword区块哈希的全部或者一部分string
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/count/search?keyword=6D5M
+

返回实例

+
{
  "data": 26,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data查询到的区块总数
+

3.5 根据哈希查询区块

+
  GET /ledgers/{ledger}/blocks/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
querykeyword区块哈希的全部或者一部分string
querystart_index查询区块结果起始序号,默认为0string
querycount查询区块结果返回数量,默认最大值为100,小于0或大于100均返回最大可返回结果集string
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/search?keyword=6D5M&fromIndex=0&count=-1
+

+

返回实例

+
{
  "data": {
    "blocks": [
      {
        "hash": "6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb"
      }
    ]
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
blocks查询到的区块列表
hash区块哈希值
height区块高度
txCount区块内交易数量
+

4 交易

+

4.1 获取账本交易总数

+
GET /ledgers/{ledger}/txs/count
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/count
+
返回实例
+
{
  "data": 688,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data交易数量
+

4.2 根据区块高度查询区块内的交易数量

+
GET /ledgers/{ledger}/blocks/height/{block_height}/txs/additional-count
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathblock_height区块高度数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs/additional-count
 
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count
+

返回实例

+
{
  "data": 86,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data交易数量
+

4.3 根据区块哈希查询区块内的交易数量

+
GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs/additional-count
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathblock_hash区块哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs/additional-count
+

返回实例

+
{
  "data": 86,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data交易数量
+

4.4 获取指定高度的区块交易列表

+
GET /ledgers/{ledger}/blocks/height/{height}/txs?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathheight区块高度数字
querystart_index查询交易的起始序号,默认为0数字
querycount查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/height/66/txs?fromIndex=0&count=-1
+

返回实例

+
{
  "data": [
    {
      "blockHeight": 1,
      "executionState": "SUCCESS",
      "transactionContent": {
        "ledgerHash": {
          "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
        },
        "operations": [
          {
            "userID": {
              "address": {
                "value": "5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v"
              },
              "pubKey": {
                "value": "mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ"
              }
            }
          },
          {
            "accountID": {
              "address": {
                "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
              },
              "pubKey": {
                "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
              }
            }
          },
          {
            "contractID": {
              "address": {
                "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
              },
              "pubKey": {
                "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
              }
            },
            "chainCode": "----------"
          },
          {
            "contractAddress": {
              "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
            },
            "event": "----------",
            "args": "----------"
          },
          {
            "writeSet": [{
              "key": "jdchain",
              "value": {
                "type": "TEXT",
                "value": {
                  "value": "----------"
                }
              },
              "expectedVersion": 0
            }],
            "accountAddress": {
              "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
            }
          }
        ],
        "hash": {
          "value": "6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA"
        }
      },
      "endpointSignatures": [
        {
          "digest": {
            "value": "42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F"
          },
          "pubKey": {
            "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
          }
        }
      ],
      "nodeSignatures": [
        {
          "digest": {
            "value": "66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U"
          },
          "pubKey": {
            "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
          }
        }
      ]
    }
  ],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
executionState交易执行结果
transactionContent.hash交易的哈希
transactionContent.operations交易的操作列表
endpointSignatures终端签名列表
nodeSignatures节点的签名列表
+

4.5 获取指定哈希的区块的交易列表

+
GET /ledgers/{ledger}/blocks/hash/{block_hash}/txs?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathblock_hash区块哈希字符串
querystart_index查询交易的起始序号,默认为0数字
querycount查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/blocks/hash/6D5MJZnybT69bXET5QdCZdLGT16rZBJEjxLkANmDuykcb/txs?fromIndex=0&count=-1
+

返回实例

+

参考

+

4.6 获取交易详细信息

+
GET /ledgers/{ledger}/txs/hash/{tx_hash}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathtx_hash交易哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/hash/6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA
+

返回实例

+
{
  "data": {
    "blockHeight": 1,
    "executionState": "SUCCESS",
    "transactionContent": {
      "ledgerHash": {
        "value": "657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs"
      },
      "operations": [
        {
          "userID": {
            "address": {
              "value": "5SmBgzsrnY6u9Y7DgSSkXfTkCgp83hiFin3v"
            },
            "pubKey": {
              "value": "mb5kukaqjWtXyAerfHU1JDtVwabSeBU5c3khMZbNh7R8VJ"
            }
          }
        },
        {
          "accountID": {
            "address": {
              "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
            },
            "pubKey": {
              "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
            }
          }
        },
        {
          "contractID": {
            "address": {
              "value": "5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha"
            },
            "pubKey": {
              "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
            }
          },
          "chainCode": "----------"
        },
        {
          "contractAddress": {
            "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
          },
          "event": "----------",
          "args": "----------"
        },
        {
          "writeSet": [{
            "key": "jdchain",
            "value": {
              "type": "TEXT",
              "value": {
                "value": "----------"
              }
            },
            "expectedVersion": 0
          }],
          "accountAddress": {
            "value": "mbC8hzmYBz2SsLLqwoBXAJiGeHrCnByBEvcaUZWscAiPqR"
          }
        }
      ],
      "hash": {
        "value": "6BLtM1agb7ERKoN5AJgZKiTjzdS7BpjgzQNYK8ZeDqotA"
      }
    },
    "endpointSignatures": [
      {
        "digest": {
          "value": "42pbfM5YKnf39Gitr4UsjTCzhhnJjwNyi8MnLFYgP4VKewTLzHitzArHEMrCt3hZYUe5ex9XvqtmiCoWpeAbdc31F"
        },
        "pubKey": {
          "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
        }
      }
    ],
    "nodeSignatures": [
      {
        "digest": {
          "value": "66SQ95SbDaApAJhN2NsFx5sfAQTxsWhMW26D5iPqXc1jZU9rJEhRnqT1nzt62ZAcCvsfrjEsay3MxqXYA5tWPoA2U"
        },
        "pubKey": {
          "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
        }
      }
    ]
  },
  "success": true
}
+

说明

+

参考

+

4.7 根据哈希查询交易总数

+
  GET /ledgers/{ledgers}/txs/count/search?keyword={keyword}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledgers所要搜索的账本范围,需要完整的账本哈希string
querykeyword交易哈希,签名者公钥,或者节点公钥的全部或者部分的string
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt
+

返回实例

+
{
  "data": 36,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data指定交易数量
+

4.8 根据哈希查询交易

+
  GET /ledgers/{ledgers}/txs/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledgers所要搜索的账本范围,需要完整的账本哈希string
querykeyword交易哈希,签名者公钥,或者节点公钥的全部或者部分的string
querystart_index查询交易的起始序号,默认为0数字
querycount查询返回交易的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/txs/search?keyword=6BLt
+

+

返回

+
{
  "data": {
    "txs": [
      {
        "hash": "6L3ehswCmC1jqBfvGJP9vaPx8qxkLsieu2aRgYepmkiw3"
      }
    ]
  },
  "success": true
}
+

5 用户

+

5.1 获取用户总数

+
GET /ledgers/{ledger}/users/count
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count
+

返回实例

+
{
  "data": 4,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data用户总数
+

5.2 获取用户列表

+
GET /ledgers/{ledger}/users?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
querystart_index查询用户的起始序号,默认为0数字
querycount查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users?fromIndex=0&count=-1
+

返回实例

+
{
  "data":[{
    "address": {
      "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
    },
    "pubKey": {
      "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
    },
    "rootHash": {
      "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
    }
  }],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + +
名称说明
address.value用户地址
pubKey.value用户公钥
+

5.3 获取用户详细信息

+
GET /ledgers/{ledger}/users/address/{address}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathaddress用户地址字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/address/55SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522
+

返回实例

+
{
  "data": {
    "address": {
      "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
    },
    "pubKey": {
      "value": "mb5kbwzACnhK9P1dVxgMPB2ySJLFyJKQbHpH7T9oRK3LpS"
    },
    "rootHash": {
      "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
    }
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value用户地址
pubKey.value用户公钥
rootHash.value用户根Hash
+

5.4 用户查询数量

+
  GET /ledgers/{ledger}/users/count/search?keyword={keyword}
+

说明

+

用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户数量,也可以返回全部用户的数量

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
querykeyword用户的公钥或者地址的全部或者部分string
querystart_index查询用户的起始序号,默认为0数字
querycount查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/count/search?keyword=5Sm
+

返回实例

+
{
  "data": 4,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data用户数量
+

5.5 用户查询

+
  GET /ledgers/{ledger}/users/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

说明

+

用户有公钥和地址两个属性,可以通过公钥或者地址查找特定用户,也可以返回全部用户的列表

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
querykeyword用户的公钥或者地址的全部或者部分string
querystart_index查询用户的起始序号,默认为0数字
querycount查询返回用户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/users/search?keyword=5Sm&fromIndex=0&count=-1
+

+

返回实例

+
{
  "data": {
    "users": [
      {
        "address": {
          "value": "5SmAGKgmXyj5VsVvJgHbYCJ67iTizwSkNpw1"
        },
        "pubKey": {
          "value": "mb97eG4bba2EjrgjXYiD9chAstjg4HaNuV5xgCtSHc5TeB"
        },
        "rootHash": {
          "value": "5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522"
        }
      }
    ]
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value用户地址
pubKey.value用户公钥
rootHash.value用户根Hash
+

6 数据账户

+

6.1 获取账户列表

+
GET /ledgers/{ledger}/accounts?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
querystart_index查询数据账户的起始序号,默认为0数字
querycount查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts?fromIndex=0&count=-1
+

返回实例

+
{
  "data":[{
    "address": {
      "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
    },
    "rootHash": {
      "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
    },
    "pubKey": {
      "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
    }
  }],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value账户地址
pubKey.value账户公钥
rootHash.value默克尔树根哈希
+

6.2 获取账户详细信息

+
GET /ledgers/{ledger}/accounts/address/{address}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathaddress账户地址字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa
+

返回实例

+
{
  "data": {
    "address": {
      "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
    },
    "rootHash": {
      "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
    },
    "pubKey": {
      "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
    }
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value账户地址
pubKey.value账户公钥
rootHash.value默克尔树根哈希
+

6.3 获取账户总数

+
GET /ledgers/{ledger}/accounts/count
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count
+

返回实例

+
{
  "data": 18,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data账户数量
+

6.4 查询数据账户匹配的数量

+
GET /ledgers/{ledger}/accounts/count/search?keyword={keyword}
+

说明

+

通过账户的公钥和地址的全部或者部分查询特定账户的总数量,也可以通过KV值的Key来查询含有该Key的账户的总数量

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希字符串
querykeyword数据账户的公钥或者地址的全部或者部分,或者是KV值的key字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/count/search?keyword=jd
+

返回实例

+
{
  "data": 2,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data账户数量
+

6.5 查询数据账户

+
  GET /ledgers/{ledger}/accounts/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

说明

+

通过账户的公钥和地址的全部或者部分查询特定账户,也可以通过KV值的Key来查询含有该Key的账户

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
querykeyword数据账户的公钥或者地址的全部或者部分,或者是KV值的keystring
querystart_index查询数据账户的起始序号,默认为0数字
querycount查询返回数据账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/search?keyword=5Sm5V&fromIndex=0&count=-1
+

+

返回实例

+
{
  "data": {
    "accounts": [
      {
        "address": {
          "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
        },
        "rootHash": {
          "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
        },
        "pubKey": {
          "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
        }
      }
    ]
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value账户地址
pubKey.value账户公钥
rootHash.value数据账户根Hash
+

6.6 获取某数据账户KV总数

+
  GET /ledgers/{ledger}/accounts/address/{address}/entries/count
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathaddress账户地址字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries/count
+

返回实例

+
{
  "data": 66,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
dataKV总数
+

6.7 获取某数据账户KV详情

+
  GET/POST /ledgers/{ledger}/accounts/address/{address}/entries?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathaddress账户地址字符串
formkeyskey详细内容列表字符串
querystart_index查询数据账户对应KV的起始序号,默认为0数字
querycount查询返回数据账户对应KV的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+
+

keys说明: + 1)keys使用表单方式提交,且keys为需要查询Key的列表,列表中每个Key都需要为完整Key + 2)Key提交方式使用GET或POST均可

+
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/entries
+

说明:表单提交参数为keys={"jd", "jdchain"}

+

返回实例

+
{
  "data": [
  {
    "key": "jd",
    "version": 0,
    "type": "TEXT",
    "value": "www.jd.com"
  },
  {
    "key": "jdchain",
    "version": 0,
    "type": "TEXT",
    "value": "www.blockchain.com"
  }],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
key
version版本号
typevalue类型
value
+

6.8 查询某数据账户键数量

+
  GET /ledgers/{ledger}/accounts/address/{address}/keys/count/search?keyword={keyword}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
pathaddress所要搜索的数据账户地址,需要完整的数据账户地址string
querykeyword键的部分字符,空表示全部string
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/count/search?keyword=j
+

返回实例

+
{
  "data": 66,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data条件查询键总数
+

6.9 查询某数据账户键

+
  GET /ledgers/{ledger}/accounts/address/{address}/keys/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
pathaddress所要搜索的数据账户地址,需要完整的数据账户地址string
querykeyword键的部分字符,空表示全部string
querystart_index查询数据账户对应Key的起始序号,默认为0数字
querycount查询返回数据账户对应Key的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/accounts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa/keys/search?keyword=j&fromIndex=0&count=-1
+

返回实例

+
{
  "data": [
  {
    "key": "jd"
  },
  {
    "key": "jdchain"
  }],
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
key
+

7 搜索

+

7.1 搜索区块链

+
  GET /ledgers/{ledger}/all/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

说明

+

通过关键字搜索区块数据,支持区块哈希,交易哈希,用户公钥和地址,合约公钥和地址,数据账户哈希和地址的搜索

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本,需要完整的账本哈希string
querykeyword关键字string
querystart_index查询匹配结果的起始序号,默认为0数字
querycount查询匹配结果的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/all/search?keyword=5Sm5V&fromIndex=0&count=-1
+

返回实例

+
{
  "message": "OK",
  "code": 0,
  "data": {
    "blocks": ...,
    "txs": ...,
    "users": ...,
    "accounts": ...,
    "contracts": ...,
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
名称说明
blocks参考
txs参考
users参考
accounts参考
contracts参考
+

8 合约

+

8.1 获取合约列表

+
GET /ledgers/{ledger}/contracts?fromIndex={start_index}&count={count}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
querystart_index查询合约的起始序号,默认为0数字
querycount查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts?fromIndex=0&count=-1
+

返回实例

+
{
  "data": [{
    "address": {
      "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
    },
    "rootHash": {
      "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
    },
    "pubKey": {
      "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
    }
  }],
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value账户地址
pubKey.value账户公钥
rootHash.value默克尔树根哈希
+

8.2 获取合约详细信息

+
GET /ledgers/{ledger}/contracts/address/{address}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
pathaddress合约地址字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/address/5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa
+

返回实例

+
{
  "data": {
    "address": {
      "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
    },
    "rootHash": {
      "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
    },
    "pubKey": {
      "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
    }
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value账户地址
pubKey.value账户公钥
rootHash.value默克尔树根哈希
+

8.3 获取合约总数

+
GET /ledgers/{ledger}/contracts/count
+

参数

+ + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger账本哈希字符串
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count
+

返回实例

+
{
  "data": 27,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data合约数量
+

8.4 查询指定合约数量

+
GET /ledgers/{ledger}/contracts/count/search?keyword={keyword}
+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本范围,需要完整的账本哈希string
querykeyword合约的公钥或者地址的全部或者一部分string
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/count/search?keyword=5Sm2
+

返回实例

+
{
  "data": 2,
  "success": true
}
+

说明

+ + + + + + + + + + + + + +
名称说明
data合约数量
+

8.5 合约查询

+
  GET /ledgers/{ledger}/contracts/search?keyword={keyword}&fromIndex={start_index}&count={count}
+

说明

+

合约有公钥和地址两个属性,可以通过合约的这两个属性查询特定合约,也可以返回一个当前所有合约的列表

+

参数

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
请求类型名称是否必需说明数据类型
pathledger所要搜索的账本范围,需要完整的账本哈希string
querykeyword合约的公钥或者地址的全部或者一部分string
querystart_index查询合约的起始序号,默认为0数字
querycount查询返回合约的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集数字
+

请求实例

+
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts/earch?keyword=5Sm2&fromIndex=0&count=-1
+

+

返回

+
{
  "data": {
    "contracts": [
      {
        "address": {
          "value": "5Sm4gWXrNpDWW9Boi4xZCzZMHboRvEDm29Fa"
        },
        "rootHash": {
          "value": "6GiAH2PBRLnoE724ia83bKVijkKsNuNU5danA4AAi5qMM"
        },
        "pubKey": {
          "value": "mavweXqvKGUAJzSxE9S15pV7c7qe9bgUn5R1HwpqmXVTUs"
        }
      }
    ]
  },
  "success": true
}
+

说明

+ + + + + + + + + + + + + + + + + + + + + +
名称说明
address.value合约地址
pubKey.value合约公钥
rootHash合约根Hash
+ diff --git a/source/deployment/deployment-gateway/src/main/resources/scripts/shutdown.sh b/source/deployment/deployment-gateway/src/main/resources/scripts/shutdown.sh new file mode 100644 index 00000000..7418f49a --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/resources/scripts/shutdown.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +#启动Home路径 +BOOT_HOME=$(cd `dirname $0`;cd ../; pwd) + +#进程启动后PID.log所在路径 +PID_LOG=$BOOT_HOME/bin/PID.log + +#从启动文件中读取PID +if [ -f "$PID_LOG" ]; then + # File exist + echo "Read PID From File:[$PID_LOG] ..." + PID_LINE=`sed -n '$p' $PID_LOG` + echo "Last Gateway Boot Info = $PID_LINE ..." + if [[ $PID_LINE == *GW_BOOT_PID* ]]; then + LOG_PID=$(echo $PID_LINE | cut -d "=" -f 2 | cut -d "[" -f 2 | cut -d "]" -f 1) + echo "Last Gateway Boot PID = $LOG_PID ..." + PID=`ps -ef | grep deployment-gateway | grep $LOG_PID | grep -v grep | awk '{print $2}'` + fi +#启动文件不存在则直接通过PS进行过滤 +else + PID=`ps -ef | grep $BOOT_HOME/lib/deployment-gateway | grep -v grep | awk '{print $2}'` +fi + +#通过Kill命令将进程杀死 +if [ -z "$PID" ]; then + echo "Unable to find gateway PID. stop aborted." +else + echo "Start to kill PID = $PID ..." + kill -9 $PID + echo "Gateway has been stopped ..." +fi \ No newline at end of file diff --git a/source/deployment/deployment-gateway/src/main/resources/scripts/startup.sh b/source/deployment/deployment-gateway/src/main/resources/scripts/startup.sh new file mode 100644 index 00000000..eecb9daf --- /dev/null +++ b/source/deployment/deployment-gateway/src/main/resources/scripts/startup.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +HOME=$(cd `dirname $0`;cd ../; pwd) +GATEWAY=$(ls $HOME/lib | grep deployment-gateway-) +if [ ! -n "$GATEWAY" ]; then + echo "GateWay Is Null !!!" +else + nohup java -jar -server $HOME/lib/$GATEWAY -c $HOME/config/gateway.conf -sp $HOME/config/application.properties $* > gw.out 2>&1 & +fi \ No newline at end of file diff --git a/source/deployment/deployment-peer/pom.xml b/source/deployment/deployment-peer/pom.xml new file mode 100644 index 00000000..fb4f6a79 --- /dev/null +++ b/source/deployment/deployment-peer/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + + com.jd.blockchain + deployment + 0.8.2.RELEASE + + deployment-peer + + + + com.jd.blockchain + peer + ${project.version} + + + com.jd.blockchain + runtime-modular + ${project.version} + + + com.jd.blockchain + runtime-modular-booter + ${project.version} + + + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.jd.blockchain.boot.peer.PeerBooter + true + . + false + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + make-assembly + package + + single + + + jdchain-peer + + src/main/resources/assembly.xml + + + + + + + + + \ No newline at end of file diff --git a/source/deployment/deployment-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java b/source/deployment/deployment-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java new file mode 100644 index 00000000..d04ada1a --- /dev/null +++ b/source/deployment/deployment-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java @@ -0,0 +1,78 @@ +package com.jd.blockchain.boot.peer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.lang.management.ManagementFactory; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.jd.blockchain.runtime.boot.HomeBooter; +import com.jd.blockchain.runtime.boot.HomeContext; + +/** + * Peer starter; + * + * @author huanghaiquan + * + */ +public class PeerBooter { + + public static final String MODULAR_FACTORY_CLASS = "com.jd.blockchain.runtime.modular.ModularFactory"; + + public static final String MODULAR_FACTORY_METHOD = "startSystem"; + + public static final Class[] MODULAR_FACTORY_METHOD_ARG_TYPES = { String.class, boolean.class, ClassLoader.class, + String.class, ClassLoader.class, String[].class }; + + public static final String SYSTEM_MAIN_CLASS = "com.jd.blockchain.peer.PeerServerBooter"; + + public static void main(String[] args) { + try { + + HomeContext homeContext = HomeBooter.createHomeContext(args); + + startPeer(homeContext); + + writePID(homeContext.getHomeDir()); + } catch (Exception e) { + System.err.println("Error!!! --[" + e.getClass().getName() + "] " + e.getMessage()); + } + } + + public static void startPeer(HomeContext home) throws IOException, ClassNotFoundException, NoSuchMethodException, + SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + + // start system; + Class modularFactoryClass = home.getSystemClassLoader().loadClass(MODULAR_FACTORY_CLASS); + Method modularFactoryMethod = modularFactoryClass.getMethod(MODULAR_FACTORY_METHOD, + MODULAR_FACTORY_METHOD_ARG_TYPES); + + Object[] systemStartingArgs = { home.getRuntimeDir(), home.isProductMode(), home.getLibsClassLoader(), + SYSTEM_MAIN_CLASS, home.getSystemClassLoader(), home.getStartingArgs() }; + modularFactoryMethod.invoke(null, systemStartingArgs); + } + + private static final void writePID(String homeDir) throws IOException { + String pidFilePath = homeDir + File.separator + "bin" + File.separator + "PID.log"; + File pidFile = new File(pidFilePath); + if (!pidFile.exists()) { + pidFile.createNewFile(); + } + String name = ManagementFactory.getRuntimeMXBean().getName(); + String pid = name.split("@")[0]; + List bootInfos = new ArrayList<>(); + bootInfos.add("JDChain peer node starts to boot ......\r\n"); + bootInfos.add(String.format("PEER_BOOT_TIME = [%s] \r\n", new Date().toString())); + bootInfos.add(String.format("PEER_BOOT_PID = [%s] \r\n", pid)); + try (FileOutputStream outputStream = new FileOutputStream(pidFile)) { + for (String bootInfo : bootInfos) { + outputStream.write(bootInfo.getBytes()); + } + outputStream.flush(); + } + } +} diff --git a/source/deployment/deployment-peer/src/main/resources/assembly.xml b/source/deployment/deployment-peer/src/main/resources/assembly.xml new file mode 100644 index 00000000..d445b9cb --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/assembly.xml @@ -0,0 +1,82 @@ + + + ${project.version} + + zip + + false + + + src/main/resources/scripts + bin + + + src/main/resources/config + config + + + src/main/resources/docs + docs + + + + + false + true + system + + com.jd.blockchain:ledger-core + com.jd.blockchain:storage-service + com.jd.blockchain:storage-rocksdb + com.jd.blockchain:storage-redis + com.jd.blockchain:storage-composite + com.jd.blockchain:runtime-modular + com.jd.blockchain:runtime-modular-booter + com.jd.blockchain:peer + com.jd.blockchain:deployment-peer + + + + false + true + libs + + com.jd.blockchain:ledger-core + com.jd.blockchain:storage-service + com.jd.blockchain:storage-rocksdb + com.jd.blockchain:storage-redis + com.jd.blockchain:storage-composite + com.jd.blockchain:runtime-modular + com.jd.blockchain:runtime-modular-booter + com.jd.blockchain:peer + com.jd.blockchain:deployment-peer + + + + + + + true + + com.jd.blockchain:tools-initializer-booter + + + libs + false + + + + true + + com.jd.blockchain:tools-keygen-booter + + + libs + false + + + + \ No newline at end of file diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/bftsmart.config b/source/deployment/deployment-peer/src/main/resources/config/init/bftsmart.config new file mode 100644 index 00000000..99bcff8d --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/init/bftsmart.config @@ -0,0 +1,172 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +############################################ +###### #Consensus Participant0 ###### +############################################ + +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +system.server.0.network.host=127.0.0.1 +system.server.0.network.port=16000 +system.server.0.network.secure=false + +############################################ +###### #Consensus Participant1 ###### +############################################ + +system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +system.server.1.network.host=127.0.0.1 +system.server.1.network.port=16010 +system.server.1.network.secure=false + +############################################ +###### #Consensus Participant2 ###### +############################################ + +system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +system.server.2.network.host=127.0.0.1 +system.server.2.network.port=16020 +system.server.2.network.secure=false + +############################################ +###### #Consensus Participant3 ###### +############################################ + +system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +system.server.3.network.host=127.0.0.1 +system.server.3.network.port=16030 +system.server.3.network.secure=false + + +############################################ +####### 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 + + diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/ledger-init.conf b/source/deployment/deployment-peer/src/main/resources/config/init/ledger-init.conf new file mode 100644 index 00000000..38b1788a --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/init/ledger-init.conf @@ -0,0 +1,66 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + + +#第0个参与方的名称 +cons_parti.0.name=xx-0.com +#第0个参与方的公钥文件路径 +cons_parti.0.pubkey-path= +#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的账本初始服务的主机 +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口 +cons_parti.0.initializer.port=17000 +#第0个参与方的账本初始服务是否开启安全连接 +cons_parti.0.initializer.secure=false + + +#第1个参与方的名称 +cons_parti.1.name=xx-1.com +#第1个参与方的公钥文件路径 +cons_parti.1.pubkey-path= +#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的账本初始服务的主机 +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口 +cons_parti.1.initializer.port=17010 +#第1个参与方的账本初始服务是否开启安全连接 +cons_parti.1.initializer.secure=false + + +#第2个参与方的名称 +cons_parti.2.name=xx-2.com +#第2个参与方的公钥文件路径 +cons_parti.2.pubkey-path= +#第2个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的账本初始服务的主机 +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口 +cons_parti.2.initializer.port=17020 +#第2个参与方的账本初始服务是否开启安全连接 +cons_parti.2.initializer.secure=false + + +#第3个参与方的名称 +cons_parti.3.name=xx-3.com +#第3个参与方的公钥文件路径 +cons_parti.3.pubkey-path= +#第3个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的账本初始服务的主机 +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口 +cons_parti.3.initializer.port=17030 +#第3个参与方的账本初始服务是否开启安全连接 +cons_parti.3.initializer.secure=false + + diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/local.conf b/source/deployment/deployment-peer/src/main/resources/config/init/local.conf new file mode 100644 index 00000000..0a749a04 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/init/local.conf @@ -0,0 +1,32 @@ +#当前参与方的 id +local.parti.id=0 + +#当前参与方的公钥 +local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna + +#当前参与方的私钥(密文编码) +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入 +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录 +#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准 +ledger.binding.out=../.. + +#账本数据库的连接字符 +#rocksdb数据库连接格式:rocksdb://{path} +#redis数据库连接格式:redis://{ip}:{prot}/{db} +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令 +ledger.db.pwd= + +#共识配置文件路径 +#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准 +consensus.conf=bftsmart.config + +#共识Providers配置 +#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider +#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider +consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/mq/ledger-init.conf b/source/deployment/deployment-peer/src/main/resources/config/init/mq/ledger-init.conf new file mode 100644 index 00000000..38b1788a --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/init/mq/ledger-init.conf @@ -0,0 +1,66 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + + +#第0个参与方的名称 +cons_parti.0.name=xx-0.com +#第0个参与方的公钥文件路径 +cons_parti.0.pubkey-path= +#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的账本初始服务的主机 +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口 +cons_parti.0.initializer.port=17000 +#第0个参与方的账本初始服务是否开启安全连接 +cons_parti.0.initializer.secure=false + + +#第1个参与方的名称 +cons_parti.1.name=xx-1.com +#第1个参与方的公钥文件路径 +cons_parti.1.pubkey-path= +#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的账本初始服务的主机 +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口 +cons_parti.1.initializer.port=17010 +#第1个参与方的账本初始服务是否开启安全连接 +cons_parti.1.initializer.secure=false + + +#第2个参与方的名称 +cons_parti.2.name=xx-2.com +#第2个参与方的公钥文件路径 +cons_parti.2.pubkey-path= +#第2个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的账本初始服务的主机 +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口 +cons_parti.2.initializer.port=17020 +#第2个参与方的账本初始服务是否开启安全连接 +cons_parti.2.initializer.secure=false + + +#第3个参与方的名称 +cons_parti.3.name=xx-3.com +#第3个参与方的公钥文件路径 +cons_parti.3.pubkey-path= +#第3个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的账本初始服务的主机 +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口 +cons_parti.3.initializer.port=17030 +#第3个参与方的账本初始服务是否开启安全连接 +cons_parti.3.initializer.secure=false + + diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/mq/local.conf b/source/deployment/deployment-peer/src/main/resources/config/init/mq/local.conf new file mode 100644 index 00000000..08e99b84 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/init/mq/local.conf @@ -0,0 +1,32 @@ +#当前参与方的 id +local.parti.id=0 + +#当前参与方的公钥 +local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna + +#当前参与方的私钥(密文编码) +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入 +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录 +#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准 +ledger.binding.out=../ + +#账本数据库的连接字符 +#rocksdb数据库连接格式:rocksdb://{path} +#redis数据库连接格式:redis://{ip}:{prot}/{db} +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令 +ledger.db.pwd= + +#共识配置文件路径 +#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准 +consensus.conf=mq.config + +#共识Providers配置 +#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider +#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider +consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider diff --git a/source/deployment/deployment-peer/src/main/resources/config/init/mq/mq.config b/source/deployment/deployment-peer/src/main/resources/config/init/mq/mq.config new file mode 100644 index 00000000..424a2e2b --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/init/mq/mq.config @@ -0,0 +1,26 @@ +# MQ连接地址,格式:{MQ类型}://{IP}:{PORT} +system.msg.queue.server=rabbit://127.0.0.1:5672 + +# 当前账本交易发送队列主题(不同账本需不同主题) +system.msg.queue.topic.tx=tx-topic + +# 当前账本结块消息应答队列主题 +system.msg.queue.topic.bl=bl-topic + +# 当前账本普通消息主题 +system.msg.queue.topic.msg=msg-topic + +# 当前账本结块最大交易数 +system.msg.queue.block.txsize=1000 + +# 当前账本结块最大时长(单位:毫秒) +system.msg.queue.block.maxdelay=2000 + +# 当前账本节点总数 +system.servers.num=4 + +# 当前账本对应节点的公钥信息列表 +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR diff --git a/source/deployment/deployment-peer/src/main/resources/config/ledger-binding.conf b/source/deployment/deployment-peer/src/main/resources/config/ledger-binding.conf new file mode 100644 index 00000000..6c3cd9e5 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/config/ledger-binding.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i + + +#第 1 个账本[6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i]的配置; +#账本的当前共识参与方的ID; +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.id=0 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.pk=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.parti.address=5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 +#账本的存储数据库的连接字符串; +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.db.uri=rocksdb:///export/Data/rocksdb/rocksdb0.db +#账本的存储数据库的连接口令; +binding.6D2q2pRxWZEogGEESqKfceuBupRoukmmfVs8Fty1KuZ6i.db.pwd= diff --git a/source/deployment/deployment-peer/src/main/resources/docs/imgs/browser.jpeg b/source/deployment/deployment-peer/src/main/resources/docs/imgs/browser.jpeg new file mode 100644 index 00000000..2cfb38a7 Binary files /dev/null and b/source/deployment/deployment-peer/src/main/resources/docs/imgs/browser.jpeg differ diff --git a/source/deployment/deployment-peer/src/main/resources/docs/imgs/keys.jpeg b/source/deployment/deployment-peer/src/main/resources/docs/imgs/keys.jpeg new file mode 100644 index 00000000..9cdfea53 Binary files /dev/null and b/source/deployment/deployment-peer/src/main/resources/docs/imgs/keys.jpeg differ diff --git a/source/deployment/deployment-peer/src/main/resources/docs/imgs/structure.png b/source/deployment/deployment-peer/src/main/resources/docs/imgs/structure.png new file mode 100644 index 00000000..bbac0652 Binary files /dev/null and b/source/deployment/deployment-peer/src/main/resources/docs/imgs/structure.png differ diff --git a/source/deployment/deployment-peer/src/main/resources/docs/安装部署.MD b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.MD new file mode 100644 index 00000000..28ea020d --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.MD @@ -0,0 +1,317 @@ +# JDChain安装部署指南 + +## 1. 部署环境 +### 1.1 系统部署结构 +![部署结构](imgs/structure.png) + +JDChain有三类节点:Peer、网关和客户端。peer节点是区块链主节点,参与共识、账本操作等;网关节点(GateWay)与Peer节点通信,负责区块链浏览器及消息传递;客户端与网关通信,可以进行写入或查询账本等操作。 + +JDChain默认采用Rocksdb作为存储数据库,每个Peer节点与一个(或多个)Rocksdb实例连接,Rocksdb实例的数量与账本相关,不同Peer节点对应不同的Rocksdb实例。 + +JDChain也支持Redis作为其存储数据库,Redis数据库不必和Peer节点安装在同一服务器,只需要配置正确即可(后面配置会说到)。 + +JDChain默认共识机制采用的是BFTSmart,该共识算法要求最低四个共识节点,BFTSmart 对作恶节点的支持为:N = 3f+1,即在N个节点共识中可允许f个作恶节点,当节点数增加时可根据该公式进行配置。 + + *本安装部署指南以四个节点为例进行说明。* + + +----- + +### 1.2 服务器配置 + +#### 1.2.1 硬件配置 +为了更好的运行,JDChain推荐使用 **Linux** 操作系统,Peer节点自身运行对内存要求较低,但对于Rocksdb存储而言,其对运行环境有较高要求,以下为单Peer节点可正常运行 **亿级交易** 的参考指标: + + 内核2.7.x及以上; + + CPU主频 ≥ 2.0GHz; + + 内存 ≥ 64G; + + 可用硬盘容量 >= 200G + + +#### 1.2.2 软件配置 + +JDChain使用Java开发,其软件需求如下: + + JDK:Version ≥ 1.8。 + + +> 注意:若数据库使用的Redis,则需要对应数据库服务器安装Redis,Redis版本要求>=3.x。 + +----- + +## 2. 系统安装与配置 +### 2.1 安装包结构 +JDChain默认发布的安装包有两个,一个是Peer节点打包程序,一个是Gateway节点打包程序,两者默认都是用tar.gz(或zip)打包的。 + +#### 2.1.1 Peer节点安装包 +Peer节点打包程序为:jdchain-peer-$version.tar.gz或jdchain-peer-$version.zip,若是tar.gz压缩文件可通过tar命令解压(如下): +```shell +tar -xzvf jdchain-peer-$version.tar.gz +``` +若是zip文件可通过unzip命令解压(如下): +```shell +unzip jdchain-peer-$version.zip +``` + +> 注意:$version为JDChain发布的版本号 + +Peer打包程序解压完后的安装包结构如下: + + + bin + - keygen.sh + - ledger-init.sh + - startup.sh + - shutdown.sh + + config + - init + + ledger-init.conf + + local.conf + + bftsmart.config(默认) + - keys + + %.priv + + %.pub + + %.pwd + - ledger-binding.conf + - application.properties + + docs + + libs + + system + +其中目录说明如下: + + **bin** :相关命令操作目录; + + **config** :对应命令的配置目录,keys路径默认可能不存在,会在执行相关脚本时自动创建; + + **docs** :相关文档保存目录; + + **libs** :项目运行依赖第三方及非system依赖包保存路径; + + **system** :项目运行系统包保存路径; + +> 1)注意: *bin* 目录下所有的文件都需要可执行权限,在Linux环境下可简单参考如下命令: +``` +cd bin +chmod 777 * +``` +> 2)注意:system文件夹包括运行时系统主要加载的jar文件,为系统安全运行定制,开发者无须关心此项。 + +#### 2.1.2 Gateway节点安装包 +Gateway节点打包程序为:jdchain-gateway-$version.tar.gz或jdchain-gateway-$version.zip,其解压完后的安装包结构如下: + + + bin + - startup.sh + - shutdown.sh + + config + - gateway.conf + + lib + + + 其中目录说明如下: + + **bin** :相关命令操作目录; + + **config** :对应命令的配置目录; + + **lib** :gateway运行时所需jar包路径; + +### 2.2 注册用户 +JDChain以一个 *密钥对* 标识一个用户,任何区块链操作都必须有用户的签名信息,因此首先需要创建一个用户。 +JDChain提供了创建用户的工具,可直接通过命令行生成一个新用户。切换到bin路径,执行命令: +```shell +./keygen.sh -n jd-com +``` +其中jd-com是生成的密钥对文件前缀,需自定义。 +执行命令过程中需要输入密钥对中 *私钥* 加密的密码,并选择是否将该密码编码保存(推荐选择保存,该密码编码会保存至%.pwd文件中)。 +> 注意:请妥善保存该密码,切勿丢失! + +执行命令后,该 *密钥对* 文件会生成到 *config/keys* 路径,如下所示: + +![keys](imgs/keys.jpeg) + +其中,jd-com.priv是签名私钥的密文形式内容(以口令哈希作为对称密钥,对签名私钥进行对称加密后的密文数据),jd-com.pub为签名公钥内容,jd-com.pwd是用于加密签名私钥的口令哈希值。 +> 注意:%.pwd记录的编码在后续Gateway配置时会用到! + +**请将所有Peer节点都按照上述流程注册一个独立的用户,在实际生产环境中该过程可能由不同的参与方完成。** + +### 2.3 账本初始化 +账本初始化是所有参与Peer节点进行共识,并生成初始化账本至数据库的过程。该过程需要所有节点共同参与,同时启动。 + +#### 2.3.1 初始化配置 +*config/init* 目录下有三个配置文件需要修改:**local.conf** 、 **ledger-init.conf** 和 **bftsmart.config** 。 + + local.conf描述账本初始化的本地(即当前节点)配置; + + ledger-init.conf描述账本初始化过程中涉及到的其他参与Peer节点配置信息; + + bftsmart.config为BFTSmart进行共识的相关配置。 + +##### 2.3.1.1 local.conf配置 +```config +#当前参与方的 id +local.parti.id=0 + +#当前参与方的公钥 +local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna + +#当前参与方的私钥(密文编码) +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入 +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录 +ledger.binding.out=../config + +#账本数据库的连接字符,下为rocksdb样例 +#rocksdb数据库连接格式:rocksdb://{path} +#redis数据库连接格式:redis://{ip}:{prot}/{db} +ledger.db.uri=rocksdb:///export/app/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令 +ledger.db.pwd= + +#共识配置文件路径 +consensus.conf=../bftsmart.config + +#共识Providers配置 +#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider +consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider +``` +其中local.parti.id从 **0** 开始编号,且不同Peer节点不能相同;local.parti.privkey为当前节点私钥,即 *config/keys/%.priv* 文件中的内容;其他参数根据实际环境进行配置。 + + +##### 2.3.1.2 ledger-init.conf配置 +```config +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + + +#第0个参与方的名称 +cons_parti.0.name=parti-0.com +#第0个参与方的公钥文件路径 +cons_parti.0.pubkey-path= +#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的账本初始服务的主机 +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口 +cons_parti.0.initializer.port=17000 +#第0个参与方的账本初始服务是否开启安全连接 +cons_parti.0.initializer.secure=false + + +#第1个参与方的名称 +cons_parti.1.name=parti-1.com +#第1个参与方的公钥文件路径 +cons_parti.1.pubkey-path= +#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数 +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的账本初始服务的主机 +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口 +cons_parti.1.initializer.port=17010 +#第1个参与方的账本初始服务是否开启安全连接 +cons_parti.1.initializer.secure=false + +``` +账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.*****中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。 + +> 注意:上述config中只显示了两个参与方(Peer节点)的配置信息,其他参与方(默认是4个节点)的配置方式一致! + +##### 2.3.1.1 bftsmart.config配置 +```config +system.block.txsize=15 + +system.block.maxdelay=500 + +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +system.server.0.network.host=127.0.0.1 +system.server.0.network.port=16000 + +system.servers.num = 4 + +system.servers.f = 1 + +system.initial.view = 0,1,2,3 +``` +> 注意:上述只是将bftsmart.config文件中主要需要修改的参数显示,以方便进行相关说明,其他参数的修改可参考具体文件中的具体说明 + +参数具体说明如下: + + system.block.txsize:每个区块结块期望含交易数量; + + system.block.maxdelay:区块结块时最大时延(单位:毫秒),该参数与system.block.txsize符合优先发生原则; + + system.server.$n.pubkey:第n节点的公钥信息; + + system.server.$n.network.host:第n个节点的域名; + + system.server.$n.network.port:第n个节点的共识端口; + + system.servers.num:共识节点总数; + + system.servers.f:共识节点最大支持的作恶节点数; + + system.initial.view:为每个共识节点分配的viewID,每个viewID之间使用英文逗号分隔,viewID不允许重复,且viewID总数需要和system.servers.num一致。 + +#### 2.3.2 初始化部署 +按照 *2.3.1章节* 配置完成后,只需要启动ledger-init.sh即可,可参考如下命令: +```shell +cd bin +./ledger-init.sh +``` +执行命令之后,会在 *config* 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。 +> 1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化! + +> 2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点! + +账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。 + +### 2.4 Peer节点安装 +Peer节点启动依赖于 *config* 目录下ledger-binding.conf的配置,该文件由 *2.3章节* 操作生成,无须做任何修改;application.properties文件中主要用于配置Peer节点对外HTTP端口。 +> 注意:application.properties文件可能不存在,可手动创建。该文件中配置适用于SpringBoot2.x版本的相关参数,不限于目前内置的启动端口。 + +由于Peer节点启动后会自动与其他参与节点进行通信,因此需要同时启动4个Peer节点,只需要执行startup.sh即可,参考命令: +```shell +cd bin +./startup.sh +``` +> 1)注意:startup.sh命令中可修改启动端口,默认为:-p 17080; + +> 2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。 + +### 2.5 Gateway节点安装 +GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。 +Gateway程序可独立部署,不需要依赖Peer节点,它的操作环境是 *2.1.2章节* 中解压后的环境,网关节点启动依赖于配置文件 *config/gateway.conf*。 + +```config +#网关的HTTP服务地址; +http.host=127.0.0.1 +#网关的HTTP服务端口; +http.port=8081 +#网关的HTTP服务上下文路径,可选; +#http.context-path= + +#共识节点的服务地址; +peer.host=127.0.0.1 +#共识节点的服务端口; +peer.port=7080 +#共识节点的服务是否启用安全证书; +peer.secure=false +#共识节点的服务提供解析器 +peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider + +#数据检索服务对应URL +#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 +data.retrieval.url=http://192.168.151.39:10001 + +#默认公钥的内容(Base58编码数据); +keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey-path= +#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +#默认私钥的解码密码; +keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +``` +其中keys.default.%配置是网关的角色配置,目前暂不支持网关自定义角色,因此,网关可选择4个参与方的任何一个作为其配置(主要是密钥对)。 +>注意:keys.default.privkey-password填写内容为*2.2章节*中生成的%.pwd文件中的内容! + +启动网关节点只需要执行:startup.sh即可,参考命令: +```shell +cd bin +./startup.sh +``` + +网关节点启动成功后,可通过访问区块链浏览器获取其账本相关信息:http://[ip]:[port] +下图为区块链浏览器首页,可供参考: + +![区块链浏览器](imgs/browser.jpeg) + +>注意:JDChain网关节点默认自带区块链浏览器!!! diff --git a/source/deployment/deployment-peer/src/main/resources/docs/安装部署.html b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.html new file mode 100644 index 00000000..1d75f3c9 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/docs/安装部署.html @@ -0,0 +1,714 @@ + + + + + 安装部署 + + +

JDChain安装部署指南

+

1. 部署环境

+

1.1 系统部署结构

+

部署结构

+

JDChain有三类节点:Peer、网关和客户端。peer节点是区块链主节点,参与共识、账本操作等;网关节点(GateWay)与Peer节点通信,负责区块链浏览器及消息传递;客户端与网关通信,可以进行写入或查询账本等操作。

+

JDChain默认采用Rocksdb作为存储数据库,每个Peer节点与一个(或多个)Rocksdb实例连接,Rocksdb实例的数量与账本相关,不同Peer节点对应不同的Rocksdb实例。

+

JDChain也支持Redis作为其存储数据库,Redis数据库不必和Peer节点安装在同一服务器,只需要配置正确即可(后面配置会说到)。

+

JDChain默认共识机制采用的是BFTSmart,该共识算法要求最低四个共识节点,BFTSmart 对作恶节点的支持为:N = 3f+1,即在N个节点共识中可允许f个作恶节点,当节点数增加时可根据该公式进行配置。

+

本安装部署指南以四个节点为例进行说明。

+
+

1.2 服务器配置

+

1.2.1 硬件配置

+

为了更好的运行,JDChain推荐使用 Linux 操作系统,Peer节点自身运行对内存要求较低,但对于Rocksdb存储而言,其对运行环境有较高要求,以下为单Peer节点可正常运行 亿级交易 的参考指标:

+
    +
  • 内核2.7.x及以上;
  • +
  • CPU主频 ≥ 2.0GHz;
  • +
  • 内存 ≥ 64G;
  • +
  • 可用硬盘容量 >= 200G
  • +
+

1.2.2 软件配置

+

JDChain使用Java开发,其软件需求如下:

+
    +
  • JDK:Version ≥ 1.8。
  • +
+
+

注意:若数据库使用的Redis,则需要对应数据库服务器安装Redis,Redis版本要求>=3.x。

+
+
+

2. 系统安装与配置

+

2.1 安装包结构

+

JDChain默认发布的安装包有两个,一个是Peer节点打包程序,一个是Gateway节点打包程序,两者默认都是用tar.gz(或zip)打包的。

+

2.1.1 Peer节点安装包

+

Peer节点打包程序为:jdchain-peer-$version.tar.gz或jdchain-peer-$version.zip,若是tar.gz压缩文件可通过tar命令解压(如下):

+
tar -xzvf jdchain-peer-$version.tar.gz
+

若是zip文件可通过unzip命令解压(如下):

+
unzip jdchain-peer-$version.zip
+
+

注意:$version为JDChain发布的版本号

+
+

Peer打包程序解压完后的安装包结构如下:

+
    +
  • bin
      +
    • keygen.sh
    • +
    • ledger-init.sh
    • +
    • startup.sh
    • +
    • shutdown.sh
    • +
    +
  • +
  • config
      +
    • init
        +
      • ledger-init.conf
      • +
      • local.conf
      • +
      • bftsmart.config(默认)
      • +
      +
    • +
    • keys
        +
      • %.priv
      • +
      • %.pub
      • +
      • %.pwd
      • +
      +
    • +
    • ledger-binding.conf
    • +
    • application.properties
    • +
    +
  • +
  • docs
  • +
  • libs
  • +
  • system
  • +
+

其中目录说明如下:

+
    +
  • bin :相关命令操作目录;
  • +
  • config :对应命令的配置目录,keys路径默认可能不存在,会在执行相关脚本时自动创建;
  • +
  • docs :相关文档保存目录;
  • +
  • libs :项目运行依赖第三方及非system依赖包保存路径;
  • +
  • system :项目运行系统包保存路径;
  • +
+
+

1)注意: bin 目录下所有的文件都需要可执行权限,在Linux环境下可简单参考如下命令:

+
cd bin
chmod 777 *

2)注意:system文件夹包括运行时系统主要加载的jar文件,为系统安全运行定制,开发者无须关心此项。

+
+

2.1.2 Gateway节点安装包

+

Gateway节点打包程序为:jdchain-gateway-$version.tar.gz或jdchain-gateway-$version.zip,其解压完后的安装包结构如下:

+
    +
  • bin
      +
    • startup.sh
    • +
    • shutdown.sh
    • +
    +
  • +
  • config
      +
    • gateway.conf
    • +
    +
  • +
  • lib
  • +
+

其中目录说明如下:

+
    +
  • bin :相关命令操作目录;
  • +
  • config :对应命令的配置目录;
  • +
  • lib :gateway运行时所需jar包路径;
  • +
+

2.2 注册用户

+

JDChain以一个 密钥对 标识一个用户,任何区块链操作都必须有用户的签名信息,因此首先需要创建一个用户。
JDChain提供了创建用户的工具,可直接通过命令行生成一个新用户。切换到bin路径,执行命令:

+
./keygen.sh -n jd-com
+

其中jd-com是生成的密钥对文件前缀,需自定义。
执行命令过程中需要输入密钥对中 私钥 加密的密码,并选择是否将该密码编码保存(推荐选择保存,该密码编码会保存至%.pwd文件中)。

+
+

注意:请妥善保存该密码,切勿丢失!

+
+

执行命令后,该 密钥对 文件会生成到 config/keys 路径,如下所示:

+

keys

+

其中,jd-com.priv是签名私钥的密文形式内容(以口令哈希作为对称密钥,对签名私钥进行对称加密后的密文数据),jd-com.pub为签名公钥内容,jd-com.pwd是用于加密签名私钥的口令哈希值。

+
+

注意:%.pwd记录的编码在后续Gateway配置时会用到!

+
+

请将所有Peer节点都按照上述流程注册一个独立的用户,在实际生产环境中该过程可能由不同的参与方完成。

+

2.3 账本初始化

+

账本初始化是所有参与Peer节点进行共识,并生成初始化账本至数据库的过程。该过程需要所有节点共同参与,同时启动。

+

2.3.1 初始化配置

+

config/init 目录下有三个配置文件需要修改:local.confledger-init.confbftsmart.config

+
    +
  • local.conf描述账本初始化的本地(即当前节点)配置;
  • +
  • ledger-init.conf描述账本初始化过程中涉及到的其他参与Peer节点配置信息;
  • +
  • bftsmart.config为BFTSmart进行共识的相关配置。
  • +
+
2.3.1.1 local.conf配置
+
#当前参与方的 id
local.parti.id=0
 
#当前参与方的公钥
local.parti.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
 
#当前参与方的私钥(密文编码)
local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
 
#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入
local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
 
#账本初始化完成后生成的"账本绑定配置文件"的输出目录
ledger.binding.out=../config
 
#账本数据库的连接字符,下为rocksdb样例
#rocksdb数据库连接格式:rocksdb://{path}
#redis数据库连接格式:redis://{ip}:{prot}/{db}
ledger.db.uri=rocksdb:///export/app/peer/rocks.db/rocksdb0.db
 
#账本数据库的连接口令
ledger.db.pwd=
 
#共识配置文件路径
consensus.conf=../bftsmart.config
 
#共识Providers配置
#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
+

其中local.parti.id从 0 开始编号,且不同Peer节点不能相同;local.parti.privkey为当前节点私钥,即 config/keys/%.priv 文件中的内容;其他参数根据实际环境进行配置。

+
2.3.1.2 ledger-init.conf配置
+
#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取;
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe
 
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途;
#ledger.name=
 
#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置;
cons_parti.count=4
 
 
#第0个参与方的名称
cons_parti.0.name=parti-0.com
#第0个参与方的公钥文件路径
cons_parti.0.pubkey-path=
#第0个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
#第0个参与方的账本初始服务的主机
cons_parti.0.initializer.host=127.0.0.1
#第0个参与方的账本初始服务的端口
cons_parti.0.initializer.port=17000
#第0个参与方的账本初始服务是否开启安全连接
cons_parti.0.initializer.secure=false
 
 
#第1个参与方的名称
cons_parti.1.name=parti-1.com
#第1个参与方的公钥文件路径
cons_parti.1.pubkey-path=
#第1个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数
cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ
#第1个参与方的账本初始服务的主机
cons_parti.1.initializer.host=127.0.0.1
#第1个参与方的账本初始服务的端口
cons_parti.1.initializer.port=17010
#第1个参与方的账本初始服务是否开启安全连接
cons_parti.1.initializer.secure=false
+

账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.*中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。

+
+

注意:上述config中只显示了两个参与方(Peer节点)的配置信息,其他参与方(默认是4个节点)的配置方式一致!

+
+
2.3.1.1 bftsmart.config配置
+
system.block.txsize=15
 
system.block.maxdelay=500
 
system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
system.server.0.network.host=127.0.0.1
system.server.0.network.port=16000
 
system.servers.num = 4
 
system.servers.f = 1
 
system.initial.view = 0,1,2,3
+
+

注意:上述只是将bftsmart.config文件中主要需要修改的参数显示,以方便进行相关说明,其他参数的修改可参考具体文件中的具体说明

+
+

参数具体说明如下:

+
    +
  • system.block.txsize:每个区块结块期望含交易数量;
  • +
  • system.block.maxdelay:区块结块时最大时延(单位:毫秒),该参数与system.block.txsize符合优先发生原则;
  • +
  • system.server.$n.pubkey:第n节点的公钥信息;
  • +
  • system.server.$n.network.host:第n个节点的域名;
  • +
  • system.server.$n.network.port:第n个节点的共识端口;
  • +
  • system.servers.num:共识节点总数;
  • +
  • system.servers.f:共识节点最大支持的作恶节点数;
  • +
  • system.initial.view:为每个共识节点分配的viewID,每个viewID之间使用英文逗号分隔,viewID不允许重复,且viewID总数需要和system.servers.num一致。
  • +
+

2.3.2 初始化部署

+

按照 2.3.1章节 配置完成后,只需要启动ledger-init.sh即可,可参考如下命令:

+
cd bin
./ledger-init.sh
+

执行命令之后,会在 config 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。

+
+

1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化!

+

2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点!

+
+

账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。

+

2.4 Peer节点安装

+

Peer节点启动依赖于 config 目录下ledger-binding.conf的配置,该文件由 2.3章节 操作生成,无须做任何修改;application.properties文件中主要用于配置Peer节点对外HTTP端口。

+
+

注意:application.properties文件可能不存在,可手动创建。该文件中配置适用于SpringBoot2.x版本的相关参数,不限于目前内置的启动端口。

+
+

由于Peer节点启动后会自动与其他参与节点进行通信,因此需要同时启动4个Peer节点,只需要执行startup.sh即可,参考命令:

+
cd bin
./startup.sh
+
+

1)注意:startup.sh命令中可修改启动端口,默认为:-p 17080;

+

2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。

+
+

2.5 Gateway节点安装

+

GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。 +Gateway程序可独立部署,不需要依赖Peer节点,它的操作环境是 2.1.2章节 中解压后的环境,网关节点启动依赖于配置文件 config/gateway.conf

+
#网关的HTTP服务地址;
http.host=127.0.0.1
#网关的HTTP服务端口;
http.port=8081
#网关的HTTP服务上下文路径,可选;
#http.context-path=
 
#共识节点的服务地址;
peer.host=127.0.0.1
#共识节点的服务端口;
peer.port=7080
#共识节点的服务是否启用安全证书;
peer.secure=false
#共识节点的服务提供解析器
peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider
 
#数据检索服务对应URL
#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示
data.retrieval.url=http://192.168.151.39:10001
 
#默认公钥的内容(Base58编码数据);
keys.default.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna
#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一;
keys.default.privkey-path=
#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一;
keys.default.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY
#默认私钥的解码密码;
keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY
+

其中keys.default.%配置是网关的角色配置,目前暂不支持网关自定义角色,因此,网关可选择4个参与方的任何一个作为其配置(主要是密钥对)。

+
+

注意:keys.default.privkey-password填写内容为2.2章节中生成的%.pwd文件中的内容!

+
+

启动网关节点只需要执行:startup.sh即可,参考命令:

+
cd bin
./startup.sh
+

网关节点启动成功后,可通过访问区块链浏览器获取其账本相关信息:http://[ip]:[port] +下图为区块链浏览器首页,可供参考:

+

区块链浏览器

+
+

注意:JDChain网关节点默认自带区块链浏览器!!!

+
+ diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/keygen.sh b/source/deployment/deployment-peer/src/main/resources/scripts/keygen.sh new file mode 100644 index 00000000..0249e85d --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/scripts/keygen.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +HOME=$(cd `dirname $0`;cd ../; pwd) +boot_file=$(ls ../libs | grep tools-keygen-booter-) +if [ ! -n "$boot_file" ]; then + echo "tools-keygen-booter is null" +else + if [ ! -d $HOME/config/keys ];then + mkdir $HOME/config/keys + echo "create new dir $HOME/config/keys" + echo "keys file will be saved $HOME/config/keys" + else + echo "keys file will be saved $HOME/config/keys" + fi + java -jar $HOME/libs/$boot_file -o $HOME/config/keys -l $HOME/config/init/local.conf $* +fi \ No newline at end of file diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/ledger-init.sh b/source/deployment/deployment-peer/src/main/resources/scripts/ledger-init.sh new file mode 100644 index 00000000..e30a91c6 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/scripts/ledger-init.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +HOME=$(cd `dirname $0`;cd ../; pwd) +boot_file=$(ls ../libs | grep tools-initializer-booter-) +if [ ! -n "$boot_file" ]; then + echo "tools-initializer-booter is null" +else + java -jar $HOME/libs/$boot_file -l $HOME/config/init/local.conf -i $HOME/config/init/ledger-init.conf $* +fi diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/shutdown.sh b/source/deployment/deployment-peer/src/main/resources/scripts/shutdown.sh new file mode 100644 index 00000000..da76d093 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/scripts/shutdown.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +#启动Home路径 +BOOT_HOME=$(cd `dirname $0`;cd ../; pwd) + +#进程启动后PID.log所在路径 +PID_LOG=$BOOT_HOME/bin/PID.log + +#从启动文件中读取PID +if [ -f "$PID_LOG" ]; then + # File exist + echo "Read PID From File:[$PID_LOG] ..." + PID_LINE=`sed -n '$p' $PID_LOG` + echo "Last Peer Boot Info = $PID_LINE ..." + if [[ $PID_LINE == *PEER_BOOT_PID* ]]; then + LOG_PID=$(echo $PID_LINE | cut -d "=" -f 2 | cut -d "[" -f 2 | cut -d "]" -f 1) + echo "Last Peer Boot PID = $LOG_PID ..." + PID=`ps -ef | grep deployment-peer | grep $LOG_PID | grep -v grep | awk '{print $2}'` + fi +#启动文件不存在则直接通过PS进行过滤 +else + PID=`ps -ef | grep $BOOT_HOME/system/deployment-peer | grep -v grep | awk '{print $2}'` +fi + +#通过Kill命令将进程杀死 +if [ -z "$PID" ]; then + echo "Unable to find peer PID. stop aborted." +else + echo "Start to kill PID = $PID ..." + kill -9 $PID + echo "Peer has been stopped ..." +fi \ No newline at end of file diff --git a/source/deployment/deployment-peer/src/main/resources/scripts/startup.sh b/source/deployment/deployment-peer/src/main/resources/scripts/startup.sh new file mode 100644 index 00000000..4318fdb0 --- /dev/null +++ b/source/deployment/deployment-peer/src/main/resources/scripts/startup.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +HOME=$(cd `dirname $0`;cd ../; pwd) +PEER=$(ls $HOME/system | grep deployment-peer-) +if [ ! -n "$PEER" ]; then + echo "Peer Is Null !!!" +else + nohup java -jar -server -Xmx2g -Xms2g $HOME/system/$PEER -home=$HOME -c $HOME/config/init/mq/ledger-binding.conf -p 7080 $* & +fi \ No newline at end of file diff --git a/source/deployment/pom.xml b/source/deployment/pom.xml new file mode 100644 index 00000000..4270f809 --- /dev/null +++ b/source/deployment/pom.xml @@ -0,0 +1,30 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + deployment + pom + + + deployment-gateway + deployment-peer + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/gateway/.gitignore b/source/gateway/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/gateway/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/gateway/pom.xml b/source/gateway/pom.xml new file mode 100644 index 00000000..42e07bbc --- /dev/null +++ b/source/gateway/pom.xml @@ -0,0 +1,119 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + gateway + + + + com.jd.blockchain + consensus-framework + ${project.version} + + + com.jd.blockchain + consensus-mq + ${project.version} + + + com.jd.blockchain + consensus-bftsmart + ${project.version} + + + com.jd.blockchain + ledger-rpc + ${project.version} + + + com.jd.blockchain + sdk-base + ${project.version} + + + + com.jd.blockchain + tools-keygen + ${project.version} + + + + com.jd.blockchain + browser + + + + com.jd.blockchain + utils-web + ${project.version} + + + + commons-io + commons-io + ${commons-io.version} + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + com.jd.blockchain.gateway.GatewayServerBooter + true + . + false + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfigProperties.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfigProperties.java new file mode 100644 index 00000000..ba16e829 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfigProperties.java @@ -0,0 +1,286 @@ +package com.jd.blockchain.gateway; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class GatewayConfigProperties { + // HTTP协议相关配置项的键的前缀; + public static final String HTTP_PREFIX = "http."; + // 网关的HTTP服务地址; + public static final String HTTP_HOST = HTTP_PREFIX + "host"; + // 网关的HTTP服务端口; + public static final String HTTP_PORT = HTTP_PREFIX + "port"; + // 网关的HTTP服务上下文路径,可选; + public static final String HTTP_CONTEXT_PATH = HTTP_PREFIX + "context-path"; + + // 共识相关配置项的键的前缀; + public static final String PEER_PREFIX = "peer."; + // 共识节点的服务地址; + public static final String PEER_HOST = PEER_PREFIX + "host"; + // 共识节点的服务端口; + public static final String PEER_PORT = PEER_PREFIX + "port"; + // 共识节点的服务是否启用安全证书; + public static final String PEER_SECURE = PEER_PREFIX + "secure"; + // 支持共识的Provider列表,以英文逗号分隔 + public static final String PEER_PROVIDERS = PEER_PREFIX + "providers"; + + // 数据检索服务URL地址 + public static final String DATA_RETRIEVAL_URL="data.retrieval.url"; + + // 密钥相关配置项的键的前缀; + public static final String KEYS_PREFIX = "keys."; + // 默认密钥相关配置项的键的前缀; + public static final String DEFAULT_KEYS_PREFIX = KEYS_PREFIX + "default."; + // 默认私钥的内容; + public static final String DEFAULT_PUBKEY = DEFAULT_KEYS_PREFIX + "pubkey"; + // 默认私钥的文件存储路径; + public static final String DEFAULT_PRIVKEY_PATH = DEFAULT_KEYS_PREFIX + "privkey-path"; + // 默认私钥的内容; + public static final String DEFAULT_PRIVKEY = DEFAULT_KEYS_PREFIX + "privkey"; + // 默认私钥的密码; + public static final String DEFAULT_PK_PWD = DEFAULT_KEYS_PREFIX + "privkey-password"; + + + private HttpConfig http = new HttpConfig(); + + private ProviderConfig providerConfig = new ProviderConfig(); + + private NetworkAddress masterPeerAddress; + + private String dataRetrievalUrl; + + private KeysConfig keys = new KeysConfig(); + + public HttpConfig http() { + return http; + } + + public NetworkAddress masterPeerAddress() { + return masterPeerAddress; + } + + public String dataRetrievalUrl() { + return this.dataRetrievalUrl; + } + + public void setDataRetrievalUrl(String dataRetrievalUrl) { + this.dataRetrievalUrl = dataRetrievalUrl; + } + + public ProviderConfig providerConfig() { + return providerConfig; + } + + public void setMasterPeerAddress(NetworkAddress peerAddress) { + if (peerAddress == null) { + throw new IllegalArgumentException("peerAddress is null!"); + } + this.masterPeerAddress = peerAddress; + } + + public KeysConfig keys() { + return keys; + } + + public GatewayConfigProperties() { + } + + public static GatewayConfigProperties resolve(String file) { + Properties props = FileUtils.readProperties(file, "UTf-8"); + return resolve(props); + } + + public static GatewayConfigProperties resolve(File file) { + Properties props = FileUtils.readProperties(file, "UTf-8"); + return resolve(props); + } + + public static GatewayConfigProperties resolve(InputStream in) { + Properties props = FileUtils.readProperties(in, "UTf-8"); + return resolve(props); + } + + public static GatewayConfigProperties resolve(Properties props) { + GatewayConfigProperties configProps = new GatewayConfigProperties(); + configProps.http.host = getProperty(props, HTTP_HOST, true); + configProps.http.port = getInt(props, HTTP_PORT, true); + configProps.http.contextPath = getProperty(props, HTTP_CONTEXT_PATH, false); + + String peerHost = getProperty(props, PEER_HOST, true); + int peerPort = getInt(props, PEER_PORT, true); + boolean peerSecure = getBoolean(props, PEER_SECURE, false); + configProps.masterPeerAddress = new NetworkAddress(peerHost, peerPort, peerSecure); + + String dataRetrievalUrl = getProperty(props, DATA_RETRIEVAL_URL, true); + configProps.dataRetrievalUrl = dataRetrievalUrl; + + String providers = getProperty(props, PEER_PROVIDERS, true); + if (providers == null || providers.length() <= 0) { + throw new IllegalArgumentException("Miss peer providers!"); + } + String[] providerArray = providers.split(","); + for (String provider : providerArray) { + configProps.providerConfig.add(provider); + } + + configProps.keys.defaultPK.pubKeyValue = getProperty(props, DEFAULT_PUBKEY, true); + configProps.keys.defaultPK.privKeyPath = getProperty(props, DEFAULT_PRIVKEY_PATH, false); + configProps.keys.defaultPK.privKeyValue = getProperty(props, DEFAULT_PRIVKEY, false); + if (configProps.keys.defaultPK.privKeyPath == null && configProps.keys.defaultPK.privKeyValue == null) { + throw new IllegalArgumentException("Miss both of pk-path and pk content!"); + } + configProps.keys.defaultPK.privKeyPassword = getProperty(props, DEFAULT_PK_PWD, true); + + return configProps; + } + + private static String getProperty(Properties props, String key, boolean required) { + String value = props.getProperty(key); + if (value != null) { + value = value.trim(); + } + if (value == null || value.length() == 0) { + if (required) { + throw new IllegalArgumentException("Miss property[" + key + "]!"); + } + return null; + } + return value; + } + + private static boolean getBoolean(Properties props, String key, boolean required) { + String strBool = getProperty(props, key, required); + if (strBool == null) { + return false; + } + return Boolean.parseBoolean(strBool); + } + + private static int getInt(Properties props, String key, boolean required) { + String strInt = getProperty(props, key, required); + if (strInt == null) { + return 0; + } + return getInt(strInt); + } + + private static int getInt(String strInt) { + return Integer.parseInt(strInt.trim()); + } + + // ------------------------------------------------------------ + + public static class ProviderConfig { + List providers = new ArrayList<>(); + + public void add(String provider) { + providers.add(provider); + } + + public List getProviders() { + return providers; + } + } + + public static class HttpConfig { + + private String host; + + private int port; + + private String contextPath; + + private HttpConfig() { + } + + private HttpConfig(String host, int port, String contextPath) { + this.host = host; + this.port = port; + this.contextPath = contextPath; + } + + 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; + } + + public String getContextPath() { + return contextPath; + } + + public void setContextPath(String contextPath) { + this.contextPath = contextPath; + } + } + + public static class KeysConfig { + + private KeyPairConfig defaultPK = new KeyPairConfig(); + + public KeyPairConfig getDefault() { + return defaultPK; + } + } + + public static class KeyPairConfig { + + private String pubKeyValue; + + private String privKeyPath; + + private String privKeyValue; + + private String privKeyPassword; + + public String getPrivKeyPath() { + return privKeyPath; + } + + public String getPrivKeyValue() { + return privKeyValue; + } + + public String getPrivKeyPassword() { + return privKeyPassword; + } + + public String getPubKeyValue() { + return pubKeyValue; + } + + public void setPubKeyValue(String pubKeyValue) { + this.pubKeyValue = pubKeyValue; + } + + public void setPrivKeyPath(String privKeyPath) { + this.privKeyPath = privKeyPath; + } + + public void setPrivKeyValue(String privKeyValue) { + this.privKeyValue = privKeyValue; + } + + public void setPrivKeyPassword(String privKeyPassword) { + this.privKeyPassword = privKeyPassword; + } + + } + +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfiguration.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfiguration.java new file mode 100644 index 00000000..c498d29a --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayConfiguration.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.gateway; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; + +@EnableAutoConfiguration +@EnableConfigurationProperties +@SpringBootApplication +@ComponentScan +public class GatewayConfiguration { +} + diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayServerBooter.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayServerBooter.java new file mode 100644 index 00000000..34b2df7b --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/GatewayServerBooter.java @@ -0,0 +1,150 @@ +package com.jd.blockchain.gateway; + +import java.io.File; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +import com.jd.blockchain.gateway.web.BlockBrowserController; +import org.apache.commons.io.FileUtils; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.ArgumentSet; +import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.ArgumentSet.ArgEntry; + + +public class GatewayServerBooter { + + // 当前参与方在初始化配置中的参与方列表的编号; + private static final String HOST_ARG = "-c"; + //sp;针对spring.config.location这个参数进行包装; + private static final String SPRING_CF_LOCATION = BaseConstant.SPRING_CF_LOCATION; + // 是否输出调试信息; + private static final String DEBUG_OPT = "-debug"; + + public static void main(String[] args) { + boolean debug = false; + try { + ArgumentSet arguments = ArgumentSet.resolve(args, ArgumentSet.setting().prefix(HOST_ARG, SPRING_CF_LOCATION).option(DEBUG_OPT)); + debug = arguments.hasOption(DEBUG_OPT); + ArgEntry argHost = arguments.getArg(HOST_ARG); + String configFile = argHost == null ? null : argHost.getValue(); + GatewayConfigProperties configProps; + if (configFile == null) { + ConsoleUtils.info("Load build-in default configuration ..."); + ClassPathResource configResource = new ClassPathResource("gateway.conf"); + try (InputStream in = configResource.getInputStream()) { + configProps = GatewayConfigProperties.resolve(in); + } + } else { + ConsoleUtils.info("Load configuration ..."); + configProps = GatewayConfigProperties.resolve(argHost.getValue()); + } + + //spring config location; + String springConfigLocation=null; + ArgumentSet.ArgEntry spConfigLocation = arguments.getArg(SPRING_CF_LOCATION); + if (spConfigLocation != null) { + springConfigLocation = spConfigLocation.getValue(); + }else { + //if no the config file, then should tip as follows. but it's not a good feeling, so we create it by inputStream; + ConsoleUtils.info("no param:-sp, format: -sp /x/xx.properties, use the default application-gw.properties "); + ClassPathResource configResource = new ClassPathResource("application-gw.properties"); + InputStream in = configResource.getInputStream(); + File targetFile = new File(System.getProperty("user.dir")+File.separator+"conf"+File.separator+"application-gw.properties"); + FileUtils.copyInputStreamToFile(in, targetFile); + springConfigLocation = "file:"+targetFile.getAbsolutePath(); + } + + // 启动服务器; + ConsoleUtils.info("Starting web server......"); + GatewayServerBooter booter = new GatewayServerBooter(configProps,springConfigLocation); + booter.start(); + + ConsoleUtils.info("Peer[%s] is connected success!", configProps.masterPeerAddress().toString()); + } catch (Exception e) { + ConsoleUtils.error("Error!! %s", e.getMessage()); + if (debug) { + e.printStackTrace(); + } + } + } + + private volatile ConfigurableApplicationContext appCtx; + private GatewayConfigProperties config; + private CryptoKeyPair defaultKeyPair; + private String springConfigLocation; + public GatewayServerBooter(GatewayConfigProperties config, String springConfigLocation) { + this.config = config; + this.springConfigLocation = springConfigLocation; + + String base58Pwd = config.keys().getDefault().getPrivKeyPassword(); + if (base58Pwd == null || base58Pwd.length() == 0) { + base58Pwd = KeyGenCommand.readPasswordString(); + } + + // 加载密钥; + PubKey pubKey = KeyGenCommand.decodePubKey(config.keys().getDefault().getPubKeyValue()); + + PrivKey privKey = null; + String base58PrivKey = config.keys().getDefault().getPrivKeyValue(); + if (base58PrivKey == null) { + //注:GatewayConfigProperties 确保了 PrivKeyValue 和 PrivKeyPath 必有其一; + privKey = KeyGenCommand.readPrivKey(config.keys().getDefault().getPrivKeyPath(), base58Pwd); + } else { + privKey = KeyGenCommand.decodePrivKey(base58PrivKey, base58Pwd); + } + defaultKeyPair = new CryptoKeyPair(pubKey, privKey); + } + + public synchronized void start() { + if (this.appCtx != null) { + throw new IllegalStateException("Gateway server is running already."); + } + this.appCtx = startServer(config.http().getHost(), config.http().getPort(), springConfigLocation, + config.http().getContextPath()); + + ConsoleUtils.info("\r\n\r\nStart connecting to peer ...."); + BlockBrowserController blockBrowserController = appCtx.getBean(BlockBrowserController.class); + blockBrowserController.setDataRetrievalUrl(config.dataRetrievalUrl()); + PeerConnector peerConnector = appCtx.getBean(PeerConnector.class); + peerConnector.connect(config.masterPeerAddress(), defaultKeyPair, config.providerConfig().getProviders()); + ConsoleUtils.info("Peer[%s] is connected success!", config.masterPeerAddress().toString()); + } + + public synchronized void close() { + if (this.appCtx == null) { + return; + } + this.appCtx.close(); + } + + private static ConfigurableApplicationContext startServer(String host, int port, String springConfigLocation, String contextPath) { + List argList = new ArrayList(); + argList.add(String.format("--server.address=%s", host)); + argList.add(String.format("--server.port=%s", port)); + + if(springConfigLocation != null){ + argList.add(String.format("--spring.config.location=%s", springConfigLocation)); + } + + if (contextPath != null) { + argList.add(String.format("--server.context-path=%s", contextPath)); + } + + String[] args = argList.toArray(new String[argList.size()]); + + // 启动服务器; + ConfigurableApplicationContext appCtx = SpringApplication.run(GatewayConfiguration.class, args); + return appCtx; + } + +} \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerConnector.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerConnector.java new file mode 100644 index 00000000..d25ea004 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerConnector.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.gateway; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.utils.net.NetworkAddress; + +import java.util.List; + +public interface PeerConnector { + + NetworkAddress getPeerAddress(); + + boolean isConnected(); + + void connect(NetworkAddress peerAddress, CryptoKeyPair defaultKeyPair, List peerProviders); + + void reconnect(); + + void close(); + +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerService.java new file mode 100644 index 00000000..38039582 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/PeerService.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.gateway; + +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.sdk.BlockchainQueryService; + +public interface PeerService { + + BlockchainQueryService getQueryService(); + + TransactionService getTransactionService(); + +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalService.java new file mode 100644 index 00000000..d891bf83 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalService.java @@ -0,0 +1,21 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.gateway.service.DataRetrievalService + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/15 下午3:08 + * Description: + */ +package com.jd.blockchain.gateway.service; + +/** + * + * @author shaozhuguang + * @create 2019/1/15 + * @since 1.0.0 + */ + +public interface DataRetrievalService { + + String retrieval(String url) throws Exception; +} \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalServiceHandler.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalServiceHandler.java new file mode 100644 index 00000000..d7dd0baf --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/DataRetrievalServiceHandler.java @@ -0,0 +1,27 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.gateway.service.DeepQueryServiceImpl + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/15 下午3:09 + * Description: + */ +package com.jd.blockchain.gateway.service; + +import com.jd.blockchain.utils.http.agent.HttpClientPool; +import org.springframework.stereotype.Component; + +/** + * + * @author shaozhuguang + * @create 2019/1/15 + * @since 1.0.0 + */ +@Component +public class DataRetrievalServiceHandler implements DataRetrievalService { + + @Override + public String retrieval(String url) throws Exception { + return HttpClientPool.get(url); + } +} \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryService.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryService.java new file mode 100644 index 00000000..4167d9f5 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryService.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.gateway.service; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.ParticipantNode; + +/** + * queryService only for gateway; + * @Author zhaogw + * @Date 2019/2/22 10:37 + */ +public interface GatewayQueryService { + /** + * get all ledgers hashs; + * @param fromIndex + * @param count + */ + HashDigest[] getLedgersHash(int fromIndex, int count); + + /** + * get the participants by range; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash, int fromIndex, int count); +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceImpl.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceImpl.java new file mode 100644 index 00000000..04132cec --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/GatewayQueryServiceImpl.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.gateway.service; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.PeerService; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.utils.QueryUtil; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import java.util.Arrays; + +import static com.jd.blockchain.utils.BaseConstant.QUERY_LIST_MAX; + +/** + * @Author zhaogw + * @Date 2019/2/22 10:39 + */ +@Component +public class GatewayQueryServiceImpl implements GatewayQueryService { + @Autowired + private PeerService peerService; + + @Override + public HashDigest[] getLedgersHash(int fromIndex, int count) { + HashDigest ledgersHash[] = peerService.getQueryService().getLedgerHashs(); + int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex,count,ledgersHash.length); + HashDigest ledgersHashNew[] = Arrays.copyOfRange(ledgersHash,indexAndCount[0],indexAndCount[0]+indexAndCount[1]); + return ledgersHashNew; + } + + @Override + public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash, int fromIndex, int count) { + ParticipantNode participantNode[] = peerService.getQueryService().getConsensusParticipants(ledgerHash); + int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex,count,participantNode.length); + ParticipantNode participantNodesNew[] = Arrays.copyOfRange(participantNode,indexAndCount[0],indexAndCount[0]+indexAndCount[1]); + return participantNodesNew; + } +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/service/PeerConnectionManager.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/PeerConnectionManager.java new file mode 100644 index 00000000..ef98fcc1 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/service/PeerConnectionManager.java @@ -0,0 +1,108 @@ +package com.jd.blockchain.gateway.service; + +import javax.annotation.PreDestroy; + +import org.springframework.stereotype.Component; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.gateway.PeerConnector; +import com.jd.blockchain.gateway.PeerService; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.sdk.BlockchainQueryService; +import com.jd.blockchain.sdk.service.PeerBlockchainServiceFactory; +import com.jd.blockchain.utils.net.NetworkAddress; + +import java.util.List; + +@Component +public class PeerConnectionManager implements PeerService, PeerConnector { + + private volatile PeerBlockchainServiceFactory peerServiceFactory; + + private volatile NetworkAddress peerAddress; + + private volatile CryptoKeyPair gateWayKeyPair; + + private volatile List peerProviders; + + @Override + public NetworkAddress getPeerAddress() { + return peerAddress; + } + + @Override + public boolean isConnected() { + return peerServiceFactory != null; + } + + @Override + public synchronized void connect(NetworkAddress peerAddress, CryptoKeyPair defaultKeyPair, List peerProviders) { + if (isConnected()) { + if (this.peerAddress.equals(peerAddress)) { + return; + } + throw new IllegalArgumentException( + "This gateway has been connected to a peer, cann't be connected to another peer before closing it!"); + } + setPeerAddress(peerAddress); + setGateWayKeyPair(defaultKeyPair); + setPeerProviders(peerProviders); + // TODO: 未实现运行时出错时动态重连; + peerServiceFactory = PeerBlockchainServiceFactory.connect(defaultKeyPair, peerAddress, peerProviders); + } + + @Override + public synchronized void reconnect() { + if (!isConnected()) { + throw new IllegalArgumentException( + "This gateway has not connected to a peer, please connect it first!!!"); + } + peerServiceFactory = PeerBlockchainServiceFactory.connect(gateWayKeyPair, peerAddress, peerProviders); + } + + @Override + public void close() { + PeerBlockchainServiceFactory serviceFactory = this.peerServiceFactory; + if (serviceFactory != null) { + this.peerServiceFactory = null; + this.peerAddress = null; + serviceFactory.close(); + } + } + + @Override + public BlockchainQueryService getQueryService() { + PeerBlockchainServiceFactory serviceFactory = this.peerServiceFactory; + if (serviceFactory == null) { + throw new IllegalStateException("Peer connection was closed!"); + } + return serviceFactory.getBlockchainService(); + } + + @Override + public TransactionService getTransactionService() { + PeerBlockchainServiceFactory serviceFactory = this.peerServiceFactory; + if (serviceFactory == null) { + throw new IllegalStateException("Peer connection was closed!"); + } + + return serviceFactory.getTransactionService(); + } + + @PreDestroy + private void destroy() { + close(); + } + + public void setPeerAddress(NetworkAddress peerAddress) { + this.peerAddress = peerAddress; + } + + public void setGateWayKeyPair(CryptoKeyPair gateWayKeyPair) { + this.gateWayKeyPair = gateWayKeyPair; + } + + public void setPeerProviders(List peerProviders) { + this.peerProviders = peerProviders; + } +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java new file mode 100644 index 00000000..1c5b1fcb --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/BlockBrowserController.java @@ -0,0 +1,519 @@ +package com.jd.blockchain.gateway.web; + +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.PeerService; +import com.jd.blockchain.gateway.service.DataRetrievalService; +import com.jd.blockchain.gateway.service.GatewayQueryService; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainExtendQueryService; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.BaseConstant; +import com.jd.blockchain.utils.ConsoleUtils; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.ArrayList; +import java.util.List; + +@RestController +@RequestMapping(path = "/") +public class BlockBrowserController implements BlockchainExtendQueryService { + private static org.slf4j.Logger LOGGER = LoggerFactory.getLogger(BlockBrowserController.class); + + @Autowired + private PeerService peerService; + + @Autowired + private DataRetrievalService dataRetrievalService; + + @Autowired + private GatewayQueryService gatewayQueryService; + + private String dataRetrievalUrl; + + private static final long BLOCK_MAX_DISPLAY = 3L; + + private static final long GENESIS_BLOCK_HEIGHT = 0L; + + @Deprecated +// @RequestMapping(method = RequestMethod.GET, path = "ledgers") + @Override + public HashDigest[] getLedgerHashs() { + return peerService.getQueryService().getLedgerHashs(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}") + @Override + public LedgerInfo getLedger(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getLedger(ledgerHash); + } + +// @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") + @Override + public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getConsensusParticipants(ledgerHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks") + public LedgerBlock[] getBlocks(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash); + long maxBlockHeight = ledgerInfo.getLatestBlockHeight(); + List ledgerBlocks = new ArrayList<>(); + for (long blockHeight = maxBlockHeight; blockHeight > GENESIS_BLOCK_HEIGHT; blockHeight--) { + LedgerBlock ledgerBlock = peerService.getQueryService().getBlock(ledgerHash, blockHeight); + ledgerBlocks.add(0, ledgerBlock); + if (ledgerBlocks.size() == BLOCK_MAX_DISPLAY) { + break; + } + } + // 最后增加创世区块 + LedgerBlock genesisBlock = peerService.getQueryService().getBlock(ledgerHash, GENESIS_BLOCK_HEIGHT); + ledgerBlocks.add(0, genesisBlock); + LedgerBlock[] blocks = new LedgerBlock[ledgerBlocks.size()]; + ledgerBlocks.toArray(blocks); + return blocks; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}") + @Override + public LedgerBlock getBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + return peerService.getQueryService().getBlock(ledgerHash, blockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}") + @Override + public LedgerBlock getBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + return peerService.getQueryService().getBlock(ledgerHash, blockHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/count") + @Override + public long getTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + return peerService.getQueryService().getTransactionCount(ledgerHash, blockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/count") + @Override + public long getTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + return peerService.getQueryService().getTransactionCount(ledgerHash, blockHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/count") + @Override + public long getTransactionTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getTransactionTotalCount(ledgerHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/count") + @Override + public long getDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + return peerService.getQueryService().getDataAccountCount(ledgerHash, blockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/count") + @Override + public long getDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + return peerService.getQueryService().getDataAccountCount(ledgerHash, blockHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/count") + @Override + public long getDataAccountTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getDataAccountTotalCount(ledgerHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/count") + @Override + public long getUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + return peerService.getQueryService().getUserCount(ledgerHash, blockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/count") + @Override + public long getUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + return peerService.getQueryService().getUserCount(ledgerHash, blockHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/count") + @Override + public long getUserTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getUserTotalCount(ledgerHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/count") + @Override + public long getContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + return peerService.getQueryService().getContractCount(ledgerHash, blockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/count") + @Override + public long getContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + return peerService.getQueryService().getContractCount(ledgerHash, blockHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/count") + @Override + public long getContractTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getContractTotalCount(ledgerHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs") + @Override + public LedgerTransaction[] getTransactions(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return peerService.getQueryService().getTransactions(ledgerHash, blockHeight, fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs") + @Override + public LedgerTransaction[] getTransactions(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return peerService.getQueryService().getTransactions(ledgerHash, blockHash, fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/hash/{contentHash}") + @Override + public LedgerTransaction getTransactionByContentHash(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "contentHash") HashDigest contentHash) { + return peerService.getQueryService().getTransactionByContentHash(ledgerHash, contentHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/state/{contentHash}") + @Override + public TransactionState getTransactionStateByContentHash(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "contentHash") HashDigest contentHash) { + return peerService.getQueryService().getTransactionStateByContentHash(ledgerHash, contentHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/address/{address}") + @Override + public UserInfo getUser(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + return peerService.getQueryService().getUser(ledgerHash, address); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}") + @Override + public AccountHeader getDataAccount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + + return peerService.getQueryService().getDataAccount(ledgerHash, address); + } + + @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, path = "ledgers/{ledgerHash}/accounts/{address}/entries") + @Override + public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, + @PathVariable("address") String address, + @RequestParam("keys") String... keys) { + return peerService.getQueryService().getDataEntries(ledgerHash, address, keys); + } + + @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries") + @Override + public KVDataEntry[] getDataEntries(@PathVariable("ledgerHash") HashDigest ledgerHash, + @PathVariable("address") String address, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return peerService.getQueryService().getDataEntries(ledgerHash, address, fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries/count") + @Override + public long getDataEntriesTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + return peerService.getQueryService().getDataEntriesTotalCount(ledgerHash, address); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}") + @Override + public AccountHeader getContract(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + return peerService.getQueryService().getContract(ledgerHash, address); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/latest") + @Override + public LedgerBlock getLatestBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + long latestBlockHeight = peerService.getQueryService().getLedger(ledgerHash).getLatestBlockHeight(); + return peerService.getQueryService().getBlock(ledgerHash, latestBlockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/additional-count") + @Override + public long getAdditionalTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + // 获取某个区块的交易总数 + long currentBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, blockHeight); + if (blockHeight == GENESIS_BLOCK_HEIGHT) { + return currentBlockTxCount; + } + long lastBlockHeight = blockHeight - 1; + long lastBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, lastBlockHeight); + // 当前区块交易数减上个区块交易数 + return currentBlockTxCount - lastBlockTxCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/additional-count") + @Override + public long getAdditionalTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash); + long currentBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, blockHash); + if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) { + return currentBlockTxCount; + } + HashDigest previousHash = currentBlock.getPreviousHash(); + long lastBlockTxCount = peerService.getQueryService().getTransactionCount(ledgerHash, previousHash); + // 当前区块交易数减上个区块交易数 + return currentBlockTxCount - lastBlockTxCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/additional-count") + @Override + public long getAdditionalTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash); + long maxBlockHeight = ledgerInfo.getLatestBlockHeight(); + long totalCount = peerService.getQueryService().getTransactionTotalCount(ledgerHash); + if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块 + return totalCount; + } + long lastTotalCount = peerService.getQueryService().getTransactionCount(ledgerHash, maxBlockHeight - 1); + return totalCount - lastTotalCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/additional-count") + @Override + public long getAdditionalDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + long currentDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, blockHeight); + if (blockHeight == GENESIS_BLOCK_HEIGHT) { + return currentDaCount; + } + long lastBlockHeight = blockHeight - 1; + long lastDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, lastBlockHeight); + return currentDaCount - lastDaCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/additional-count") + @Override + public long getAdditionalDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash); + long currentBlockDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, blockHash); + if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) { + return currentBlockDaCount; + } + HashDigest previousHash = currentBlock.getPreviousHash(); + long lastBlockDaCount = peerService.getQueryService().getDataAccountCount(ledgerHash, previousHash); + // 当前区块数据账户数量减上个区块数据账户数量 + return currentBlockDaCount - lastBlockDaCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/additional-count") + @Override + public long getAdditionalDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash); + long maxBlockHeight = ledgerInfo.getLatestBlockHeight(); + long totalCount = peerService.getQueryService().getDataAccountTotalCount(ledgerHash); + if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块 + return totalCount; + } + long lastTotalCount = peerService.getQueryService().getDataAccountCount(ledgerHash, maxBlockHeight - 1); + return totalCount - lastTotalCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/additional-count") + @Override + public long getAdditionalUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + long currentUserCount = peerService.getQueryService().getUserCount(ledgerHash, blockHeight); + if (blockHeight == GENESIS_BLOCK_HEIGHT) { + return currentUserCount; + } + long lastBlockHeight = blockHeight - 1; + long lastUserCount = peerService.getQueryService().getUserCount(ledgerHash, lastBlockHeight); + return currentUserCount - lastUserCount; + } + + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/additional-count") + @Override + public long getAdditionalUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash); + long currentBlockUserCount = peerService.getQueryService().getUserCount(ledgerHash, blockHash); + if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) { + return currentBlockUserCount; + } + HashDigest previousHash = currentBlock.getPreviousHash(); + long lastBlockUserCount = peerService.getQueryService().getUserCount(ledgerHash, previousHash); + // 当前区块用户数量减上个区块用户数量 + return currentBlockUserCount - lastBlockUserCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/additional-count") + @Override + public long getAdditionalUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash); + long maxBlockHeight = ledgerInfo.getLatestBlockHeight(); + long totalCount = peerService.getQueryService().getUserTotalCount(ledgerHash); + if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块 + return totalCount; + } + long lastTotalCount = peerService.getQueryService().getUserCount(ledgerHash, maxBlockHeight - 1); + return totalCount - lastTotalCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/additional-count") + @Override + public long getAdditionalContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + long currentContractCount = peerService.getQueryService().getContractCount(ledgerHash, blockHeight); + if (blockHeight == GENESIS_BLOCK_HEIGHT) { + return currentContractCount; + } + long lastBlockHeight = blockHeight - 1; + long lastContractCount = peerService.getQueryService().getUserCount(ledgerHash, lastBlockHeight); + return currentContractCount - lastContractCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/additional-count") + @Override + public long getAdditionalContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerBlock currentBlock = peerService.getQueryService().getBlock(ledgerHash, blockHash); + long currentBlockContractCount = peerService.getQueryService().getContractCount(ledgerHash, blockHash); + if (currentBlock.getHeight() == GENESIS_BLOCK_HEIGHT) { + return currentBlockContractCount; + } + HashDigest previousHash = currentBlock.getPreviousHash(); + long lastBlockContractCount = peerService.getQueryService().getUserCount(ledgerHash, previousHash); + // 当前区块合约数量减上个区块合约数量 + return currentBlockContractCount - lastBlockContractCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/additional-count") + @Override + public long getAdditionalContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerInfo ledgerInfo = peerService.getQueryService().getLedger(ledgerHash); + long maxBlockHeight = ledgerInfo.getLatestBlockHeight(); + long totalCount = peerService.getQueryService().getContractTotalCount(ledgerHash); + if (maxBlockHeight == GENESIS_BLOCK_HEIGHT) { // 只有一个创世区块 + return totalCount; + } + long lastTotalCount = peerService.getQueryService().getContractCount(ledgerHash, maxBlockHeight - 1); + return totalCount - lastTotalCount; + } + + @RequestMapping(method = RequestMethod.GET, path = "utils/pubkey/{pubkey}/addr") + public String getAddrByPubKey(@PathVariable(name = "pubkey") String strPubKey) { + PubKey pubKey = KeyGenCommand.decodePubKey(strPubKey); + return AddressEncoding.generateAddress(pubKey).toBase58(); + } + + @RequestMapping(method = RequestMethod.GET, value = "ledgers/{ledgerHash}/**/search") + public Object dataRetrieval(@PathVariable(name = "ledgerHash") HashDigest ledgerHash,HttpServletRequest request) { + String result; + if (dataRetrievalUrl == null || dataRetrievalUrl.length() <= 0) { + result = "{'message':'OK','data':'" + "data.retrieval.url is empty" + "'}"; + } else { + String queryParams = request.getQueryString() == null ? "": request.getQueryString(); + String fullQueryUrl = new StringBuffer(dataRetrievalUrl) + .append(request.getRequestURI()) + .append(BaseConstant.DELIMETER_QUESTION) + .append(queryParams) + .toString(); + try { + result = dataRetrievalService.retrieval(fullQueryUrl); + ConsoleUtils.info("request = {%s} \r\n result = {%s} \r\n", fullQueryUrl, result); + } catch (Exception e) { + result = "{'message':'OK','data':'" + e.getMessage() + "'}"; + } + } + return result; + } + + public void setDataRetrievalUrl(String dataRetrievalUrl) { + this.dataRetrievalUrl = dataRetrievalUrl; + } + + /** + * get all ledgers count; + */ + @RequestMapping(method = RequestMethod.GET, path = "ledgers/count") + @Override + public int getLedgersCount() { + return peerService.getQueryService().getLedgerHashs().length; + } + + /** + * get all ledgers hashs; + */ + @RequestMapping(method = RequestMethod.GET, path = "ledgers") + public HashDigest[] getLedgersHash(@RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return gatewayQueryService.getLedgersHash(fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants/count") + public int getConsensusParticipantCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + return peerService.getQueryService().getConsensusParticipants(ledgerHash).length; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") + public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return gatewayQueryService.getConsensusParticipants(ledgerHash,fromIndex,count); + } + + /** + * get more users by fromIndex and count; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users") + @Override + public AccountHeader[] getUsers(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return peerService.getQueryService().getUsers(ledgerHash, fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts") + @Override + public AccountHeader[] getDataAccounts(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return peerService.getQueryService().getDataAccounts(ledgerHash, fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts") + @Override + public AccountHeader[] getContractAccounts(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + return peerService.getQueryService().getContractAccounts(ledgerHash, fromIndex, count); + } +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayGlobalExceptionHandler.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayGlobalExceptionHandler.java new file mode 100644 index 00000000..463ad782 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayGlobalExceptionHandler.java @@ -0,0 +1,43 @@ +package com.jd.blockchain.gateway.web; + +import javax.servlet.http.HttpServletRequest; + +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 com.jd.blockchain.utils.BusinessException; +import com.jd.blockchain.utils.web.model.ErrorCode; +import com.jd.blockchain.utils.web.model.WebResponse; +import com.jd.blockchain.utils.web.model.WebResponse.ErrorMessage; + +/** + * 全局异常处理类 + */ +@RestControllerAdvice +public class GatewayGlobalExceptionHandler { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + @ExceptionHandler(value = Exception.class) + @ResponseBody + public WebResponse json(HttpServletRequest req, Exception ex) { + ErrorMessage message = null; + String reqURL = "[" + req.getMethod() + "] " + req.getRequestURL().toString(); + if (ex instanceof BusinessException) { + logger.error("BusinessException occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() + "] " + + ex.getMessage(), ex); + BusinessException businessException = (BusinessException) ex; + message = new ErrorMessage(businessException.getErrorCode(), businessException.getMessage()); + } else { + logger.error("Unexpected exception occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() + + "]" + ex.getMessage(), ex); + message = new ErrorMessage(ErrorCode.UNEXPECTED.getValue(), + ErrorCode.UNEXPECTED.getDescription(ex.getMessage())); + } + WebResponse response = WebResponse.createFailureResult(message); + return response; + } + +} \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayJsonResponseAdvice.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayJsonResponseAdvice.java new file mode 100644 index 00000000..ec66c6cd --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayJsonResponseAdvice.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.gateway.web; + +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; + +import com.jd.blockchain.utils.web.model.WebResponse; + +@RestControllerAdvice +public class GatewayJsonResponseAdvice implements ResponseBodyAdvice { + + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + if (MappingJackson2HttpMessageConverter.class == converterType + && (returnType.getContainingClass().getName().startsWith("com.jd") + || returnType.getDeclaringClass().getName().startsWith("com.jd"))) { + return true; + } + return false; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, + Class> selectedConverterType, ServerHttpRequest request, + ServerHttpResponse response) { + if (body == null) { + return WebResponse.createSuccessResult(null); + } + if (body instanceof ResponseEntity) { + return body; + } + // 把返回结果自动转换为 WebResponse; + if (body instanceof WebResponse) { + return body; + } + return WebResponse.createSuccessResult(body); + } + +} \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayTimeTasks.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayTimeTasks.java new file mode 100644 index 00000000..819691e2 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayTimeTasks.java @@ -0,0 +1,46 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.gateway.web.GatewayTimeTasks + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/16 下午6:17 + * Description: + */ +package com.jd.blockchain.gateway.web; + +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.PeerConnector; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.*; + +/** + * + * @author shaozhuguang + * @create 2019/1/16 + * @since 1.0.0 + */ +@Component +@EnableScheduling +public class GatewayTimeTasks { + + private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(GatewayTimeTasks.class); + + @Autowired + private PeerConnector peerConnector; + + //每30分钟执行一次 + @Scheduled(cron = "0 */8 * * * * ") + public void updateLedger(){ + try { + peerConnector.reconnect(); + } catch (Exception e) { + LOGGER.error(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebSecurityConfigurer.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebSecurityConfigurer.java new file mode 100644 index 00000000..857d18ae --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebSecurityConfigurer.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.gateway.web; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class GatewayWebSecurityConfigurer extends WebSecurityConfigurerAdapter { + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().permitAll(); + http.csrf().disable(); + } +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebServerConfigurer.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebServerConfigurer.java new file mode 100644 index 00000000..ed43d2a7 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/GatewayWebServerConfigurer.java @@ -0,0 +1,71 @@ +package com.jd.blockchain.gateway.web; + +import java.util.List; + +import com.jd.blockchain.web.serializes.ByteArrayObjectUtil; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import com.jd.blockchain.utils.web.model.JsonWebResponseMessageConverter; +import com.jd.blockchain.web.converters.BinaryMessageConverter; +import com.jd.blockchain.web.converters.HashDigestInputConverter; + +/** + * @author zhuguang + * @date 2018-08-08 + */ +@Configuration +public class GatewayWebServerConfigurer implements WebMvcConfigurer { + + static { + JSONSerializeUtils.disableCircularReferenceDetect(); + JSONSerializeUtils.configStringSerializer(ByteArray.class); + } + + + @Override + public void extendMessageConverters(List> converters) { + int index = converters.size(); + for (int i = 0; i < converters.size(); i++) { + if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) { + index = i; + break; + } + } + + JsonWebResponseMessageConverter jsonConverter = new JsonWebResponseMessageConverter(false); + + converters.add(index, jsonConverter); + + converters.add(0, new BinaryMessageConverter()); + + initByteArrayJsonSerialize(); + } + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new HashDigestInputConverter()); + } + + @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"); + } + + private void initByteArrayJsonSerialize() { + ByteArrayObjectUtil.init(); + } +} diff --git a/source/gateway/src/main/java/com/jd/blockchain/gateway/web/TxProcessingController.java b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/TxProcessingController.java new file mode 100644 index 00000000..c2cba6f8 --- /dev/null +++ b/source/gateway/src/main/java/com/jd/blockchain/gateway/web/TxProcessingController.java @@ -0,0 +1,70 @@ +package com.jd.blockchain.gateway.web; + + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.PeerService; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.utils.BusinessException; +import com.jd.blockchain.web.converters.BinaryMessageConverter; + +/** + * @author huanghaiquan + * + */ +@RestController +public class TxProcessingController implements TransactionService { + + @Autowired + private PeerService peerService; + + @RequestMapping(path = "rpc/tx", method = RequestMethod.POST, consumes = BinaryMessageConverter.CONTENT_TYPE_VALUE, produces = BinaryMessageConverter.CONTENT_TYPE_VALUE) + @Override + public @ResponseBody TransactionResponse process(@RequestBody TransactionRequest txRequest) { + // 检查交易请求的信息是否完整; + HashDigest ledgerHash = txRequest.getTransactionContent().getLedgerHash(); + if (ledgerHash == null) { + // 未指定交易的账本; + throw new IllegalArgumentException("The TransactionRequest miss ledger hash!"); + } + + // 预期的请求中不应该包含节点签名,首个节点签名应该由当前网关提供; + if (txRequest.getNodeSignatures() != null && txRequest.getNodeSignatures().length > 0) { + throw new IllegalArgumentException("Gateway cann't accept TransactionRequest with any NodeSignature!"); + } + + // TODO:检查参与者的签名; + DigitalSignature[] partiSigns = txRequest.getEndpointSignatures(); + if (partiSigns == null || partiSigns.length == 0) { + // 缺少参与者签名,则采用检查托管账户并进行托管签名;如果请求未包含托管账户,或者托管账户认证失败,则返回401错误; + // TODO: 未实现! + throw new IllegalStateException("Not implemented!"); + } else { + // 验证签名; + byte[] content = BinaryEncodingUtils.encode(txRequest.getTransactionContent(), TransactionContent.class); + for (DigitalSignature sign : partiSigns) { + SignatureFunction signFunc = CryptoUtils.sign(sign.getPubKey().getAlgorithm()); + if (!signFunc.verify(sign.getDigest(), sign.getPubKey(), content)) { + throw new BusinessException("The validation of participant signatures fail!"); + } + } + } + + // 注:转发前自动附加网关的签名并转发请求至共识节点;异步的处理方式 + return peerService.getTransactionService().process(txRequest); + } +} + diff --git a/source/gateway/src/main/resources/application-gw.properties b/source/gateway/src/main/resources/application-gw.properties new file mode 100644 index 00000000..b87537f3 --- /dev/null +++ b/source/gateway/src/main/resources/application-gw.properties @@ -0,0 +1 @@ +spring.mvc.favicon.enabled=false \ No newline at end of file diff --git a/source/gateway/src/main/resources/banner.txt b/source/gateway/src/main/resources/banner.txt new file mode 100644 index 00000000..c39618bd --- /dev/null +++ b/source/gateway/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄ +▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░▌ ▐░▌ + ▀▀▀▀▀█░█▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ + ▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌ ▐░▐░▌ +▐░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░░▌ + ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀ + diff --git a/source/gateway/src/main/resources/gateway.conf b/source/gateway/src/main/resources/gateway.conf new file mode 100644 index 00000000..84f00679 --- /dev/null +++ b/source/gateway/src/main/resources/gateway.conf @@ -0,0 +1,28 @@ +#网关的HTTP服务地址; +http.host=127.0.0.1 +#网关的HTTP服务端口; +http.port=8081 +#网关的HTTP服务上下文路径,可选; +#http.context-path= + +#共识节点的服务地址; +peer.host=127.0.0.1 +#共识节点的服务端口; +peer.port=7080 +#共识节点的服务是否启用安全证书; +peer.secure=false +#共识节点的服务提供解析器 +peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider + +#数据检索服务对应URL +#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 +data.retrieval.url=http://192.168.1.1:10001 + +#默认公钥的内容(Base58编码数据); +keys.default.pubkey=endPsK36g6bhgn5bj66uyX4uxqnkfGvdjpxWurAA5hbf8vVoVi8H +#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey-path= +#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey=177gjwmuvDnccAvrvmJyCN1dgAqqGzfYpe3pZ8dWWNBneM5GgdsS96vgjvBP4fX61jWfohQ +#默认私钥的解码密码; +keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY \ No newline at end of file diff --git a/source/gateway/src/main/resources/log4j2.xml b/source/gateway/src/main/resources/log4j2.xml new file mode 100644 index 00000000..3fe4b122 --- /dev/null +++ b/source/gateway/src/main/resources/log4j2.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/GatewayConfigPropertiesTest.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/GatewayConfigPropertiesTest.java new file mode 100644 index 00000000..5bcb142c --- /dev/null +++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/GatewayConfigPropertiesTest.java @@ -0,0 +1,58 @@ +package test.com.jd.blockchain.gateway; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.gateway.GatewayConfigProperties; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.security.ShaUtils; + +public class GatewayConfigPropertiesTest { + + @Test + public void test() { + ClassPathResource gatewayConfigResource = new ClassPathResource("gateway.conf"); + try (InputStream in = gatewayConfigResource.getInputStream()) { + GatewayConfigProperties configProps = GatewayConfigProperties.resolve(in); + assertEquals("192.168.10.108", configProps.http().getHost()); + assertEquals(80, configProps.http().getPort()); + assertNull(configProps.http().getContextPath()); + + assertEquals("10.1.6.61", configProps.masterPeerAddress().getHost()); + assertEquals(7100, configProps.masterPeerAddress().getPort()); + assertTrue(configProps.masterPeerAddress().isSecure()); + + assertEquals("http://192.168.1.1:10001", configProps.dataRetrievalUrl()); + + assertEquals("keys/default.priv", configProps.keys().getDefault().getPrivKeyPath()); + assertEquals("64hnH4a8n48LeEmNxUPcaZ1J", configProps.keys().getDefault().getPrivKeyValue()); + assertEquals("a8n48LeEP", configProps.keys().getDefault().getPrivKeyPassword()); + + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + + @Test + public void generateDefaultPassword() { + //generate default base58 password for gateway.conf + String password = "abc"; + String encodePassword; + byte[] pwdBytes = BytesUtils.toBytes(password, "UTF-8"); + encodePassword = Base58Utils.encode(ShaUtils.hash_256(pwdBytes)); + + return; + + } + +} diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestDeserializer.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestDeserializer.java new file mode 100644 index 00000000..82967c7f --- /dev/null +++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestDeserializer.java @@ -0,0 +1,31 @@ +package test.com.jd.blockchain.gateway.data; + +import java.lang.reflect.Type; + +import com.alibaba.fastjson.parser.DefaultJSONParser; +import com.alibaba.fastjson.parser.JSONToken; +import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.codec.Base58Utils; + +public class HashDigestDeserializer implements ObjectDeserializer{ + + public static final HashDigestDeserializer INSTANCE = new HashDigestDeserializer(); + + @SuppressWarnings("unchecked") + @Override + public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + if (type instanceof Class && HashDigest.class.isAssignableFrom((Class) type)) { + String base58Str = parser.parseObject(String.class); + byte[] hashBytes = Base58Utils.decode(base58Str); + return (T) new HashDigest(hashBytes); + } + return (T) parser.parse(fieldName); + } + + @Override + public int getFastMatchToken() { + return JSONToken.LBRACE; + } + +} diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestJSONSerializeTest.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestJSONSerializeTest.java new file mode 100644 index 00000000..8d7709d2 --- /dev/null +++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestJSONSerializeTest.java @@ -0,0 +1,66 @@ +package test.com.jd.blockchain.gateway.data; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.util.Random; + +import org.junit.Test; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +public class HashDigestJSONSerializeTest { + + private static class TestData { + + private int id; + + private HashDigest hash; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public HashDigest getHash() { + return hash; + } + + public void setHash(HashDigest hash) { + this.hash = hash; + } + + } + + @Test + public void test() throws Exception { + JSONSerializeUtils.configSerialization(HashDigest.class, HashDigestSerializer.INSTANCE, + HashDigestDeserializer.INSTANCE); + + + HashDigest hash = new HashDigest(CryptoAlgorithm.SHA256, "jd-test".getBytes()); + + String hashJson = JSONSerializeUtils.serializeToJSON(hash, true); + HashDigest hashDigest = JSONSerializeUtils.deserializeFromJSON(hashJson, HashDigest.class); + + assertArrayEquals(hash.getRawDigest(), hashDigest.getRawDigest()); + assertEquals(hash.getAlgorithm(), hashDigest.getAlgorithm()); + + TestData data = new TestData(); + data.setHash(hash); + data.setId(10); + + String json = JSONSerializeUtils.serializeToJSON(data, true); + + TestData desData = JSONSerializeUtils.deserializeFromJSON(json, TestData.class); + assertEquals(data.getHash(), desData.getHash()); + assertEquals(data.getId(), desData.getId()); + } + +} diff --git a/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestSerializer.java b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestSerializer.java new file mode 100644 index 00000000..8545d38d --- /dev/null +++ b/source/gateway/src/test/java/test/com/jd/blockchain/gateway/data/HashDigestSerializer.java @@ -0,0 +1,27 @@ +package test.com.jd.blockchain.gateway.data; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeWriter; +import com.jd.blockchain.crypto.hash.HashDigest; + +public class HashDigestSerializer implements ObjectSerializer { + + public static HashDigestSerializer INSTANCE = new HashDigestSerializer(); + + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) + throws IOException { + SerializeWriter out = serializer.out; + if (object == null) { + out.writeNull(); + return; + } + HashDigest hash = (HashDigest) object; + out.writeString(hash.toBase58()); + } + +} diff --git a/source/gateway/src/test/resources/gateway.conf b/source/gateway/src/test/resources/gateway.conf new file mode 100644 index 00000000..b99068a2 --- /dev/null +++ b/source/gateway/src/test/resources/gateway.conf @@ -0,0 +1,29 @@ + +#网关的HTTP服务地址; +http.host=192.168.10.108 +#网关的HTTP服务端口; +http.port=80 +#网关的HTTP服务上下文路径,可选; +#http.context-path= + +#共识节点的服务地址; +peer.host=10.1.6.61 +#共识节点的服务端口; +peer.port=7100 +#共识节点的服务是否启用安全证书; +peer.secure=true +#共识节点的服务提供解析器 +peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider + +#数据检索服务对应URL +#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 +data.retrieval.url=http://192.168.1.1:10001 + +#默认公钥的内容(Base58编码数据); +keys.default.pubkey=64hnNxUPcH4a8NxUPc +#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey-path=keys/default.priv +#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; +keys.default.privkey=64hnH4a8n48LeEmNxUPcaZ1J +#默认私钥的解码密码; +keys.default.privkey-password=a8n48LeEP \ No newline at end of file diff --git a/source/ledger/ledger-core/pom.xml b/source/ledger/ledger-core/pom.xml new file mode 100644 index 00000000..bd23883e --- /dev/null +++ b/source/ledger/ledger-core/pom.xml @@ -0,0 +1,105 @@ + + 4.0.0 + + com.jd.blockchain + ledger + 0.8.2.RELEASE + + ledger-core + + + + com.jd.blockchain + ledger-model + ${project.version} + + + com.jd.blockchain + storage-service + ${project.version} + + + com.jd.blockchain + binary-proto + ${project.version} + + + com.jd.blockchain + crypto-framework + ${project.version} + + + com.jd.blockchain + storage-service + ${project.version} + + + com.jd.blockchain + contract-framework + ${project.version} + + + com.jd.blockchain + contract-jvm + ${project.version} + + + org.springframework + spring-context + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountAccessPolicy.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountAccessPolicy.java new file mode 100644 index 00000000..eb2763d9 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountAccessPolicy.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.ledger.core; + + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.utils.Bytes; + +/** + * 账户访问策略; + * + * @author huanghaiquan + * + */ +public interface AccountAccessPolicy { + + /** + * Check access policy before committing the specified account;
+ * + * @param account + * @return Return true if it satisfies this policy, or false if it doesn't; + */ + boolean checkCommitting(AccountHeader account); + + boolean checkRegistering(Bytes address, PubKey pubKey); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountPrivilege.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountPrivilege.java new file mode 100644 index 00000000..c57debff --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountPrivilege.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.ledger.core; + +public interface AccountPrivilege { + + /** + * 数据“读”的操作码; + * + * @return + */ + byte getReadingOpCode(); + + /** + * “写”的操作码; + * + * @return + */ + byte getWrittingOpCode(); + + /** + * 其它的扩展操作码; + * + * @return + */ + byte[] getExtOpCodes(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java new file mode 100644 index 00000000..511a939f --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AccountSet.java @@ -0,0 +1,456 @@ +package com.jd.blockchain.ledger.core; + +import java.util.HashMap; +import java.util.Map; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +public class AccountSet implements Transactional, MerkleProvable { + + static { + DataContractRegistry.register(AccountHeader.class); + } + + private final String keyPrefix; + + private MerkleDataSet merkleDataset; + + /** + * The cache of latest version accounts, including accounts getting by querying + * and by new regiestering ; + * + */ + // TODO:未考虑大数据量时,由于缺少过期策略,会导致内存溢出的问题; + private Map latestAccountsCache = new HashMap<>(); + + private ExPolicyKVStorage baseExStorage; + + private VersioningKVStorage baseVerStorage; + + private CryptoSetting cryptoSetting; + + private boolean updated; + + private AccountAccessPolicy accessPolicy; + + public boolean isReadonly() { + return merkleDataset.isReadonly(); + } + + public AccountSet(CryptoSetting cryptoSetting, String keyPrefix, ExPolicyKVStorage exStorage, + VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { + this(null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy); + } + + public AccountSet(HashDigest rootHash, CryptoSetting cryptoSetting, String keyPrefix, ExPolicyKVStorage exStorage, + VersioningKVStorage verStorage, boolean readonly, AccountAccessPolicy accessPolicy) { + this.keyPrefix = keyPrefix; + this.cryptoSetting = cryptoSetting; + this.baseExStorage = exStorage; + this.baseVerStorage = verStorage; + this.merkleDataset = new MerkleDataSet(rootHash, cryptoSetting, keyPrefix, this.baseExStorage, + this.baseVerStorage, readonly); + this.accessPolicy = accessPolicy; + } + + // public HashDigest getAccountRootHash() { + // return merkleDataset.getRootHash(); + // } + + @Override + public HashDigest getRootHash() { + return merkleDataset.getRootHash(); + } + + @Override + public MerkleProof getProof(Bytes key) { + return merkleDataset.getProof(key); + } + + public AccountHeader[] getAccounts(int fromIndex, int count) { + byte[][] results = merkleDataset.getLatestValues(fromIndex, count); + AccountHeader[] accounts = new AccountHeader[results.length]; + + for (int i = 0; i < results.length; i++) { + accounts[i] = deserialize(results[i]); + } + return accounts; + } + +// private VersioningAccount deserialize(byte[] txBytes) { +//// return BinaryEncodingUtils.decode(txBytes, null, Account.class); +// AccountHeaderData accInfo = BinaryEncodingUtils.decode(txBytes); +//// return new BaseAccount(accInfo.getAddress(), accInfo.getPubKey(), null, cryptoSetting, +//// baseExStorage, baseVerStorage, true, accessPolicy); +// return new VersioningAccount(accInfo.getAddress(), accInfo.getPubKey(), accInfo.getRootHash(), cryptoSetting, +// keyPrefix, baseExStorage, baseVerStorage, true, accessPolicy, accInfo.); +// } + + private AccountHeader deserialize(byte[] txBytes) { + return BinaryEncodingUtils.decode(txBytes); + } + + /** + * 返回账户的总数量; + * + * @return + */ + public long getTotalCount() { + return merkleDataset.getDataCount(); + } + + /** + * 返回最新版本的 Account; + * + * @param address + * @return + */ + public BaseAccount getAccount(Bytes address) { + return this.getAccount(address, -1); + } + + /** + * 账户是否存在;
+ * + * 如果指定的账户已经注册(通过 {@link #register(String, PubKey)} 方法),但尚未提交(通过 + * {@link #commit()} 方法),此方法对该账户仍然返回 false; + * + * @param address + * @return + */ + public boolean contains(Bytes address) { + long latestVersion = getVersion(address); + return latestVersion > -1; + } + + /** + * 返回指定账户的版本;
+ * 如果账户已经注册,则返回该账户的最新版本,值大于等于 0;
+ * 如果账户不存在,则返回 -1;
+ * 如果指定的账户已经注册(通过 {@link #register(String, PubKey)} 方法),但尚未提交(通过 + * {@link #commit()} 方法),此方法对该账户仍然返回 0; + * + * @param address + * @return + */ + public long getVersion(Bytes address) { + VersioningAccount acc = latestAccountsCache.get(address); + if (acc != null) { + // 已注册尚未提交,也返回 -1; + return acc.version == -1 ? 0 : acc.version; + } + + return merkleDataset.getVersion(address); + } + + /** + * 返回指定版本的 Account; + * + * 只有最新版本的账户才能可写的,其它都是只读; + * + * @param address + * 账户地址; + * @param version + * 账户版本;如果指定为 -1,则返回最新版本; + * @return + */ + public BaseAccount getAccount(Bytes address, long version) { + version = version < 0 ? -1 : version; + VersioningAccount acc = latestAccountsCache.get(address); + if (acc != null && version == -1) { + return acc; + } else if (acc != null && acc.version == version) { + return acc; + } + + long latestVersion = merkleDataset.getVersion(address); + if (latestVersion < 0) { + // Not exist; + return null; + } + if (version > latestVersion) { + return null; + } + + // 如果是不存在的,或者刚刚新增未提交的账户,则前面一步查询到的 latestVersion 小于 0, 代码不会执行到此; + if (acc != null && acc.version != latestVersion) { + // 当执行到此处时,并且缓冲列表中缓存了最新的版本, + // 如果当前缓存的最新账户的版本和刚刚从存储中检索得到的最新版本不一致,可能存在外部的并发更新,这超出了系统设计的逻辑; + + // TODO:如果是今后扩展至集群方案时,这种不一致的原因可能是由其它集群节点实例执行了更新,这种情况下,最好是放弃旧缓存,并重新加载和缓存最新版本; + // by huanghaiquan at 2018-9-2 23:03:00; + throw new IllegalStateException("The latest version in cache is not equals the latest version in storage! " + + "Mybe some asynchronzing updating are performed out of current server."); + } + + // Now, be sure that "acc == null", so get account from storage; + + byte[] bytes = merkleDataset.getValue(address, version); + if (bytes == null) { + return null; + } + + // Set readonly for the old version account; + boolean readonly = (version > -1 && version < latestVersion) || isReadonly(); + + // String prefix = address.concat(LedgerConsts.KEY_SEPERATOR); + // ExPolicyKVStorage ss = PrefixAppender.prefix(prefix, baseExStorage); + // VersioningKVStorage vs = PrefixAppender.prefix(prefix, baseVerStorage); + // BaseAccount accDS = deserialize(bytes, cryptoSetting, ss, vs, readonly); + String prefix = keyPrefix + address; + acc = deserialize(bytes, cryptoSetting, prefix, baseExStorage, baseVerStorage, readonly, latestVersion); + if (!readonly) { + // cache the latest version witch enable reading and writing; + // readonly version of account not necessary to be cached; + latestAccountsCache.put(address, acc); + } + return acc; + } + + /** + * 注册一个新账户;
+ * + * 如果账户已经存在,则会引发 {@link LedgerException} 异常;
+ * + * 如果指定的地址和公钥不匹配,则会引发 {@link LedgerException} 异常; + * + * @param address + * 区块链地址; + * @param pubKey + * 公钥; + * @return 注册成功的账户对象; + */ + public BaseAccount register(Bytes address, PubKey pubKey) { + if (isReadonly()) { + throw new IllegalArgumentException("This AccountSet is readonly!"); + } + + verifyAddressEncoding(address, pubKey); + + VersioningAccount cachedAcc = latestAccountsCache.get(address); + if (cachedAcc != null) { + if (cachedAcc.version < 0) { + // 同一个新账户已经注册,但尚未提交,所以重复注册不会引起任何变化; + return cachedAcc; + } + // 相同的账户已经存在; + throw new LedgerException("The registering account already exist!"); + } + long version = merkleDataset.getVersion(address); + if (version >= 0) { + throw new LedgerException("The registering account already exist!"); + } + + if (!accessPolicy.checkRegistering(address, pubKey)) { + throw new LedgerException("Account Registering was rejected for the access policy!"); + } + + // String prefix = address.concat(LedgerConsts.KEY_SEPERATOR); + // ExPolicyKVStorage accExStorage = PrefixAppender.prefix(prefix, + // baseExStorage); + // VersioningKVStorage accVerStorage = PrefixAppender.prefix(prefix, + // baseVerStorage); + // BaseAccount accDS = createInstance(address, pubKey, cryptoSetting, + // accExStorage, accVerStorage); + + String prefix = keyPrefix + address; + VersioningAccount acc = createInstance(address, pubKey, cryptoSetting, prefix, baseExStorage, baseVerStorage, -1); + latestAccountsCache.put(address, acc); + updated = true; + + return acc; + } + + private void verifyAddressEncoding(Bytes address, PubKey pubKey) { + Bytes chAddress = AddressEncoding.generateAddress(pubKey); + if (!chAddress.equals(address)) { + throw new LedgerException("The registering Address mismatch the specified PubKey!"); + } + } + + private VersioningAccount createInstance(Bytes address, PubKey pubKey, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, long version) { + return new VersioningAccount(address, pubKey, cryptoSetting, keyPrefix, exStorage, verStorage, accessPolicy, version); + } + + private VersioningAccount deserialize(byte[] bytes, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, long version) { + AccountHeader accInfo = BinaryEncodingUtils.decode(bytes); + return new VersioningAccount(accInfo.getAddress(), accInfo.getPubKey(), accInfo.getRootHash(), cryptoSetting, + keyPrefix, exStorage, verStorage, readonly, accessPolicy, version); + } + + private byte[] serialize(AccountHeader account) { + return BinaryEncodingUtils.encode(account, AccountHeader.class); + } + + @Override + public boolean isUpdated() { + return updated; + } + + @Override + public void commit() { + if (!updated) { + return; + } + try { + for (VersioningAccount acc : latestAccountsCache.values()) { + // updated or new created; + if (acc.isUpdated() || acc.version < 0) { + // 提交更改,更新哈希; + acc.commit(); + byte[] value = serialize(acc); + long ver = merkleDataset.setValue(acc.getAddress(), value, acc.version); + if (ver < 0) { + // Update fail; + throw new LedgerException("Account updating fail! --[Address=" + acc.getAddress() + "]"); + } + } + } + merkleDataset.commit(); + } finally { + updated = false; + latestAccountsCache.clear(); + } + } + + @Override + public void cancel() { + if (!updated) { + return; + } + String[] addresses = new String[latestAccountsCache.size()]; + latestAccountsCache.keySet().toArray(addresses); + for (String address : addresses) { + VersioningAccount acc = latestAccountsCache.remove(address); + // cancel; + if (acc.isUpdated()) { + acc.cancel(); + } + } + updated = false; + } + + public static class AccountHeaderData implements AccountHeader { + + private Bytes address; + private PubKey pubKey; + private HashDigest rootHash; + + @DConstructor(name = "AccountHeaderData") + public AccountHeaderData(@FieldSetter(name = "getAddress", type = "String") Bytes address, + @FieldSetter(name = "getPubKey", type = "PubKey") PubKey pubKey, + @FieldSetter(name = "getRootHash", type = "HashDigest") HashDigest rootHash) { + this.address = address; + this.pubKey = pubKey; + this.rootHash = rootHash; + } + + @Override + public Bytes getAddress() { + return address; + } + + @Override + public PubKey getPubKey() { + return pubKey; + } + + @Override + public HashDigest getRootHash() { + return rootHash; + } + + } + + private class VersioningAccount extends BaseAccount { + + // private final BaseAccount account; + + private final long version; + + // public VersioningAccount(BaseAccount account, long version) { + // this.account = account; + // this.version = version; + // } + + public VersioningAccount(Bytes address, PubKey pubKey, HashDigest rootHash, CryptoSetting cryptoSetting, + String keyPrefix, ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, + AccountAccessPolicy accessPolicy, long version) { + super(address, pubKey, rootHash, cryptoSetting, keyPrefix, exStorage, verStorage, readonly, accessPolicy); + this.version = version; + } + + public VersioningAccount(Bytes address, PubKey pubKey, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy, + long version) { + super(address, pubKey, cryptoSetting, keyPrefix, exStorage, verStorage, accessPolicy); + this.version = version; + } + + // @Override + // public Bytes getAddress() { + // return account.getAddress(); + // } + // + // @Override + // public PubKey getPubKey() { + // return account.getPubKey(); + // } + // + // @Override + // public HashDigest getRootHash() { + // return account.getRootHash(); + // } + // + // @Override + // public MerkleProof getProof(Bytes key) { + // return account.getProof(key); + // } + // + // @Override + // public boolean isReadonly() { + // return account.isReadonly(); + // } + + @Override + public long setBytes(Bytes key, byte[] value, long version) { + long v = super.setBytes(key, value, version); + if (v > -1) { + updated = true; + } + return v; + } + + // @Override + // public long getKeyVersion(Bytes key) { + // return account.getKeyVersion(key); + // } + // + // @Override + // public byte[] getBytes(Bytes key) { + // return account.getBytes(key); + // } + // + // @Override + // public byte[] getBytes(Bytes key, long version) { + // return account.getBytes(key, version); + // } + + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizableDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizableDataSet.java new file mode 100644 index 00000000..ca0a406a --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizableDataSet.java @@ -0,0 +1,168 @@ +//package com.jd.blockchain.ledger.core; +// +//import com.jd.blockchain.crypto.hash.HashDigest; +// +//import my.utils.Scratchable; +//import my.utils.io.ByteArray; +//import my.utils.io.BytesUtils; +//import my.utils.io.ExistancePolicyKVStorage; +//import my.utils.io.VersioningKVStorage; +// +///** +// * 可进行授权控制的数据集合; +// * +// * @author huanghaiquan +// * +// */ +//public class AuthorizableDataSet implements Scratchable { +// +// public static final String DATA_PREFIX = "DATA" + LedgerConsts.KEY_SEPERATOR; +//// public static final String PRIVILEGE_PREFIX = "PRVL" + LedgerConsts.KEY_SEPERATOR; +// +// private static final String DEFAULT_PRIVILEGE_KEY = "%"; +// +// private DataAccessable accessable; +// +// protected MerkleDataSet data; +// +//// private PrivilegeDataSet privileges; +// +// /** +// * Create a new Account instance; +// * +// * @param address +// * @param pubKey +// */ +// protected AuthorizableDataSet(CryptoSetting merkleTreeSetting, ExistancePolicyKVStorage simpleStorage, +// VersioningKVStorage versioningStorage) { +// this(null, merkleTreeSetting, null, simpleStorage, versioningStorage); +// } +// +// protected AuthorizableDataSet(byte[] dataRootHash, CryptoSetting merkleTreeSetting, byte[] privilegeRootHash, +// ExistancePolicyKVStorage simpleStorage, VersioningKVStorage versioningStorage) { +// this(dataRootHash, merkleTreeSetting, privilegeRootHash, simpleStorage, versioningStorage, false); +// } +// +// protected AuthorizableDataSet(byte[] dataRootHash, CryptoSetting merkleTreeSetting, byte[] privilegeRootHash, +// ExistancePolicyKVStorage simpleStorage, VersioningKVStorage versioningStorage, boolean readonly) { +// this.data = new MerkleDataSet(dataRootHash, merkleTreeSetting, +// PrefixAppender.prefix(DATA_PREFIX, simpleStorage), +// PrefixAppender.prefix(DATA_PREFIX, versioningStorage), readonly); +// +//// this.privileges = new PrivilegeDataSet(privilegeRootHash, merkleTreeSetting, +//// PrefixAppender.prefix(PRIVILEGE_PREFIX, simpleStorage), +//// PrefixAppender.prefix(PRIVILEGE_PREFIX, versioningStorage), readonly); +// } +// +// public ByteArray getDataRootHash() { +// return data.getRootHash(); +// } +// +//// public ByteArray getPrivilegeRootHash() { +//// return privileges.getRootHash(); +//// } +// +// /** +// * +// * @param userAddress +// * @param op +// * @param enable +// */ +// public void setPrivilege(String userAddress, byte op, boolean enable) { +// +// } +// +// /** +// * +// * @param op +// * @param enable +// */ +// public void setDefaultPrivilege(byte op, boolean enable) { +// } +// +// public boolean checkCurrentUserPrivilege() { +// return false; +// } +// +// /** +// * Return the latest version entry associated the specified key; If the key +// * doesn't exist, then return -1; +// * +// * @param key +// * @return +// */ +// public long getVersion(String key) { +// return data.getVersion(key); +// } +// +// protected long setString(String key, String value, long version) { +// checkWritting(); +// byte[] bytes = BytesUtils.toBytes(value, LedgerConsts.CHARSET); +// return data.setValue(key, bytes, version); +// } +// +// protected String getString(String key) { +// checkReading(); +// byte[] value = data.getValue(key); +// return BytesUtils.toString(value, LedgerConsts.CHARSET); +// } +// +// protected String getString(String key, long version) { +// checkReading(); +// byte[] value = data.getValue(key, version); +// return BytesUtils.toString(value, LedgerConsts.CHARSET); +// } +// +// protected long setValue(String key, byte[] value, long version) { +// checkWritting(); +// return data.setValue(key, value, version); +// } +// +// protected byte[] getValue(String key) { +// checkReading(); +// return data.getValue(key); +// } +// +// protected byte[] getValue(String key, long version) { +// checkReading(); +// return data.getValue(key, version); +// } +// +// private void checkWritting() { +// // Check writting enable; +// } +// +// private void checkReading() { +// // TODO Check privilege of reading; +// } +// +// // /** +// // * 数据“读”的操作码; +// // * +// // * @return +// // */ +// // protected abstract AccountPrivilege getPrivilege(); +// +// @Override +// public boolean isUpdated() { +// return data.isUpdated(); +//// return data.isUpdated()|| privileges.isUpdated(); +// } +// +// @Override +// public void commit() { +// if (data.isUpdated()) { +// data.commit(); +// } +//// if (privileges.isUpdated()) { +//// privileges.commit(); +//// } +// } +// +// @Override +// public void cancel() { +// data.cancel(); +//// privileges.cancel(); +// } +// +//} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Authorization.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Authorization.java new file mode 100644 index 00000000..cba2ffe4 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Authorization.java @@ -0,0 +1,40 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.DigitalSignature; + +/** + * {@link Authorization} 抽象了对特定用户/角色的授权信息; + * + * @author huanghaiquan + * + */ +public interface Authorization { + + /** + * 被授权用户/角色的地址; + * + * @return + */ + String getAddress(); + + /** + * 授权码;
+ * + * @return + */ + byte[] getCode(); + + /** + * 授权者的签名; + * + * @return + */ + DigitalSignature getSignature(); + + // /** + // * 授权生成的时间戳; + // * @return + // */ + // long getTs(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizationVO.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizationVO.java new file mode 100644 index 00000000..24d7f125 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/AuthorizationVO.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.DigitalSignature; + +public class AuthorizationVO implements Authorization { + + private String address; + + private byte[] code; + + private DigitalSignature signature; + + + @Override + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + @Override + public byte[] getCode() { + return code; + } + + public void setCode(byte[] code) { + this.code = code; + } + + @Override + public DigitalSignature getSignature() { + return signature; + } + + + public void setSignature(DigitalSignature signature) { + this.signature = signature; + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/BaseAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/BaseAccount.java new file mode 100644 index 00000000..5417dc2a --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/BaseAccount.java @@ -0,0 +1,222 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.BlockchainIdentityData; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +/** + * 事务性的基础账户; + * + * @author huanghaiquan + * + */ +public class BaseAccount implements AccountHeader, MerkleProvable, Transactional { + + private BlockchainIdentity bcid; + + protected MerkleDataSet dataset; + + private AccountAccessPolicy accessPolicy; + + /** + * Create a new Account with the specified address and pubkey;
+ * + * At the same time, a empty merkle dataset is also created for this account, + * which is used for storing data of this account.
+ * + * Note that, the blockchain identity of the account is not stored in the + * account's merkle dataset, but is stored by the outer invoker; + * + * @param address + * @param pubKey + */ + public BaseAccount(Bytes address, PubKey pubKey, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { + this(address, pubKey, null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy); + } + + /** + * Create a new Account with the specified address and pubkey;
+ * + * At the same time, a empty merkle dataset is also created for this account, + * which is used for storing data of this account.
+ * + * Note that, the blockchain identity of the account is not stored in the + * account's merkle dataset, but is stored by the outer invoker; + * + * @param bcid + * @param cryptoSetting + * @param exStorage + * @param verStorage + * @param accessPolicy + */ + public BaseAccount(BlockchainIdentity bcid, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { + this(bcid, null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy); + } + + /** + * Create a account instance with the specified address and pubkey and load it's + * merkle dataset with the specified root hash. which is used for storing data + * of this account.
+ * + * @param address + * @param pubKey + * @param dataRootHash + * merkle root hash of account's data; if null be set, create a new + * empty merkle dataset; + * @param cryptoSetting + * @param exStorage + * @param verStorage + * @param readonly + * @param accessPolicy + */ + public BaseAccount(Bytes address, PubKey pubKey, HashDigest dataRootHash, CryptoSetting cryptoSetting, + String keyPrefix, ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, + AccountAccessPolicy accessPolicy) { + this(new BlockchainIdentityData(address, pubKey), dataRootHash, cryptoSetting, keyPrefix, exStorage, verStorage, + readonly, accessPolicy); + } + + public BaseAccount(BlockchainIdentity bcid, HashDigest dataRootHash, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, + AccountAccessPolicy accessPolicy) { + this.bcid = bcid; + this.dataset = new MerkleDataSet(dataRootHash, cryptoSetting, keyPrefix, exStorage, verStorage, readonly); + this.accessPolicy = accessPolicy; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.AccountDataSet#getAddress() + */ + @Override + public Bytes getAddress() { + return bcid.getAddress(); + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.AccountDataSet#getPubKey() + */ + @Override + public PubKey getPubKey() { + return bcid.getPubKey(); + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.AccountDataSet#getRootHash() + */ + @Override + public HashDigest getRootHash() { + return dataset.getRootHash(); + } + + @Override + public MerkleProof getProof(Bytes key) { + return dataset.getProof(key); + } + + /** + * 是否只读; + * + * @return + */ + public boolean isReadonly() { + return dataset.isReadonly(); + } + + /** + * Create or update the value associated the specified key if the version + * checking is passed.
+ * + * The value of the key will be updated only if it's latest version equals the + * specified version argument.
+ * If the key doesn't exist, the version checking will be ignored, and key will + * be created with a new sequence number as id.
+ * It also could specify the version argument to -1 to ignore the version + * checking. + *

+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ * + * @param key + * The key of data; + * @param value + * The value of data; + * @param version + * The expected version of the key. + * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then + * return -1; + */ + public long setBytes(Bytes key, byte[] value, long version) { + // TODO: 支持多种数据类型; + return dataset.setValue(key, value, version); + } + + /** + * Return the latest version entry associated the specified key; If the key + * doesn't exist, then return -1; + * + * @param key + * @return + */ + public long getKeyVersion(Bytes key) { + return dataset.getVersion(key); + } + + /** + * return the latest version's value; + * + * @param key + * @return return null if not exist; + */ + public byte[] getBytes(Bytes key) { + return dataset.getValue(key); + } + + /** + * Return the specified version's value; + * + * @param key + * @param version + * @return return null if not exist; + */ + public byte[] getBytes(Bytes key, long version) { + return dataset.getValue(key, version); + } + + @Override + public boolean isUpdated() { + return dataset.isUpdated(); + } + + @Override + public void commit() { + if (!accessPolicy.checkCommitting(this)) { + throw new LedgerException("Account Committing was rejected for the access policy!"); + } + + dataset.commit(); + } + + @Override + public void cancel() { + dataset.cancel(); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Consensus.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Consensus.java new file mode 100644 index 00000000..da7b6104 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Consensus.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.ledger.core; + + +/** + * @author hhq + * @version 1.0 + * @created 14-6��-2018 12:13:32 + */ +public class Consensus { + + public P2PRealm m_P2PRealm; + + public Consensus(){ + + } + + public void finalize() throws Throwable { + + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ConsensusConfig.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ConsensusConfig.java new file mode 100644 index 00000000..a947556e --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ConsensusConfig.java @@ -0,0 +1,28 @@ +//package com.jd.blockchain.ledger.core; +// +//import com.jd.blockchain.ledger.ConsensusSetting; +// +//public class ConsensusConfig implements ConsensusSetting { +// +// private byte[] value; +// +// public ConsensusConfig() { +// } +// +// public ConsensusConfig(ConsensusSetting setting) { +// if (setting != null) { +// this.value = setting.getValue(); +// } +// } +// +// +// @Override +// public byte[] getValue() { +// return value; +// } +// +// +// public void setValue(byte[] value) { this.value = value;} +// +// +//} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccount.java new file mode 100644 index 00000000..bdabb6dc --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccount.java @@ -0,0 +1,80 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +public class ContractAccount implements AccountHeader { + + // private static final String CONTRACT_INFO_PREFIX = "INFO" + + // LedgerConsts.KEY_SEPERATOR; + private static final Bytes CONTRACT_INFO_PREFIX = Bytes.fromString("INFO" + LedgerConsts.KEY_SEPERATOR); + + // private static final String CHAIN_CODE_KEY = "CHAIN-CODE"; + private static final Bytes CHAIN_CODE_KEY = Bytes.fromString("CHAIN-CODE"); + + private BaseAccount accBase; + + public ContractAccount(BaseAccount accBase) { + this.accBase = accBase; + } + + @Override + public Bytes getAddress() { + return accBase.getAddress(); + } + + @Override + public PubKey getPubKey() { + return accBase.getPubKey(); + } + + @Override + public HashDigest getRootHash() { + return accBase.getRootHash(); + } + + public MerkleProof getChaincodeProof() { + return accBase.getProof(CHAIN_CODE_KEY); + } + + public MerkleProof getPropertyProof(Bytes key) { + return accBase.getProof(encodePropertyKey(key)); + } + + public long setChaincode(byte[] chaincode, long version) { + return accBase.setBytes(CHAIN_CODE_KEY, chaincode, version); + } + + public byte[] getChainCode() { + return accBase.getBytes(CHAIN_CODE_KEY); + } + + public byte[] getChainCode(long version) { + return accBase.getBytes(CHAIN_CODE_KEY, version); + } + + public long getChaincodeVersion() { + return accBase.getKeyVersion(CHAIN_CODE_KEY); + } + + public long setProperty(Bytes key, String value, long version) { + return accBase.setBytes(encodePropertyKey(key), BytesUtils.toBytes(value), version); + } + + public String getProperty(Bytes key) { + return BytesUtils.toString(accBase.getBytes(encodePropertyKey(key))); + } + + public String getProperty(Bytes key, long version) { + return BytesUtils.toString(accBase.getBytes(encodePropertyKey(key), version)); + } + + private Bytes encodePropertyKey(Bytes key) { + return CONTRACT_INFO_PREFIX.concat(key); +// return key.concatTo(CONTRACT_INFO_PREFIX); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccountSet.java new file mode 100644 index 00000000..612de4b8 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ContractAccountSet.java @@ -0,0 +1,122 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +public class ContractAccountSet implements MerkleProvable, Transactional { + + private AccountSet accountSet; + + public ContractAccountSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exStorage, + VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { + accountSet = new AccountSet(cryptoSetting, prefix, exStorage, verStorage, accessPolicy); + } + + public ContractAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, + AccountAccessPolicy accessPolicy) { + accountSet = new AccountSet(dataRootHash, cryptoSetting, prefix, exStorage, verStorage, readonly, accessPolicy); + } + + public AccountHeader[] getAccounts(int fromIndex, int count) { + return accountSet.getAccounts(fromIndex,count); + } + + public boolean isReadonly() { + return accountSet.isReadonly(); + } + + @Override + public HashDigest getRootHash() { + return accountSet.getRootHash(); + } + + /** + * 返回合约总数; + * + * @return + */ + public long getTotalCount() { + return accountSet.getTotalCount(); + } + + @Override + public MerkleProof getProof(Bytes address) { + return accountSet.getProof(address); + } + + public boolean contains(Bytes address) { + return accountSet.contains(address); + } + + public ContractAccount getContract(Bytes address) { + BaseAccount accBase = accountSet.getAccount(address); + return new ContractAccount(accBase); + } + + public ContractAccount getContract(Bytes address, long version) { + BaseAccount accBase = accountSet.getAccount(address, version); + return new ContractAccount(accBase); + } + + /** + * 部署一项新的合约链码; + * + * @param address + * 合约账户地址; + * @param pubKey + * 合约账户公钥; + * @param addressSignature + * 地址签名;合约账户的私钥对地址的签名; + * @param chaincode + * 链码内容; + * @return 合约账户; + */ + public ContractAccount deploy(Bytes address, PubKey pubKey, DigitalSignature addressSignature, byte[] chaincode) { + // TODO: 校验和记录合约地址签名; + BaseAccount accBase = accountSet.register(address, pubKey); + ContractAccount contractAcc = new ContractAccount(accBase); + contractAcc.setChaincode(chaincode, -1); + return contractAcc; + } + + /** + * 更新指定账户的链码; + * + * @param address + * 合约账户地址; + * @param chaincode + * 链码内容; + * @param version + * 链码版本; + * @return 返回链码的新版本号; + */ + public long update(Bytes address, byte[] chaincode, long version) { + BaseAccount accBase = accountSet.getAccount(address); + ContractAccount contractAcc = new ContractAccount(accBase); + return contractAcc.setChaincode(chaincode, version); + } + + @Override + public boolean isUpdated() { + return accountSet.isUpdated(); + } + + @Override + public void commit() { + accountSet.commit(); + } + + @Override + public void cancel() { + accountSet.cancel(); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/CryptoConfig.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/CryptoConfig.java new file mode 100644 index 00000000..b296dfc1 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/CryptoConfig.java @@ -0,0 +1,40 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.ledger.CryptoSetting; + +public class CryptoConfig implements CryptoSetting { + + private CryptoAlgorithm hashAlgorithm; + + private boolean autoVerifyHash; + + public CryptoConfig() { + } + + public CryptoConfig(CryptoSetting setting) { + this.hashAlgorithm = setting.getHashAlgorithm(); + this.autoVerifyHash = setting.getAutoVerifyHash(); + } + + + @Override + public CryptoAlgorithm getHashAlgorithm() { + return hashAlgorithm; + } + + @Override + public boolean getAutoVerifyHash() { + return autoVerifyHash; + } + + public void setHashAlgorithm(CryptoAlgorithm hashAlgorithm) { + this.hashAlgorithm = hashAlgorithm; + } + + public void setAutoVerifyHash(boolean autoVerifyHash) { + this.autoVerifyHash = autoVerifyHash; + } + + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java new file mode 100644 index 00000000..2b2271ae --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccount.java @@ -0,0 +1,155 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.KVDataObject; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.QueryUtil; +import com.jd.blockchain.utils.ValueType; + +public class DataAccount implements AccountHeader, MerkleProvable { + private BaseAccount baseAccount; + + public DataAccount(BaseAccount accBase) { + this.baseAccount = accBase; + } + + @Override + public Bytes getAddress() { + return baseAccount.getAddress(); + } + + @Override + public PubKey getPubKey() { + return baseAccount.getPubKey(); + } + + @Override + public HashDigest getRootHash() { + return baseAccount.getRootHash(); + } + + /** + * 返回指定数据的存在性证明; + */ + @Override + public MerkleProof getProof(Bytes key) { + return baseAccount.getProof(key); + } + + public long setBytes(Bytes key, byte[] value, long version) { + return baseAccount.setBytes(key, value, version); + } + + /** + * Return the latest version entry associated the specified key; If the key + * doesn't exist, then return -1; + * + * @param key + * @return + */ + public long getDataVersion(String key) { + return baseAccount.getKeyVersion(Bytes.fromString(key)); + } + + /** + * Return the latest version entry associated the specified key; If the key + * doesn't exist, then return -1; + * + * @param key + * @return + */ + public long getDataVersion(Bytes key) { + return baseAccount.getKeyVersion(key); + } + + /** + * return the latest version's value; + * + * @param key + * @return return null if not exist; + */ + public byte[] getBytes(String key) { + return baseAccount.getBytes(Bytes.fromString(key)); + } + + /** + * return the latest version's value; + * + * @param key + * @return return null if not exist; + */ + public byte[] getBytes(Bytes key) { + return baseAccount.getBytes(key); + } + + /** + * return the specified version's value; + * + * @param key + * @param version + * @return return null if not exist; + */ + public byte[] getBytes(String key, long version) { + return baseAccount.getBytes(Bytes.fromString(key), version); + } + + /** + * return the specified version's value; + * + * @param key + * @param version + * @return return null if not exist; + */ + public byte[] getBytes(Bytes key, long version) { + return baseAccount.getBytes(key, version); + } + + /** + * return the specified index's KVDataEntry; + * + * @param fromIndex + * @param count + * @return return null if not exist; + */ + + public KVDataEntry[] getDataEntries(int fromIndex, int count) { + if (getDataEntriesTotalCount() == 0 || count == 0) { + return null; + } + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)getDataEntriesTotalCount()); + fromIndex = pages[0]; + count = pages[1]; + + KVDataEntry[] kvDataEntries = new KVDataEntry[count]; + byte[] value; + String key; + long ver; + for (int i = 0; i < count; i++) { + value = baseAccount.dataset.getValuesAtIndex(fromIndex); + key = baseAccount.dataset.getKeyAtIndex(fromIndex); + ver = baseAccount.dataset.getVersion(key); + BytesValue decodeData = BinaryEncodingUtils.decode(value); + kvDataEntries[i] = new KVDataObject(key, ver, ValueType.valueOf(decodeData.getType().CODE), decodeData.getValue().toBytes()); + fromIndex++; + } + + return kvDataEntries; + } + + /** + * return the dataAccount's kv total count; + * + * @param + * @param + * @return return total count; + */ + public long getDataEntriesTotalCount() { + return baseAccount.dataset.getDataCount(); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccountSet.java new file mode 100644 index 00000000..8d92eb5a --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/DataAccountSet.java @@ -0,0 +1,83 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +public class DataAccountSet implements MerkleProvable, Transactional { + + private AccountSet accountSet; + + public DataAccountSet(CryptoSetting cryptoSetting, String prefix,ExPolicyKVStorage exStorage, + VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { + accountSet = new AccountSet(cryptoSetting,prefix, exStorage, verStorage, accessPolicy); + } + + public DataAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, + AccountAccessPolicy accessPolicy) { + accountSet = new AccountSet(dataRootHash, cryptoSetting, prefix, exStorage, verStorage, readonly, accessPolicy); + } + + public AccountHeader[] getAccounts(int fromIndex, int count) { + return accountSet.getAccounts(fromIndex,count); + } + + public boolean isReadonly() { + return accountSet.isReadonly(); + } + + @Override + public HashDigest getRootHash() { + return accountSet.getRootHash(); + } + + public long getTotalCount() { + return accountSet.getTotalCount(); + } + + /** + * 返回账户的存在性证明; + */ + @Override + public MerkleProof getProof(Bytes address) { + return accountSet.getProof(address); + } + + public DataAccount register(Bytes address, PubKey pubKey, DigitalSignature addressSignature) { + // TODO: 未实现对地址签名的校验和记录; + BaseAccount accBase = accountSet.register(address, pubKey); + return new DataAccount(accBase); + } + + public DataAccount getDataAccount(Bytes address) { + BaseAccount accBase = accountSet.getAccount(address); + return new DataAccount(accBase); + } + + public DataAccount getDataAccount(Bytes address, long version) { + BaseAccount accBase = accountSet.getAccount(address, version); + return new DataAccount(accBase); + } + + @Override + public boolean isUpdated() { + return accountSet.isUpdated(); + } + + @Override + public void commit() { + accountSet.commit(); + } + + @Override + public void cancel() { + accountSet.cancel(); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Gateway.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Gateway.java new file mode 100644 index 00000000..6874d8aa --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Gateway.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.ParticipantNode; + +/** + * @author hhq + * @version 1.0 + * @created 14-6��-2018 12:13:32 + */ +public class Gateway extends Node { + + public ParticipantNode m_Participant; + + public Gateway(){ + + } + + public void finalize() throws Throwable { + super.finalize(); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericAccountDataset.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericAccountDataset.java new file mode 100644 index 00000000..76831c3e --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericAccountDataset.java @@ -0,0 +1,36 @@ +//package com.jd.blockchain.ledger.core; +// +//public class GenericAccountDataset { +// +// private Class dataClazz; +// +// private AccountDataSet dataset; +// +// protected GenericAccountDataset(AccountDataSet dataset, Class dataClazz) { +// this.dataClazz = dataClazz; +// this.dataset = dataset; +// } +// +// protected T getData(String key) { +// byte[] value = dataset.getBytes(key); +// return deserialize(value); +// } +// +// protected T getData(String key, long version) { +// byte[] value = dataset.getBytes(key, version); +// return deserialize(value); +// } +// +// protected long setData(String key, T data, long version) { +// byte[] value = serialize(data); +// return dataset.setBytes(key, value, version); +// } +// +// private byte[] serialize(T data) { +// throw new IllegalStateException("Not implemented!"); +// } +// +// private T deserialize(byte[] value) { +// throw new IllegalStateException("Not implemented!"); +// } +//} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericDataEntry.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericDataEntry.java new file mode 100644 index 00000000..3e73d582 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericDataEntry.java @@ -0,0 +1,11 @@ +//package com.jd.blockchain.ledger.core; +// +//public interface GenericDataEntry { +// +// String getKey(); +// +// long getVersion(); +// +// T getValue(); +// +//} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataEntry.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataEntry.java new file mode 100644 index 00000000..8a8c5635 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataEntry.java @@ -0,0 +1,9 @@ +//package com.jd.blockchain.ledger.core; +// +//public interface GenericMerkleDataEntry { +// +// GenericDataEntry getData(); +// +// MerkleProof getProof(); +// +//} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataSet.java new file mode 100644 index 00000000..31df6427 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/GenericMerkleDataSet.java @@ -0,0 +1,259 @@ +//package com.jd.blockchain.ledger.core; +// +//import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +//import com.jd.blockchain.crypto.hash.HashDigest; +// +//import my.utils.Transactional; +//import my.utils.io.ByteArray; +//import my.utils.io.ExistentialKVStorage; +//import my.utils.io.VersioningKVEntry; +//import my.utils.io.VersioningKVStorage; +// +///** +// * @author huanghaiquan +// * +// * @param +// * @param +// */ +//public class GenericMerkleDataSet implements Transactional { +// +// /** +// * 数据契约的接口类型; +// */ +// private Class dataContractClazz; +// +// /** +// * 数据契约的实现类型; +// */ +// private Class dataClazz; +// +// private MerkleDataSet merkleDataSet; +// +// private Class[] extImplClazzes; +// +// /** +// * 创建一个新的 Merkle 数据集; +// * +// * @param defaultMerkleHashAlgorithm +// * @param verifyMerkleHashOnLoad +// * @param merkleTreeStorage +// * @param dataStorage +// * @param dataContractClazz +// * @param dataClazz +// * @param extImplClazzes +// */ +// public GenericMerkleDataSet(CryptoSetting merkleTreeSetting, ExistentialKVStorage merkleTreeStorage, +// VersioningKVStorage dataStorage, Class dataContractClazz, Class dataClazz, +// Class... extImplClazzes) { +// if (!dataContractClazz.isAssignableFrom(dataClazz)) { +// throw new IllegalArgumentException(String.format( +// "The specified data class doesn't implement the specified data contract class! --[DataContractClass=%s][DataClass=%s]", +// dataContractClazz.getName(), dataClazz.getName())); +// } +// this.merkleDataSet = new MerkleDataSet(merkleTreeSetting, merkleTreeStorage, dataStorage); +// this.dataContractClazz = dataContractClazz; +// this.dataClazz = dataClazz; +// this.extImplClazzes = extImplClazzes; +// } +// +// /** +// * 从指定的 Merkle 根创建 Merkle 数据集; +// * +// * @param defaultMerkleHashAlgorithm +// * @param verifyMerkleHashOnLoad +// * @param merkleTreeStorage +// * @param dataStorage +// * @param dataContractClazz +// * @param dataClazz +// * @param extImplClazzes +// */ +// public GenericMerkleDataSet(HashDigest merkleRootHash, CryptoSetting merkleTreeSetting, +// ExistentialKVStorage merkleTreeStorage, VersioningKVStorage dataStorage, boolean readonly, Class dataContractClazz, +// Class dataClazz, Class... extImplClazzes) { +// if (!dataContractClazz.isAssignableFrom(dataClazz)) { +// throw new IllegalArgumentException(String.format( +// "The specified data class doesn't implement the specified data contract class! --[DataContractClass=%s][DataClass=%s]", +// dataContractClazz.getName(), dataClazz.getName())); +// } +// this.merkleDataSet = new MerkleDataSet(merkleRootHash, merkleTreeSetting, merkleTreeStorage, dataStorage, readonly); +// this.dataContractClazz = dataContractClazz; +// this.dataClazz = dataClazz; +// this.extImplClazzes = extImplClazzes; +// } +// +// /** +// * @return +// */ +// public HashDigest getRootHash() { +// return merkleDataSet.getRootHash(); +// } +// +// /** +// * Get the merkle proof of the specified key;
+// * +// * The proof doesn't represent the latest changes until do +// * committing({@link #commit()}). +// * +// * @param key +// * @return Return the {@link MerkleProof} instance, or null if the key doesn't +// * exist. +// */ +// public MerkleProof getProof(String key) { +// return merkleDataSet.getProof(key); +// } +// +// /** +// * Create or update the value associated the specified key if the version +// * checking is passed.
+// * +// * The value of the key will be updated only if it's latest version equals the +// * specified version argument.
+// * If the key doesn't exist, the version checking will be ignored, and key will +// * be created with a new sequence number as id.
+// * It also could specify the version argument to -1 to ignore the version +// * checking. +// *

+// * If updating is performed, the version of the key increase by 1.
+// * If creating is performed, the version of the key initialize by 0.
+// * +// * @param key +// * The key of data; +// * @param value +// * The value of data; +// * @param version +// * The expected version of the key. +// */ +// public long setValue(String key, T value, long version) { +// byte[] bytesValue = BinaryEncodingUtils.encode(value, dataContractClazz); +// return merkleDataSet.setValue(key, bytesValue, version); +// } +// +// /** +// * +// * @param key +// * @param version +// */ +// public T getValue(String key, long version) { +// byte[] bytesValue = merkleDataSet.getValue(key, version); +// return decode(bytesValue); +// } +// +// /** +// * +// * @param version +// * @param key +// */ +// public T getValue(String key) { +// byte[] bytesValue = merkleDataSet.getValue(key); +// return decode(bytesValue); +// } +// +// public GenericDataEntry getDataEntry(String key) { +// VersioningKVEntry entry = merkleDataSet.getDataEntry(key); +// T value = decode(entry.getValue()); +// return new GenericDataEntryWrapper<>(entry, value); +// } +// +// public GenericDataEntry getDataEntry(String key, long version) { +// VersioningKVEntry entry = merkleDataSet.getDataEntry(key, version); +// T value = decode(entry.getValue()); +// return new GenericDataEntryWrapper<>(entry, value); +// } +// +// public GenericMerkleDataEntry getMerkleEntry(String key, long version) { +// GenericDataEntry data = getDataEntry(key, version); +// MerkleProof proof = merkleDataSet.getProof(key); +// return new GenericMerkleDataEntryWrapperr(data, proof); +// } +// +// public GenericMerkleDataEntry getMerkleEntry(String key) { +// GenericDataEntry data = getDataEntry(key); +// MerkleProof proof = merkleDataSet.getProof(key); +// return new GenericMerkleDataEntryWrapperr(data, proof); +// } +// +// @Override +// public boolean isUpdated() { +// return merkleDataSet.isUpdated(); +// } +// +// @Override +// public void commit() { +// merkleDataSet.commit(); +// } +// +// @Override +// public void cancel() { +// merkleDataSet.cancel(); +// } +// +// private T decode(byte[] bytesValue) { +// return BinaryEncodingUtils.decode(bytesValue, null, dataClazz); +// } +// +// /** +// * @author huanghaiquan +// * +// * @param +// * @param +// */ +// private static class GenericDataEntryWrapper implements GenericDataEntry { +// +// private String key; +// +// private long version; +// +// private T value; +// +// public GenericDataEntryWrapper(VersioningKVEntry binaryEntry, T value) { +// this.key = binaryEntry.getKey(); +// this.version = binaryEntry.getVersion(); +// this.value = value; +// } +// +// @Override +// public String getKey() { +// return key; +// } +// +// @Override +// public long getVersion() { +// return version; +// } +// +// @Override +// public T getValue() { +// return value; +// } +// +// } +// +// /** +// * @author huanghaiquan +// * +// * @param +// */ +// private static class GenericMerkleDataEntryWrapperr implements GenericMerkleDataEntry { +// +// private GenericDataEntry data; +// +// private MerkleProof proof; +// +// public GenericMerkleDataEntryWrapperr(GenericDataEntry data, MerkleProof proof) { +// this.data = data; +// this.proof = proof; +// } +// +// @Override +// public GenericDataEntry getData() { +// return data; +// } +// +// @Override +// public MerkleProof getProof() { +// return proof; +// } +// +// } +// +//} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdminAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdminAccount.java new file mode 100644 index 00000000..44ff9ee1 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdminAccount.java @@ -0,0 +1,348 @@ +package com.jd.blockchain.ledger.core; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; +import com.jd.blockchain.storage.service.VersioningKVStorage; + +public class LedgerAdminAccount implements Transactional, LedgerAdministration { + + static { + DataContractRegistry.register(LedgerMetadata.class); + } + + private static Logger LOGGER = LoggerFactory.getLogger(LedgerAdminAccount.class); + + public static final String LEDGER_META_PREFIX = "MTA" + LedgerConsts.KEY_SEPERATOR; + public static final String LEDGER_PARTICIPANT_PREFIX = "PAR" + LedgerConsts.KEY_SEPERATOR; + public static final String LEDGER_PRIVILEGE_PREFIX = "PVL" + LedgerConsts.KEY_SEPERATOR; + + private final Bytes metaPrefix; + private final Bytes privilegePrefix; + + private LedgerMetadata origMetadata; + + private LedgerMetadataImpl metadata; + + /** + * 原来的账本设置; + * + *
+ * 对 LedgerMetadata 修改的新配置不能立即生效,需要达成共识后,在下一次区块计算中才生效; + */ + private LedgerSetting previousSetting; + + /** + * 账本的参与节点; + */ + private ParticipantDataSet participants; + + // /** + // * 账本的全局权限设置; + // */ + // private PrivilegeDataSet privileges; + + private ExPolicyKVStorage settingsStorage; + + private HashDigest adminAccountHash; + + private boolean readonly; + + private boolean updated; + + public HashDigest getHash() { + return adminAccountHash; + } + + public boolean isReadonly() { + return readonly; + } + + /** + * 初始化账本的管理账户; + * + *
+ * + * 只在新建账本时调用此方法; + * + * @param ledgerSeed + * @param setting + * @param partiList + * @param exPolicyStorage + * @param versioningStorage + */ + public LedgerAdminAccount(LedgerInitSetting initSetting, String keyPrefix, ExPolicyKVStorage exPolicyStorage, + VersioningKVStorage versioningStorage) { + this.metaPrefix = Bytes.fromString(keyPrefix + LEDGER_META_PREFIX); + this.privilegePrefix = Bytes.fromString(keyPrefix + LEDGER_PRIVILEGE_PREFIX); + + ParticipantNode[] parties = initSetting.getConsensusParticipants(); + if (parties.length == 0) { + throw new LedgerException("No participant!"); + } + + // 检查参与者列表是否已经按照 id 升序排列,并且 id 不冲突; + // 注:参与者的 id 要求从 0 开始编号,顺序依次递增,不允许跳空; + for (int i = 0; i < parties.length; i++) { +// if (parties[i].getAddress() != i) { +// throw new LedgerException("The id of participant isn't match the order of the participant list!"); +// } + } + + // 初始化元数据; + this.metadata = new LedgerMetadataImpl(); + this.metadata.setSeed(initSetting.getLedgerSeed()); + // 新配置; + this.metadata.setting = new LedgerConfiguration(initSetting.getConsensusProvider(), + initSetting.getConsensusSettings(), initSetting.getCryptoSetting()); + this.previousSetting = new LedgerConfiguration(initSetting.getConsensusProvider(), + initSetting.getConsensusSettings(), initSetting.getCryptoSetting()); + this.adminAccountHash = null; + + // 基于原配置初始化参与者列表; + String partiPrefix = keyPrefix + LEDGER_PARTICIPANT_PREFIX; + this.participants = new ParticipantDataSet(previousSetting.getCryptoSetting(), partiPrefix, exPolicyStorage, + versioningStorage); + + for (ParticipantNode p : parties) { + this.participants.addConsensusParticipant(p); + } + + // 初始化其它属性; + this.settingsStorage = exPolicyStorage; + this.readonly = false; + } + + public LedgerAdminAccount(HashDigest adminAccountHash, String keyPrefix, ExPolicyKVStorage kvStorage, + VersioningKVStorage versioningKVStorage, boolean readonly) { + this.metaPrefix = Bytes.fromString(keyPrefix + LEDGER_META_PREFIX); + this.privilegePrefix = Bytes.fromString(keyPrefix + LEDGER_PRIVILEGE_PREFIX); + this.settingsStorage = kvStorage; + this.readonly = readonly; + this.origMetadata = loadAndVerifySettings(adminAccountHash); + this.metadata = new LedgerMetadataImpl(origMetadata); + // 复制记录一份配置作为上一个区块的原始配置,该实例仅供读取,不做修改,也不会回写到存储; + this.previousSetting = new LedgerConfiguration(metadata.getSetting()); + this.adminAccountHash = adminAccountHash; + // this.privileges = new PrivilegeDataSet(metadata.getPrivilegesHash(), + // metadata.getSetting().getCryptoSetting(), + // PrefixAppender.prefix(LEDGER_PRIVILEGE_PREFIX, kvStorage), + // PrefixAppender.prefix(LEDGER_PRIVILEGE_PREFIX, versioningKVStorage), + // readonly); + + // this.participants = new ParticipantDataSet(metadata.getParticipantsHash(), + // previousSetting.getCryptoSetting(), + // PrefixAppender.prefix(LEDGER_PARTICIPANT_PREFIX, kvStorage), + // PrefixAppender.prefix(LEDGER_PARTICIPANT_PREFIX, versioningKVStorage), + // readonly); + String partiPrefix = keyPrefix + LEDGER_PARTICIPANT_PREFIX; + this.participants = new ParticipantDataSet(metadata.getParticipantsHash(), previousSetting.getCryptoSetting(), + partiPrefix, kvStorage, versioningKVStorage, readonly); + } + + private LedgerMetadata loadAndVerifySettings(HashDigest adminAccountHash) { + // String base58Hash = adminAccountHash.toBase58(); + // String key = encodeMetadataKey(base58Hash); + Bytes key = encodeMetadataKey(adminAccountHash); + byte[] bytes = settingsStorage.get(key); + if (!CryptoUtils.hashCrypto().verify(adminAccountHash, bytes)) { + LOGGER.error("The hash verification of ledger settings fail! --[HASH=" + key + "]"); + throw new LedgerException("The hash verification of ledger settings fail!"); + } + return deserializeMetadata(bytes); + } + + private Bytes encodeMetadataKey(HashDigest metadataHash) { + // return LEDGER_META_PREFIX + metadataHash; + // return metaPrefix + metadataHash; + return metaPrefix.concat(metadataHash); + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.LedgerAdministration#getMetadata() + */ + @Override + public LedgerMetadata getMetadata() { + return metadata; + } + + /** + * 返回原来的账本配置; + * + *
+ * 此方法总是返回从上一个区块加载的账本配置,即时调用 {@link #setLedgerSetting(LedgerSetting)} 做出了新的更改; + * + * @return + */ + public LedgerSetting getPreviousSetting() { + return previousSetting; + } + + /** + * 返回当前设置的账本配置; + * + * @return + */ + public LedgerSetting getSetting() { + return metadata.getSetting(); + } + + /** + * 更新账本配置; + * + * @param ledgerSetting + */ + public void setLedgerSetting(LedgerSetting ledgerSetting) { + if (readonly) { + throw new IllegalArgumentException("This merkle dataset is readonly!"); + } + metadata.setSetting(ledgerSetting); + } + + @Override + public long getParticipantCount() { + return participants.getParticipantCount(); + } + +// /* +// * (non-Javadoc) +// * +// * @see +// * com.jd.blockchain.ledger.core.LedgerAdministration#getParticipant(java.lang. +// * String) +// */ +// @Override +// public ParticipantNode getParticipant(int id) { +// return participants.getParticipant(id); +// } + + @Override + public ParticipantNode[] getParticipants() { + return participants.getParticipants(); + } + + /** + * 加入新的参与方; 如果指定的参与方已经存在,则引发 LedgerException 异常; + * + * @param participant + */ + public void addParticipant(ParticipantNode participant) { + participants.addConsensusParticipant(participant); + } + + @Override + public boolean isUpdated() { + return updated || participants.isUpdated(); + } + + @Override + public void commit() { + if (!isUpdated()) { + return; + } + participants.commit(); + + metadata.setParticipantsHash(participants.getRootHash()); + + // 基于之前的密码配置来计算元数据的哈希; + byte[] metadataBytes = serializeMetadata(metadata); + HashDigest metadataHash = CryptoUtils.hashCrypto() + .getFunction(previousSetting.getCryptoSetting().getHashAlgorithm()).hash(metadataBytes); + if (adminAccountHash == null || !adminAccountHash.equals(metadataHash)) { + // update modify; + // String base58MetadataHash = metadataHash.toBase58(); + // String metadataKey = encodeMetadataKey(base58MetadataHash); + Bytes metadataKey = encodeMetadataKey(metadataHash); + + boolean nx = settingsStorage.set(metadataKey, metadataBytes, ExPolicy.NOT_EXISTING); + if (!nx) { + // 有可能发生了并发写入冲突,不同的节点都向同一个存储服务器上写入数据; + // throw new LedgerException( + // "Ledger metadata already exist! --[LedgerMetadataHash=" + base58MetadataHash + // + "]"); + // LOGGER.warn("Ledger metadata already exist! --[MetadataHash=" + + // base58MetadataHash + "]"); + } + + adminAccountHash = metadataHash; + } + + updated = false; + } + + private LedgerMetadata deserializeMetadata(byte[] bytes) { + return BinaryEncodingUtils.decode(bytes); + } + + private byte[] serializeMetadata(LedgerMetadataImpl config) { + return BinaryEncodingUtils.encode(config, LedgerMetadata.class); + } + + @Override + public void cancel() { + if (!isUpdated()) { + return; + } + participants.cancel(); + metadata = new LedgerMetadataImpl(origMetadata); + } + + public static class LedgerMetadataImpl implements LedgerMetadata { + + private byte[] seed; + + private LedgerSetting setting; + + private HashDigest participantsHash; + + public LedgerMetadataImpl() { + } + + public LedgerMetadataImpl(LedgerMetadata metadata) { + this.seed = metadata.getSeed(); + this.setting = metadata.getSetting(); + this.participantsHash = metadata.getParticipantsHash(); + } + + @Override + public byte[] getSeed() { + return seed; + } + + @Override + public LedgerSetting getSetting() { + return setting; + } + + @Override + public HashDigest getParticipantsHash() { + return participantsHash; + } + + public void setSeed(byte[] seed) { + this.seed = seed; + } + + public void setSetting(LedgerSetting setting) { + // copy a new instance; + this.setting = new LedgerConfiguration(setting); + } + + public void setParticipantsHash(HashDigest participantsHash) { + this.participantsHash = participantsHash; + } + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdminPrivilege.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdminPrivilege.java new file mode 100644 index 00000000..b436b5da --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdminPrivilege.java @@ -0,0 +1,5 @@ +package com.jd.blockchain.ledger.core; + +public enum LedgerAdminPrivilege { + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdministration.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdministration.java new file mode 100644 index 00000000..79632ae4 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerAdministration.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.ParticipantNode; + +public interface LedgerAdministration { + + LedgerMetadata getMetadata(); + + long getParticipantCount(); + +// ParticipantNode getParticipant(int id); + + ParticipantNode[] getParticipants(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerConfiguration.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerConfiguration.java new file mode 100644 index 00000000..ef34122b --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerConfiguration.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.utils.Bytes; + +public class LedgerConfiguration implements LedgerSetting { + + private String consensusProvider; + + private Bytes consensusSetting; + + private CryptoConfig cryptoSetting; + + public LedgerConfiguration() { + this.cryptoSetting = new CryptoConfig(); + } + + public LedgerConfiguration(LedgerSetting origSetting) { + if (origSetting != null) { + this.consensusProvider = origSetting.getConsensusProvider(); + this.consensusSetting = origSetting.getConsensusSetting(); + this.cryptoSetting = new CryptoConfig(origSetting.getCryptoSetting()); + } else { + this.cryptoSetting = new CryptoConfig(); + } + } + + public LedgerConfiguration(String consensusProvider, Bytes consensusSetting, CryptoSetting cryptoSetting) { + this.consensusProvider = consensusProvider; + this.consensusSetting = consensusSetting; + this.cryptoSetting = new CryptoConfig(cryptoSetting); + } + + @Override + public Bytes getConsensusSetting() { + return consensusSetting; + } + + public void setConsensusSetting(Bytes consensusSetting) { + this.consensusSetting = consensusSetting; + } + + @Override + public CryptoConfig getCryptoSetting() { + return cryptoSetting; + } + + @Override + public String getConsensusProvider() { + return consensusProvider; + } + + public void setConsensusProvider(String consensusProvider) { + this.consensusProvider = consensusProvider; + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerConsts.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerConsts.java new file mode 100644 index 00000000..8ba67ede --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerConsts.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.core; + +public class LedgerConsts { + + public static final String CHARSET= "UTF-8"; + + public static final String KEY_SEPERATOR = "/"; + + public static final int MAX_LIST_COUNT = 1000; + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerDataSet.java new file mode 100644 index 00000000..40f63da6 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerDataSet.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.ledger.core; + +/** + * {@link LedgerDataSet} 表示账本在某一个区块上的数据集合; + * + * @author huanghaiquan + * + */ +public interface LedgerDataSet{ + + boolean isReadonly(); + + LedgerAdminAccount getAdminAccount(); + + UserAccountSet getUserAccountSet(); + + DataAccountSet getDataAccountSet(); + + ContractAccountSet getContractAccountSet(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java new file mode 100644 index 00000000..c781ffe2 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerEditor.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionRequest; + +/** + * {@link LedgerEditor} 定义了对账本的编辑视图; + *

+ * + * {@link LedgerEditor} 以上一个区块作为数据编辑的起点;
+ * 对账本数据({@link #getDataSet()})的批量更改可以作为一个交易({@link LedgerTransaction})整体提交暂存,形成暂存点; + *
+ * + * @author huanghaiquan + * + */ +public interface LedgerEditor { + + /** + * 开始新事务;
+ * + * 方法返回之前,将会校验交易请求的用户签名列表和节点签名列表,并在后续对数据集 + * {@link LedgerTransactionContext#getDataSet()} 的操作时,校验这些用户和节点是否具备权限;
+ * + * 校验失败将引发异常 {@link LedgerException}; + *

+ * + * 调用者通过获得的 {@link LedgerTransactionContext} 对象对账本进行操作,这些写入操作可以一起提交(通过方法 + * {@link LedgerTransactionContext#commit(com.jd.blockchain.ledger.ExecutionState)}),
+ * 或者全部回滚(通过方法 {@link LedgerTransactionContext#rollback()}),以此实现原子性写入; + *

+ * + * 每一次事务性的账本写入操作在提交后,都会记录该事务相关的系统全局快照,以交易对象 {@link LedgerTransaction} 进行保存;

+ * + * 注:方法不解析、不执行交易中的操作; + * + * @param txRequest + * @return + */ + LedgerTransactionContext newTransaction(TransactionRequest txRequest); + + /** + * 暂存当前的数据变更,并预提交生成新区块; + * + * @param txRequest + * @param result + * @return + */ + LedgerBlock prepare(); + + /** + * 提交数据; + */ + void commit(); + + /** + * 丢弃所有数据变更以及暂存的交易; + */ + void cancel(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerException.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerException.java new file mode 100644 index 00000000..cca719cb --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerException.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.ledger.core; + +public class LedgerException extends RuntimeException { + + private static final long serialVersionUID = -4090881296855827888L; + + public LedgerException(String message) { + super(message); + } + + public LedgerException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitDecision.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitDecision.java new file mode 100644 index 00000000..8d3f35b2 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitDecision.java @@ -0,0 +1,46 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 账本初始化决定; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.METADATA_INIT_DECISION) +public interface LedgerInitDecision { + + /** + * 做出许可的参与方 ID; + * + * @return + */ + @DataField(order=1, primitiveType=ValueType.INT32) + int getParticipantId(); + + /** + * 新建账本的哈希; + * @return + */ + @DataField(order=2, primitiveType = ValueType.BYTES) + HashDigest getLedgerHash(); + + /** + * 参数方的签名; + * + *
+ * + * 这是对“参与方ID({@link #getParticipantId()})”+“新账本的哈希({@link #getLedgerHash()})”做出的签名; + * + * @return + */ + @DataField(order=3, primitiveType = ValueType.BYTES) + SignatureDigest getSignature(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitPermission.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitPermission.java new file mode 100644 index 00000000..46319be8 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitPermission.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.utils.ValueType; + +/** + * 账本初始化许可; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.METADATA_INIT_PERMISSION) +public interface LedgerInitPermission { + + /** + * 做出许可的参与方 ID; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.INT32) + int getParticipantId(); + + /** + * 参数方对初始化交易的签名; + * + *

+ * + * 初始化交易即账本的第一个交易,需要满足如下规则:
+ * 1、不指定账本 hash;
+ * 2、交易的第一个操作是({@link LedgerInitOperation}) ;
+ * 3、后续的操作是按照参与者列表({@link LedgerInitOperation#getInitSetting()})的顺序依次注册相应的初始用户的操作; + * + *

+ * 此签名将作为交易的节点签名,按照参与者列表的顺序加入到初始化交易的节点签名列表; + * + * @return + */ + @DataField(order = 2, primitiveType = ValueType.BYTES) + SignatureDigest getTransactionSignature(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitPermissionData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitPermissionData.java new file mode 100644 index 00000000..d1b72212 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerInitPermissionData.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; + +public class LedgerInitPermissionData implements LedgerInitPermission { + + private int participantId; + + private SignatureDigest transactionSignature; + + /** + * a private contructor for deserialize; + */ + private LedgerInitPermissionData() { + } + + public LedgerInitPermissionData(int participantId, SignatureDigest initTxSignature) { + this.participantId = participantId; + this.transactionSignature = initTxSignature; + } + + @Override + public int getParticipantId() { + return participantId; + } + + @Override + public SignatureDigest getTransactionSignature() { + return transactionSignature; + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerManage.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerManage.java new file mode 100644 index 00000000..46b85c7d --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerManage.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.storage.service.KVStorageService; + +/** + * 账本管理器; + * + * @author huanghaiquan + * + */ +public interface LedgerManage extends LedgerService { + + LedgerRepository register(HashDigest ledgerHash, KVStorageService storageService); + + void unregister(HashDigest ledgerHash); + + /** + * 创建新账本; + * + * @param initSetting + * 初始化配置; + * @param initPermissions + * 参与者的初始化授权列表;与参与者列表一致; + * @return + */ + LedgerEditor newLedger(LedgerInitSetting initSetting, KVStorageService storageService); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerMetadata.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerMetadata.java new file mode 100644 index 00000000..3655e7b0 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerMetadata.java @@ -0,0 +1,36 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code = TypeCodes.METADATA) +public interface LedgerMetadata { + + /** + * 账本的初始化种子; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + byte[] getSeed(); + + /** + * 共识参与方的默克尔树的根; + * + * @return + */ + @DataField(order = 2, primitiveType = ValueType.BYTES) + HashDigest getParticipantsHash(); + + /** + * 账本配置; + * + * @return + */ + @DataField(order = 3, refContract = true) + LedgerSetting getSetting(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerRepository.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerRepository.java new file mode 100644 index 00000000..1b55f918 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerRepository.java @@ -0,0 +1,134 @@ +package com.jd.blockchain.ledger.core; + +import java.io.Closeable; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; + +public interface LedgerRepository extends Closeable { + + /** + * 账本哈希,这是账本的唯一标识; + * + * @return + */ + HashDigest getHash(); + + /** + * 最新区块高度; + * + * @return + */ + long getLatestBlockHeight(); + + /** + * 最新区块哈希; + * + * @return + */ + HashDigest getLatestBlockHash(); + + /** + * 最新区块; + * + * @return + */ + LedgerBlock getLatestBlock(); + + /** + * 指定高度的区块哈希; + * + * @param height + * @return + */ + HashDigest getBlockHash(long height); + + /** + * 指定高度的区块; + * + * @param height + * @return + */ + LedgerBlock getBlock(long height); + + LedgerAdministration getAdminInfo(); + + LedgerBlock getBlock(HashDigest hash); + + LedgerDataSet getDataSet(LedgerBlock block); + + TransactionSet getTransactionSet(LedgerBlock block); + + LedgerAdminAccount getAdminAccount(LedgerBlock block); + + UserAccountSet getUserAccountSet(LedgerBlock block); + + DataAccountSet getDataAccountSet(LedgerBlock block); + + ContractAccountSet getContractAccountSet(LedgerBlock block); + + default LedgerDataSet getDataSet() { + return getDataSet(getLatestBlock()); + } + + default TransactionSet getTransactionSet() { + return getTransactionSet(getLatestBlock()); + } + + default LedgerAdminAccount getAdminAccount() { + return getAdminAccount(getLatestBlock()); + } + + default UserAccountSet getUserAccountSet() { + return getUserAccountSet(getLatestBlock()); + } + + default DataAccountSet getDataAccountSet() { + return getDataAccountSet(getLatestBlock()); + } + + default ContractAccountSet getContractAccountSet() { + return getContractAccountSet(getLatestBlock()); + } + + /** + * 重新检索最新区块,同时更新缓存; + * + * @return + */ + LedgerBlock retrieveLatestBlock(); + + /** + * 重新检索最新区块,同时更新缓存; + * + * @return + */ + long retrieveLatestBlockHeight(); + + /** + * 重新检索最新区块哈希,同时更新缓存; + * + * @return + */ + HashDigest retrieveLatestBlockHash(); + + /** + * 创建新区块的编辑器; + * + * @return + */ + LedgerEditor createNextBlock(); + + /** + * 获取新区块的编辑器; + *

+ * + * 如果未创建新的区块,或者新区块已经提交或取消,则返回 null; + * + * @return + */ + LedgerEditor getNextBlockEditor(); + + @Override + void close(); +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerService.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerService.java new file mode 100644 index 00000000..8e12352e --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerService.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.hash.HashDigest; + +/** + * 账本管理器; + * + * @author huanghaiquan + * + */ +public interface LedgerService { + + /** + * 返回已注册的账本哈希; + * @return + */ + HashDigest[] getLedgerHashs(); + + /** + * 获取指定的账本数据库;
+ * + * 如果指定的账本不存在,则抛出 {@link LedgerException}; + * + * @param ledgerHash + * @return + */ + LedgerRepository getLedger(HashDigest ledgerHash); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerSetting.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerSetting.java new file mode 100644 index 00000000..67c503c9 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerSetting.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code = TypeCodes.METADATA_LEDGER_SETTING) +public interface LedgerSetting { + + @DataField(order=0, primitiveType=ValueType.TEXT) + String getConsensusProvider(); + + @DataField(order=1, primitiveType=ValueType.BYTES) + Bytes getConsensusSetting(); + + @DataField(order=2, refContract=true) + CryptoSetting getCryptoSetting(); + +// PrivilegeModelSetting getPrivilegesModelSetting(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java new file mode 100644 index 00000000..9b653ce8 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/LedgerTransactionContext.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionRequest; + +/** + * 事务上下文; + * + * @author huanghaiquan + * + */ +public interface LedgerTransactionContext { + + /** + * 账本数据; + * + * @return + */ + LedgerDataSet getDataSet(); + + /** + * 交易请求; + * + * @return + */ + TransactionRequest getRequestTX(); + + /** + * 提交对账本数据的修改,以指定的交易状态提交交易; + * + * @param txResult + * @return + */ + LedgerTransaction commit(TransactionState txResult); + + /** + * 抛弃对账本数据的修改,以指定的交易状态提交交易;
+ * + * 通常来说,当在开启事务之后,修改账本或者尝试提交交易({@link #commit(TransactionState)})时发生错误,都应该抛弃数据,通过此方法记录一个表示错误状态的交易; + * + * @param txResult + * @return + */ + LedgerTransaction discardAndCommit(TransactionState txResult); + + /** + * 回滚事务,抛弃本次事务的所有数据更新; + */ + void rollback(); +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataEntry.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataEntry.java new file mode 100644 index 00000000..527e7115 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataEntry.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.storage.service.VersioningKVEntry; + +public interface MerkleDataEntry { + + VersioningKVEntry getData(); + + MerkleProof getProof(); +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataNode.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataNode.java new file mode 100644 index 00000000..63db9103 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataNode.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.utils.Bytes; + +public interface MerkleDataNode extends MerkleNode { + + long getSN(); + + Bytes getKey(); + + long getVersion(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java new file mode 100644 index 00000000..65f3b458 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleDataSet.java @@ -0,0 +1,506 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.utils.BufferedKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 对新的数据项按顺序递增进行编号的 Merkle 数据集;
+ * + * 注:此实现不是线程安全的; + * + * @author huanghaiquan + * + */ +public class MerkleDataSet implements Transactional, MerkleProvable { + + /** + * 4 MB MaxSize of value; + */ + public static final int MAX_SIZE_OF_VALUE = 4 * 1024 * 1024; + + public static final String ORIG_KEY_SEPERATOR = LedgerConsts.KEY_SEPERATOR; + + public static final String SN_PREFIX = "SN" + ORIG_KEY_SEPERATOR; + public static final String DATA_PREFIX = "KV" + ORIG_KEY_SEPERATOR; + public static final String MERKLE_TREE_PREFIX = "MKL" + ORIG_KEY_SEPERATOR; + + private final Bytes snKeyPrefix; + private final Bytes dataKeyPrefix; + private final Bytes merkleKeyPrefix; + + private BufferedKVStorage bufferedStorage; + + private VersioningKVStorage valueStorage; + + private ExPolicyKVStorage snStorage; + + private MerkleTree merkleTree; + + private SNGenerator snGenerator; + + private boolean readonly; + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleProvable#getRootHash() + */ + @Override + public HashDigest getRootHash() { + return merkleTree.getRootHash(); + } + + /** + * 创建一个新的 MerkleDataSet; + * + * @param setting + * 密码设置; + * @param exPolicyStorage + * 默克尔树的存储; + * @param versioningStorage + * 数据的存储; + */ + public MerkleDataSet(CryptoSetting setting, String keyPrefix, ExPolicyKVStorage exPolicyStorage, + VersioningKVStorage versioningStorage) { + // 缓冲对KV的写入; + this.bufferedStorage = new BufferedKVStorage(exPolicyStorage, versioningStorage, false); + + // 把存储数据值、SN、Merkle节点的 key 分别加入独立的前缀,避免针对 key 的注入攻击; + // this.valueStorage = PrefixAppender.prefix(DATA_PREFIX, (VersioningKVStorage) + // bufferedStorage); + // this.snStorage = PrefixAppender.prefix(SN_PREFIX, (ExPolicyKVStorage) + // bufferedStorage); + snKeyPrefix = Bytes.fromString(keyPrefix + SN_PREFIX); + dataKeyPrefix = Bytes.fromString(keyPrefix + DATA_PREFIX); + this.valueStorage = bufferedStorage; + this.snStorage = bufferedStorage; + + // MerkleTree 本身是可缓冲的; + // ExPolicyKVStorage merkleTreeStorage = + // PrefixAppender.prefix(MERKLE_TREE_PREFIX, exPolicyStorage); + merkleKeyPrefix = Bytes.fromString(keyPrefix + MERKLE_TREE_PREFIX); + ExPolicyKVStorage merkleTreeStorage = exPolicyStorage; + this.merkleTree = new MerkleTree(setting, merkleKeyPrefix, merkleTreeStorage); + this.snGenerator = new MerkleSequenceSNGenerator(merkleTree); + } + + /** + * 从指定的 Merkle 根构建的 MerkleDataSet; + * + * @param dataStorage + * @param defaultMerkleHashAlgorithm + * @param verifyMerkleHashOnLoad + * @param merkleTreeStorage + * @param snGenerator + */ + public MerkleDataSet(HashDigest merkleRootHash, CryptoSetting setting, String keyPrefix, + ExPolicyKVStorage exPolicyStorage, VersioningKVStorage versioningStorage, boolean readonly) { + // 缓冲对KV的写入; + this.bufferedStorage = new BufferedKVStorage(exPolicyStorage, versioningStorage, false); + + // 把存储数据值、SN、Merkle节点的 key 分别加入独立的前缀,避免针对 key 的注入攻击; + snKeyPrefix = Bytes.fromString(keyPrefix + SN_PREFIX); + dataKeyPrefix = Bytes.fromString(keyPrefix + DATA_PREFIX); + this.valueStorage = bufferedStorage; + this.snStorage = bufferedStorage; + + // MerkleTree 本身是可缓冲的; + merkleKeyPrefix = Bytes.fromString(keyPrefix + MERKLE_TREE_PREFIX); + ExPolicyKVStorage merkleTreeStorage = exPolicyStorage; + this.merkleTree = new MerkleTree(merkleRootHash, setting, merkleKeyPrefix, merkleTreeStorage, readonly); + + this.snGenerator = new MerkleSequenceSNGenerator(merkleTree); + this.readonly = readonly; + } + + public boolean isReadonly() { + return readonly; + } + + public long getDataCount() { + return merkleTree.getDataCount(); + } + + public long getMaxIndex() { + return merkleTree.getMaxSn(); + } + + public byte[][] getLatestValues(int fromIndex, int count) { + if (count > LedgerConsts.MAX_LIST_COUNT) { + throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); + } + if (fromIndex < 0 || (fromIndex + count) > merkleTree.getDataCount()) { + throw new IllegalArgumentException("Index out of bound!"); + } + byte[][] values = new byte[count][]; + for (int i = 0; i < count; i++) { + MerkleDataNode dataNode = merkleTree.getData(fromIndex + i); + Bytes dataKey = encodeDataKey(dataNode.getKey()); + values[i] = valueStorage.get(dataKey, dataNode.getVersion()); + } + return values; + } + + /** + * get the data at the specific index; + * @param fromIndex + * @return + */ + public byte[] getValuesAtIndex(int fromIndex) { + MerkleDataNode dataNode = merkleTree.getData(fromIndex); + Bytes dataKey = encodeDataKey(dataNode.getKey()); + return valueStorage.get(dataKey, dataNode.getVersion()); + } + + /** + * get the key at the specific index; + * @param fromIndex + * @return + */ + public String getKeyAtIndex(int fromIndex) { + MerkleDataNode dataNode = merkleTree.getData(fromIndex); + return new String(dataNode.getKey().toBytes()); + } + + + /** + * Create or update the value associated the specified key if the version + * checking is passed.
+ * + * The value of the key will be updated only if it's latest version equals the + * specified version argument.
+ * If the key doesn't exist, it will be created when the version arg was -1. + *

+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ * + * @param key + * The key of data; + * @param value + * The value of data; + * @param version + * The expected latest version of the key. + * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then + * return -1; + */ + public long setValue(String key, byte[] value, long version) { + return setValue(Bytes.fromString(key), value, version); + } + + /** + * Create or update the value associated the specified key if the version + * checking is passed.
+ * + * The value of the key will be updated only if it's latest version equals the + * specified version argument.
+ * If the key doesn't exist, it will be created when the version arg was -1. + *

+ * If updating is performed, the version of the key increase by 1.
+ * If creating is performed, the version of the key initialize by 0.
+ * + * @param key + * The key of data; + * @param value + * The value of data; + * @param version + * The expected latest version of the key. + * @return The new version of the key.
+ * If the key is new created success, then return 0;
+ * If the key is updated success, then return the new version;
+ * If this operation fail by version checking or other reason, then + * return -1; + */ + public long setValue(Bytes key, byte[] value, long version) { + if (readonly) { + throw new IllegalArgumentException("This merkle dataset is readonly!"); + } + if (value.length > MAX_SIZE_OF_VALUE) { + throw new IllegalArgumentException( + "The size of value is great than the max size[" + MAX_SIZE_OF_VALUE + "]!"); + } + Bytes dataKey = encodeDataKey(key); + long latestVersion = valueStorage.getVersion(dataKey); + if (version != latestVersion) { + return -1; + } + + // set into versioning kv storage before adding to merkle tree, in order to + // check version confliction first; + long sn; + long newVersion; + if (version < 0) { + // creating ; + sn = snGenerator.generate(key); + newVersion = valueStorage.set(dataKey, value, -1); + if (newVersion < 0) { + return -1; + } + byte[] snBytes = BytesUtils.toBytes(sn); + Bytes snKey = encodeSNKey(key); + boolean nx = snStorage.set(snKey, snBytes, ExPolicy.NOT_EXISTING); + if (!nx) { + throw new LedgerException("SN already exist! --[KEY=" + key + "]"); + } + } else { + // updating; + + // TODO: 未在当前实例的层面,实现对输入键-值的缓冲,而直接写入了存储,而 MerkleTree 在未调用 commit + // 之前是缓冲的,这使得在存储层面的数据会不一致,而未来需要优化; + newVersion = valueStorage.set(dataKey, value, version); + if (newVersion < 0) { + return -1; + } + + sn = getSN(key); + } + + // update merkle tree; + merkleTree.setData(sn, key, newVersion, value); + // TODO: 未在当前实例的层面,实现对输入键-值的缓冲,而直接写入了存储,而 MerkleTree 在未调用 commit + // 之前是缓冲的,这使得在存储层面的数据会不一致,而未来需要优化; + + return newVersion; + } + + private Bytes encodeSNKey(Bytes key) { + return new Bytes(snKeyPrefix, key); + } + + private Bytes encodeDataKey(Bytes key) { + return new Bytes(dataKeyPrefix, key); + } + + /** + * 返回指定 key 对应的序号,如果不存在,则返回 -1; + * + * @param key + * @return + */ + private long getSN(Bytes key) { + // SN-KEY index entry has never changed; + Bytes snKey = encodeSNKey(key); + byte[] snBytes = snStorage.get(snKey); + if (snBytes == null) { + // throw new IllegalStateException("Cann't found SN of key[" + key + "] from + // data storage!"); + return -1; + } + return BytesUtils.toLong(snBytes); + } + + /** + * 返回默克尔树中记录的指定键的版本,在由默克尔树表示的数据集的快照中,这是指定键的最新版本,
+ * 但该版本有可能小于实际存储的最新版本(由于后续追加的新修改被之后生成的快照维护); + * + * @param key + * @return 返回指定的键的版本;如果不存在,则返回 -1; + */ + private long getMerkleVersion(Bytes key) { + long sn = getSN(key); + if (sn < 0) { + return -1; + } + MerkleDataNode mdn = merkleTree.getData(sn); + if (mdn == null) { + return -1; + } + return mdn.getVersion(); + } + + /** + * Return the specified version's value;
+ * + * If the key with the specified version doesn't exist, then return null;
+ * If the version is specified to -1, then return the latest version's value; + * + * @param key + * @param version + */ + public byte[] getValue(String key, long version) { + return getValue(Bytes.fromString(key), version); + } + + /** + * Return the specified version's value;
+ * + * If the key with the specified version doesn't exist, then return null;
+ * If the version is specified to -1, then return the latest version's value; + * + * @param key + * @param version + */ + public byte[] getValue(Bytes key, long version) { + long latestVersion = getMerkleVersion(key); + if (latestVersion < 0 || version > latestVersion) { + // key not exist, or the specified version is out of the latest version indexed + // by the current merkletree; + return null; + } + version = version < 0 ? latestVersion : version; + Bytes dataKey = encodeDataKey(key); + return valueStorage.get(dataKey, version); + } + + /** + * Return the latest version's value; + * + * @param key + * @return return null if not exist; + */ + public byte[] getValue(String key) { + return getValue(Bytes.fromString(key)); + } + + /** + * Return the latest version's value; + * + * @param key + * @return return null if not exist; + */ + public byte[] getValue(Bytes key) { + long latestVersion = getMerkleVersion(key); + if (latestVersion < 0) { + return null; + } + Bytes dataKey = encodeDataKey(key); + return valueStorage.get(dataKey, latestVersion); + } + + /** + * Return the latest version entry associated the specified key; If the key + * doesn't exist, then return -1; + * + * @param key + * @return + */ + public long getVersion(String key) { + return getMerkleVersion(Bytes.fromString(key)); + } + + /** + * Return the latest version entry associated the specified key; If the key + * doesn't exist, then return -1; + * + * @param key + * @return + */ + public long getVersion(Bytes key) { + return getMerkleVersion(key); + } + + public VersioningKVEntry getDataEntry(String key) { + return getDataEntry(Bytes.fromString(key)); + } + + public VersioningKVEntry getDataEntry(Bytes key) { + long latestVersion = getMerkleVersion(key); + if (latestVersion < 0) { + return null; + } + return valueStorage.getEntry(key, latestVersion); + } + + public VersioningKVEntry getDataEntry(Bytes key, long version) { + long latestVersion = getMerkleVersion(key); + if (latestVersion < 0 || version > latestVersion) { + // key not exist, or the specified version is out of the latest version indexed + // by the current merkletree; + return null; + } + version = version < 0 ? latestVersion : version; + return valueStorage.getEntry(key, version); + } + + public MerkleDataEntry getMerkleEntry(Bytes key, long version) { + VersioningKVEntry dataEntry = getDataEntry(key, version); + if (dataEntry == null) { + return null; + } + MerkleProof proof = getProof(key); + return new MerkleDataEntryWrapper(dataEntry, proof); + } + + public MerkleDataEntry getMerkleEntry(Bytes key) { + VersioningKVEntry dataEntry = getDataEntry(key); + if (dataEntry == null) { + return null; + } + MerkleProof proof = getProof(key); + return new MerkleDataEntryWrapper(dataEntry, proof); + } + + public MerkleProof getProof(String key) { + return getProof(Bytes.fromString(key)); + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleProvable#getProof(java.lang.String) + */ + @Override + public MerkleProof getProof(Bytes key) { + long sn = getSN(key); + if (sn < 0) { + return null; + } + return merkleTree.getProof(sn); + } + + /** + * A wrapper for {@link VersioningKVEntry} and {@link MerkleProof}; + * + * @author huanghaiquan + * + */ + private static class MerkleDataEntryWrapper implements MerkleDataEntry { + + private VersioningKVEntry data; + private MerkleProof proof; + + public MerkleDataEntryWrapper(VersioningKVEntry data, MerkleProof proof) { + this.data = data; + this.proof = proof; + } + + @Override + public VersioningKVEntry getData() { + return data; + } + + @Override + public MerkleProof getProof() { + return proof; + } + + } + + @Override + public boolean isUpdated() { + return bufferedStorage.isUpdated() || merkleTree.isUpdated(); + } + + @Override + public void commit() { + bufferedStorage.commit(); + merkleTree.commit(); + } + + @Override + public void cancel() { + bufferedStorage.cancel(); + merkleTree.cancel(); + snGenerator = new MerkleSequenceSNGenerator(merkleTree); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleNode.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleNode.java new file mode 100644 index 00000000..63130b09 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleNode.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.hash.HashDigest; + +public interface MerkleNode { + + HashDigest getNodeHash(); + + /** + * 节点的深度; + * + * 叶子节点的深度为 0;每一级父节点的深度加 1 ; + * + * @return + */ + int getLevel(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProof.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProof.java new file mode 100644 index 00000000..06a8339f --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProof.java @@ -0,0 +1,82 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.hash.HashDigest; + +public interface MerkleProof { + + /** + * 所证明的数据节点的序列号; + * + * @return + */ + long getSN(); + + /** + * 最大层级数;
+ * 叶子节点(即数据节点)的层级为 0,数据节点之上的每一级父节点的层级加 1, 最大层级便是根节点的层级; + * + * @return + * + * @see MerkleTree#getLevel() + */ + int getLevels(); + + /** + * 返回证明中指定层级的节点的哈希; + *

+ * + * @param level + * 参数值为 0 返回的是数据节点的哈希;
+ * 参数值为 {@link #getLevels()} 返回的是根节点的哈希; + * @return + */ + HashDigest getHash(int level); + + MerkleNode getNode(int level); + +// /** +// * 返回证明中指定层级的数据起始序号; +// * +// * @param level +// * @return +// */ +// long getStartingSN(int level); + +// /** +// * 返回证明中指定层级的数据记录总数; +// * +// * @param level +// * @return +// */ +// long getDataCount(int level); + + /** + * 返回根节点的哈希; + * + * @return + */ + default HashDigest getRootHash() { + return getHash(getLevels()); + } + + /** + * 返回数据节点的哈希; + * + * @return + */ + default HashDigest getDataHash() { + return getHash(0); + } + + default MerkleDataNode getDataNode() { + return (MerkleDataNode)getNode(0); + } + + /** + * 返回字符串形式的哈希路径; + * + * @return + */ + @Override + String toString(); +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProofException.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProofException.java new file mode 100644 index 00000000..1dd50173 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProofException.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.ledger.core; + +/** + * 默克尔证明异常; + *

+ * 表示 {@link MerkleTree} 在处理数据时数据无法通过校验,潜在的原因可能是数据已被修改或篡改; + * + * @author huanghaiquan + * + */ +public class MerkleProofException extends RuntimeException { + + private static final long serialVersionUID = 4110511167046780109L; + + public MerkleProofException(String message) { + super(message); + } + + public MerkleProofException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProvable.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProvable.java new file mode 100644 index 00000000..ec556e16 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleProvable.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.Bytes; + +public interface MerkleProvable { + + HashDigest getRootHash(); + + /** + * Get the merkle proof of the latest version of specified key;
+ * + * The proof doesn't represent the latest changes until do + * committing({@link #commit()}). + * + * @param key + * @return Return the {@link MerkleProof} instance, or null if the key doesn't + * exist. + */ + MerkleProof getProof(Bytes key); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleSequenceSNGenerator.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleSequenceSNGenerator.java new file mode 100644 index 00000000..e61a3f71 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleSequenceSNGenerator.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.ledger.core; + +import java.util.concurrent.atomic.AtomicLong; + +import com.jd.blockchain.utils.Bytes; + +public class MerkleSequenceSNGenerator implements SNGenerator { + + private AtomicLong sn; + + public MerkleSequenceSNGenerator(MerkleTree merkleTree) { + this.sn = new AtomicLong(merkleTree.getMaxSn() + 1); + } + + @Override + public long generate(Bytes key) { + return sn.getAndIncrement(); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleTree.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleTree.java new file mode 100644 index 00000000..90c9289b --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/MerkleTree.java @@ -0,0 +1,1601 @@ +package com.jd.blockchain.ledger.core; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveTask; +import java.util.concurrent.atomic.AtomicLong; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.NumberMask; + +/** + * 默克尔树; + *

+ * 树的level是按照倒置的方式计算,而不是以根节点的距离衡量,即叶子节点的 level 是 0;
+ * 所有的数据的哈希索引都以叶子节点进行记录;
+ * 每一个数据节点都以标记一个序列号(Sequence Number, 缩写为 SN),并按照序列号的大小统一地在 level 0 + * 上排列,并填充从根节点到数据节点的所有路径节点;
+ * 随着数据节点的增加,整棵树以倒置方式向上增长(根节点在上,叶子节点在下),此设计带来显著特性是已有节点的信息都可以不必修改; + * + *

+ * 注:此实现不是线程安全的;
+ * 但由于对单个账本中的写入过程被设计为同步写入,因而非线程安全的设计并不会影响在此场景下的使用,而且由于省去了线程间同步操作,反而提升了性能; + * + * @author huanghaiquan + * + */ +public class MerkleTree implements Transactional { + + public static final int TREE_DEGREE = 16; + + public static final int MAX_LEVEL = 15; + + public static final long MAX_DATACOUNT = power(TREE_DEGREE, MAX_LEVEL); + + public static final long MAX_SN = MAX_DATACOUNT - 1; + + public static final String NODE_PREFIX = "MKT://"; + + public static final String PATH_SEPERATOR = "/"; + + public static final boolean PARALLEL; + + static { + // get property from System; + PARALLEL = Boolean.getBoolean("parallel-merkle"); + System.out.println("------ [[ parallel-merkle=" + PARALLEL + " ]] -----"); + } + + private final Bytes keyPrefix; + + private CryptoSetting setting; + + private ExPolicyKVStorage kvStorage; + + private boolean readonly; + + private SortedMap updatedDataNodes = Collections.synchronizedSortedMap(new TreeMap<>()); + + private PathNode root; + + /** + * Merkle树的根哈希; + * + * @return + */ + public HashDigest getRootHash() { + return root.getNodeHash(); + } + + /** + * 当前 Merkle 数据记录的数据节点的总数; + * + * @return + */ + public long getDataCount() { + return root.getDataCount(); + } + + /** + * 节点的层级; + *

+ * 叶子节点(即数据节点)的层级为 0;每一级父节点的层级加 1 ; + * + * @return + */ + public int getLevel() { + return root.getLevel(); + } + + /** + * 构建空的树; + * + * @param kvStorage + */ + public MerkleTree(CryptoSetting setting, String keyPrefix, ExPolicyKVStorage kvStorage) { + this(null, setting, Bytes.fromString(keyPrefix), kvStorage, false); + } + + /** + * 构建空的树; + * + * @param kvStorage + */ + public MerkleTree(CryptoSetting setting, Bytes keyPrefix, ExPolicyKVStorage kvStorage) { + this(null, setting, keyPrefix, kvStorage, false); + } + + // /** + // * 创建一颗可写的 Merkle 树; + // * + // * @param rootHash + // * 节点的根Hash; 如果指定为 null,则实际上创建一个空的 Merkle Tree; + // * @param verifyOnLoad + // * 从外部存储加载节点时是否校验节点的哈希; + // * @param kvStorage + // * 保存 Merkle 节点的存储服务; + // */ + // public MerkleTree(HashDigest rootHash, CryptoSetting setting, + // ExistentialKVStorage kvStorage) { + // this(rootHash, setting, kvStorage, false); + // } + + /** + * 创建 Merkle 树; + * + * @param rootHash + * 节点的根Hash; 如果指定为 null,则实际上创建一个空的 Merkle Tree; + * @param verifyOnLoad + * 从外部存储加载节点时是否校验节点的哈希; + * @param kvStorage + * 保存 Merkle 节点的存储服务; + * @param readonly + * 是否只读; + */ + public MerkleTree(HashDigest rootHash, CryptoSetting setting, String keyPrefix, ExPolicyKVStorage kvStorage, + boolean readonly) { + this(rootHash, setting, Bytes.fromString(keyPrefix), kvStorage, readonly); + } + + /** + * 创建 Merkle 树; + * + * @param rootHash + * 节点的根Hash; 如果指定为 null,则实际上创建一个空的 Merkle Tree; + * @param verifyOnLoad + * 从外部存储加载节点时是否校验节点的哈希; + * @param kvStorage + * 保存 Merkle 节点的存储服务; + * @param readonly + * 是否只读; + */ + public MerkleTree(HashDigest rootHash, CryptoSetting setting, Bytes keyPrefix, ExPolicyKVStorage kvStorage, + boolean readonly) { + this.setting = setting; + this.keyPrefix = keyPrefix; + this.kvStorage = kvStorage; + this.readonly = readonly; + if (rootHash == null) { + root = new PathNode(setting.getHashAlgorithm(), 0, (byte) 1, 0); + } else { + PathNode rootNode = loadPathNode(rootHash, setting.getAutoVerifyHash()); + if (rootNode == null) { + throw new IllegalStateException( + "The root path node[" + Base58Utils.encode(rootHash.toBytes()) + "] not exist!"); + } + if (rootNode.getStartingSN() != 0) { + String hashStr = Base58Utils.encode(rootNode.getNodeHash().toBytes()); + throw new MerkleProofException(String.format( + "The starting sn of the specified merkle root node is not zero! --[RootNodeHash=%s]", + hashStr)); + } + this.root = rootNode; + } + } + + /** + * 返回数据的默克尔证明; + * + *

+ * 如果不存在,则返回 null; + *

+ * 如果 sn 超出范围,则引发 {@link IndexOutOfBoundsException} ; + * + * @param sn + * 数据的序列号; + * @return 默克尔证明的实例; + */ + public MerkleProof getProof(long sn) { + MerkleNode[] nodePath = new MerkleNode[root.level + 1]; + MerkleDataNode dataNode = seekPath(sn, nodePath); + if (dataNode == null) { + return null; + } + for (int i = 0; i < nodePath.length; i++) { + if (i < nodePath.length - 1) { + // PathNode will be changed on updating data; + // So record the path info with the immutable ProofNodeEntry instead; + PathNode n = (PathNode) nodePath[i]; + ProofNodeEntry p = new ProofNodeEntry(); + p.nodeHash = n.getNodeHash(); + p.level = n.getLevel(); + p.dataCount = n.getDataCount(); + p.startingSN = n.getStartingSN(); + nodePath[i] = p; + } + } + return new MerkleProofImpl(sn, nodePath); + } + + /** + * 以指定序号建立对指定键值的索引; + *

+ * + *

+ * 此方法不会立即更新整棵默克尔树,直到方法 {@link #commit()} 被调用; + * + *

+ * 注:默克尔树只保存指定数据的哈希以及关联的键,而不会保存数据原文,因此调用者需要自己处理对数据的存储;
+ * 此外,哈希计算是把键和数据内容拼接一起进行计算的; + * + * @param sn + * 与此数据唯一相关的序列号;sn 必须大于等于 0 ; + * @param key + * 与此数据唯一相关的键; + * @param version + * @param hashedData + * 要参与哈希计算的数据内容;注:此参数值并不会被默克尔树保存; + * @return + */ + public MerkleDataNode setData(long sn, String key, long version, byte[] hashedData) { + return setData(sn, Bytes.fromString(key), version, hashedData); + } + + /** + * 以指定序号建立对指定键值的索引; + *

+ * + *

+ * 此方法不会立即更新整棵默克尔树,直到方法 {@link #commit()} 被调用; + * + *

+ * 注:默克尔树只保存指定数据的哈希以及关联的键,而不会保存数据原文,因此调用者需要自己处理对数据的存储;
+ * 此外,哈希计算是把键和数据内容拼接一起进行计算的; + * + * @param sn + * 与此数据唯一相关的序列号;sn 必须大于等于 0 ; + * @param key + * 与此数据唯一相关的键; + * @param version + * @param hashedData + * 要参与哈希计算的数据内容;注:此参数值并不会被默克尔树保存; + * @return + */ + public MerkleDataNode setData(long sn, Bytes key, long version, byte[] hashedData) { + if (readonly) { + throw new IllegalStateException("This merkle tree is readonly!"); + } + if (sn < 0) { + throw new IllegalArgumentException("The sn is negative!"); + } + if (sn > MAX_SN) { + throw new IllegalArgumentException("The sn is great than MAX[" + MAX_SN + "]!"); + } + DataNode dataNode = DataNode.newDataNode(setting.getHashAlgorithm(), sn, key, version, hashedData); + updatedDataNodes.put(sn, dataNode); + return dataNode; + } + + public MerkleDataNode getData(long sn) { + DataNode dataNode = updatedDataNodes.get(sn); + if (dataNode != null) { + return dataNode; + } + return seekPath(sn, null); + } + + /** + * Return the max sequence number in all data nodes;
+ * + * Return -1 if no data node existed; + * + * @return + */ + public long getMaxSn() { + DataNode maxDataNode = getMaxDataNode(); + if (maxDataNode == null) { + return -1; + } + return maxDataNode.sn; + } + + private DataNode getMaxDataNode() { + MerkleNode node = root; + PathNode pathNode; + int idx = -1; + while (node.getLevel() > 0) { + pathNode = (PathNode) node; + // find the last child, because all children are ascension sorted by sn; + for (idx = pathNode.childrenHashes.length - 1; idx > -1; idx--) { + if (pathNode.childrenHashes[idx] != null) { + break; + } + } + if (idx == -1) { + // no child; + return null; + } + // if child node have been loaded, then load it; + if (pathNode.children[idx] == null) { + if (pathNode.getLevel() > 1) { + // load path node; + PathNode child = loadPathNode(pathNode.childrenHashes[idx], setting.getAutoVerifyHash()); + pathNode.attachChildNode(child, idx); + } else { + DataNode child = loadDataNode(pathNode.childrenHashes[idx], setting.getAutoVerifyHash()); + pathNode.attachChildNode(child, idx); + } + } + node = pathNode.children[idx]; + } + return (DataNode) node; + } + + @Override + public boolean isUpdated() { + return updatedDataNodes.size() > 0; + } + + /** + * 回滚上一次提交以来的变更; + */ + @Override + public void cancel() { + updatedDataNodes.clear(); + } + + /** + * 根据新修改重新计算哈希,并将产生变更的节点保存到存储服务;
+ * + * 注:调用者在执行批量更改时,不应该在每一次更改之后立即调用此方法,而是完成批量操作之后,仅调用此一次 commit 操作,这样可以更改的中间重复地对 + * hash 节点进行计算,达到性能优化的目的; + */ + @Override + public void commit() { + if (!isUpdated()) { + return; + } + long sn; + PathNode leafPathNode = null; + // 按照升序处理; + Set updatedLeafNodes = new HashSet<>(); + for (Entry entry : updatedDataNodes.entrySet()) { + sn = entry.getKey().longValue(); + if (leafPathNode == null || sn >= leafPathNode.startingSN + leafPathNode.interval) { + leafPathNode = extendPaths(sn); + } + leafPathNode.setData(sn, entry.getValue()); + updatedLeafNodes.add(leafPathNode); + } + + // 遍历涉及更改的所有路径节点,重新计算根节点哈希; + if (PARALLEL) { + concurrentRehash(updatedLeafNodes); + } else { + rehash(updatedLeafNodes); + } + + // List updatedNodes = Collections.synchronizedList(new + // LinkedList<>()); + // rehash(root, updatedNodes); + // + // // 把更改的节点写入到存储服务; + // for (AbstractMerkleNode merkleNode : updatedNodes) { + // String key = encodeNodeKey(merkleNode.getNodeHash()); + // boolean nx = kvStorage.set(key, merkleNode.toBytes(), ExPolicy.NOT_EXISTING); + // if (!nx) { + // throw new LedgerException("Merkle node already exist!"); + // } + // } + + // 清空缓存; + updatedDataNodes.clear(); + } + + private void saveNode(AbstractMerkleNode merkleNode) { + Bytes key = encodeNodeKey(merkleNode.getNodeHash()); + boolean nx = kvStorage.set(key, merkleNode.toBytes(), ExPolicy.NOT_EXISTING); + if (!nx) { + throw new LedgerException("Merkle node already exist!"); + } + } + + private void rehash(Set updatedPathNodes) { + + Set updatedParentNodes = new HashSet<>(); + + for (PathNode pathNode : updatedPathNodes) { + AbstractMerkleNode[] children = pathNode.children; + HashDigest[] childrenHashes = pathNode.childrenHashes; + boolean updated = false; + for (int i = 0; i < children.length; i++) { + if (children[i] == null) { + continue; + } + HashDigest origChildHash = childrenHashes[i]; + HashDigest newChildHash = children[i].getNodeHash(); + if (origChildHash == null) { + childrenHashes[i] = newChildHash; + updated = true; + if (pathNode.level == 1) { + // 在叶子节点上发现新增加了数据节点; + pathNode.increaseDataCount(1); + // 同时保存新增的数据节点; + saveNode(children[i]); + } + } else if (!origChildHash.equals(newChildHash)) { + childrenHashes[i] = newChildHash; + updated = true; + if (pathNode.level == 1) { + // 同时保存更新的数据节点; + saveNode(children[i]); + } + } + } + + if (updated) { + // 计算节点哈希: + pathNode.rehash(); + + saveNode(pathNode); + + if (pathNode.parent != null) { + updatedParentNodes.add((PathNode) pathNode.parent); + } + } + } + + if (updatedParentNodes.size() > 0) { + rehash(updatedParentNodes); + } + } + + private void concurrentRehash(Set updatedPathNodes) { + + // Set updatedParentNodes = new HashSet<>(); + // List tasks = new ArrayList<>(); + // for (PathNode pathNode : updatedPathNodes) { + // RehashTask task = new RehashTask(pathNode, kvStorage); + // ForkJoinPool.commonPool().execute(task); + // tasks.add(task); + // } + PathNode[] nodes = updatedPathNodes.toArray(new PathNode[updatedPathNodes.size()]); + RehashTask task = new RehashTask(nodes, this, kvStorage); + List updatedNodes = ForkJoinPool.commonPool().invoke(task); + Set updatedParentNodes = new HashSet<>(); + for (PathNode pathNode : updatedNodes) { + if (pathNode.parent != null) { + updatedParentNodes.add((PathNode) pathNode.parent); + } + } + if (updatedParentNodes.size() > 0) { + rehash(updatedParentNodes); + } + } + + /** + * @author huanghaiquan + * + */ + private static class RehashTask extends RecursiveTask> { + + private MerkleTree tree; + + private PathNode[] pathNodes; + + private ExPolicyKVStorage kvStorage; + + private static int TASK_THRESHOLD = 100; + + public RehashTask(PathNode[] pathNodes, MerkleTree tree, ExPolicyKVStorage kvStorage) { + this.tree = tree; + this.pathNodes = pathNodes; + this.kvStorage = kvStorage; + } + + @Override + protected List compute() { + int count = pathNodes.length; + if (count > TASK_THRESHOLD) { + PathNode[] nodes1 = new PathNode[count / 2]; + PathNode[] nodes2 = new PathNode[count - nodes1.length]; + System.arraycopy(pathNodes, 0, nodes1, 0, nodes1.length); + System.arraycopy(pathNodes, nodes1.length, nodes2, 0, nodes2.length); + RehashTask task1 = new RehashTask(nodes1, tree, kvStorage); + RehashTask task2 = new RehashTask(nodes2, tree, kvStorage); + ForkJoinTask.invokeAll(task1, task2); + List updatedNodes = task1.join(); + updatedNodes.addAll(task2.join()); + return updatedNodes; + } else { + List updatedNodes = new ArrayList<>(); + for (PathNode pathNode : pathNodes) { + if (rehash(pathNode)) { + updatedNodes.add(pathNode); + } + } + return updatedNodes; + } + } + + private boolean rehash(PathNode pathNode) { + AbstractMerkleNode[] children = pathNode.children; + HashDigest[] childrenHashes = pathNode.childrenHashes; + boolean updated = false; + for (int i = 0; i < children.length; i++) { + if (children[i] == null) { + continue; + } + HashDigest origChildHash = childrenHashes[i]; + HashDigest newChildHash = children[i].getNodeHash(); + if (origChildHash == null) { + childrenHashes[i] = newChildHash; + updated = true; + if (pathNode.level == 1) { + // 在叶子节点上发现新增加了数据节点; + // newDataCount++; + pathNode.increaseDataCount(1); + // 同时保存新增的数据节点; + tree.saveNode(children[i]); + } + } else if (!origChildHash.equals(newChildHash)) { + childrenHashes[i] = newChildHash; + updated = true; + if (pathNode.level == 1) { + // 同时保存更新的数据节点; + tree.saveNode(children[i]); + } + } + } + + if (updated) { + // 计算节点哈希: + pathNode.rehash(); + tree.saveNode(pathNode); + + // if (pathNode.parent != null) { + // return (PathNode) pathNode.parent; + // } + return true; + } + + return false; + } + + // private void saveNode(AbstractMerkleNode merkleNode) { + // String key = encodeNodeKey(merkleNode.getNodeHash()); + // boolean nx = kvStorage.set(key, merkleNode.toBytes(), ExPolicy.NOT_EXISTING); + // if (!nx) { + // throw new LedgerException("Merkle node already exist!"); + // } + // } + + } + + /** + * 重新计算所有子节点以及自身的哈希,并返回新加入的数据节点的数量; + * + * @param pathNode + * 需要重新计算 hash 的路径节点; + * @param updatedNodes + * 用于记录已更新节点的列表; + * @return + */ + private int rehash(PathNode pathNode, List updatedNodes) { + // int newDataCount = 0; + boolean updated = false; + + // 先检查并更新子节点的 hash; + AbstractMerkleNode[] children = pathNode.children; + HashDigest[] childrenHashes = pathNode.childrenHashes; + + if (pathNode.level == 1) { + // 检查作为叶子的数据节点的哈希是否更新; + // 注:因为数据节点加入时已经进行过计算了它本身的哈希,在此不需重新计算; + for (int i = 0; i < children.length; i++) { + if (children[i] == null) { + continue; + } + HashDigest origChildHash = childrenHashes[i]; + HashDigest newChildHash = children[i].getNodeHash(); + if (origChildHash == null) { + // newDataCount++; + pathNode.increaseDataCount(1); + childrenHashes[i] = newChildHash; + updated = true; + updatedNodes.add(children[i]); + } else if (!origChildHash.equals(newChildHash)) { + childrenHashes[i] = newChildHash; + updated = true; + updatedNodes.add(children[i]); + } + } + } else { + for (int i = 0; i < children.length; i++) { + if (children[i] == null) { + continue; + } + + // 递归重新计算子路径的哈希; + // 更新数据节点数量; + // newDataCount += rehash((PathNode) children[i], updatedNodes); + rehash((PathNode) children[i], updatedNodes); + + HashDigest origChildHash = childrenHashes[i]; + HashDigest newChildHash = children[i].getNodeHash(); + if (origChildHash == null || !origChildHash.equals(newChildHash)) { + childrenHashes[i] = newChildHash; + updated = true; + } + } + } + + // 如果子节点发生了更改,则重新计算当前节点的 hash; + // 注:当加入了新的数据节点,即 newDataCount > 0 时,必然地 updated > 0 + if (updated) { + // 更新数据节点的计数器; + // pathNode.dataCount += newDataCount; + + // 计算节点哈希: + pathNode.rehash(); + + updatedNodes.add(pathNode); + } + + // return newDataCount; + return 0; + } + + /** + * 加载或创建能够到达指定序号的数据节点的完整路径节点,并返回路径最末端的路径节点; + * + * @param sn + * @return + */ + private PathNode extendPaths(long sn) { + if (root.getLevel() < 0) { + throw new IllegalStateException("The level of root is negative!"); + } + if (root.getLevel() > 15) { + // 超过15会导致接下来的幂运算溢出; + throw new IllegalStateException("The level of root is out of range, and it will lead to a overflow!"); + } + if (root.startingSN != 0) { + // 根节点的起始序号应该为 0 ; + throw new IllegalStateException("The start sn of root is not zero!"); + } + + // 如果指定的序号超出当前根节点的范围,需要向上扩展根节点; + // 逻辑最大序号(logicMaxSN)满足:logicMaxSN=rootNode.startSN + rootNode.interval; + // 由于rootNode.startSN==0,所以简化为:logicMaxSN=rootNode.interval; + long logicMaxSN = -1; + PathNode rootNode = root; + while ((logicMaxSN = rootNode.interval) <= sn) { + if (rootNode.getLevel() == 15) { + throw new IllegalStateException( + "The specified starting sn exceed the upper limit[" + logicMaxSN + "]!"); + } + PathNode newRoot = new PathNode(setting.getHashAlgorithm(), rootNode.startingSN, + (byte) (rootNode.getLevel() + 1), rootNode.getDataCount()); + newRoot.attachChildNode(rootNode, 0); + rootNode = newRoot; + } + this.root = rootNode; + + // 加载或创建从根节点到目标序号 + PathNode leafPathNode = rootNode; + while (leafPathNode.level > 1) { + int index = leafPathNode.index(sn); + if (leafPathNode.children[index] == null) { + if (leafPathNode.childrenHashes[index] == null) { + // 创建新节点; + leafPathNode.newEmptyChild(setting.getHashAlgorithm(), index); + } else { + // 加载节点; + PathNode node = loadPathNode(leafPathNode.childrenHashes[index], setting.getAutoVerifyHash()); + if (node == null) { + throw new IllegalStateException("The merkle path node[" + + leafPathNode.childrenHashes[index].toBase58() + "] not exist!"); + } + leafPathNode.attachChildNode(node, index); + } + } + leafPathNode = (PathNode) leafPathNode.children[index]; + } + + return leafPathNode; + } + + /** + * 查找指定序号的数据节点,并记录该数据节点的完整路径; + * + *
+ * 路径由节点数组表示,首个元素是根节点,最后一个元素是数据节点; + *

+ * + * 如果 sn 超出范围,则引发 {@link IndexOutOfBoundsException} ; + * + * @param sn + * 数据节点的序列号; + * @param path + * 用于记录节点路径的列表,长度必须大于等于当前默克尔树的总的层级(即 path.length 大于等于 root.level + + * 1);
+ * 如果参数为 null,则不记录; + * @return 序列号对应的数据节点;
+ * 如果不存在,则返回 null,注意,此时指定的路径参数 path 依然写入了查找过程的路径; + */ + private MerkleDataNode seekPath(long sn, MerkleNode[] path) { + /* + * 如果指定的序号超出当前根节点的范围,需要向上扩展根节点; + * + * 逻辑最大序号(logicMaxSN)满足:logicMaxSN=rootNode.startSN + rootNode.interval; + * 由于rootNode.startSN==0,所以简化为:logicMaxSN=rootNode.interval; + */ + long logicMaxSN = root.interval; + if (sn < 0 || sn >= logicMaxSN) { + throw new IndexOutOfBoundsException("The specified sn is out of range!"); + } + + if (path != null && path.length < (root.level + 1)) { + throw new IllegalArgumentException("The path length is too short to contain all path nodes!"); + } + // 加载或创建从根节点到目标序号 + // List path = new ArrayList<>(); + // MerkleNode[] path = new MerkleNode[root.level + 1]; + // path.add(leafPathNode); + PathNode leafPathNode = root; + if (path != null) { + path[path.length - leafPathNode.level - 1] = leafPathNode; + } + + while (leafPathNode.level > 1) { + int index = leafPathNode.index(sn); + if (leafPathNode.children[index] == null) { + if (leafPathNode.childrenHashes[index] == null) { + // 节点不存在; + return null; + } else { + // 加载节点; + PathNode node = loadPathNode(leafPathNode.childrenHashes[index], setting.getAutoVerifyHash()); + if (node == null) { + return null; + } + leafPathNode.attachChildNode(node, index); + } + } + leafPathNode = (PathNode) leafPathNode.children[index]; + if (path != null) { + path[path.length - leafPathNode.level - 1] = leafPathNode; + } + } + + // 数据节点; + int index = leafPathNode.index(sn); + if (leafPathNode.children[index] == null) { + if (leafPathNode.childrenHashes[index] == null) { + // 节点不存在; + return null; + } else { + // 加载节点; + DataNode dataNode = loadDataNode(leafPathNode.childrenHashes[index], setting.getAutoVerifyHash()); + if (dataNode == null) { + return null; + } + leafPathNode.setData(sn, dataNode); + } + } + if (path != null) { + path[path.length - leafPathNode.children[index].getLevel() - 1] = leafPathNode.children[index]; + } + return (MerkleDataNode) leafPathNode.children[index]; + } + + private Bytes encodeNodeKey(HashDigest hashBytes) { + // return keyPrefix + hashBytes.toBase58(); + return new Bytes(keyPrefix, hashBytes.toBytes()); + } + + /** + * Load {@link AbstractMerkleNode} from storage service; + * + *

+ * + * @param hashDigest + * @param verify + * @return return instance of {@link PathNode}, or null if not exist; + */ + private PathNode loadPathNode(HashDigest hashDigest, boolean verify) { + Bytes key = encodeNodeKey(hashDigest); + byte[] bytes = kvStorage.get(key); + if (bytes == null || bytes.length == 0) { + return null; + } + PathNode pathNode = PathNode.parse(bytes, verify); + if (verify && !hashDigest.equals(pathNode.getNodeHash())) { + String keyStr = hashDigest.toBase58(); + String actualHashStr = pathNode.getNodeHash().toBase58(); + throw new MerkleProofException(String.format( + "The actually hash of PathNode is not equal with it's key! -- [Key=%s][ActualHash=%s]", keyStr, + actualHashStr)); + } + return pathNode; + } + + private DataNode loadDataNode(HashDigest hashBytes, boolean verify) { + Bytes key = encodeNodeKey(hashBytes); + byte[] bytes = kvStorage.get(key); + if (bytes == null || bytes.length == 0) { + return null; + } + DataNode dataNode = DataNode.parse(bytes); + if (verify && !hashBytes.equals(dataNode.nodeHash)) { + String keyStr = hashBytes.toBase58(); + String actualHashStr = dataNode.nodeHash.toBase58(); + throw new MerkleProofException(String.format( + "The actually hash of DataNode is not equal with it's key! -- [Key=%s][ActualHash=%s]", keyStr, + actualHashStr)); + } + return dataNode; + } + + /** + * 计算 value 的 x 次方; + *

+ * 注:此方法不处理溢出;调用者需要自行规避; + * + * @param value + * @param x + * 大于等于 0 的整数; + * @return + */ + private static long power(long value, int x) { + if (x == 0) { + return 1; + } + long r = value; + for (int i = 1; i < x; i++) { + r *= value; + } + return r; + } + + // ================================================= + + /** + * 数据证明; + * + *

+ * 数据证明是由从 Merkle Tree 的根节点出发到目标数据节点的经过的全部节点构成的一条路径; + *

+ * + * @author huanghaiquan + * + */ + private static class MerkleProofImpl implements MerkleProof { + + /** + * 从根节点到数据节点的路径;
+ * 路径的首个元素是根节点,最后一个原始是数据节点; + */ + private MerkleNode[] path; + + private long sn; + + private MerkleProofImpl(long sn, MerkleNode[] path) { + this.sn = sn; + this.path = path; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.MerkleProof#getSN() + */ + @Override + public long getSN() { + return sn; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.MerkleProof#getLevel() + */ + @Override + public int getLevels() { + return path[0].getLevel(); + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.MerkleProof#getHash(int) + */ + @Override + public HashDigest getHash(int level) { + return path[path.length - 1 - level].getNodeHash(); + } + + @Override + public MerkleNode getNode(int level) { + return path[path.length - 1 - level]; + } + + // @Override + // public long getStartingSN(int level) { + // return path[path.length - 1 - level].startingSN; + // } + // + // @Override + // public long getDataCount(int level) { + // return path[path.length - 1 - level].dataCount; + // } + + @Override + public String toString() { + StringBuilder strPath = new StringBuilder(NODE_PREFIX); + for (int i = 0; i < path.length; i++) { + if (i > 0) { + strPath.append(PATH_SEPERATOR); + } + strPath.append(path[i].getNodeHash().toBase58()); + } + return strPath.toString(); + } + + @Override + public int hashCode() { + return Arrays.hashCode(path); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj instanceof MerkleProofImpl) { + MerkleProofImpl proof1 = (MerkleProofImpl) obj; + if (path.length != proof1.path.length) { + return false; + } + for (int i = 0; i < path.length; i++) { + if (!path[i].equals(proof1.path[i])) { + return false; + } + } + return true; + } + return false; + } + + } + + private static class ProofNodeEntry implements MerkleNode { + + private int level; + + @SuppressWarnings("unused") + private long dataCount; + + @SuppressWarnings("unused") + private long startingSN; + + private HashDigest nodeHash; + + @Override + public HashDigest getNodeHash() { + return nodeHash; + } + + @Override + public int getLevel() { + return level; + } + + @Override + public int hashCode() { + return nodeHash.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj instanceof ProofNodeEntry) { + ProofNodeEntry node1 = (ProofNodeEntry) obj; + return this.nodeHash.equals(node1.nodeHash); + } + return false; + } + } + + /** + * Abstract node of merkle tree; + * + * @author huanghaiquan + * + */ + private static abstract class AbstractMerkleNode implements BytesSerializable, MerkleNode { + + protected HashDigest nodeHash; + + protected AbstractMerkleNode parent; + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleNode#getNodeHash() + */ + @Override + public HashDigest getNodeHash() { + return nodeHash; + } + + /** + * 直接或间接从属在该节点下的数据节点的起始序号; + * + * @return + */ + protected abstract long getStartingSN(); + + /** + * 直接或间接从属在该节点下的数据节点的数量; + * + * @return + */ + protected abstract long getDataCount(); + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleNode#getLevel() + */ + @Override + public abstract int getLevel(); + + } + + /** + * 路径节点; + * + * @author huanghaiquan + * + */ + private static class PathNode extends AbstractMerkleNode { + + /** + * 当前节点采用的 hash 算法; + */ + private CryptoAlgorithm hashAlgorithm; + + /** + * 节点的起始序列号; + */ + public final long startingSN; + + /** + * 节点在 MerkleTree 中的层级; + */ + public final int level; + + /** + * 当前节点的序列号区间大小; + */ + public final long interval; + + /** + * 当前节点的直接子节点的序列号区间大小; + */ + public final long subinterval; + + /** + * nodeHash = Hash(startSn + childCount + childNodeHashes); + */ + // private HashDigest nodeHash; + + public HashDigest[] childrenHashes; + + public AbstractMerkleNode[] children; + + // private long dataCount; + private AtomicLong dataCount; + + /** + * 增加数据节点计数器;
+ * 操作将级联更新到根节点; + * + * @param newCount + */ + public void increaseDataCount(int newCount) { + dataCount.addAndGet(newCount); + if (parent != null) { + ((PathNode) parent).increaseDataCount(newCount); + } + } + + /** + * 创建一个路径节点; + * + * @param startingSN + * 路径节点表示的子树的起始序列号; + * @param level + * 路径节点的层级深度;路径节点的深度从 1 开始往上递增(数据节点作为树的深度为 0); + * @param dataCount + * 路径节点表示的子树所包含的数据节点的数量; + */ + private PathNode(CryptoAlgorithm hashAlgorithm, long startingSN, int level, long dataCount) { + this(hashAlgorithm, startingSN, level, dataCount, new HashDigest[TREE_DEGREE], null); + } + + private PathNode(long startingSN, int level, long dataCount, HashDigest[] childrenHashes, HashDigest nodeHash) { + this(nodeHash.getAlgorithm(), startingSN, level, dataCount, childrenHashes, nodeHash); + } + + private PathNode(CryptoAlgorithm defaultHashAlgorithm, long startingSN, int level, long dataCount, + HashDigest[] childrenHashes, HashDigest nodeHash) { + if (startingSN < 0) { + throw new IllegalArgumentException("The specified starting sn of PathNode is negative!"); + } + if (level < 1) { + throw new IllegalArgumentException("The specified level of PathNode is less than 1!"); + } + if (dataCount < 0) { + throw new IllegalArgumentException("The specified data count of PathNode is negative!"); + } + this.hashAlgorithm = defaultHashAlgorithm; + this.startingSN = startingSN; + this.level = level; + + this.interval = computeInterval(); + this.subinterval = computeSubinterval(); + + if (dataCount > this.interval) { + throw new IllegalArgumentException("The specified data count of PathNode exceed the upper limit!"); + } + this.dataCount = new AtomicLong(dataCount); + + this.children = new AbstractMerkleNode[TREE_DEGREE]; + + this.childrenHashes = childrenHashes; + this.nodeHash = nodeHash; + } + + public void attachChildNode(PathNode node, int index) { + if (node.level != this.level - 1) { + throw new IllegalArgumentException("The level of the attaching child node is illegal!"); + } + long expectedStartingSN = startingSN + subinterval * index; + if (expectedStartingSN != node.startingSN) { + throw new IllegalArgumentException("The starting sn of the attaching child node is illegal!"); + } + children[index] = node; + node.parent = this; + } + + public void attachChildNode(DataNode dataNode, int index) { + if (level != 1) { + throw new IllegalStateException("Cann't set data by the PathNode witch's isn't the leaf path!"); + } + children[index] = dataNode; + dataNode.parent = this; + } + + private void setData(long sn, DataNode dataNode) { + assert sn >= startingSN + && sn < startingSN + interval : "The specified sn of the DataNode exceed the upper limit!"; + + int index = (int) (sn - startingSN); + attachChildNode(dataNode, index); + } + + /** + * 计算当前节点的序列号区间大小; + * + * @return + */ + private long computeInterval() { + return power(TREE_DEGREE, level); + } + + /** + * 计算当前节点的直接子节点的序列号区间大小; + * + * @return + */ + private long computeSubinterval() { + return power(TREE_DEGREE, level - 1); + } + + /** + * 定位指定序号在此路径的子节点列表中的下标索引; + * + * @param sn + * @return + */ + private int index(long sn) { + long s = subinterval; + long offset = sn - startingSN; + return (int) ((offset - offset % s) / s); + } + + private PathNode newEmptyChild(CryptoAlgorithm hashAlgorithm, int index) { + long newStartingSN = startingSN + subinterval * index; + PathNode child = new PathNode(hashAlgorithm, newStartingSN, (byte) (level - 1), 0); + attachChildNode(child, index); + return child; + } + + public long getStartingSN() { + return startingSN; + } + + @Override + public long getDataCount() { + return dataCount.get(); + } + + @Override + public int getLevel() { + return level; + } + + /** + * 序列化当前节点的属性;包括节点哈希、数据节点计数器、子节点哈希列表; + */ + @Override + public byte[] toBytes() { + int bodySize = getBodySize(); + int hashSize = nodeHash.size(); + int totalSize = bodySize + NumberMask.TINY.MAX_HEADER_LENGTH + hashSize; + byte[] totalBytes = new byte[totalSize]; + int offset = generateBodyBytes(totalBytes); + + offset += NumberMask.TINY.writeMask(hashSize, totalBytes, offset); + System.arraycopy(nodeHash.toBytes(), 0, totalBytes, offset, hashSize); + offset += hashSize; + return totalBytes; + } + + private int getBodySize() { + int totalSize = 8 + 4 + 8;// startingSN + level + dataCount; + HashDigest h; + for (int i = 0; i < TREE_DEGREE; i++) { + h = childrenHashes[i]; + totalSize += NumberMask.TINY.getMaskLength(h == null ? 0 : h.size()); + if (h != null) { + totalSize += h.size(); + } + } + return totalSize; + } + + private int generateBodyBytes(byte[] bodyBytes) { + int offset = 0; + offset += BytesUtils.toBytes(startingSN, bodyBytes, offset); + + offset += BytesUtils.toBytes(level, bodyBytes, offset); + + offset += BytesUtils.toBytes(getDataCount(), bodyBytes, offset); + + HashDigest h; + for (int i = 0; i < TREE_DEGREE; i++) { + h = childrenHashes[i]; + if (h == null) { + // 只写入一个字节的长度头部,值为 0; + bodyBytes[offset] = 0; + offset++; + } else { + int len = h.size(); + offset += NumberMask.TINY.writeMask(len, bodyBytes, offset); + System.arraycopy(h.toBytes(), 0, bodyBytes, offset, len); + offset += len; + } + } + + return offset; + } + + /** + * 从指定的字节数组反序列化节点; + * + * @param bytes + * 字节数组;合法的输入应等同于 {@link #toBytes()} 方法的输出; + * @param checkHash + * 是否重新计算并校验节点的哈希; + * @return + */ + private static PathNode parse(byte[] bytes, boolean checkHash) { + int offset = 0; + + long startingSN = BytesUtils.toLong(bytes, offset); + offset += 8; + + int level = BytesUtils.toInt(bytes, offset); + offset += 4; + + long dataCount = BytesUtils.toLong(bytes, offset); + offset += 8; + + HashDigest[] childrenHashes = new HashDigest[TREE_DEGREE]; + byte[] h; + for (int i = 0; i < TREE_DEGREE; i++) { + int hashSize = NumberMask.TINY.resolveMaskedNumber(bytes, offset); + offset += NumberMask.TINY.getMaskLength(hashSize); + + if (hashSize == 0) { + continue; + } + h = new byte[hashSize]; + System.arraycopy(bytes, offset, h, 0, hashSize); + offset += hashSize; + childrenHashes[i] = CryptoUtils.hashCrypto().resolveHashDigest(h); + } + + int hashSize = NumberMask.TINY.resolveMaskedNumber(bytes, offset); + offset += NumberMask.TINY.getMaskLength(hashSize); + + byte[] nodeHashBytes = new byte[hashSize]; + System.arraycopy(bytes, offset, nodeHashBytes, 0, hashSize); + offset += hashSize; + + HashDigest nodeHash = CryptoUtils.hashCrypto().resolveHashDigest(nodeHashBytes); + + PathNode node = new PathNode(startingSN, level, dataCount, childrenHashes, nodeHash); + if (checkHash) { + HashDigest actualHash = node.computeBodyHash(); + if (!node.nodeHash.equals(actualHash)) { + String origHashStr = node.nodeHash.toBase58(); + String actualHashStr = actualHash.toBase58(); + throw new MerkleProofException(String.format( + "The actually hash of PathNode is not equal with it's original hash! -- [OrigHash=%s][ActualHash=%s]", + origHashStr, actualHashStr)); + } + } + + return node; + } + + /** + * 重新计算并更新当前节点的哈希; + */ + public void rehash() { + this.nodeHash = computeBodyHash(); + } + + /** + * 计算节点的 hash,但不会更新 {@link #getNodeHash()} 属性; + *

+ * + * 节点哈希: + * nodeHash = Hash(toBytes(startingSN) + toBytes(level) + toBytes(dataCount) + h1 + h2 + ... + h16); + * + * + * @param pathNode + * @return + */ + private HashDigest computeBodyHash() { + int totalSize = getBodySize(); + byte[] bodyBytes = new byte[totalSize]; + generateBodyBytes(bodyBytes); + return CryptoUtils.hash(hashAlgorithm).hash(bodyBytes); + } + + } + + /** + * 数据节点; + * + * @author huanghaiquan + * + */ + private static class DataNode extends AbstractMerkleNode implements MerkleDataNode { + + private long sn; + + private Bytes key; + + private long version; + + private byte[] dataNodeBytes; + + private DataNode(long sn, Bytes key, long version, HashDigest dataHash, byte[] dataBytes) { + this.sn = sn; + this.key = key; + this.version = version; + this.nodeHash = dataHash; + this.dataNodeBytes = dataBytes; + } + + private static DataNode newDataNode(CryptoAlgorithm hashAlgorithm, long sn, Bytes key, long version, + byte[] hashedData) { + // byte[] keyStrBytes = BytesUtils.toBytes(key); + // int maskSize = NumberMask.SHORT.getMaskLength(keyStrBytes.length); + int keySize = key.size(); + int maskSize = NumberMask.SHORT.getMaskLength(keySize); + + // int bodySize = 8 + maskSize + keyStrBytes.length + 8;// sn + key + version; + int bodySize = 8 + maskSize + keySize + 8;// sn + key + version; + byte[] bodyBytes = new byte[bodySize]; + + int offset = 0; + offset += BytesUtils.toBytes(sn, bodyBytes, 0); + + // NumberMask.SHORT.writeMask(keyStrBytes.length, bodyBytes, offset); + NumberMask.SHORT.writeMask(keySize, bodyBytes, offset); + offset += maskSize; + + // System.arraycopy(keyStrBytes, 0, bodyBytes, offset, keyStrBytes.length); + // System.arraycopy(keyStrBytes, 0, bodyBytes, offset, keyStrBytes.length); + // offset += keyStrBytes.length; + offset += key.copyTo(bodyBytes, offset, keySize); + + // TODO: version; + offset += BytesUtils.toBytes(version, bodyBytes, offset); + + byte[] dataBytes = BytesUtils.concat(bodyBytes, hashedData); + + HashDigest dataHash = CryptoUtils.hash(hashAlgorithm).hash(dataBytes); + + int hashMaskSize = NumberMask.TINY.getMaskLength(dataHash.size()); + int dataNodeSize = bodySize + hashMaskSize + dataHash.size(); + byte[] dataNodeBytes = new byte[dataNodeSize]; + + offset = 0; + System.arraycopy(bodyBytes, 0, dataNodeBytes, offset, bodySize); + offset += bodySize; + NumberMask.TINY.writeMask(dataHash.size(), dataNodeBytes, offset); + offset += hashMaskSize; + System.arraycopy(dataHash.toBytes(), 0, dataNodeBytes, offset, dataHash.size()); + + return new DataNode(sn, key, version, dataHash, dataNodeBytes); + } + + @Override + protected long getStartingSN() { + return sn; + } + + @Override + protected long getDataCount() { + return 1; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleDataNode#getLevel() + */ + @Override + public int getLevel() { + return 0; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleDataNode#getSN() + */ + @Override + public long getSN() { + return sn; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleDataNode#getKey() + */ + @Override + public Bytes getKey() { + return key; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.MerkleDataNode#getVersion() + */ + @Override + public long getVersion() { + return version; + } + + @Override + public byte[] toBytes() { + // ByteArrayOutputStream out = new ByteArrayOutputStream(); + // + // BytesUtils.writeLong(sn, out); + // + // byte[] keyBytes = BytesUtils.toBytes(key); + // BytesEncoding.write(keyBytes, NumberMask.SHORT, out); + // + // BytesUtils.writeLong(version, out); + + // int hashSize = nodeHash.size(); + // + // int totalSize = dataNodeBytes.length + NumberMask.TINY.MAX_HEADER_LENGTH + + // hashSize; + // byte[] totalBytes = new byte[totalSize]; + // + // int offset = 0; + // System.arraycopy(dataNodeBytes, 0, totalBytes, offset, dataNodeBytes.length); + // offset += dataNodeBytes.length; + + // BytesEncoding.write(nodeHash.toBytes(), NumberMask.SHORT, out); + // NumberMask.TINY.writeMask(hashSize, totalBytes, offset); + // offset += NumberMask.TINY.MAX_HEADER_LENGTH; + // + // System.arraycopy(nodeHash.toBytes(), 0, totalBytes, offset, hashSize); + + return dataNodeBytes; + } + + private static DataNode parse(byte[] bytes) { + // InputStream in = new ByteArrayInputStream(bytes); + + int offset = 0; + long sn = BytesUtils.toLong(bytes, offset); + offset += 8; + + // byte[] keyBytes = BytesEncoding.read(NumberMask.SHORT, in); + // String key = BytesUtils.toString(keyBytes); + int keySize = NumberMask.SHORT.resolveMaskedNumber(bytes, offset); + offset += NumberMask.SHORT.getMaskLength(keySize); + byte[] keyBytes = new byte[keySize]; + System.arraycopy(bytes, offset, keyBytes, 0, keySize); + offset += keySize; + // String key = BytesUtils.toString(keyBytes); + Bytes key = new Bytes(keyBytes); + + // long version = BytesUtils.readLong(in); + long version = BytesUtils.toLong(bytes, offset); + offset += 8; + + // byte[] dataHashBytes = BytesEncoding.read(NumberMask.SHORT, in); + int hashSize = NumberMask.TINY.resolveMaskedNumber(bytes, offset); + offset += NumberMask.TINY.getMaskLength(hashSize); + byte[] dataHashBytes = new byte[hashSize]; + System.arraycopy(bytes, offset, dataHashBytes, 0, hashSize); + offset += hashSize; + HashDigest dataHash = CryptoUtils.hashCrypto().resolveHashDigest(dataHashBytes); + return new DataNode(sn, key, version, dataHash, bytes); + } + + @Override + public int hashCode() { + return nodeHash.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj instanceof DataNode) { + DataNode node1 = (DataNode) obj; + return this.nodeHash.equals(node1.nodeHash); + } + return false; + } + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Node.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Node.java new file mode 100644 index 00000000..fd8df0cd --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Node.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.core; + + +public class Node { + + public Node(){ + + } + + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/OperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/OperationHandle.java new file mode 100644 index 00000000..da4e8f43 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/OperationHandle.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; + +public interface OperationHandle { + + /** + * 是否支持指定类型的操作; + * + * @param operationType + * @return + */ + boolean support(Class operationType); + + /** + * 解析和执行操作; + * + * @param op + * 操作实例; + * @param newBlockDataset + * 需要修改的新区块的数据集; + * @param requestContext + * 交易请求上下文; + * @param previousBlockDataset + * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; + */ + void process(Operation op, LedgerDataSet newBlockDataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/P2PRealm.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/P2PRealm.java new file mode 100644 index 00000000..c7480e07 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/P2PRealm.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.ledger.core; + + +/** + * @author hhq + * @version 1.0 + * @created 14-6��-2018 12:13:33 + */ +public class P2PRealm { + + public Peer m_Peer; + + public P2PRealm(){ + + } + + public void finalize() throws Throwable { + + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ParticipantCertData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ParticipantCertData.java new file mode 100644 index 00000000..62b649ce --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ParticipantCertData.java @@ -0,0 +1,64 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.ParticipantNode; + +/** + * 参与方证书数据对象; + * + * @author huanghaiquan + * + */ +public class ParticipantCertData implements ParticipantNode { + + private int id; + + private String address; + private String name; + private PubKey pubKey; + + public ParticipantCertData() { + } + + public ParticipantCertData(ParticipantNode participantNode) { + this.address = participantNode.getAddress(); + this.name = participantNode.getName(); + this.pubKey = participantNode.getPubKey(); + } + + public ParticipantCertData(String address, String name, PubKey pubKey) { + this.address = address; + this.name = name; + this.pubKey = pubKey; + } + + + + @Override + public String getAddress() { + return address; + } + + @Override + public String getName() { + return name; + } + + @Override + public PubKey getPubKey() { + return pubKey; + } + + + + public int getId() { + return id; + } + + + + public void setId(int id) { + this.id = id; + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ParticipantDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ParticipantDataSet.java new file mode 100644 index 00000000..9ada7bc9 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/ParticipantDataSet.java @@ -0,0 +1,108 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +public class ParticipantDataSet implements Transactional, MerkleProvable { + + static { + DataContractRegistry.register(ParticipantNode.class); + } + + private MerkleDataSet dataset; + + public ParticipantDataSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exPolicyStorage, + VersioningKVStorage verStorage) { + dataset = new MerkleDataSet(cryptoSetting, prefix, exPolicyStorage, verStorage); + } + + public ParticipantDataSet(HashDigest merkleRootHash, CryptoSetting cryptoSetting, String prefix, + ExPolicyKVStorage exPolicyStorage, VersioningKVStorage verStorage, boolean readonly) { + dataset = new MerkleDataSet(merkleRootHash, cryptoSetting, prefix, exPolicyStorage, verStorage, readonly); + } + + @Override + public HashDigest getRootHash() { + return dataset.getRootHash(); + } + + @Override + public MerkleProof getProof(Bytes key) { + return dataset.getProof(key); + } + + @Override + public boolean isUpdated() { + return dataset.isUpdated(); + } + + @Override + public void commit() { + dataset.commit(); + } + + @Override + public void cancel() { + dataset.cancel(); + } + + public long getParticipantCount() { + return dataset.getDataCount(); + } + + /** + * 加入新的共识参与方;
+ * 如果指定的共识参与方已经存在,则引发 {@link LedgerException} 异常; + * + * @param participant + */ + public void addConsensusParticipant(ParticipantNode participant) { + Bytes key = encodeKey(participant.getAddress()); + byte[] participantBytes = BinaryEncodingUtils.encode(participant, ParticipantNode.class); + long nv = dataset.setValue(key, participantBytes, -1); + if (nv < 0) { + throw new LedgerException("Participant already exist! --[id=" + key + "]"); + } + } + + private Bytes encodeKey(String address) { + // return id + ""; + return Bytes.fromString(address); + } + + /** + * 返回指定地址的参与方凭证; + * + *
+ * 如果不存在,则返回 null; + * + * @param address + * @return + */ + public ParticipantNode getParticipant(String address) { + Bytes key = encodeKey(address); + byte[] bytes = dataset.getValue(key); + if (bytes == null) { + return null; + } + return BinaryEncodingUtils.decode(bytes); + } + + public ParticipantNode[] getParticipants() { + byte[][] bytes = dataset.getLatestValues(0, (int)dataset.getDataCount()); + ParticipantNode[] pns = new ParticipantNode[bytes.length]; + + for (int i = 0; i < pns.length; i++) { + pns[i] = BinaryEncodingUtils.decode(bytes[i]); + } + return pns; + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Peer.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Peer.java new file mode 100644 index 00000000..2ef46c99 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Peer.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.ledger.ParticipantNode; + +/** + * @author hhq + * @version 1.0 + * @created 14-6��-2018 12:13:33 + */ +public class Peer extends Node { + + public ParticipantNode m_Participant; + + public Peer(){ + + } + + public void finalize() throws Throwable { + super.finalize(); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrefixAppender.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrefixAppender.java new file mode 100644 index 00000000..a6a68a35 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrefixAppender.java @@ -0,0 +1,119 @@ +//package com.jd.blockchain.ledger.core; +// +//import com.jd.blockchain.storage.service.ExPolicyKVStorage; +//import com.jd.blockchain.storage.service.VersioningKVEntry; +//import com.jd.blockchain.storage.service.VersioningKVStorage; +// +//public class PrefixAppender { +// +// private PrefixAppender() { +// } +// +// /** +// * Wrapper the specified {@link VersioningKVStorage} and auto prefix it's key +// * with the specified String; +// * +// * @param prefix +// * @param storage +// * @return +// */ +// public static VersioningKVStorage prefix(String prefix, VersioningKVStorage storage) { +// return new VersioningKVStoragePrefixAppender(prefix, storage); +// } +// +//// public static SimpleKVStorage prefix(String prefix, SimpleKVStorage storage) { +//// return new SimpleKVStoragePrefixAppender(prefix, storage); +//// } +// +// public static ExPolicyKVStorage prefix(String prefix, ExPolicyKVStorage storage) { +// return new ExistancePolicyKVStoragePrefixAppender(prefix, storage); +// } +// +// private static String encodePrefixKey(String prefix, String key) { +// return prefix.concat(key); +// } +// +//// private static class SimpleKVStoragePrefixAppender implements SimpleKVStorage { +//// +//// private SimpleKVStorage storage; +//// +//// private String prefix; +//// +//// private SimpleKVStoragePrefixAppender(String prefix, SimpleKVStorage dataStorage) { +//// this.prefix = prefix; +//// this.storage = dataStorage; +//// } +//// +//// @Override +//// public byte[] get(String key) { +//// return storage.get(encodePrefixKey(prefix, key)); +//// } +//// +//// @Override +//// public boolean set(String key, byte[] value) { +//// return storage.set(encodePrefixKey(prefix, key), value); +//// } +//// } +// +// private static class ExistancePolicyKVStoragePrefixAppender implements ExPolicyKVStorage { +// +// private ExPolicyKVStorage storage; +// +// private String prefix; +// +// private ExistancePolicyKVStoragePrefixAppender(String prefix, ExPolicyKVStorage dataStorage) { +// this.prefix = prefix; +// this.storage = dataStorage; +// } +// +// @Override +// public byte[] get(String key) { +// return storage.get(encodePrefixKey(prefix, key)); +// } +// +// @Override +// public boolean exist(String key) { +// return storage.exist(key); +// } +// +// @Override +// public boolean set(String key, byte[] value, ExPolicy ex) { +// return storage.set(encodePrefixKey(prefix, key), value, ex); +// } +// } +// +// private static class VersioningKVStoragePrefixAppender implements VersioningKVStorage { +// +// private VersioningKVStorage storage; +// +// private String prefix; +// +// private VersioningKVStoragePrefixAppender(String prefix, VersioningKVStorage dataStorage) { +// this.prefix = prefix; +// this.storage = dataStorage; +// } +// +// +// @Override +// public long getVersion(String key) { +// return storage.getVersion(encodePrefixKey(prefix, key)); +// } +// +// @Override +// public VersioningKVEntry getEntry(String key, long version) { +// return storage.getEntry(encodePrefixKey(prefix, key), version); +// } +// +// @Override +// public byte[] get(String key, long version) { +// return storage.get(encodePrefixKey(prefix, key), version); +// } +// +// @Override +// public long set(String key, byte[] value, long version) { +// return storage.set(encodePrefixKey(prefix, key), value, version); +// } +// +// } +// +//} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Privilege.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Privilege.java new file mode 100644 index 00000000..d5a71455 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/Privilege.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.core; + +import java.util.SortedSet; + +public interface Privilege { + + SortedSet getOpCodes(); + + long getVersion(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrivilegeDataSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrivilegeDataSet.java new file mode 100644 index 00000000..9bdc3f3b --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrivilegeDataSet.java @@ -0,0 +1,21 @@ +//package com.jd.blockchain.ledger.core; +// +//import com.jd.blockchain.crypto.hash.HashDigest; +//import com.jd.blockchain.ledger.data.DigitalSignatureBlob; +// +//import my.utils.io.ExistentialKVStorage; +//import my.utils.io.VersioningKVStorage; +// +//public class PrivilegeDataSet extends GenericMerkleDataSet { +// +// public PrivilegeDataSet(CryptoSetting setting, ExistentialKVStorage merkleTreeStorage, VersioningKVStorage dataStorage) { +// this(null, setting, merkleTreeStorage, dataStorage, false); +// } +// +// public PrivilegeDataSet(HashDigest rootHash, CryptoSetting setting, ExistentialKVStorage merkleTreeStorage, +// VersioningKVStorage dataStorage, boolean readonly) { +// super(rootHash, setting, merkleTreeStorage, dataStorage, readonly, Authorization.class, AuthorizationVO.class, +// DigitalSignatureBlob.class); +// } +// +//} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrivilegeModelSetting.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrivilegeModelSetting.java new file mode 100644 index 00000000..d5017e83 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/PrivilegeModelSetting.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.ledger.core; + +public interface PrivilegeModelSetting { + + long getLatestVersion(); + + Privilege getPrivilege(long version); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SNGenerator.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SNGenerator.java new file mode 100644 index 00000000..50ae5c10 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SNGenerator.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.utils.Bytes; + +/** + * 序列号生成器; + * + * @author huanghaiquan + * + */ +public interface SNGenerator { + + long generate(Bytes key); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SmartContract.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SmartContract.java new file mode 100644 index 00000000..80fa27ba --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/SmartContract.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.binaryproto.DataContract; + +@DataContract(code=0x01) +public interface SmartContract { + + String getName(); + + String getDescription(); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/TransactionRequestContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/TransactionRequestContext.java new file mode 100644 index 00000000..324c4e0e --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/TransactionRequestContext.java @@ -0,0 +1,70 @@ +package com.jd.blockchain.ledger.core; + +import java.util.Set; + +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.utils.Bytes; + +/** + * 交易请求上下文; + * + * @author huanghaiquan + * + */ +public interface TransactionRequestContext { + + /** + * 交易请求; + * + * @return + */ + TransactionRequest getRequest(); + + /** + * 签名发起请求的终端用户的地址列表; + * + * @return + */ + Set getEndpoints(); + + /** + * 签名发起请求的节点的地址列表; + * + * @return + */ + Set getNodes(); + + /** + * 请求的终端发起人列表中是否包含指定地址的终端用户; + * + * @param address + * @return + */ + boolean containsEndpoint(Bytes address); + + /** + * 请求的经手节点列表中是否包含指定地址的节点; + * + * @param address + * @return + */ + boolean containsNode(Bytes address); + + /** + * 获取交易请求中指定地址的终端的签名; + * + * @param address + * @return + */ + DigitalSignature getEndpointSignature(Bytes address); + + /** + * 获取交易请求中指定地址的节点的签名; + * + * @param address + * @return + */ + DigitalSignature getNodeSignature(Bytes address); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/TransactionSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/TransactionSet.java new file mode 100644 index 00000000..f28655e3 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/TransactionSet.java @@ -0,0 +1,171 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +public class TransactionSet implements Transactional, MerkleProvable { + + static { + DataContractRegistry.register(LedgerTransaction.class); + } + + private static final String TX_STATE_PREFIX = "STA" + LedgerConsts.KEY_SEPERATOR; + + private final Bytes txStatePrefix; + + private MerkleDataSet txSet; + + public LedgerTransaction[] getTxs(int fromIndex, int count) { + if (count > LedgerConsts.MAX_LIST_COUNT) { + throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); + } + byte[][] results = getValuesByIndex(fromIndex, count); + LedgerTransaction[] ledgerTransactions = new LedgerTransaction[results.length]; + + for (int i = 0; i < results.length; i++) { + ledgerTransactions[i] = deserialize(results[i]); + } + return ledgerTransactions; + } + + public byte[][] getValuesByIndex(int fromIndex, int count) { + byte[][] values = new byte[count][]; + for (int i = 0; i < count; i++) { + values[i] = txSet.getValuesAtIndex(fromIndex * 2); + fromIndex++; + } + return values; + } + + @Override + public HashDigest getRootHash() { + return txSet.getRootHash(); + } + + @Override + public MerkleProof getProof(Bytes key) { + return txSet.getProof(key); + } + + public long getTotalCount() { + // 每写入一个交易,同时写入交易内容Hash与交易结果的索引,因此交易记录数为集合总记录数除以 2; + return txSet.getDataCount() / 2; + } + + /** + * Create a new TransactionSet which can be added transaction; + * + * @param setting + * @param merkleTreeStorage + * @param dataStorage + */ + public TransactionSet(CryptoSetting setting, String keyPrefix, ExPolicyKVStorage merkleTreeStorage, + VersioningKVStorage dataStorage) { + this.txStatePrefix = Bytes.fromString(keyPrefix + TX_STATE_PREFIX); + this.txSet = new MerkleDataSet(setting, keyPrefix, merkleTreeStorage, dataStorage); + } + + /** + * Create TransactionSet which is readonly to the history transactions; + * + * @param setting + * @param merkleTreeStorage + * @param dataStorage + */ + public TransactionSet(HashDigest txRootHash, CryptoSetting setting, String keyPrefix, + ExPolicyKVStorage merkleTreeStorage, VersioningKVStorage dataStorage, boolean readonly) { + this.txStatePrefix = Bytes.fromString(keyPrefix + TX_STATE_PREFIX); + this.txSet = new MerkleDataSet(txRootHash, setting, keyPrefix, merkleTreeStorage, dataStorage, readonly); + } + + /** + * @param txRequest + * @param result + */ + public void add(LedgerTransaction tx) { + // TODO: 优化对交易内存存储的优化,应对大数据量单交易,共享操作的“写集”与实际写入账户的KV版本; + // 序列化交易内容; + byte[] txBytes = serialize(tx); + // 以交易内容的 hash 为 key; + // String key = tx.getTransactionContent().getHash().toBase58(); + Bytes key = new Bytes(tx.getTransactionContent().getHash().toBytes()); + // 交易只有唯一的版本; + long v = txSet.setValue(key, txBytes, -1); + if (v < 0) { + throw new LedgerException("Transaction is persisted repeatly! --[" + key + "]"); + } + // 以交易内容的hash值为key,单独记录交易结果的索引,以便快速查询交易结果; + Bytes resultKey = encodeTxStateKey(key); + v = txSet.setValue(resultKey, new byte[] { tx.getExecutionState().CODE }, -1); + if (v < 0) { + throw new LedgerException("Transaction result is persisted repeatly! --[" + key + "]"); + } + } + + /** + * @param txContentHash + * Base58 编码的交易内容的哈希; + * @return + */ + public LedgerTransaction get(HashDigest txContentHash) { + // transaction has only one version; + Bytes key = new Bytes(txContentHash.toBytes()); + // byte[] txBytes = txSet.getValue(txContentHash.toBase58(), 0); + byte[] txBytes = txSet.getValue(key, 0); + if (txBytes == null) { + return null; + } + LedgerTransaction tx = deserialize(txBytes); + return tx; + } + + public TransactionState getTxState(HashDigest txContentHash) { + Bytes resultKey = encodeTxStateKey(txContentHash); + // transaction has only one version; + byte[] bytes = txSet.getValue(resultKey, 0); + if (bytes == null || bytes.length == 0) { + return null; + } + return TransactionState.valueOf(bytes[0]); + } + + private Bytes encodeTxStateKey(Bytes txContentHash) { + return new Bytes(txStatePrefix, txContentHash); + } + + private LedgerTransaction deserialize(byte[] txBytes) { + return BinaryEncodingUtils.decode(txBytes); + } + + private byte[] serialize(LedgerTransaction txRequest) { + return BinaryEncodingUtils.encode(txRequest, LedgerTransaction.class); + } + + public boolean isReadonly() { + return txSet.isReadonly(); + } + + @Override + public boolean isUpdated() { + return txSet.isUpdated(); + } + + @Override + public void commit() { + txSet.commit(); + } + + @Override + public void cancel() { + txSet.cancel(); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/UserAccount.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/UserAccount.java new file mode 100644 index 00000000..6db9fcee --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/UserAccount.java @@ -0,0 +1,82 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.UserInfo; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 用户账户; + * + * @author huanghaiquan + * + */ +public class UserAccount implements UserInfo { + + private static final Bytes USER_INFO_PREFIX = Bytes.fromString("PROP" + LedgerConsts.KEY_SEPERATOR); + + private static final Bytes DATA_PUB_KEY = Bytes.fromString("DATA-PUBKEY"); + + private BaseAccount baseAccount; + + @Override + public Bytes getAddress() { + return baseAccount.getAddress(); + } + + @Override + public PubKey getPubKey() { + return baseAccount.getPubKey(); + } + + @Override + public HashDigest getRootHash() { + return baseAccount.getRootHash(); + } + + public UserAccount(BaseAccount baseAccount) { + this.baseAccount = baseAccount; + } + + public PubKey getDataPubKey() { + byte[] pkBytes = baseAccount.getBytes(DATA_PUB_KEY); + if (pkBytes == null) { + return null; + } + return CryptoUtils.crypto().asymmetricCryptography().resolvePubKey(pkBytes); + } + + public long setDataPubKey(PubKey pubKey) { + byte[] pkBytes = pubKey.toBytes(); + return baseAccount.setBytes(DATA_PUB_KEY, pkBytes, -1); + } + + public long setDataPubKey(PubKey pubKey, long version) { + byte[] pkBytes = pubKey.toBytes(); + return baseAccount.setBytes(DATA_PUB_KEY, pkBytes, version); + } + + public long setProperty(String key, String value, long version) { + return setProperty(Bytes.fromString(key), value, version); + } + + public long setProperty(Bytes key, String value, long version) { + return baseAccount.setBytes(encodePropertyKey(key), BytesUtils.toBytes(value), version); + } + + public String getProperty(Bytes key) { + return BytesUtils.toString(baseAccount.getBytes(encodePropertyKey(key))); + } + + public String getProperty(Bytes key, long version) { + return BytesUtils.toString(baseAccount.getBytes(encodePropertyKey(key), version)); + } + + private Bytes encodePropertyKey(Bytes key) { +// return key.concatTo(USER_INFO_PREFIX); + return USER_INFO_PREFIX.concat(key); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/UserAccountSet.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/UserAccountSet.java new file mode 100644 index 00000000..08364fe7 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/UserAccountSet.java @@ -0,0 +1,110 @@ +package com.jd.blockchain.ledger.core; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +/** + * @author huanghaiquan + * + */ +public class UserAccountSet implements Transactional, MerkleProvable { + + private AccountSet accountSet; + + public UserAccountSet(CryptoSetting cryptoSetting, String keyPrefix, ExPolicyKVStorage simpleStorage, + VersioningKVStorage versioningStorage, AccountAccessPolicy accessPolicy) { + accountSet = new AccountSet(cryptoSetting, keyPrefix, simpleStorage, versioningStorage, accessPolicy); + } + + public UserAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, + AccountAccessPolicy accessPolicy) { + accountSet = new AccountSet(dataRootHash, cryptoSetting, keyPrefix, exStorage, verStorage, readonly, + accessPolicy); + } + + public AccountHeader[] getAccounts(int fromIndex, int count) { + return accountSet.getAccounts(fromIndex,count); + } + + /** + * 返回用户总数; + * + * @return + */ + public long getTotalCount() { + return accountSet.getTotalCount(); + } + + public boolean isReadonly() { + return accountSet.isReadonly(); + } + + @Override + public HashDigest getRootHash() { + return accountSet.getRootHash(); + } + + @Override + public MerkleProof getProof(Bytes key) { + return accountSet.getProof(key); + } + + public UserAccount getUser(String address) { + return getUser(Bytes.fromBase58(address)); + } + + public UserAccount getUser(Bytes address) { + BaseAccount baseAccount = accountSet.getAccount(address); + return new UserAccount(baseAccount); + } + + public boolean contains(Bytes address) { + return accountSet.contains(address); + } + + public UserAccount getUser(Bytes address, long version) { + BaseAccount baseAccount = accountSet.getAccount(address, version); + return new UserAccount(baseAccount); + } + + /** + * 注册一个新用户;
+ * + * 如果用户已经存在,则会引发 {@link LedgerException} 异常;
+ * + * 如果指定的地址和公钥不匹配,则会引发 {@link LedgerException} 异常; + * + * @param address + * 区块链地址; + * @param pubKey + * 公钥; + * @return 注册成功的用户对象; + */ + public UserAccount register(Bytes address, PubKey pubKey) { + BaseAccount baseAccount = accountSet.register(address, pubKey); + return new UserAccount(baseAccount); + } + + @Override + public boolean isUpdated() { + return accountSet.isUpdated(); + } + + @Override + public void commit() { + accountSet.commit(); + } + + @Override + public void cancel() { + accountSet.cancel(); + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/DefaultOperationHandleRegisteration.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/DefaultOperationHandleRegisteration.java new file mode 100644 index 00000000..c5ea9bdc --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/DefaultOperationHandleRegisteration.java @@ -0,0 +1,74 @@ +package com.jd.blockchain.ledger.core.impl; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.PostConstruct; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.LedgerException; +import com.jd.blockchain.ledger.core.OperationHandle; +import com.jd.blockchain.ledger.core.impl.handles.ContractCodeDeployOperationHandle; +import com.jd.blockchain.ledger.core.impl.handles.ContractEventSendOperationHandle; +import com.jd.blockchain.ledger.core.impl.handles.DataAccountKVSetOperationHandle; +import com.jd.blockchain.ledger.core.impl.handles.DataAccountRegisterOperationHandle; +import com.jd.blockchain.ledger.core.impl.handles.UserRegisterOperationHandle; + +@Component +public class DefaultOperationHandleRegisteration implements OperationHandleRegisteration { + + private List opHandles = new ArrayList<>(); + + +// private UserRegisterOperationHandle userRegHandle; +// +// private DataAccountRegisterOperationHandle dataAccRegHandle; +// +// private DataAccountKVSetOperationHandle dataAccKVSetHandle; +// +// private ContractCodeDeployOperationHandle contractDplHandle; +// +// private ContractEventSendOperationHandle contractEvtSendHandle; + + public DefaultOperationHandleRegisteration() { + initDefaultHandles(); + } + + /** + * 针对不采用bean依赖注入的方式来处理; + */ + private void initDefaultHandles(){ + opHandles.add(new DataAccountKVSetOperationHandle()); + opHandles.add(new DataAccountRegisterOperationHandle()); + opHandles.add(new UserRegisterOperationHandle()); + opHandles.add(new ContractCodeDeployOperationHandle()); + opHandles.add(new ContractEventSendOperationHandle()); + } + +// @PostConstruct +// private void init() { +// opHandles.add(dataAccKVSetHandle); +// opHandles.add(dataAccRegHandle); +// opHandles.add(userRegHandle); +// opHandles.add(contractDplHandle); +// opHandles.add(contractEvtSendHandle); +// } + + /* (non-Javadoc) + * @see com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration#getHandle(java.lang.Class) + */ + @Override + public OperationHandle getHandle(Class operationType) { + for (OperationHandle handle : opHandles) { + if (handle.support(operationType)) { + return handle; + } + } + throw new LedgerException("Unsupported operation type[" + operationType.getName() + "]!"); + } + + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/GenesisLedgerStorageProxy.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/GenesisLedgerStorageProxy.java new file mode 100644 index 00000000..5682c50a --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/GenesisLedgerStorageProxy.java @@ -0,0 +1,67 @@ +//package com.jd.blockchain.ledger.core.impl; +// +//import com.jd.blockchain.storage.service.ExPolicyKVStorage; +//import com.jd.blockchain.storage.service.VersioningKVEntry; +//import com.jd.blockchain.storage.service.VersioningKVStorage; +//import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +// +///** +// * 账本存储代理;
+// * +// * @author huanghaiquan +// * +// */ +//class GenesisLedgerStorageProxy implements VersioningKVStorage, ExPolicyKVStorage { +// +// private VersioningKVStorage versioningStorage; +// +// private ExPolicyKVStorage exPolicyStorage; +// +// @Override +// public long getVersion(String key) { +// // Storage of genesis ledger is totally empty; +// return -1; +// } +// +// @Override +// public VersioningKVEntry getEntry(String key, long version) { +// return null; +// } +// +// @Override +// public byte[] get(String key, long version) { +// return null; +// } +// +// @Override +// public long set(String key, byte[] value, long version) { +// if (versioningStorage == null) { +// throw new IllegalStateException("The persistent storage of ledger is not ready!"); +// } +// return versioningStorage.set(key, value, version); +// } +// +// @Override +// public byte[] get(String key) { +// // Storage of genesis ledger is totally empty; +// return null; +// } +// +// @Override +// public boolean exist(String key) { +// return false; +// } +// +// @Override +// public boolean set(String key, byte[] value, ExPolicy ex) { +// if (exPolicyStorage == null) { +// throw new IllegalStateException("The persistent storage of ledger is not ready!"); +// } +// return exPolicyStorage.set(key, value, ex); +// } +// +// public void setPersistentStorage(ExPolicyKVStorage exPolicyStorage, VersioningKVStorage persistentStorage) { +// this.exPolicyStorage = exPolicyStorage; +// this.versioningStorage = persistentStorage; +// } +//} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java new file mode 100644 index 00000000..972cb70c --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerBlockData.java @@ -0,0 +1,163 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; + +public class LedgerBlockData implements LedgerBlock { + + static { + DataContractRegistry.register(LedgerBlock.class); + } + + private HashDigest hash; + + private long height; + + private HashDigest ledgerHash; + + private HashDigest previousHash; + + private HashDigest adminAccountHash; + + private HashDigest userAccountSetHash; + + // private HashDigest userPrivilegeHash; + + private HashDigest dataAccountSetHash; + + // private HashDigest dataPrivilegeHash; + + private HashDigest contractAccountSetHash; + + // private HashDigest contractPrivilegeHash; + + private HashDigest transactionSetHash; + + public LedgerBlockData() { + } + + public LedgerBlockData(LedgerBlock block) { + this.hash = block.getHash(); + this.height = block.getHeight(); + this.ledgerHash = block.getLedgerHash(); + this.previousHash = block.getPreviousHash(); + this.adminAccountHash = block.getAdminAccountHash(); + this.userAccountSetHash = block.getUserAccountSetHash(); + this.dataAccountSetHash = block.getDataAccountSetHash(); + this.contractAccountSetHash = block.getContractAccountSetHash(); + this.transactionSetHash = block.getTransactionSetHash(); + } + + public void setAdminAccountHash(HashDigest adminAccountHash) { + this.adminAccountHash = adminAccountHash; + } + + public void setUserAccountSetHash(HashDigest userAccountSetHash) { + this.userAccountSetHash = userAccountSetHash; + } + + // public void setUserPrivilegeHash(HashDigest userPrivilegeHash) { + // this.userPrivilegeHash = userPrivilegeHash; + // } + + public void setDataAccountSetHash(HashDigest dataAccountSetHash) { + this.dataAccountSetHash = dataAccountSetHash; + } + + // public void setDataPrivilegeHash(HashDigest dataPrivilegeHash) { + // this.dataPrivilegeHash = dataPrivilegeHash; + // } + + public void setContractAccountSetHash(HashDigest contractAccountSetHash) { + this.contractAccountSetHash = contractAccountSetHash; + } + + // public void setContractPrivilegeHash(HashDigest contractPrivilegeHash) { + // this.contractPrivilegeHash = contractPrivilegeHash; + // } + + public void setTransactionSetHash(HashDigest transactionSetHash) { + this.transactionSetHash = transactionSetHash; + } + + @DConstructor(name = "LedgerBlockData") + public LedgerBlockData(@FieldSetter(name = "getHeight", type = "long") long height, + @FieldSetter(name = "getLedgerHash", type = "HashDigest") HashDigest ledgerHash, + @FieldSetter(name = "getPreviousHash", type = "HashDigest") HashDigest previousHash) { + this.height = height; + this.ledgerHash = ledgerHash; + this.previousHash = previousHash; + } + + @Override + public HashDigest getHash() { + return hash; + } + + @Override + public HashDigest getPreviousHash() { + return previousHash; + } + + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + + @Override + public long getHeight() { + return height; + } + + @Override + public HashDigest getAdminAccountHash() { + return adminAccountHash; + } + + @Override + public HashDigest getUserAccountSetHash() { + return userAccountSetHash; + } + + // @Override + // public HashDigest getUserPrivilegeHash() { + // return userPrivilegeHash; + // } + + @Override + public HashDigest getDataAccountSetHash() { + return dataAccountSetHash; + } + + // @Override + // public HashDigest getDataPrivilegeHash() { + // return dataPrivilegeHash; + // } + + @Override + public HashDigest getContractAccountSetHash() { + return contractAccountSetHash; + } + + // @Override + // public HashDigest getContractPrivilegeHash() { + // return contractPrivilegeHash; + // } + + @Override + public HashDigest getTransactionSetHash() { + return transactionSetHash; + } + + public void setHash(HashDigest blockHash) { + this.hash = blockHash; + } + + public void setLedgerHash(HashDigest ledgerHash) { + this.ledgerHash = ledgerHash; + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerDataSetImpl.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerDataSetImpl.java new file mode 100644 index 00000000..4ec9d657 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerDataSetImpl.java @@ -0,0 +1,92 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.utils.Transactional; + +public class LedgerDataSetImpl implements LedgerDataSet, Transactional { + + private LedgerAdminAccount adminAccount; + + private UserAccountSet userAccountSet; + + private DataAccountSet dataAccountSet; + + private ContractAccountSet contractAccountSet; + + private boolean readonly; + + + /** + * Create new block; + * @param adminAccount + * @param userAccountSet + * @param dataAccountSet + * @param contractAccountSet + * @param readonly + */ + public LedgerDataSetImpl(LedgerAdminAccount adminAccount, + UserAccountSet userAccountSet, DataAccountSet dataAccountSet, ContractAccountSet contractAccountSet, + boolean readonly) { + this.adminAccount = adminAccount; + this.userAccountSet = userAccountSet; + this.dataAccountSet = dataAccountSet; + this.contractAccountSet = contractAccountSet; + + this.readonly = readonly; + } + + @Override + public LedgerAdminAccount getAdminAccount() { + return adminAccount; + } + + @Override + public UserAccountSet getUserAccountSet() { + return userAccountSet; + } + + @Override + public DataAccountSet getDataAccountSet() { + return dataAccountSet; + } + + @Override + public ContractAccountSet getContractAccountSet() { + return contractAccountSet; + } + + @Override + public boolean isUpdated() { + return adminAccount.isUpdated() || userAccountSet.isUpdated() || dataAccountSet.isUpdated() + || contractAccountSet.isUpdated(); + } + + @Override + public void commit() { + if (readonly) { + throw new IllegalStateException("Readonly ledger dataset which cann't been committed!"); + } + if (!isUpdated()) { + return; + } + + adminAccount.commit(); + userAccountSet.commit(); + dataAccountSet.commit(); + contractAccountSet.commit(); + } + + @Override + public void cancel() { + adminAccount.cancel(); + userAccountSet.cancel(); + dataAccountSet.cancel(); + contractAccountSet.cancel(); + } + + @Override + public boolean isReadonly() { + return readonly; + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerInitializer.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerInitializer.java new file mode 100644 index 00000000..78fec379 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerInitializer.java @@ -0,0 +1,85 @@ +//package com.jd.blockchain.ledger.core.impl; +// +//import com.jd.blockchain.crypto.hash.HashDigest; +//import com.jd.blockchain.ledger.TransactionState; +//import com.jd.blockchain.ledger.LedgerBlock; +//import com.jd.blockchain.ledger.LedgerTransaction; +//import com.jd.blockchain.ledger.TransactionRequest; +//import com.jd.blockchain.ledger.core.LedgerDataSet; +//import com.jd.blockchain.ledger.core.LedgerEditor; +//import com.jd.blockchain.ledger.core.LedgerManage; +//import com.jd.blockchain.ledger.core.LedgerTransactionContext; +//import com.jd.blockchain.ledger.core.PrefixAppender; +//import com.jd.blockchain.storage.service.ExPolicyKVStorage; +//import com.jd.blockchain.storage.service.KVStorageService; +//import com.jd.blockchain.storage.service.VersioningKVStorage; +//import com.jd.blockchain.storage.service.utils.BufferedKVStorage; +// +///** +// * 账本初始化;
+// * +// * 初始生成账本时,所有的KV数据先缓冲写入到内存中,待计算得到账本 hash 之后,再重写入到与账本hash相关的持久化存储; +// * +// * @author huanghaiquan +// * +// */ +//class LedgerInitializer implements LedgerEditor { +// +// private KVStorageService baseStorage; +// +// private GenesisLedgerStorageProxy ledgerStorageProxy; +// +// private BufferedKVStorage genesisBufferedStorage; +// +// private LedgerEditor genesisBlockEditor; +// +// private LedgerBlock genesisBlock; +// +// private LedgerManage ledgerManager; +// +// LedgerInitializer(LedgerEditor genesisBlockEditor, BufferedKVStorage bufferedStorage, +// GenesisLedgerStorageProxy ledgerStorageProxy, KVStorageService kvStorage, LedgerManage ledgerManager) { +// this.genesisBlockEditor = genesisBlockEditor; +// this.genesisBufferedStorage = bufferedStorage; +// this.ledgerStorageProxy = ledgerStorageProxy; +// this.baseStorage = kvStorage; +// +// this.ledgerManager = ledgerManager; +// } +// +// @Override +// public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { +// return genesisBlockEditor.newTransaction(txRequest); +// } +// +// @Override +// public LedgerBlock prepare() { +// // create genesis block; +// genesisBlock = genesisBlockEditor.prepare(); +// +// return genesisBlock; +// } +// +// @Override +// public void commit() { +// // commit data of editor; it will flush data to genesisBufferedStorage; +// genesisBlockEditor.commit(); +// +// // redirect persistence to storage which created for this new ledger with ledger hash; +// HashDigest ledgerHash = genesisBlock.getHash(); +// String ledgerPrefix =LedgerManager.getLedgerStoragePrefix(ledgerHash); +// ExPolicyKVStorage ledgerExStorage = PrefixAppender.prefix(ledgerPrefix, baseStorage.getExPolicyKVStorage()); +// VersioningKVStorage ledgerVerStorage = PrefixAppender.prefix(ledgerPrefix, baseStorage.getVersioningKVStorage()); +// +// // ready to persistent; +// ledgerStorageProxy.setPersistentStorage(ledgerExStorage, ledgerVerStorage); +// +// // flush output; +// genesisBufferedStorage.flush(); +// } +// +// @Override +// public void cancel() { +// genesisBlockEditor.cancel(); +// } +//} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerManager.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerManager.java new file mode 100644 index 00000000..8f305f0e --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerManager.java @@ -0,0 +1,119 @@ +package com.jd.blockchain.ledger.core.impl; + +import java.util.HashMap; +import java.util.Map; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.core.LedgerConsts; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.KVStorageService; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.codec.Base58Utils; + +/** + * 账本管理器; + * + * @author huanghaiquan + * + */ +public class LedgerManager implements LedgerManage { + + private static final String LEDGER_PREFIX = "LDG://"; + + // @Autowired + // private ExistentialKVStorage exPolicyStorage; + // + // @Autowired + // private VersioningKVStorage versioningStorage; + + // private PrivilegeModelSetting privilegeModel = new PrivilegeModelConfig(); + + private Map ledgers = new HashMap<>(); + + @Override + public HashDigest[] getLedgerHashs() { + return ledgers.keySet().toArray(new HashDigest[ledgers.size()]); + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.LedgerManager#getLedger(com.jd.blockchain. + * crypto.hash.HashDigest) + */ + @Override + public LedgerRepository getLedger(HashDigest ledgerHash) { + LedgerRepositoryContext ledgerCtx = ledgers.get(ledgerHash); + if (ledgerCtx == null) { + return null; + } + + return ledgerCtx.ledgerRepo; + } + + @Override + public LedgerRepository register(HashDigest ledgerHash, KVStorageService storageService) { + VersioningKVStorage ledgerVersioningStorage = storageService.getVersioningKVStorage(); + ExPolicyKVStorage ledgerExPolicyStorage = storageService.getExPolicyKVStorage(); + LedgerRepository ledgerRepo = new LedgerRepositoryImpl(ledgerHash, LEDGER_PREFIX, ledgerExPolicyStorage, + ledgerVersioningStorage); + + LedgerRepositoryContext ledgerCtx = new LedgerRepositoryContext(); + ledgerCtx.ledgerRepo = ledgerRepo; + ledgerCtx.storageService = storageService; + ledgers.put(ledgerHash, ledgerCtx); + return ledgerRepo; + } + + @Override + public void unregister(HashDigest ledgerHash) { + LedgerRepositoryContext ledgerCtx = ledgers.get(ledgerHash); + if (ledgerCtx != null) { + ledgerCtx.ledgerRepo.close(); + ledgers.remove(ledgerHash); + ledgerCtx.ledgerRepo = null; + ledgerCtx.storageService = null; + } + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.LedgerManager#newLedger(com.jd.blockchain. + * ledger.core.ConsensusConfig, com.jd.blockchain.ledger.core.CryptoConfig) + */ + @Override + public LedgerEditor newLedger(LedgerInitSetting initSetting, KVStorageService storageService) { + // GenesisLedgerStorageProxy genesisStorageProxy = new + // GenesisLedgerStorageProxy(); + // BufferedKVStorage bufferedStorage = new + // BufferedKVStorage(genesisStorageProxy, genesisStorageProxy, false); + + // LedgerEditor genesisBlockEditor = + // LedgerTransactionalEditor.createEditor(initSetting, + // bufferedStorage, bufferedStorage); + + // return new LedgerInitializer(genesisBlockEditor, bufferedStorage, + // genesisStorageProxy, storageService, this); + + LedgerEditor genesisBlockEditor = LedgerTransactionalEditor.createEditor(initSetting, LEDGER_PREFIX, + storageService.getExPolicyKVStorage(), storageService.getVersioningKVStorage()); + return genesisBlockEditor; + } + + static String getLedgerStoragePrefix(HashDigest ledgerHash) { + String base58LedgerHash = Base58Utils.encode(ledgerHash.toBytes()); + return LEDGER_PREFIX + base58LedgerHash + LedgerConsts.KEY_SEPERATOR; + } + + private static class LedgerRepositoryContext { + + private LedgerRepository ledgerRepo; + + private KVStorageService storageService; + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java new file mode 100644 index 00000000..82e1ec23 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerQueryService.java @@ -0,0 +1,325 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.sdk.BlockchainQueryService; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.QueryUtil; +import com.jd.blockchain.utils.ValueType; + +public class LedgerQueryService implements BlockchainQueryService { + + private LedgerService ledgerService; + + public LedgerQueryService(LedgerService ledgerService) { + this.ledgerService = ledgerService; + } + + @Override + public HashDigest[] getLedgerHashs() { + return ledgerService.getLedgerHashs(); + } + + @Override + public LedgerInfo getLedger(HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerInfo ledgerInfo =new LedgerInfo(); + ledgerInfo.setHash(ledger.getHash()); + ledgerInfo.setLatestBlockHash(ledger.getLatestBlockHash()); + ledgerInfo.setLatestBlockHeight(ledger.getLatestBlockHeight()); + return ledgerInfo; + } + + @Override + public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + LedgerAdministration administration = ledger.getAdminAccount(block); + return administration.getParticipants(); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + return ledger.getBlock(height); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + return ledger.getBlock(blockHash); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.getTotalCount(); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.getTotalCount(); + } + + @Override + public long getTransactionTotalCount(HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.getTotalCount(); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getTotalCount(); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getTotalCount(); + } + + @Override + public long getDataAccountTotalCount(HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getTotalCount(); + } + + @Override + public long getUserCount(HashDigest ledgerHash, long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getTotalCount(); + } + + @Override + public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getTotalCount(); + } + + @Override + public long getUserTotalCount(HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getTotalCount(); + } + + @Override + public long getContractCount(HashDigest ledgerHash, long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getTotalCount(); + } + + @Override + public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getTotalCount(); + } + + @Override + public long getContractTotalCount(HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getTotalCount(); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock ledgerBlock = ledger.getBlock(height); + TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); + int lastHeightTxTotalNums = 0; + + if (height > 0) { + lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); + } + + int currentHeightTxTotalNums = (int)ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); + //取当前高度的增量交易数,在增量交易里进行查找 + int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; +// +// if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { +// fromIndex = 0; +// } +// if (count == -1) { +// fromIndex = 0; +// count = currentHeightTxNums; +// } +// if (count > currentHeightTxNums) { +// count = currentHeightTxNums - fromIndex; +// } + int indexAndCount [] = QueryUtil.calFromIndexAndCount(fromIndex,count,currentHeightTxNums); + return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0] , indexAndCount[1]); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock ledgerBlock = ledger.getBlock(blockHash); + long height = ledgerBlock.getHeight(); + TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); + int lastHeightTxTotalNums = 0; + + if (height > 0) { + lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); + } + + int currentHeightTxTotalNums = (int)ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); + //取当前块hash的增量交易数,在增量交易里进行查找 + int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; + +// if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { +// fromIndex = 0; +// } +// if (count == -1) { +// fromIndex = 0; +// count = currentHeightTxNums; +// } +// if (count > currentHeightTxNums) { +// count = currentHeightTxNums - fromIndex; +// } + int indexAndCount [] = QueryUtil.calFromIndexAndCount(fromIndex,count,currentHeightTxNums); + return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0] , indexAndCount[1]); + } + + @Override + public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.get(contentHash); + } + + @Override + public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.getTxState(contentHash); + } + + @Override + public UserInfo getUser(HashDigest ledgerHash, String address) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getUser(address); + + } + + @Override + public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { + if (keys == null || keys.length == 0) { + return null; + } + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + + KVDataEntry[] entries = new KVDataEntry[keys.length]; + long ver; + for (int i = 0; i < entries.length; i++) { + ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); + if (ver < 0) { + entries[i] = new KVDataObject(keys[i], -1, ValueType.NIL, null); + }else { + byte[] value = dataAccount.getBytes(Bytes.fromString(keys[i]), ver); + BytesValue decodeData = BinaryEncodingUtils.decode(value); + entries[i] = new KVDataObject(keys[i], ver, ValueType.valueOf(decodeData.getType().CODE), decodeData.getValue().toBytes()); + } + } + + return entries; + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { + + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + + return dataAccount.getDataEntries(fromIndex, count); + } + + @Override + public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { + + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + + return dataAccount.getDataEntriesTotalCount(); + } + + @Override + public AccountHeader getContract(HashDigest ledgerHash, String address) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getContract(Bytes.fromBase58(address)); + } + + @Override + public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) userAccountSet.getTotalCount()); + return userAccountSet.getAccounts(pages[0], pages[1]); + } + + @Override + public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)dataAccountSet.getTotalCount()); + return dataAccountSet.getAccounts(pages[0],pages[1]); + } + + @Override + public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)contractAccountSet.getTotalCount()); + return contractAccountSet.getAccounts(pages[0],pages[1]); + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java new file mode 100644 index 00000000..5128d971 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerRepositoryImpl.java @@ -0,0 +1,645 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockBody; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerDataSnapshot; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.core.AccountAccessPolicy; +import com.jd.blockchain.ledger.core.ContractAccountSet; +import com.jd.blockchain.ledger.core.DataAccountSet; +import com.jd.blockchain.ledger.core.LedgerAdminAccount; +import com.jd.blockchain.ledger.core.LedgerAdministration; +import com.jd.blockchain.ledger.core.LedgerConsts; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerException; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.LedgerSetting; +import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.ledger.core.TransactionSet; +import com.jd.blockchain.ledger.core.UserAccountSet; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; + +/** + * 账本的存储结构:
+ * + * 1、账本数据以版本化KV存储({@link VersioningKVStorage})为基础;
+ * + * 2、以账本hash为 key,保存账本的每一个区块的hash,对应的版本序号恰好一致地表示了区块高度;
+ * + * 3、区块数据以区块 hash 加上特定前缀({@link #BLOCK_PREFIX}) 构成 key + * 进行保存,每个区块只有唯一个版本,在存储时会进行版本唯一性校验;
+ * + * @author huanghaiquan + * + */ +public class LedgerRepositoryImpl implements LedgerRepository { + + private static final Bytes LEDGER_PREFIX = Bytes.fromString("IDX" + LedgerConsts.KEY_SEPERATOR); + + private static final Bytes BLOCK_PREFIX = Bytes.fromString("BLK" + LedgerConsts.KEY_SEPERATOR); + + private static final Bytes USER_SET_PREFIX = Bytes.fromString("USRS" + LedgerConsts.KEY_SEPERATOR); + + private static final Bytes DATA_SET_PREFIX = Bytes.fromString("DATS" + LedgerConsts.KEY_SEPERATOR); + + private static final Bytes CONTRACT_SET_PREFIX = Bytes.fromString("CTRS" + LedgerConsts.KEY_SEPERATOR); + + private static final Bytes TRANSACTION_SET_PREFIX = Bytes.fromString("TXS" + LedgerConsts.KEY_SEPERATOR); + + private static final AccountAccessPolicy DEFAULT_ACCESS_POLICY = new OpeningAccessPolicy(); + + private HashDigest ledgerHash; + + private final String keyPrefix; + + private Bytes ledgerIndexKey; + + private VersioningKVStorage versioningStorage; + + private ExPolicyKVStorage exPolicyStorage; + + private volatile LedgerState latestState; + + private volatile LedgerEditor nextBlockEditor; + + private volatile boolean closed = false; + + public LedgerRepositoryImpl(HashDigest ledgerHash, String keyPrefix, ExPolicyKVStorage exPolicyStorage, + VersioningKVStorage versioningStorage) { + this.keyPrefix = keyPrefix; + + this.ledgerHash = ledgerHash; + this.versioningStorage = versioningStorage; + this.exPolicyStorage = exPolicyStorage; + this.ledgerIndexKey = encodeLedgerIndexKey(ledgerHash); + + if (getLatestBlockHeight() < 0) { + throw new LedgerException("Ledger doesn't exist!"); + } + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.LedgerRepository#getHash() + */ + @Override + public HashDigest getHash() { + return ledgerHash; + } + + @Override + public HashDigest getLatestBlockHash() { + if (latestState == null) { + return innerGetBlockHash(innerGetLatestBlockHeight()); + } + return latestState.block.getHash(); + } + + @Override + public long getLatestBlockHeight() { + if (latestState == null) { + return innerGetLatestBlockHeight(); + } + return latestState.block.getHeight(); + } + + @Override + public LedgerBlock getLatestBlock() { + LedgerState state = getLatestState(); + return state.block; + } + + private LedgerState getLatestState() { + LedgerState state = latestState; + if (state == null) { + LedgerBlock latestBlock = innerGetBlock(innerGetLatestBlockHeight()); + state = new LedgerState(latestBlock); + latestState = state; + } + return state; + } + + @Override + public LedgerBlock retrieveLatestBlock() { + LedgerBlock latestBlock = innerGetBlock(innerGetLatestBlockHeight()); + latestState = new LedgerState(latestBlock); + return latestBlock; + } + + @Override + public HashDigest retrieveLatestBlockHash() { + HashDigest latestBlockHash = innerGetBlockHash(innerGetLatestBlockHeight()); + if (latestState != null && !latestBlockHash.equals(latestState.block.getHash())) { + latestState = null; + } + return latestBlockHash; + } + + @Override + public long retrieveLatestBlockHeight() { + long latestBlockHeight = innerGetLatestBlockHeight(); + if (latestState != null && latestBlockHeight != latestState.block.getHeight()) { + latestState = null; + } + return latestBlockHeight; + } + + private long innerGetLatestBlockHeight() { + return versioningStorage.getVersion(ledgerIndexKey); + } + + @Override + public HashDigest getBlockHash(long height) { + LedgerBlock blk = latestState == null ? null : latestState.block; + if (blk != null && height == blk.getHeight()) { + return blk.getHash(); + } + return innerGetBlockHash(height); + } + + private HashDigest innerGetBlockHash(long height) { + if (height < 0) { + return null; + } + // get block hash by height; + byte[] hashBytes = versioningStorage.get(ledgerIndexKey, height); + if (hashBytes == null || hashBytes.length == 0) { + return null; + } + return CryptoUtils.hashCrypto().resolveHashDigest(hashBytes); + } + + @Override + public LedgerBlock getBlock(long height) { + LedgerBlock blk = latestState == null ? null : latestState.block; + if (blk != null && height == blk.getHeight()) { + return blk; + } + return innerGetBlock(height); + } + + private LedgerBlock innerGetBlock(long height) { + if (height < 0) { + return null; + } + return innerGetBlock(getBlockHash(height)); + } + + @Override + public LedgerBlock getBlock(HashDigest blockHash) { + LedgerBlock blk = latestState == null ? null : latestState.block; + if (blk != null && blockHash.equals(blk.getHash())) { + return blk; + } + return innerGetBlock(blockHash); + } + + private LedgerBlock innerGetBlock(HashDigest blockHash) { + Bytes key = encodeBlockStorageKey(blockHash); + // Every one block has only one version; + byte[] blockBytes = versioningStorage.get(key, 0); + LedgerBlockData block = new LedgerBlockData(deserialize(blockBytes)); + + if (!blockHash.equals(block.getHash())) { + throw new LedgerException("Block hash not equals to it's storage key!"); + } + + // verify hash; + // boolean requiredVerifyHash = + // adminAccount.getMetadata().getSetting().getCryptoSetting().getAutoVerifyHash(); + // TODO: 未实现从配置中加载是否校验 Hash 的设置; + boolean requiredVerifyHash = false; + if (requiredVerifyHash) { + byte[] blockBodyBytes = null; + if (block.getHeight() == 0) { + // 计算创世区块的 hash 时,不包括 ledgerHash 字段; + block.setLedgerHash(null); + blockBodyBytes = BinaryEncodingUtils.encode(block, BlockBody.class); + // 恢复; + block.setLedgerHash(block.getHash()); + } else { + blockBodyBytes = BinaryEncodingUtils.encode(block, BlockBody.class); + } + boolean pass = CryptoUtils.hashCrypto().verify(blockHash, blockBodyBytes); + if (!pass) { + throw new LedgerException("Block hash verification fail!"); + } + } + + // verify height; + HashDigest indexedHash = getBlockHash(block.getHeight()); + if (indexedHash == null || !indexedHash.equals(blockHash)) { + throw new LedgerException( + "Illegal ledger state in storage that ledger height index doesn't match it's block data in height[" + + block.getHeight() + "] and block hash[" + Base58Utils.encode(blockHash.toBytes()) + + "] !"); + } + + return block; + } + + @Override + public LedgerAdministration getAdminInfo() { + return getAdminAccount(getLatestBlock()); + } + + private LedgerBlock deserialize(byte[] blockBytes) { + return BinaryEncodingUtils.decode(blockBytes); + } + + @Override + public TransactionSet getTransactionSet(LedgerBlock block) { + long height = getLatestBlockHeight(); + TransactionSet transactionSet = null; + if (height == block.getHeight()) { + // 缓存读; + LedgerState state = getLatestState(); + transactionSet = state.transactionSet; + if (transactionSet == null) { + LedgerAdminAccount adminAccount = getAdminAccount(block); + transactionSet = loadTransactionSet(block.getTransactionSetHash(), + adminAccount.getMetadata().getSetting().getCryptoSetting(), keyPrefix, exPolicyStorage, + versioningStorage, true); + state.transactionSet = transactionSet; + } + return transactionSet; + } + LedgerAdminAccount adminAccount = getAdminAccount(block); + // All of existing block is readonly; + return loadTransactionSet(block.getTransactionSetHash(), + adminAccount.getMetadata().getSetting().getCryptoSetting(), keyPrefix, exPolicyStorage, + versioningStorage, true); + } + + @Override + public LedgerAdminAccount getAdminAccount(LedgerBlock block) { + long height = getLatestBlockHeight(); + LedgerAdminAccount adminAccount = null; + if (height == block.getHeight()) { + // 缓存读; + LedgerState state = getLatestState(); + adminAccount = state.adminAccount; + if (adminAccount == null) { + adminAccount = new LedgerAdminAccount(block.getAdminAccountHash(), keyPrefix, exPolicyStorage, + versioningStorage, true); + state.adminAccount = adminAccount; + } + return adminAccount; + } + + return new LedgerAdminAccount(block.getAdminAccountHash(), keyPrefix, exPolicyStorage, versioningStorage, true); + } + + @Override + public UserAccountSet getUserAccountSet(LedgerBlock block) { + long height = getLatestBlockHeight(); + UserAccountSet userAccountSet = null; + if (height == block.getHeight()) { + // 缓存读; + LedgerState state = getLatestState(); + userAccountSet = state.userAccountSet; + if (userAccountSet == null) { + LedgerAdminAccount adminAccount = getAdminAccount(block); + userAccountSet = loadUserAccountSet(block.getUserAccountSetHash(), + adminAccount.getPreviousSetting().getCryptoSetting(), keyPrefix, exPolicyStorage, + versioningStorage, true); + state.userAccountSet = userAccountSet; + } + return userAccountSet; + } + LedgerAdminAccount adminAccount = getAdminAccount(block); + return loadUserAccountSet(block.getUserAccountSetHash(), adminAccount.getPreviousSetting().getCryptoSetting(), + keyPrefix, exPolicyStorage, versioningStorage, true); + } + + @Override + public DataAccountSet getDataAccountSet(LedgerBlock block) { + long height = getLatestBlockHeight(); + DataAccountSet dataAccountSet = null; + if (height == block.getHeight()) { + // 缓存读; + LedgerState state = getLatestState(); + dataAccountSet = state.dataAccountSet; + if (dataAccountSet == null) { + LedgerAdminAccount adminAccount = getAdminAccount(block); + dataAccountSet = loadDataAccountSet(block.getDataAccountSetHash(), + adminAccount.getPreviousSetting().getCryptoSetting(), keyPrefix, exPolicyStorage, + versioningStorage, true); + state.dataAccountSet = dataAccountSet; + } + return dataAccountSet; + } + + LedgerAdminAccount adminAccount = getAdminAccount(block); + return loadDataAccountSet(block.getDataAccountSetHash(), adminAccount.getPreviousSetting().getCryptoSetting(), + keyPrefix, exPolicyStorage, versioningStorage, true); + } + + @Override + public ContractAccountSet getContractAccountSet(LedgerBlock block) { + long height = getLatestBlockHeight(); + ContractAccountSet contractAccountSet = null; + if (height == block.getHeight()) { + // 缓存读; + LedgerState state = getLatestState(); + contractAccountSet = state.contractAccountSet; + if (contractAccountSet == null) { + LedgerAdminAccount adminAccount = getAdminAccount(block); + contractAccountSet = loadContractAccountSet(block.getContractAccountSetHash(), + adminAccount.getPreviousSetting().getCryptoSetting(), keyPrefix, exPolicyStorage, + versioningStorage, true); + state.contractAccountSet = contractAccountSet; + } + return contractAccountSet; + } + + LedgerAdminAccount adminAccount = getAdminAccount(block); + return loadContractAccountSet(block.getContractAccountSetHash(), + adminAccount.getPreviousSetting().getCryptoSetting(), keyPrefix, exPolicyStorage, versioningStorage, + true); + } + + @Override + public LedgerDataSet getDataSet(LedgerBlock block) { + long height = getLatestBlockHeight(); + LedgerDataSet ledgerDataSet = null; + if (height == block.getHeight()) { + // 缓存读; + LedgerState state = getLatestState(); + ledgerDataSet = state.ledgerDataSet; + if (ledgerDataSet == null) { + ledgerDataSet = innerDataSet(block); + state.ledgerDataSet = ledgerDataSet; + } + return ledgerDataSet; + } + + // All of existing block is readonly; + return innerDataSet(block); + } + + private LedgerDataSet innerDataSet(LedgerBlock block) { + LedgerAdminAccount adminAccount = getAdminAccount(block); + UserAccountSet userAccountSet = getUserAccountSet(block); + DataAccountSet dataAccountSet = getDataAccountSet(block); + ContractAccountSet contractAccountSet = getContractAccountSet(block); + return new LedgerDataSetImpl(adminAccount, userAccountSet, dataAccountSet, contractAccountSet, true); + } + + @Override + public synchronized LedgerEditor createNextBlock() { + if (closed) { + throw new LedgerException( + "Ledger repository has been closed!"); + } + if (this.nextBlockEditor != null) { + throw new LedgerException( + "A new block is in process, cann't create another one until it finish by committing or canceling."); + } + LedgerBlock previousBlock = getLatestBlock(); + LedgerTransactionalEditor editor = LedgerTransactionalEditor.createEditor( + getAdminInfo().getMetadata().getSetting(), previousBlock, keyPrefix, exPolicyStorage, + versioningStorage); + NewBlockCommittingMonitor committingMonitor = new NewBlockCommittingMonitor(editor, this); + this.nextBlockEditor = committingMonitor; + return committingMonitor; + } + + @Override + public LedgerEditor getNextBlockEditor() { + return nextBlockEditor; + } + + @Override + public synchronized void close() { + if (closed) { + return; + } + if (this.nextBlockEditor != null) { + throw new LedgerException( + "A new block is in process, cann't close the ledger repository!"); + } + closed = true; + } + + static Bytes encodeLedgerIndexKey(HashDigest ledgerHash) { + // return LEDGER_PREFIX + Base58Utils.encode(ledgerHash.toBytes()); + // return new Bytes(ledgerHash.toBytes()).concatTo(LEDGER_PREFIX); + return LEDGER_PREFIX.concat(ledgerHash); + } + + static Bytes encodeBlockStorageKey(HashDigest blockHash) { + // String key = ByteArray.toBase58(blockHash.toBytes()); + // return BLOCK_PREFIX + key; + + return BLOCK_PREFIX.concat(blockHash); + } + + static LedgerDataSetImpl newDataSet(LedgerInitSetting initSetting, String keyPrefix, + ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { + LedgerAdminAccount adminAccount = new LedgerAdminAccount(initSetting, keyPrefix, ledgerExStorage, + ledgerVerStorage); + + String usersetKeyPrefix = keyPrefix + USER_SET_PREFIX; + String datasetKeyPrefix = keyPrefix + DATA_SET_PREFIX; + String contractsetKeyPrefix = keyPrefix + CONTRACT_SET_PREFIX; + // String txsetKeyPrefix = keyPrefix + TRANSACTION_SET_PREFIX; + + // UserAccountSet userAccountSet = new + // UserAccountSet(adminAccount.getSetting().getCryptoSetting(), + // PrefixAppender.prefix(USER_SET_PREFIX, ledgerExStorage), + // PrefixAppender.prefix(USER_SET_PREFIX, ledgerVerStorage), + // DEFAULT_ACCESS_POLICY); + UserAccountSet userAccountSet = new UserAccountSet(adminAccount.getSetting().getCryptoSetting(), + usersetKeyPrefix, ledgerExStorage, ledgerVerStorage, DEFAULT_ACCESS_POLICY); + + // DataAccountSet dataAccountSet = new + // DataAccountSet(adminAccount.getSetting().getCryptoSetting(), + // PrefixAppender.prefix(DATA_SET_PREFIX, ledgerExStorage), + // PrefixAppender.prefix(DATA_SET_PREFIX, ledgerVerStorage), + // DEFAULT_ACCESS_POLICY); + DataAccountSet dataAccountSet = new DataAccountSet(adminAccount.getSetting().getCryptoSetting(), + datasetKeyPrefix, ledgerExStorage, ledgerVerStorage, DEFAULT_ACCESS_POLICY); + + // ContractAccountSet contractAccountSet = new + // ContractAccountSet(adminAccount.getSetting().getCryptoSetting(), + // PrefixAppender.prefix(CONTRACT_SET_PREFIX, ledgerExStorage), + // PrefixAppender.prefix(CONTRACT_SET_PREFIX, ledgerVerStorage), + // DEFAULT_ACCESS_POLICY); + ContractAccountSet contractAccountSet = new ContractAccountSet(adminAccount.getSetting().getCryptoSetting(), + contractsetKeyPrefix, ledgerExStorage, ledgerVerStorage, DEFAULT_ACCESS_POLICY); + + LedgerDataSetImpl newDataSet = new LedgerDataSetImpl(adminAccount, userAccountSet, dataAccountSet, + contractAccountSet, false); + + return newDataSet; + } + + static TransactionSet newTransactionSet(LedgerSetting ledgerSetting, String keyPrefix, + ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { + // TransactionSet transactionSet = new + // TransactionSet(ledgerSetting.getCryptoSetting(), + // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), + // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerVerStorage)); + + String txsetKeyPrefix = keyPrefix + TRANSACTION_SET_PREFIX; + + TransactionSet transactionSet = new TransactionSet(ledgerSetting.getCryptoSetting(), txsetKeyPrefix, + ledgerExStorage, ledgerVerStorage); + return transactionSet; + } + + static LedgerDataSetImpl loadDataSet(LedgerDataSnapshot dataSnapshot, String keyPrefix, + ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage, boolean readonly) { + LedgerAdminAccount adminAccount = new LedgerAdminAccount(dataSnapshot.getAdminAccountHash(), keyPrefix, + ledgerExStorage, ledgerVerStorage, readonly); + + CryptoSetting cryptoSetting = adminAccount.getPreviousSetting().getCryptoSetting(); + + UserAccountSet userAccountSet = loadUserAccountSet(dataSnapshot.getUserAccountSetHash(), cryptoSetting, + keyPrefix, ledgerExStorage, ledgerVerStorage, readonly); + + DataAccountSet dataAccountSet = loadDataAccountSet(dataSnapshot.getDataAccountSetHash(), cryptoSetting, + keyPrefix, ledgerExStorage, ledgerVerStorage, readonly); + + ContractAccountSet contractAccountSet = loadContractAccountSet(dataSnapshot.getContractAccountSetHash(), + cryptoSetting, keyPrefix, ledgerExStorage, ledgerVerStorage, readonly); + + LedgerDataSetImpl dataset = new LedgerDataSetImpl(adminAccount, userAccountSet, dataAccountSet, + contractAccountSet, readonly); + + return dataset; + } + + static UserAccountSet loadUserAccountSet(HashDigest userAccountSetHash, CryptoSetting cryptoSetting, + String keyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage, + boolean readonly) { + // return new UserAccountSet(userAccountSetHash, cryptoSetting, + // PrefixAppender.prefix(USER_SET_PREFIX, ledgerExStorage), + // PrefixAppender.prefix(USER_SET_PREFIX, ledgerVerStorage), readonly, + // DEFAULT_ACCESS_POLICY); + + String usersetKeyPrefix = keyPrefix + USER_SET_PREFIX; + return new UserAccountSet(userAccountSetHash, cryptoSetting, usersetKeyPrefix, ledgerExStorage, + ledgerVerStorage, readonly, DEFAULT_ACCESS_POLICY); + } + + static DataAccountSet loadDataAccountSet(HashDigest dataAccountSetHash, CryptoSetting cryptoSetting, + String keyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage, + boolean readonly) { + // return new DataAccountSet(dataAccountSetHash, cryptoSetting, + // PrefixAppender.prefix(DATA_SET_PREFIX, ledgerExStorage, + // PrefixAppender.prefix(DATA_SET_PREFIX, ledgerVerStorage), readonly, + // DEFAULT_ACCESS_POLICY); + + String datasetKeyPrefix = keyPrefix + DATA_SET_PREFIX; + return new DataAccountSet(dataAccountSetHash, cryptoSetting, datasetKeyPrefix, ledgerExStorage, + ledgerVerStorage, readonly, DEFAULT_ACCESS_POLICY); + } + + static ContractAccountSet loadContractAccountSet(HashDigest contractAccountSetHash, CryptoSetting cryptoSetting, + String keyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage, + boolean readonly) { + // return new ContractAccountSet(contractAccountSetHash, cryptoSetting, + // PrefixAppender.prefix(CONTRACT_SET_PREFIX, ledgerExStorage, + // PrefixAppender.prefix(CONTRACT_SET_PREFIX, ledgerVerStorage), readonly, + // DEFAULT_ACCESS_POLICY); + + String contractsetKeyPrefix = keyPrefix + CONTRACT_SET_PREFIX; + return new ContractAccountSet(contractAccountSetHash, cryptoSetting, contractsetKeyPrefix, ledgerExStorage, + ledgerVerStorage, readonly, DEFAULT_ACCESS_POLICY); + } + + static TransactionSet loadTransactionSet(HashDigest txsetHash, CryptoSetting cryptoSetting, String keyPrefix, + ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage, boolean readonly) { + // return new TransactionSet(txsetHash, cryptoSetting, + // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerExStorage), + // PrefixAppender.prefix(TRANSACTION_SET_PREFIX, ledgerVerStorage), readonly); + + String txsetKeyPrefix = keyPrefix + TRANSACTION_SET_PREFIX; + return new TransactionSet(txsetHash, cryptoSetting, txsetKeyPrefix, ledgerExStorage, ledgerVerStorage, + readonly); + + } + + private static class NewBlockCommittingMonitor implements LedgerEditor { + + private LedgerTransactionalEditor editor; + + private LedgerRepositoryImpl ledgerRepo; + + public NewBlockCommittingMonitor(LedgerTransactionalEditor editor, LedgerRepositoryImpl ledgerRepo) { + this.editor = editor; + this.ledgerRepo = ledgerRepo; + } + + @Override + public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { + return editor.newTransaction(txRequest); + } + + @Override + public LedgerBlock prepare() { + return editor.prepare(); + } + + @Override + public void commit() { + try { + editor.commit(); + LedgerBlock latestBlock = editor.getNewlyBlock(); + ledgerRepo.latestState = new LedgerState(latestBlock); + } finally { + ledgerRepo.nextBlockEditor = null; + } + } + + @Override + public void cancel() { + try { + editor.cancel(); + } finally { + ledgerRepo.nextBlockEditor = null; + } + } + + } + + /** + * 维护账本某个区块的数据状态的缓存结构; + * + * @author huanghaiquan + * + */ + private static class LedgerState { + + private final LedgerBlock block; + + private volatile LedgerAdminAccount adminAccount; + + private volatile UserAccountSet userAccountSet; + + private volatile DataAccountSet dataAccountSet; + + private volatile ContractAccountSet contractAccountSet; + + private volatile TransactionSet transactionSet; + + private volatile LedgerDataSet ledgerDataSet; + + public LedgerState(LedgerBlock block) { + this.block = block; + } + + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionContextImpl.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionContextImpl.java new file mode 100644 index 00000000..c8c09d93 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionContextImpl.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.storage.service.utils.BufferedKVStorage; + diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java new file mode 100644 index 00000000..a76dd68b --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionData.java @@ -0,0 +1,179 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionState; + +public class LedgerTransactionData implements LedgerTransaction { + + private TransactionStagedSnapshot txSnapshot; + + private TransactionContent transactionContent; + + private DigitalSignature[] endpointSignatures; + + private DigitalSignature[] nodeSignatures; + + private TransactionState executionState; + + private HashDigest hash; + + private long blockHeight; + + // private HashDigest adminAccountHash; + // + // private HashDigest userAccountSetHash; + // + // private HashDigest dataAccountSetHash; + // + // private HashDigest contractAccountSetHash; + + /** + * Declare a private no-arguments constructor for deserializing purpose; + */ + private LedgerTransactionData() { +// this.txSnapshot = new TransactionStagedSnapshot(); + } + + /** + * @param blockHeight + * 区块链高度; + * @param txReq + * 交易请求; + * @param execState + * 执行状态; + * @param txSnapshot + * 交易级的系统快照; + */ + public LedgerTransactionData(long blockHeight, TransactionRequest txReq, TransactionState execState, + TransactionStagedSnapshot txSnapshot) { + this.blockHeight = blockHeight; +// this.txSnapshot = txSnapshot == null ? new TransactionStagedSnapshot() : txSnapshot; + this.txSnapshot = txSnapshot; + this.transactionContent = txReq.getTransactionContent(); + this.endpointSignatures = txReq.getEndpointSignatures(); + this.nodeSignatures = txReq.getNodeSignatures(); + this.executionState = execState; + this.hash = txReq.getHash(); + } + + @Override + public HashDigest getHash() { + return this.hash; + } + + @Override + public long getBlockHeight() { + return this.blockHeight; + } + + @Override + public TransactionState getExecutionState() { + return executionState; + } + + @Override + public TransactionContent getTransactionContent() { + return this.transactionContent; + } + + @Override + public DigitalSignature[] getEndpointSignatures() { + return this.endpointSignatures; + } + + @Override + public DigitalSignature[] getNodeSignatures() { + return nodeSignatures; + } + + @Override + public HashDigest getAdminAccountHash() { + return txSnapshot == null ? null : txSnapshot.getAdminAccountHash(); + } + + @Override + public HashDigest getUserAccountSetHash() { + return txSnapshot == null ? null :txSnapshot.getUserAccountSetHash(); + } + + @Override + public HashDigest getDataAccountSetHash() { + return txSnapshot == null ? null :txSnapshot.getDataAccountSetHash(); + } + + @Override + public HashDigest getContractAccountSetHash() { + return txSnapshot == null ? null :txSnapshot.getContractAccountSetHash(); + } + + public void setTxSnapshot(TransactionStagedSnapshot txSnapshot) { + if (txSnapshot == null) { + throw new IllegalArgumentException("Transaction snapshot argument is null!"); + } + this.txSnapshot = txSnapshot; + } + + public void setTransactionContent(TransactionContent content) { + this.transactionContent = content; + } + + public void setEndpointSignatures(Object[] participantSignatures) { + int length = participantSignatures.length; + this.endpointSignatures = new DigitalSignature[length]; + for (int i = 0; i < length; i++) { + this.endpointSignatures[i] = (DigitalSignature) participantSignatures[i]; + } + } + + public void setNodeSignatures(Object[] nodeSignatures) { + int length = nodeSignatures.length; + this.nodeSignatures = new DigitalSignature[length]; + for (int i = 0; i < length; i++) { + this.nodeSignatures[i] = (DigitalSignature) nodeSignatures[i]; + } + } + + public void setExecutionState(TransactionState executionState) { + this.executionState = executionState; + } + + public void setHash(HashDigest hash) { + this.hash = hash; + } + + public void setBlockHeight(long blockHeight) { + this.blockHeight = blockHeight; + } + + public void setAdminAccountHash(HashDigest adminAccountHash) { + if (txSnapshot == null) { + txSnapshot = new TransactionStagedSnapshot(); + } + txSnapshot.setAdminAccountHash(adminAccountHash); + } + + public void setUserAccountSetHash(HashDigest userAccountSetHash) { + if (txSnapshot == null) { + txSnapshot = new TransactionStagedSnapshot(); + } + txSnapshot.setUserAccountSetHash(userAccountSetHash); + } + + public void setDataAccountSetHash(HashDigest dataAccountSetHash) { + if (txSnapshot == null) { + txSnapshot = new TransactionStagedSnapshot(); + } + txSnapshot.setDataAccountSetHash(dataAccountSetHash); + } + + public void setContractAccountSetHash(HashDigest contractAccountSetHash) { + if (txSnapshot == null) { + txSnapshot = new TransactionStagedSnapshot(); + } + txSnapshot.setContractAccountSetHash(contractAccountSetHash); + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java new file mode 100644 index 00000000..eb30d1d5 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/LedgerTransactionalEditor.java @@ -0,0 +1,447 @@ +package com.jd.blockchain.ledger.core.impl; + +import java.util.Stack; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockBody; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerDataSnapshot; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerSetting; +import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.ledger.core.TransactionSet; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.utils.BufferedKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; + +public class LedgerTransactionalEditor implements LedgerEditor { + + private static final boolean PARALLEL_DB_WRITE; + + static { + PARALLEL_DB_WRITE = Boolean.getBoolean("parallel-dbwrite"); + System.out.println("------ [[ parallel-dbwrite=" + PARALLEL_DB_WRITE + " ]] ------"); + } + + private final String ledgerKeyPrefix; + + private CryptoSetting cryptoSetting; + + private LedgerBlockData newlyBlock; + + private Stack stagedSnapshots = new Stack<>(); + + private boolean prepared = false; + + private boolean canceled = false; + + private boolean committed = false; + + private BufferedKVStorage bufferedStorage; + + /** + * 最近一个交易上下文; + */ + private LedgerDataContext lastTxCtx; + + private LedgerDataContext newTxCtx; + + private LedgerTransactionalEditor(CryptoSetting cryptoSetting, LedgerBlockData newlyBlock, + StagedSnapshot startingPoint, String ledgerKeyPrefix, BufferedKVStorage bufferedStorage) { + this.ledgerKeyPrefix = ledgerKeyPrefix; + this.cryptoSetting = cryptoSetting; + this.newlyBlock = newlyBlock; + this.bufferedStorage = bufferedStorage; + + this.stagedSnapshots.push(startingPoint); + } + + public static LedgerTransactionalEditor createEditor(LedgerSetting ledgerSetting, LedgerBlock previousBlock, + String ledgerKeyPrefix, ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { + // new block; + LedgerBlockData currBlock = new LedgerBlockData(previousBlock.getHeight() + 1, previousBlock.getLedgerHash(), + previousBlock.getHash()); + + // init storage; + BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, PARALLEL_DB_WRITE); + + StagedSnapshot startingPoint = new TxSnapshot(previousBlock, previousBlock.getTransactionSetHash()); + + // instantiate editor; + return new LedgerTransactionalEditor(ledgerSetting.getCryptoSetting(), currBlock, startingPoint, + ledgerKeyPrefix, txStagedStorage); + } + + public static LedgerTransactionalEditor createEditor(LedgerInitSetting initSetting, String ledgerKeyPrefix, + ExPolicyKVStorage ledgerExStorage, VersioningKVStorage ledgerVerStorage) { + LedgerBlockData genesisBlock = new LedgerBlockData(0, null, null); + StagedSnapshot startingPoint = new GenesisSnapshot(initSetting); + // init storage; + BufferedKVStorage txStagedStorage = new BufferedKVStorage(ledgerExStorage, ledgerVerStorage, false); + return new LedgerTransactionalEditor(initSetting.getCryptoSetting(), genesisBlock, startingPoint, + ledgerKeyPrefix, txStagedStorage); + } + + private void commitTxSnapshot(TxSnapshot snapshot) { + lastTxCtx = newTxCtx; + newTxCtx = null; + stagedSnapshots.push(snapshot); + } + + private void rollbackNewTx() { + newTxCtx = null; + } + + // public LedgerDataSet getLatestDataSet() { + // if (lastTxCtx == null) { + // return null; + // } + // return lastTxCtx.getDataSet(); + // } + + public LedgerBlock getNewlyBlock() { + return newlyBlock; + } + + @Override + public LedgerTransactionContext newTransaction(TransactionRequest txRequest) { + checkState(); + // TODO:验证交易签名; + + BufferedKVStorage txBuffStorage = null; + LedgerDataSetImpl txDataset = null; + TransactionSet txset = null; + if (lastTxCtx == null) { + // init storage of new transaction; + // txBuffStorage = new BufferedKVStorage(bufferedStorage, bufferedStorage, + // false); + txBuffStorage = bufferedStorage; + + // load the starting point of the new transaction; + StagedSnapshot previousSnapshot = stagedSnapshots.peek(); + if (previousSnapshot instanceof GenesisSnapshot) { + // Genesis; + GenesisSnapshot snpht = (GenesisSnapshot) previousSnapshot; + txDataset = LedgerRepositoryImpl.newDataSet(snpht.initSetting, ledgerKeyPrefix, txBuffStorage, + txBuffStorage); + txset = LedgerRepositoryImpl.newTransactionSet(txDataset.getAdminAccount().getSetting(), + ledgerKeyPrefix, txBuffStorage, txBuffStorage); + } else { + // TxSnapshot; reload dataset and txset; + TxSnapshot snpht = (TxSnapshot) previousSnapshot; + // load dataset; + txDataset = LedgerRepositoryImpl.loadDataSet(snpht.dataSnapshot, ledgerKeyPrefix, txBuffStorage, + txBuffStorage, false); + + // load tx set; + txset = LedgerRepositoryImpl.loadTransactionSet(snpht.transactionSetHash, this.cryptoSetting, + ledgerKeyPrefix, txBuffStorage, txBuffStorage, false); + } + + lastTxCtx = new LedgerDataContext(txDataset, txset, txBuffStorage); + } else { + // Reuse previous object to optimize performance; + txBuffStorage = lastTxCtx.storage; + txDataset = lastTxCtx.dataset; + txset = lastTxCtx.txset; + } + + // newTxCtx = new LedgerTransactionContextImpl(newlyBlock.getHeight(), + // txRequest, txDataset, txset, txBuffStorage, + // this); + // return newTxCtx; + + return new LedgerTransactionContextImpl(newlyBlock.getHeight(), txRequest, txDataset, txset, txBuffStorage, + this); + } + + @Override + public LedgerBlock prepare() { + checkState(); + + if (newTxCtx != null) { + throw new IllegalStateException("There is a opening transaction which isn't committed or rollbacked!"); + } + if (lastTxCtx == null) { + // Genesis; + throw new IllegalStateException("No transaction to prepare!"); + } + + // do commit when transaction isolation level is BLOCK; + lastTxCtx.dataset.commit(); + lastTxCtx.txset.commit(); + + newlyBlock.setAdminAccountHash(lastTxCtx.dataset.getAdminAccount().getHash()); + newlyBlock.setContractAccountSetHash(lastTxCtx.dataset.getContractAccountSet().getRootHash()); + newlyBlock.setDataAccountSetHash(lastTxCtx.dataset.getDataAccountSet().getRootHash()); + newlyBlock.setUserAccountSetHash(lastTxCtx.dataset.getUserAccountSet().getRootHash()); + newlyBlock.setTransactionSetHash(lastTxCtx.txset.getRootHash()); + + // compute block hash; + byte[] blockBodyBytes = BinaryEncodingUtils.encode(newlyBlock, BlockBody.class); + HashDigest blockHash = CryptoUtils.hashCrypto().getFunction(cryptoSetting.getHashAlgorithm()) + .hash(blockBodyBytes); + newlyBlock.setHash(blockHash); + if (newlyBlock.getLedgerHash() == null) { + // init GenesisBlock's ledger hash; + newlyBlock.setLedgerHash(blockHash); + } + + // persist block bytes; + // only one version per block; + byte[] blockBytes = BinaryEncodingUtils.encode(newlyBlock, LedgerBlock.class); + Bytes blockStorageKey = LedgerRepositoryImpl.encodeBlockStorageKey(newlyBlock.getHash()); + long v = bufferedStorage.set(blockStorageKey, blockBytes, -1); + if (v < 0) { + throw new IllegalStateException( + "Block already exist! --[BlockHash=" + Base58Utils.encode(newlyBlock.getHash().toBytes()) + "]"); + } + + // persist block hash to ledger index; + HashDigest ledgerHash = newlyBlock.getLedgerHash(); + Bytes ledgerIndexKey = LedgerRepositoryImpl.encodeLedgerIndexKey(ledgerHash); + long expectedVersion = newlyBlock.getHeight() - 1; + v = bufferedStorage.set(ledgerIndexKey, newlyBlock.getHash().toBytes(), expectedVersion); + if (v < 0) { + throw new IllegalStateException("Index of BlockHash already exist! --[BlockHash=" + + Base58Utils.encode(newlyBlock.getHash().toBytes()) + "]"); + } + + prepared = true; + return newlyBlock; + } + + @Override + public void commit() { + if (committed) { + throw new IllegalStateException("LedgerEditor had been committed!"); + } + if (canceled) { + throw new IllegalStateException("LedgerEditor had been canceled!"); + } + if (!prepared) { + // 未就绪; + throw new IllegalStateException("LedgerEditor has not prepared!"); + } + + bufferedStorage.flush(); + + committed = true; + } + + @Override + public void cancel() { + if (committed) { + throw new IllegalStateException("LedgerEditor had been committed!"); + } + if (canceled) { + return; + } + + canceled = true; + // if (newTxCtx != null) { + // newTxCtx.rollback(); + // newTxCtx = null; + // } + bufferedStorage.cancel(); + } + + private void checkState() { + if (prepared) { + throw new IllegalStateException("LedgerEditor had been prepared!"); + } + if (committed) { + throw new IllegalStateException("LedgerEditor had been committed!"); + } + if (canceled) { + throw new IllegalStateException("LedgerEditor had been canceled!"); + } + } + + // --------------------------- inner type -------------------------- + + private static interface StagedSnapshot { + + } + + private static class GenesisSnapshot implements StagedSnapshot { + + private LedgerInitSetting initSetting; + + public GenesisSnapshot(LedgerInitSetting initSetting) { + this.initSetting = initSetting; + } + } + + private static class TxSnapshot implements StagedSnapshot { + + /** + * 账本数据的快照; + */ + private LedgerDataSnapshot dataSnapshot; + + /** + * 交易集合的快照(根哈希); + */ + private HashDigest transactionSetHash; + + public TxSnapshot(LedgerDataSnapshot dataSnapshot, HashDigest txSetHash) { + this.dataSnapshot = dataSnapshot; + this.transactionSetHash = txSetHash; + } + + } + + private static class LedgerDataContext { + + protected LedgerDataSetImpl dataset; + + protected TransactionSet txset; + + protected BufferedKVStorage storage; + + public LedgerDataContext(LedgerDataSetImpl dataset, TransactionSet txset, BufferedKVStorage storage) { + this.dataset = dataset; + this.txset = txset; + this.storage = storage; + } + + } + + private static class LedgerTransactionContextImpl extends LedgerDataContext implements LedgerTransactionContext { + + private long blockHeight; + + private LedgerTransactionalEditor editor; + + private TransactionRequest txRequest; + + // private LedgerDataSetImpl dataset; + // + // private TransactionSet txset; + // + // private BufferedKVStorage storage; + + private boolean committed = false; + + private boolean rollbacked = false; + + private LedgerTransactionContextImpl(long blockHeight, TransactionRequest txRequest, LedgerDataSetImpl dataset, + TransactionSet txset, BufferedKVStorage storage, LedgerTransactionalEditor editor) { + super(dataset, txset, storage); + this.txRequest = txRequest; + // this.dataset = dataset; + // this.txset = txset; + // this.storage = storage; + this.editor = editor; + this.blockHeight = blockHeight; + } + + @Override + public LedgerDataSet getDataSet() { + return dataset; + } + + @Override + public TransactionRequest getRequestTX() { + return txRequest; + } + + @Override + public LedgerTransaction commit(TransactionState txResult) { + checkTxState(); + + // capture snapshot + // this.dataset.commit(); + // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); + + // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, + // txResult, txDataSnapshot); + LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); + this.txset.add(tx); + // this.txset.commit(); + + // this.storage.flush(); + + // TODO: 未处理出错时 dataset 和 txset 的内部状态恢复,有可能出现不一致的情况; + + // put snapshot into stack; + // TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); + // editor.commitTxSnapshot(snapshot); + + committed = true; + return tx; + } + + @Override + public LedgerTransaction discardAndCommit(TransactionState txResult) { + checkTxState(); + + // 未处理 + // dataset.cancel(); + + // TransactionStagedSnapshot txDataSnapshot = takeSnapshot(); + // LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, + // txResult, txDataSnapshot); + LedgerTransactionData tx = new LedgerTransactionData(blockHeight, txRequest, txResult, null); + this.txset.add(tx); + // this.txset.commit(); + + // this.storage.flush(); + + // TODO: 未处理出错时 dataset 和 txset 的内部状态恢复,有可能出现不一致的情况; + + // put snapshot into stack; + // TxSnapshot snapshot = new TxSnapshot(txDataSnapshot, txset.getRootHash()); + // editor.commitTxSnapshot(snapshot); + + committed = true; + return tx; + } + + private TransactionStagedSnapshot takeSnapshot() { + TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); + txDataSnapshot.setAdminAccountHash(dataset.getAdminAccount().getHash()); + txDataSnapshot.setContractAccountSetHash(dataset.getContractAccountSet().getRootHash()); + txDataSnapshot.setDataAccountSetHash(dataset.getDataAccountSet().getRootHash()); + txDataSnapshot.setUserAccountSetHash(dataset.getUserAccountSet().getRootHash()); + return txDataSnapshot; + } + + @Override + public void rollback() { + if (this.rollbacked) { + return; + } + if (this.committed) { + throw new IllegalStateException("Transaction had been committed!"); + } + // dataset.cancel(); + // storage.cancel(); + + // editor.rollbackNewTx(); + + rollbacked = true; + } + + private void checkTxState() { + if (this.committed) { + throw new IllegalStateException("Transaction had been committed!"); + } + if (this.rollbacked) { + throw new IllegalStateException("Transaction had been rollbacked!"); + } + } + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OpeningAccessPolicy.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OpeningAccessPolicy.java new file mode 100644 index 00000000..bc97dd02 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OpeningAccessPolicy.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.core.AccountAccessPolicy; +import com.jd.blockchain.utils.Bytes; + +/** + * 开放的访问策略;
+ * + * 不做任何访问限制; + * + * @author huanghaiquan + * + */ +public class OpeningAccessPolicy implements AccountAccessPolicy { + + @Override + public boolean checkCommitting(AccountHeader account) { + return true; + } + + @Override + public boolean checkRegistering(Bytes address, PubKey pubKey) { + return true; + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OperationHandleContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OperationHandleContext.java new file mode 100644 index 00000000..6e702e0b --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OperationHandleContext.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.ledger.Operation; + +public interface OperationHandleContext { + + void handle(Operation operation); + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OperationHandleRegisteration.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OperationHandleRegisteration.java new file mode 100644 index 00000000..169c5249 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/OperationHandleRegisteration.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.ledger.core.OperationHandle; + +public interface OperationHandleRegisteration { + + OperationHandle getHandle(Class operationType); + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java new file mode 100644 index 00000000..c7cfab12 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionBatchProcessor.java @@ -0,0 +1,264 @@ +package com.jd.blockchain.ledger.core.impl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerException; +import com.jd.blockchain.ledger.core.LedgerService; +import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.ledger.core.OperationHandle; +import com.jd.blockchain.ledger.core.TransactionRequestContext; +import com.jd.blockchain.ledger.service.TransactionBatchProcess; +import com.jd.blockchain.ledger.service.TransactionBatchResult; +import com.jd.blockchain.ledger.service.TransactionBatchResultHandle; +import com.jd.blockchain.utils.Bytes; + +public class TransactionBatchProcessor implements TransactionBatchProcess { + + private static final Logger LOGGER = LoggerFactory.getLogger(TransactionBatchProcessor.class); + + private LedgerService ledgerService; + + private LedgerEditor newBlockEditor; + + private LedgerDataSet previousBlockDataset; + + private OperationHandleRegisteration opHandles; + + // 新创建的交易; + private LedgerBlock block; + + private TransactionState globalResult; + + private List responseList = new ArrayList<>(); + + private TransactionBatchResult batchResult; + + /** + * @param newBlockEditor + * 新区块的数据编辑器; + * @param previousBlockDataset + * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; + * @param opHandles + * 操作处理对象注册表; + */ + public TransactionBatchProcessor(LedgerEditor newBlockEditor, LedgerDataSet previousBlockDataset, + OperationHandleRegisteration opHandles, LedgerService ledgerService) { + this.newBlockEditor = newBlockEditor; + this.previousBlockDataset = previousBlockDataset; + this.opHandles = opHandles; + this.ledgerService = ledgerService; + } + + /* + * (non-Javadoc) + * + * @see + * com.jd.blockchain.ledger.core.impl.TransactionBatchProcess#schedule(com.jd. + * blockchain.ledger.TransactionRequest) + */ + @Override + public TransactionResponse schedule(TransactionRequest request) { + // 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; + LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); + TransactionState result; + try { + LedgerDataSet dataset = txCtx.getDataSet(); + TransactionRequestContext reqCtx = new TransactionRequestContextImpl(request); + // TODO: 验证签名者的有效性; + for (Bytes edpAddr : reqCtx.getEndpoints()) { + if (!previousBlockDataset.getUserAccountSet().contains(edpAddr)) { + throw new LedgerException("The endpoint signer[" + edpAddr + "] was not registered!"); + } + } + for (Bytes edpAddr : reqCtx.getNodes()) { + if (!previousBlockDataset.getUserAccountSet().contains(edpAddr)) { + throw new LedgerException("The node signer[" + edpAddr + "] was not registered!"); + } + } + + // 执行操作; + Operation[] ops = request.getTransactionContent().getOperations(); + OperationHandleContext handleContext = new OperationHandleContext() { + @Override + public void handle(Operation operation) { + //assert; Instance of operation are one of User related operations or DataAccount related operations; + OperationHandle hdl = opHandles.getHandle(operation.getClass()); + hdl.process(operation, dataset, reqCtx, previousBlockDataset, this, ledgerService); + } + }; + OperationHandle opHandle; + for (Operation op : ops) { + opHandle = opHandles.getHandle(op.getClass()); + opHandle.process(op, dataset, reqCtx, previousBlockDataset, handleContext, ledgerService); + } + + // 提交交易(事务); + result = TransactionState.SUCCESS; + txCtx.commit(result); + } catch (LedgerException e) { + // TODO: 识别更详细的异常类型以及执行对应的处理; + result = TransactionState.LEDGER_ERROR; + txCtx.discardAndCommit(TransactionState.LEDGER_ERROR); + LOGGER.warn(String.format("Transaction rollback caused by the ledger exception! --[TxHash=%s] --%s", + request.getHash().toBase58(), e.getMessage()), e); + } catch (Exception e) { + result = TransactionState.SYSTEM_ERROR; + txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR); + LOGGER.warn(String.format("Transaction rollback caused by the system exception! --[TxHash=%s] --%s", + request.getHash().toBase58(), e.getMessage()), e); + } + + TxResponseHandle resp = new TxResponseHandle(request, result); + responseList.add(resp); + + return resp; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.core.impl.TransactionBatchProcess#prepare() + */ + @Override + public TransactionBatchResultHandle prepare() { + if (batchResult != null) { + throw new IllegalStateException("Batch result has already been prepared or canceled!"); + } + block = newBlockEditor.prepare(); + batchResult = new TransactionBatchResultHandleImpl(); + return (TransactionBatchResultHandle) batchResult; + } + + /* + * (non-Javadoc) + * + * @see + * com.jd.blockchain.ledger.core.impl.TransactionBatchProcess#cancel(com.jd. + * blockchain.ledger.ExecutionState) + */ + @Override + public TransactionBatchResult cancel(TransactionState errorResult) { + if (batchResult != null) { + throw new IllegalStateException("Batch result has already been prepared or canceled!"); + } + + cancelInError(errorResult); + + batchResult = new TransactionBatchResultImpl(); + return batchResult; + } + + @Override + public long blockHeight() { + if (block != null) { + return block.getHeight(); + } + return 0; + } + + private void commitSuccess() { + newBlockEditor.commit(); + onCommitted(); + } + + private void cancelInError(TransactionState errorResult) { + if (errorResult == TransactionState.SUCCESS) { + throw new IllegalArgumentException("Cann't cancel by an success result!"); + } + newBlockEditor.cancel(); + this.globalResult = errorResult; + onCanceled(); + } + + /** + * 模板事件方法:交易已提交; + */ + protected void onCommitted() { + } + + /** + * 模板事件方法:交易已取消; + */ + protected void onCanceled() { + } + + private class TxResponseHandle implements TransactionResponse { + + private TransactionRequest request; + + private TransactionState result; + + public TxResponseHandle(TransactionRequest request, TransactionState result) { + this.request = request; + this.result = result; + } + + @Override + public HashDigest getContentHash() { + return request.getTransactionContent().getHash(); + } + + @Override + public TransactionState getExecutionState() { + return result; + } + + @Override + public HashDigest getBlockHash() { + return block == null ? null : block.getHash(); + } + + @Override + public long getBlockHeight() { + return block == null ? -1 : block.getHeight(); + } + + @Override + public boolean isSuccess() { + return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; + } + + } + + private class TransactionBatchResultImpl implements TransactionBatchResult { + + @Override + public LedgerBlock getBlock() { + return block; + } + + @Override + public Iterator getResponses() { + return responseList.iterator(); + } + + } + + private class TransactionBatchResultHandleImpl extends TransactionBatchResultImpl + implements TransactionBatchResultHandle { + + @Override + public void commit() { + commitSuccess(); + } + + @Override + public void cancel(TransactionState errorResult) { + cancelInError(errorResult); + } + + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionEngineImpl.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionEngineImpl.java new file mode 100644 index 00000000..ba255719 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionEngineImpl.java @@ -0,0 +1,106 @@ +package com.jd.blockchain.ledger.core.impl; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.ledger.LedgerBlock; +import org.springframework.beans.factory.annotation.Autowired; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.LedgerService; +import com.jd.blockchain.ledger.service.TransactionBatchProcess; +import com.jd.blockchain.ledger.service.TransactionEngine; + +public class TransactionEngineImpl implements TransactionEngine { + + @Autowired + private LedgerService ledgerService; + + @Autowired + private OperationHandleRegisteration opHdlRegs; + + private Map batchs = new ConcurrentHashMap<>(); + + public TransactionEngineImpl() { + } + + public TransactionEngineImpl(LedgerService ledgerService, OperationHandleRegisteration opHdlRegs) { + this.ledgerService = ledgerService; + this.opHdlRegs = opHdlRegs; + } + + @Override + public synchronized TransactionBatchProcess createNextBatch(HashDigest ledgerHash) { + TransactionBatchProcessor batch = batchs.get(ledgerHash); + if (batch != null) { + throw new IllegalStateException( + "The transaction batch process of ledger already exist! Cann't create another one!"); + } + + LedgerRepository ledgerRepo = ledgerService.getLedger(ledgerHash); + + LedgerBlock ledgerBlock = ledgerRepo.getLatestBlock(); + LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); + LedgerDataSet previousBlockDataset = ledgerRepo.getDataSet(ledgerBlock); + batch = new InnerTransactionBatchProcessor(ledgerHash, newBlockEditor, previousBlockDataset, opHdlRegs, + ledgerService, ledgerBlock.getHeight()); + batchs.put(ledgerHash, batch); + return batch; + } + + @Override + public TransactionBatchProcess getBatch(HashDigest ledgerHash) { + return batchs.get(ledgerHash); + } + + private void finishBatch(HashDigest ledgerHash) { + batchs.remove(ledgerHash); + } + + private class InnerTransactionBatchProcessor extends TransactionBatchProcessor { + + private HashDigest ledgerHash; + + private long blockHeight; + + /** + * 创建交易批处理器; + * + * @param ledgerHash + * 账本哈希; + * @param newBlockEditor + * 新区块的数据编辑器; + * @param previousBlockDataset + * 新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; + * @param opHandles + * 操作处理对象注册表; + */ + public InnerTransactionBatchProcessor(HashDigest ledgerHash, LedgerEditor newBlockEditor, + LedgerDataSet previousBlockDataset, OperationHandleRegisteration opHandles, + LedgerService ledgerService, long blockHeight) { + super(newBlockEditor, previousBlockDataset, opHandles, ledgerService); + this.ledgerHash = ledgerHash; + this.blockHeight = blockHeight; + } + + @Override + protected void onCommitted() { + super.onCommitted(); + finishBatch(ledgerHash); + } + + @Override + protected void onCanceled() { + super.onCanceled(); + finishBatch(ledgerHash); + } + + @Override + public long blockHeight() { + return this.blockHeight; + } + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionRequestContextImpl.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionRequestContextImpl.java new file mode 100644 index 00000000..ec53360a --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionRequestContextImpl.java @@ -0,0 +1,80 @@ +package com.jd.blockchain.ledger.core.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.core.TransactionRequestContext; +import com.jd.blockchain.utils.Bytes; + +/** + * @Author zhaogw + * @Date 2018/9/5 14:52 + */ +public class TransactionRequestContextImpl implements TransactionRequestContext { + + private TransactionRequest request; + + private Map endpointSignatures = new HashMap<>(); + + private Map nodeSignatures = new HashMap<>(); + + public TransactionRequestContextImpl(TransactionRequest request) { + this.request = request; + resolveSigners(); + } + + private void resolveSigners() { + if (request.getEndpointSignatures() != null) { + for (DigitalSignature signature : request.getEndpointSignatures()) { + Bytes address = AddressEncoding.generateAddress(signature.getPubKey()); + endpointSignatures.put(address, signature); + } + } + if (request.getEndpointSignatures() != null) { + for (DigitalSignature signature : request.getNodeSignatures()) { + Bytes address = AddressEncoding.generateAddress(signature.getPubKey()); + nodeSignatures.put(address, signature); + } + } + } + + @Override + public TransactionRequest getRequest() { + return request; + } + + @Override + public Set getEndpoints() { + return endpointSignatures.keySet(); + } + + @Override + public Set getNodes() { + return nodeSignatures.keySet(); + } + + @Override + public boolean containsEndpoint(Bytes address) { + return endpointSignatures.containsKey(address); + } + + @Override + public boolean containsNode(Bytes address) { + return nodeSignatures.containsKey(address); + } + + @Override + public DigitalSignature getEndpointSignature(Bytes address) { + return endpointSignatures.get(address); + } + + @Override + public DigitalSignature getNodeSignature(Bytes address) { + return nodeSignatures.get(address); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionStagedSnapshot.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionStagedSnapshot.java new file mode 100644 index 00000000..b3c37055 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/TransactionStagedSnapshot.java @@ -0,0 +1,49 @@ +package com.jd.blockchain.ledger.core.impl; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerDataSnapshot; + +public class TransactionStagedSnapshot implements LedgerDataSnapshot { + + private HashDigest adminAccountHash; + private HashDigest userAccountSetHash; + private HashDigest dataAccountSetHash; + private HashDigest contractAccountSetHash; + + @Override + public HashDigest getAdminAccountHash() { + return adminAccountHash; + } + + @Override + public HashDigest getUserAccountSetHash() { + return userAccountSetHash; + } + + @Override + public HashDigest getDataAccountSetHash() { + return dataAccountSetHash; + } + + @Override + public HashDigest getContractAccountSetHash() { + return contractAccountSetHash; + } + + public void setAdminAccountHash(HashDigest adminAccountHash) { + this.adminAccountHash = adminAccountHash; + } + + public void setUserAccountSetHash(HashDigest userAccountSetHash) { + this.userAccountSetHash = userAccountSetHash; + } + + public void setDataAccountSetHash(HashDigest dataAccountSetHash) { + this.dataAccountSetHash = dataAccountSetHash; + } + + public void setContractAccountSetHash(HashDigest contractAccountSetHash) { + this.contractAccountSetHash = contractAccountSetHash; + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractCodeDeployOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractCodeDeployOperationHandle.java new file mode 100644 index 00000000..fc84830f --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractCodeDeployOperationHandle.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.ledger.core.impl.handles; + +import com.jd.blockchain.ledger.ContractCodeDeployOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerService; +import com.jd.blockchain.ledger.core.OperationHandle; +import com.jd.blockchain.ledger.core.TransactionRequestContext; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; + +import org.springframework.stereotype.Service; + +@Service +public class ContractCodeDeployOperationHandle implements OperationHandle { + + @Override + public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { + ContractCodeDeployOperation contractOP = (ContractCodeDeployOperation) op; + // TODO: 校验合约代码的正确性; + + // TODO: 请求者应该提供合约账户的公钥签名,已确定注册的地址的唯一性; + dataset.getContractAccountSet().deploy(contractOP.getContractID().getAddress(), + contractOP.getContractID().getPubKey(), contractOP.getAddressSignature(), contractOP.getChainCode()); + } + + @Override + public boolean support(Class operationType) { + return ContractCodeDeployOperation.class.isAssignableFrom(operationType); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java new file mode 100644 index 00000000..498457af --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractEventSendOperationHandle.java @@ -0,0 +1,54 @@ +package com.jd.blockchain.ledger.core.impl.handles; + +import com.jd.blockchain.contract.ContractServiceProviders; +import com.jd.blockchain.contract.model.LocalContractEventContext; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.ledger.core.impl.LedgerQueryService; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; +import org.springframework.stereotype.Service; + +import static com.jd.blockchain.utils.BaseConstant.CONTRACT_SERVICE_PROVIDER; + +@Service +public class ContractEventSendOperationHandle implements OperationHandle { + @Override + public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext opHandleContext, LedgerService ledgerService) { + ContractEventSendOperation contractOP = (ContractEventSendOperation) op; + // 先从账本校验合约的有效性; + // 注意:必须在前一个区块的数据集中进行校验,因为那是经过共识的数据;从当前新区块链数据集校验则会带来攻击风险:未经共识的合约得到执行; + if (!previousBlockDataset.getContractAccountSet().contains(contractOP.getContractAddress())) { + throw new LedgerException( + String.format("Target contract of ContractEvent was not registered! --[ContractAddress=%s]", + contractOP.getContractAddress())); + } + + //创建合约的账本上下文实例; + LedgerQueryService queryService = new LedgerQueryService(ledgerService) ; + ContractLedgerContext ledgerContext = new ContractLedgerContext(queryService, opHandleContext); + + // TODO:从合约引擎加载合约,执行合约代码; + ContractAccount contract = previousBlockDataset.getContractAccountSet() + .getContract(contractOP.getContractAddress()); + try { + // 在调用方法前,需要加载上下文信息; + LocalContractEventContext localContractEventContext = new LocalContractEventContext( + requestContext.getRequest().getTransactionContent().getLedgerHash(),contract.getChainCode(), contractOP.getEvent()); + localContractEventContext.setArgs(contractOP.getArgs()).setTransactionRequest(requestContext.getRequest()). + setLedgerContext(ledgerContext); + ContractServiceProviders.getProvider(CONTRACT_SERVICE_PROVIDER).getEngine().setupContract( + contract.getAddress().toBase58(),contract.getChaincodeVersion(),contract.getChainCode()). + processEvent(localContractEventContext); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public boolean support(Class operationType) { + return ContractEventSendOperation.class.isAssignableFrom(operationType); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractLedgerContext.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractLedgerContext.java new file mode 100644 index 00000000..b422a7ce --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/ContractLedgerContext.java @@ -0,0 +1,324 @@ +package com.jd.blockchain.ledger.core.impl.handles; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.contract.model.LedgerContext; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; +import com.jd.blockchain.ledger.data.DataAccountKVSetOperationBuilder; +import com.jd.blockchain.ledger.data.DataAccountRegisterOperationBuilder; +import com.jd.blockchain.ledger.data.DataAccountRegisterOperationBuilderImpl; +import com.jd.blockchain.ledger.data.KVData; +import com.jd.blockchain.ledger.data.UserRegisterOperationBuilder; +import com.jd.blockchain.ledger.data.UserRegisterOperationBuilderImpl; +import com.jd.blockchain.sdk.BlockchainQueryService; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +public class ContractLedgerContext implements LedgerContext { + + private BlockchainQueryService innerQueryService; + + private OperationHandleContext opHandleContext; + + private List generatedOpList = new ArrayList<>(); + + public ContractLedgerContext(BlockchainQueryService innerQueryService, OperationHandleContext opHandleContext) { + this.innerQueryService = innerQueryService; + this.opHandleContext = opHandleContext; + } + + @Override + public HashDigest[] getLedgerHashs() { + return innerQueryService.getLedgerHashs(); + } + + @Override + public LedgerInfo getLedger(HashDigest ledgerHash) { + return innerQueryService.getLedger(ledgerHash); + } + + @Override + public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { + return innerQueryService.getConsensusParticipants(ledgerHash); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, long height) { + return innerQueryService.getBlock(ledgerHash, height); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { + return innerQueryService.getBlock(ledgerHash, blockHash); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, long height) { + return innerQueryService.getTransactionCount(ledgerHash, height); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { + return innerQueryService.getTransactionCount(ledgerHash, blockHash); + } + + @Override + public long getTransactionTotalCount(HashDigest ledgerHash) { + return innerQueryService.getTransactionTotalCount(ledgerHash); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, long height) { + return innerQueryService.getDataAccountCount(ledgerHash, height); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { + return innerQueryService.getDataAccountCount(ledgerHash, blockHash); + } + + @Override + public long getDataAccountTotalCount(HashDigest ledgerHash) { + return innerQueryService.getDataAccountTotalCount(ledgerHash); + } + + @Override + public long getUserCount(HashDigest ledgerHash, long height) { + return innerQueryService.getUserCount(ledgerHash, height); + } + + @Override + public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { + return innerQueryService.getUserCount(ledgerHash, blockHash); + } + + @Override + public long getUserTotalCount(HashDigest ledgerHash) { + return innerQueryService.getUserTotalCount(ledgerHash); + } + + @Override + public long getContractCount(HashDigest ledgerHash, long height) { + return innerQueryService.getContractCount(ledgerHash, height); + } + + @Override + public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { + return innerQueryService.getContractCount(ledgerHash, blockHash); + } + + @Override + public long getContractTotalCount(HashDigest ledgerHash) { + return innerQueryService.getContractTotalCount(ledgerHash); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { + return innerQueryService.getTransactions(ledgerHash, height, fromIndex, count); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { + return innerQueryService.getTransactions(ledgerHash, blockHash, fromIndex, count); + } + + @Override + public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + return innerQueryService.getTransactionByContentHash(ledgerHash, contentHash); + } + + @Override + public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + return innerQueryService.getTransactionStateByContentHash(ledgerHash, contentHash); + } + + @Override + public UserInfo getUser(HashDigest ledgerHash, String address) { + return innerQueryService.getUser(ledgerHash, address); + } + + @Override + public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { + return innerQueryService.getDataAccount(ledgerHash, address); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { + return innerQueryService.getDataEntries(ledgerHash, address, keys); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { + return innerQueryService.getDataEntries(ledgerHash, address, fromIndex, count); + } + + @Override + public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { + return innerQueryService.getDataEntriesTotalCount(ledgerHash, address); + } + + @Override + public AccountHeader getContract(HashDigest ledgerHash, String address) { + return innerQueryService.getContract(ledgerHash, address); + } + + // ---------------------------user()---------------------------- + + @Override + public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { + return innerQueryService.getUsers(ledgerHash, fromIndex, count); + } + + @Override + public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { + return innerQueryService.getDataAccounts(ledgerHash, fromIndex, count); + } + + @Override + public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { + return innerQueryService.getContractAccounts(ledgerHash, fromIndex, count); + } + + @Override + public UserRegisterOperationBuilder users() { + return new UserRegisterOperationBuilder1(); + } + + @Override + public DataAccountRegisterOperationBuilder dataAccounts() { + return new DataAccountRegisterOperationBuilder1(); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { + return new DataAccountKVSetOperationExecBuilder(Bytes.fromBase58(accountAddress)); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(Bytes accountAddress) { + return new DataAccountKVSetOperationExecBuilder(accountAddress); + } + + // ========end============= + + private class DataAccountRegisterOperationBuilder1 implements DataAccountRegisterOperationBuilder { + @Override + public DataAccountRegisterOperation register(BlockchainIdentity accountID) { + final DataAccountRegisterOperationBuilderImpl DATA_ACC_REG_OP_BUILDER = new DataAccountRegisterOperationBuilderImpl(); + DataAccountRegisterOperation op = DATA_ACC_REG_OP_BUILDER.register(accountID); + generatedOpList.add(op); + opHandleContext.handle(op); + return op; + } + } + + private class UserRegisterOperationBuilder1 implements UserRegisterOperationBuilder { + private final UserRegisterOperationBuilderImpl USER_REG_OP_BUILDER = new UserRegisterOperationBuilderImpl(); + + @Override + public UserRegisterOperation register(BlockchainIdentity userID) { + UserRegisterOperation op = USER_REG_OP_BUILDER.register(userID); + generatedOpList.add(op); + opHandleContext.handle(op); + return op; + } + } + + // -------------------------------- + + private class DataAccountKVSetOperationExecBuilder implements DataAccountKVSetOperationBuilder { + + private Bytes accountAddress; + + private SingleKVSetOpTemplate op; + + public DataAccountKVSetOperationExecBuilder(Bytes accountAddress) { + this.accountAddress = accountAddress; + } + + public boolean isJson(String str) { + boolean result = false; + try { + Object obj=JSON.parse(str); + result = true; + } catch (Exception e) { + result=false; + } + return result; + } + + @Override + public DataAccountKVSetOperation getOperation() { + return op; + } + + @Override + public DataAccountKVSetOperationBuilder set(String key, byte[] value, long expVersion) { + BytesValue bytesValue = new BytesValueImpl(DataType.BYTES, value); + this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); + generatedOpList.add(op); + opHandleContext.handle(op); + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { + BytesValue bytesValue; + if (isJson(value)) { + bytesValue = new BytesValueImpl(DataType.JSON, value.getBytes()); + } + else { + bytesValue = new BytesValueImpl(DataType.TEXT, value.getBytes()); + } + this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); + generatedOpList.add(op); + opHandleContext.handle(op); + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion) { + BytesValue bytesValue = new BytesValueImpl(DataType.BYTES, value.toBytes()); + this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); + generatedOpList.add(op); + opHandleContext.handle(op); + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, long value, long expVersion) { + BytesValue bytesValue = new BytesValueImpl(DataType.INT64, BytesUtils.toBytes(value)); + this.op = new SingleKVSetOpTemplate(key, bytesValue, expVersion); + generatedOpList.add(op); + opHandleContext.handle(op); + return this; + } + + /** + * 单个KV写入操作; + * + * @author huanghaiquan + * + */ + private class SingleKVSetOpTemplate implements DataAccountKVSetOperation { + + private KVWriteEntry[] writeset = new KVWriteEntry[1]; + + private SingleKVSetOpTemplate(String key, BytesValue value, long expVersion) { + writeset[0] = new KVData(key, value, expVersion); + } + + @Override + public Bytes getAccountAddress() { + return accountAddress; + } + + @Override + public KVWriteEntry[] getWriteSet() { + return writeset; + } + + } + } +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java new file mode 100644 index 00000000..6e45999c --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountKVSetOperationHandle.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.ledger.core.impl.handles; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; +import com.jd.blockchain.utils.Bytes; +import org.springframework.stereotype.Service; + +@Service +public class DataAccountKVSetOperationHandle implements OperationHandle{ + static { + DataContractRegistry.register(BytesValue.class); + } + + @Override + public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { + DataAccountKVSetOperation kvWriteOp = (DataAccountKVSetOperation) op; + DataAccount account = dataset.getDataAccountSet().getDataAccount(kvWriteOp.getAccountAddress()); + KVWriteEntry[] writeset = kvWriteOp.getWriteSet(); + for (KVWriteEntry kvw : writeset) { + byte[] value = BinaryEncodingUtils.encode(kvw.getValue(), BytesValue.class); + account.setBytes(Bytes.fromString(kvw.getKey()), value, kvw.getExpectedVersion()); + } + } + + @Override + public boolean support(Class operationType) { + return DataAccountKVSetOperation.class.isAssignableFrom(operationType); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountRegisterOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountRegisterOperationHandle.java new file mode 100644 index 00000000..c4d3fd61 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/DataAccountRegisterOperationHandle.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.ledger.core.impl.handles; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerService; +import com.jd.blockchain.ledger.core.OperationHandle; +import com.jd.blockchain.ledger.core.TransactionRequestContext; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; + +import org.springframework.stereotype.Service; + +@Service +public class DataAccountRegisterOperationHandle implements OperationHandle{ + + @Override + public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { + DataAccountRegisterOperation dataAccountRegOp = (DataAccountRegisterOperation) op; + BlockchainIdentity bid = dataAccountRegOp.getAccountID(); + + //TODO: 校验用户身份; + + //TODO: 请求者应该提供数据账户的公钥签名,已确定注册的地址的唯一性; + dataset.getDataAccountSet().register(bid.getAddress(), bid.getPubKey(), null); + } + + @Override + public boolean support(Class operationType) { + return DataAccountRegisterOperation.class.isAssignableFrom(operationType); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/UserRegisterOperationHandle.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/UserRegisterOperationHandle.java new file mode 100644 index 00000000..aef14906 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/impl/handles/UserRegisterOperationHandle.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.ledger.core.impl.handles; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerService; +import com.jd.blockchain.ledger.core.OperationHandle; +import com.jd.blockchain.ledger.core.TransactionRequestContext; +import com.jd.blockchain.ledger.core.impl.OperationHandleContext; + +public class UserRegisterOperationHandle implements OperationHandle{ + + @Override + public void process(Operation op, LedgerDataSet dataset, TransactionRequestContext requestContext, + LedgerDataSet previousBlockDataset, OperationHandleContext handleContext, LedgerService ledgerService) { + UserRegisterOperation userRegOp = (UserRegisterOperation) op; + BlockchainIdentity bid = userRegOp.getUserID(); + dataset.getUserAccountSet().register(bid.getAddress(), bid.getPubKey()); + } + + @Override + public boolean support(Class operationType) { + return UserRegisterOperation.class.isAssignableFrom(operationType); + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/serialize/LedgerBlockDeserializer.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/serialize/LedgerBlockDeserializer.java new file mode 100644 index 00000000..7b69a0c2 --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/serialize/LedgerBlockDeserializer.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.ledger.core.serialize; + +import com.alibaba.fastjson.parser.DefaultJSONParser; +import com.alibaba.fastjson.parser.JSONToken; +import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.codec.Base58Utils; + +import java.lang.reflect.Type; + +public class LedgerBlockDeserializer implements ObjectDeserializer{ + + @SuppressWarnings("unchecked") + @Override + public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + if (type instanceof Class && HashDigest.class.isAssignableFrom((Class) type)) { + String base58Str = parser.parseObject(String.class); + byte[] hashBytes = Base58Utils.decode(base58Str); + return (T) new HashDigest(hashBytes); + } + return (T) parser.parse(fieldName); + } + + @Override + public int getFastMatchToken() { + return JSONToken.LBRACE; + } + +} diff --git a/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/serialize/LedgerBlockSerializer.java b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/serialize/LedgerBlockSerializer.java new file mode 100644 index 00000000..2107e92d --- /dev/null +++ b/source/ledger/ledger-core/src/main/java/com/jd/blockchain/ledger/core/serialize/LedgerBlockSerializer.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.ledger.core.serialize; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeWriter; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; + +import java.lang.reflect.Type; + +public class LedgerBlockSerializer implements ObjectSerializer { + + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) { + SerializeWriter out = serializer.out; + if (object == null) { + out.writeNull(); + return; + } + if (object instanceof LedgerBlock) { + LedgerBlock ledgerBlock = (LedgerBlock) object; +// out.writeString(hash.toBase58()); + } + } +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/AccountSetTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/AccountSetTest.java new file mode 100644 index 00000000..bd15285b --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/AccountSetTest.java @@ -0,0 +1,54 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.core.AccountSet; +import com.jd.blockchain.ledger.core.BaseAccount; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.impl.OpeningAccessPolicy; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; + +public class AccountSetTest { + + @Test + public void test() { + OpeningAccessPolicy accessPolicy = new OpeningAccessPolicy(); + + MemoryKVStorage storage = new MemoryKVStorage(); + + CryptoConfig cryptoConf = new CryptoConfig(); + cryptoConf.setAutoVerifyHash(true); + cryptoConf.setHashAlgorithm(CryptoAlgorithm.SHA256); + + String keyPrefix = ""; + AccountSet accset = new AccountSet(cryptoConf,keyPrefix, storage, storage, accessPolicy); + + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + accset.register(userKey.getAddress(), userKey.getPubKey()); + + BaseAccount userAcc = accset.getAccount(userKey.getAddress()); + assertNotNull(userAcc); + assertTrue(accset.contains(userKey.getAddress())); + + accset.commit(); + HashDigest rootHash = accset.getRootHash(); + assertNotNull(rootHash); + + AccountSet reloadAccSet = new AccountSet(rootHash, cryptoConf, keyPrefix,storage, storage, true, accessPolicy); + BaseAccount reloadUserAcc = reloadAccSet.getAccount(userKey.getAddress()); + assertNotNull(reloadUserAcc); + assertTrue(reloadAccSet.contains(userKey.getAddress())); + + assertEquals(userAcc.getAddress(), reloadUserAcc.getAddress()); + assertEquals(userAcc.getPubKey(), reloadUserAcc.getPubKey()); + } + +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/BaseAccountTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/BaseAccountTest.java new file mode 100644 index 00000000..acdeff44 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/BaseAccountTest.java @@ -0,0 +1,81 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import org.junit.Test; +import org.springframework.util.StringUtils; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.core.BaseAccount; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.impl.OpeningAccessPolicy; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * + * @author huanghaiquan + * + */ +public class BaseAccountTest { + + @Test + public void basicTest() { + String keyPrefix = ""; + MemoryKVStorage testStorage = new MemoryKVStorage(); + + CryptoConfig cryptoConf = new CryptoConfig(); + cryptoConf.setAutoVerifyHash(true); + cryptoConf.setHashAlgorithm(CryptoAlgorithm.SHA256); + + OpeningAccessPolicy accPlc = new OpeningAccessPolicy(); + + BlockchainKeyPair bck = BlockchainKeyGenerator.getInstance().generate(); + + // 新建账户; + BaseAccount baseAccount = new BaseAccount(bck.getIdentity(), cryptoConf, keyPrefix, testStorage, testStorage, + accPlc); + assertFalse(baseAccount.isUpdated());// 空的账户; + assertFalse(baseAccount.isReadonly()); + + // 在空白状态下写入数据; + long v = baseAccount.setBytes(Bytes.fromString("A"), BytesUtils.toBytes("VALUE_A"), 0); + // 预期失败; + assertEquals(-1, v); + + v = baseAccount.setBytes(Bytes.fromString("A"), BytesUtils.toBytes("VALUE_A"), 1); + // 预期失败; + assertEquals(-1, v); + + v = baseAccount.setBytes(Bytes.fromString("A"), BytesUtils.toBytes("VALUE_A"), -1); + // 预期成功; + assertEquals(0, v); + + v = baseAccount.setBytes(Bytes.fromString("A"), BytesUtils.toBytes("VALUE_A-1"), -1); + // 已经存在版本,指定版本号-1,预期导致失败; + assertEquals(-1, v); + + baseAccount.commit(); + v = 0; + for (int i = 0; i < 10; i++) { + long s = baseAccount.setBytes(Bytes.fromString("A"), BytesUtils.toBytes("VALUE_A_" + i), v); + baseAccount.commit(); + // 预期成功; + assertEquals(v + 1, s); + v++; + } + + v = baseAccount.setBytes(Bytes.fromString("A"), BytesUtils.toBytes("VALUE_A_" + v), v + 1); + // 预期成功; + assertEquals(-1, v); + + System.out.println("============== commit =============="); + baseAccount.commit(); + + } + +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerAccountTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerAccountTest.java new file mode 100644 index 00000000..11681165 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerAccountTest.java @@ -0,0 +1,62 @@ +package test.com.jd.blockchain.ledger; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.ledger.UserInfo; +import com.jd.blockchain.ledger.core.AccountSet; +import com.jd.blockchain.ledger.core.LedgerAdminAccount; +import com.jd.blockchain.ledger.core.LedgerMetadata; +import com.jd.blockchain.utils.Bytes; + +import org.junit.Before; +import org.junit.Test; + +import java.util.Random; + +import static org.junit.Assert.assertEquals; + +/** + * Created by zhangshuang3 on 2018/9/3. + */ +public class LedgerAccountTest { + byte[] seed = null; + byte[] settingValue = null; + byte[] rawDigestBytes = null; + + @Before + public void initCfg() throws Exception{ + Random rand = new Random(); + seed = new byte[8]; + settingValue = new byte[8]; + rawDigestBytes = new byte[8]; + rand.nextBytes(seed); + rand.nextBytes(settingValue); + rand.nextBytes(rawDigestBytes); + DataContractRegistry.register(AccountHeader.class); + DataContractRegistry.register(UserInfo.class); + } + + @Test + public void testSerialize_AccountHeader() { + String address = "xxxxxxxxxxxx"; + PubKey pubKey = new PubKey(CryptoAlgorithm.SM2, rawDigestBytes); + HashDigest hashDigest = new HashDigest(CryptoAlgorithm.SHA256, rawDigestBytes); + AccountSet.AccountHeaderData accountHeaderData = new AccountSet.AccountHeaderData(Bytes.fromString(address), pubKey, hashDigest); + + //encode and decode + byte[] encodeBytes = BinaryEncodingUtils.encode(accountHeaderData, AccountHeader.class); + AccountHeader deAccountHeaderData = BinaryEncodingUtils.decode(encodeBytes); + + //verify start + assertEquals(accountHeaderData.getAddress(), deAccountHeaderData.getAddress()); + assertEquals(accountHeaderData.getPubKey(), deAccountHeaderData.getPubKey()); + assertEquals(accountHeaderData.getRootHash(), deAccountHeaderData.getRootHash()); + + } + +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerAdminAccountTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerAdminAccountTest.java new file mode 100644 index 00000000..bad75327 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerAdminAccountTest.java @@ -0,0 +1,207 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; +import java.util.Random; + +import org.junit.Test; + +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerAdminAccount; +import com.jd.blockchain.ledger.core.LedgerConfiguration; +import com.jd.blockchain.ledger.core.LedgerMetadata; +import com.jd.blockchain.ledger.data.ConsensusParticipantData; +import com.jd.blockchain.ledger.data.LedgerInitSettingData; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class LedgerAdminAccountTest { + + private Random rand = new Random(); + + @Test + public void test() { + String keyPrefix = ""; + LedgerInitSettingData initSetting = new LedgerInitSettingData(); + ConsensusParticipantData[] parties = new ConsensusParticipantData[5]; + BlockchainKeyPair[] bckeys = new BlockchainKeyPair[parties.length]; + for (int i = 0; i < parties.length; i++) { + bckeys[i] = BlockchainKeyGenerator.getInstance().generate(); + parties[i] = new ConsensusParticipantData(); + parties[i].setId(i); + parties[i].setAddress(AddressEncoding.generateAddress(bckeys[i].getPubKey()).toBase58()); + parties[i].setHostAddress(new NetworkAddress("192.168.10." + (10 + i), 10010 + 10 * i)); + parties[i].setName("Participant[" + i + "]"); + parties[i].setPubKey(bckeys[i].getPubKey()); + } + ConsensusParticipantData[] parties1 = Arrays.copyOf(parties, 4); + initSetting.setConsensusParticipants(parties1); + + byte[] csSysSettingBytes = new byte[64]; + rand.nextBytes(csSysSettingBytes); + initSetting.setConsensusSettings(new Bytes(csSysSettingBytes)); + initSetting.setConsensusProvider("consensus-provider"); + + CryptoConfig cryptoSetting = new CryptoConfig(); + cryptoSetting.setAutoVerifyHash(true); + cryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + initSetting.setCryptoSetting(cryptoSetting); + + byte[] ledgerSeed = new byte[16]; + rand.nextBytes(ledgerSeed); + initSetting.setLedgerSeed(ledgerSeed); + + MemoryKVStorage testStorage = new MemoryKVStorage(); + + // Create intance with init setting; + LedgerAdminAccount ledgerAdminAccount = new LedgerAdminAccount(initSetting, keyPrefix, testStorage, + testStorage); + + // New created instance is updated until being committed; + assertTrue(ledgerAdminAccount.isUpdated()); + // Hash of account is null until being committed; + assertNull(ledgerAdminAccount.getHash()); + + LedgerMetadata meta = ledgerAdminAccount.getMetadata(); + assertNull(meta.getParticipantsHash()); + + // Commit, and check the storage keys; + ledgerAdminAccount.commit(); + + // New created instance isn't updated after being committed; + assertFalse(ledgerAdminAccount.isUpdated()); + // Hash of account isn't null after being committed; + assertNotNull(ledgerAdminAccount.getHash()); + + meta = ledgerAdminAccount.getMetadata(); + assertNotNull(meta.getParticipantsHash()); + + // ---------------------- + // Reload account from storage with readonly mode, and check the integrity of + // data; + HashDigest adminAccHash = ledgerAdminAccount.getHash(); + LedgerAdminAccount reloadAdminAccount = new LedgerAdminAccount(adminAccHash, keyPrefix, testStorage, + testStorage, true); + + // verify realod settings of admin account; + verifyRealoadingSettings(reloadAdminAccount, adminAccHash, ledgerAdminAccount.getMetadata()); + + // verify the consensus participant list; + verifyReadlingParities(reloadAdminAccount, parties1); + + // It will throw exeception because of this account is readonly; + verifyReadonlyState(reloadAdminAccount); + + // -------------- + // reload again with writing mode; + reloadAdminAccount = new LedgerAdminAccount(adminAccHash, keyPrefix, testStorage, testStorage, false); + LedgerConfiguration newSetting = new LedgerConfiguration(reloadAdminAccount.getPreviousSetting()); + byte[] newCsSettingBytes = new byte[64]; + rand.nextBytes(newCsSettingBytes); + newSetting.setConsensusSetting(new Bytes(newCsSettingBytes)); + newSetting.getCryptoSetting().setAutoVerifyHash(false); + reloadAdminAccount.setLedgerSetting(newSetting); + + reloadAdminAccount.addParticipant(parties[4]); + reloadAdminAccount.commit(); + + // record the new account hash; + HashDigest newAccHash = reloadAdminAccount.getHash(); + LedgerMetadata newMeta = reloadAdminAccount.getMetadata(); + + // load the last version of account and verify again; + reloadAdminAccount = new LedgerAdminAccount(adminAccHash, keyPrefix, testStorage, testStorage, true); + verifyRealoadingSettings(reloadAdminAccount, adminAccHash, ledgerAdminAccount.getMetadata()); + verifyReadlingParities(reloadAdminAccount, parties1); + verifyReadonlyState(reloadAdminAccount); + + // load the hash of new committing; + reloadAdminAccount = new LedgerAdminAccount(newAccHash, keyPrefix, testStorage, testStorage, true); + verifyRealoadingSettings(reloadAdminAccount, newAccHash, newMeta); + verifyReadlingParities(reloadAdminAccount, parties); + verifyReadonlyState(reloadAdminAccount); + + // System.out.println("========= [LedgerAdminAccount Test] Show generated + // storage keys... ======="); + // testStorage.printStoragedKeys(); + } + + private void verifyRealoadingSettings(LedgerAdminAccount actualAccount, HashDigest expHash, + LedgerMetadata expMeta) { + // 验证基本信息; + assertFalse(actualAccount.isUpdated()); + assertTrue(actualAccount.isReadonly()); + + assertEquals(expHash, actualAccount.getHash()); + + // verify metadata; + LedgerMetadata rlmeta = actualAccount.getMetadata(); + assertEquals(expMeta.getParticipantsHash(), rlmeta.getParticipantsHash()); + + assertTrue(BytesUtils.equals(expMeta.getSeed(), rlmeta.getSeed())); + + assertNotNull(rlmeta.getSetting()); + assertTrue(expMeta.getSetting().getConsensusSetting().equals(rlmeta.getSetting().getConsensusSetting())); + assertEquals(expMeta.getSetting().getConsensusProvider(), rlmeta.getSetting().getConsensusProvider()); + + assertEquals(expMeta.getSetting().getCryptoSetting().getAutoVerifyHash(), + rlmeta.getSetting().getCryptoSetting().getAutoVerifyHash()); + assertEquals(expMeta.getSetting().getCryptoSetting().getHashAlgorithm(), + rlmeta.getSetting().getCryptoSetting().getHashAlgorithm()); + } + + private void verifyReadlingParities(LedgerAdminAccount actualAccount, ParticipantNode[] expParties) { + assertEquals(expParties.length, actualAccount.getParticipantCount()); + ParticipantNode[] actualPaticipants = actualAccount.getParticipants(); + assertEquals(expParties.length, actualPaticipants.length); + for (int i = 0; i < actualPaticipants.length; i++) { + ParticipantNode rlParti = actualPaticipants[i]; + assertEquals(expParties[i].getAddress(), rlParti.getAddress()); + assertEquals(expParties[i].getName(), rlParti.getName()); +// assertEquals(expParties[i].getConsensusAddress(), rlParti.getConsensusAddress()); + assertEquals(expParties[i].getPubKey(), rlParti.getPubKey()); + } + } + + private void verifyReadonlyState(LedgerAdminAccount actualAccount) { + ConsensusParticipantData newParti = new ConsensusParticipantData(); + newParti.setId((int) actualAccount.getParticipantCount()); + newParti.setHostAddress( + new NetworkAddress("192.168.10." + (10 + newParti.getAddress()), 10010 + 10 * newParti.getId())); + newParti.setName("Participant[" + newParti.getAddress() + "]"); + + BlockchainKeyPair newKey = BlockchainKeyGenerator.getInstance().generate(); + newParti.setPubKey(newKey.getPubKey()); + + Throwable ex = null; + try { + actualAccount.addParticipant(newParti); + } catch (Exception e) { + ex = e; + } + assertNotNull(ex); + + ex = null; + try { + LedgerConfiguration newLedgerSetting = new LedgerConfiguration(actualAccount.getSetting()); + actualAccount.setLedgerSetting(newLedgerSetting); + } catch (Exception e) { + ex = e; + } + assertNotNull(ex); + } + +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerBlockImplTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerBlockImplTest.java new file mode 100644 index 00000000..d00bcd05 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerBlockImplTest.java @@ -0,0 +1,113 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.LedgerBlockImplTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午10:45 + * Description: + */ +package test.com.jd.blockchain.ledger; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerDataSnapshot; +import com.jd.blockchain.ledger.core.impl.LedgerBlockData; +import com.jd.blockchain.ledger.core.impl.TransactionStagedSnapshot; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class LedgerBlockImplTest { + + private LedgerBlockData data; + + @Before + public void initLedgerBlockImpl() { + DataContractRegistry.register(LedgerBlock.class); + DataContractRegistry.register(LedgerDataSnapshot.class); + long height = 9999L; + HashDigest ledgerHash = new HashDigest(CryptoAlgorithm.SHA256, "zhangsan".getBytes()); + HashDigest previousHash = new HashDigest(CryptoAlgorithm.SHA256, "lisi".getBytes()); + data = new LedgerBlockData(height, ledgerHash, previousHash); + data.setHash(new HashDigest(CryptoAlgorithm.SHA256, "wangwu".getBytes())); + data.setTransactionSetHash(new HashDigest(CryptoAlgorithm.SHA256, "zhaoliu".getBytes())); + + // 设置LedgerDataSnapshot相关属性 + data.setAdminAccountHash(new HashDigest(CryptoAlgorithm.SHA256, "jd1".getBytes())); + data.setDataAccountSetHash(new HashDigest(CryptoAlgorithm.SHA256, "jd2".getBytes())); + data.setUserAccountSetHash(new HashDigest(CryptoAlgorithm.SHA256, "jd3".getBytes())); + data.setContractAccountSetHash(new HashDigest(CryptoAlgorithm.SHA256, "jd4".getBytes())); + + } + + @Test + public void testSerialize_LedgerBlock() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, LedgerBlock.class); + LedgerBlock resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getHash(), data.getHash()); + assertEquals(resolvedData.getHeight(), data.getHeight()); + assertEquals(resolvedData.getLedgerHash(), data.getLedgerHash()); + assertEquals(resolvedData.getPreviousHash(), data.getPreviousHash()); + assertEquals(resolvedData.getTransactionSetHash(), data.getTransactionSetHash()); + assertEquals(resolvedData.getAdminAccountHash(), data.getAdminAccountHash()); + assertEquals(resolvedData.getContractAccountSetHash(), data.getContractAccountSetHash()); + assertEquals(resolvedData.getDataAccountSetHash(), data.getDataAccountSetHash()); + assertEquals(resolvedData.getUserAccountSetHash(), data.getUserAccountSetHash()); + System.out.println("------Assert OK ------"); + } + + //notice: LedgerBlock interface has more field info than LedgerDataSnapshot interface, so cannot deserialize LedgerBlock + // with LedgerDataSnapshot encode + //@Test + //public void testSerialize_LedgerDataSnapshot() throws Exception { + //byte[] serialBytes = BinaryEncodingUtils.encode(data, LedgerDataSnapshot.class); + //LedgerDataSnapshot resolvedData = BinaryEncodingUtils.decode(serialBytes, null, + //LedgerBlockData.class); + //System.out.println("------Assert start ------"); + //assertEquals(resolvedData.getAdminAccountHash(), data.getAdminAccountHash()); + //assertEquals(resolvedData.getAdminAccountHash(), data.getAdminAccountHash()); + //assertEquals(resolvedData.getContractAccountSetHash(), data.getContractAccountSetHash()); + //assertEquals(resolvedData.getDataAccountSetHash(), data.getDataAccountSetHash()); + //assertEquals(resolvedData.getUserAccountSetHash(), data.getUserAccountSetHash()); + //System.out.println("------Assert OK ------"); + //} + + @Test + public void testSerialize_LedgerDataSnapshot() throws Exception { + TransactionStagedSnapshot transactionStagedSnapshot = new TransactionStagedSnapshot(); + + HashDigest admin = new HashDigest(CryptoAlgorithm.SHA256, "alice".getBytes()); + HashDigest contract = new HashDigest(CryptoAlgorithm.SHA256, "bob".getBytes()); + HashDigest data = new HashDigest(CryptoAlgorithm.SHA256, "jerry".getBytes()); + HashDigest user = new HashDigest(CryptoAlgorithm.SHA256, "tom".getBytes()); + + transactionStagedSnapshot.setAdminAccountHash(admin); + transactionStagedSnapshot.setContractAccountSetHash(contract); + transactionStagedSnapshot.setDataAccountSetHash(data); + transactionStagedSnapshot.setUserAccountSetHash(user); + + byte[] serialBytes = BinaryEncodingUtils.encode(transactionStagedSnapshot, LedgerDataSnapshot.class); + LedgerDataSnapshot resolvedData = BinaryEncodingUtils.decode(serialBytes); + + //verify start + assertEquals(resolvedData.getAdminAccountHash(), transactionStagedSnapshot.getAdminAccountHash()); + assertEquals(resolvedData.getContractAccountSetHash(), transactionStagedSnapshot.getContractAccountSetHash()); + assertEquals(resolvedData.getDataAccountSetHash(), transactionStagedSnapshot.getDataAccountSetHash()); + assertEquals(resolvedData.getUserAccountSetHash(), transactionStagedSnapshot.getUserAccountSetHash()); + //verify succeed + + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java new file mode 100644 index 00000000..f7b94a22 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerEditerTest.java @@ -0,0 +1,129 @@ +package test.com.jd.blockchain.ledger; + +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.*; + +import org.junit.Test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.core.impl.LedgerTransactionalEditor; +import com.jd.blockchain.ledger.data.ConsensusParticipantData; +import com.jd.blockchain.ledger.data.LedgerInitSettingData; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import static org.junit.Assert.*; + +public class LedgerEditerTest { + + static { + DataContractRegistry.register(com.jd.blockchain.ledger.TransactionContent.class); + DataContractRegistry.register(com.jd.blockchain.ledger.UserRegisterOperation.class); + DataContractRegistry.register(com.jd.blockchain.ledger.BlockBody.class); + } + + String ledgerKeyPrefix = "LDG://"; + AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + + // 存储; + MemoryKVStorage storage = new MemoryKVStorage(); + + TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(null, signatureFunction); + + // 创建初始化配置; + LedgerInitSetting initSetting = createLedgerInitSetting(); + + // 创建账本; + LedgerEditor ldgEdt = LedgerTransactionalEditor.createEditor(initSetting, ledgerKeyPrefix, storage, storage); + LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); + LedgerDataSet ldgDS = txCtx.getDataSet(); + + CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); + + @Test + public void testWriteDataAccoutKvOp() { + + BlockchainKeyPair dataKP = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); + + DataAccount dataAccount = ldgDS.getDataAccountSet().register(dataKP.getAddress(), dataKP.getPubKey(), null); + + dataAccount.setBytes(Bytes.fromString("A"), "abc".getBytes(), -1); + + LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); + LedgerBlock block = ldgEdt.prepare(); + // 提交数据,写入存储; + ldgEdt.commit(); + + byte[] bytes = dataAccount.getBytes("A"); + assertArrayEquals("abc".getBytes(), bytes); + } + + /** + * 测试创建账本; + */ + @Test + public void testLedgerEditorCreation() { + + BlockchainKeyPair userKP = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); + UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); + userAccount.setProperty("Name", "孙悟空", -1); + userAccount.setProperty("Age", "10000", -1); + + LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); + + assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); + assertEquals(0, tx.getBlockHeight()); + + LedgerBlock block = ldgEdt.prepare(); + + assertEquals(0, block.getHeight()); + assertNotNull(block.getHash()); + assertNull(block.getPreviousHash()); + + assertEquals(block.getHash(), block.getLedgerHash()); + + // 提交数据,写入存储; + ldgEdt.commit(); + + } + + private LedgerInitSetting createLedgerInitSetting() { + CryptoConfig defCryptoSetting = new CryptoConfig(); + defCryptoSetting.setAutoVerifyHash(true); + defCryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + + LedgerInitSettingData initSetting = new LedgerInitSettingData(); + + initSetting.setLedgerSeed(BytesUtils.toBytes("A Test Ledger seed!", "UTF-8")); + initSetting.setCryptoSetting(defCryptoSetting); + ConsensusParticipantData[] parties = new ConsensusParticipantData[2]; + parties[0] = new ConsensusParticipantData(); + parties[0].setId(0); + parties[0].setName("John"); + CryptoKeyPair kp0 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + parties[0].setPubKey(kp0.getPubKey()); + parties[0].setAddress(AddressEncoding.generateAddress(kp0.getPubKey()).toBase58()); + parties[0].setHostAddress(new NetworkAddress("192.168.1.6", 9000)); + + parties[1] = new ConsensusParticipantData(); + parties[1].setId(1); + parties[1].setName("John"); + CryptoKeyPair kp1 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + parties[1].setPubKey(kp1.getPubKey()); + parties[1].setAddress(AddressEncoding.generateAddress(kp1.getPubKey()).toBase58()); + parties[1].setHostAddress(new NetworkAddress("192.168.1.7", 9000)); + initSetting.setConsensusParticipants(parties); + + return initSetting; + } +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerInitOperationTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerInitOperationTest.java new file mode 100644 index 00000000..2a1a363f --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerInitOperationTest.java @@ -0,0 +1,126 @@ +package test.com.jd.blockchain.ledger; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.ParticipantCertData; +import com.jd.blockchain.ledger.data.ConsensusParticipantData; +import com.jd.blockchain.ledger.data.LedgerInitOpTemplate; +import com.jd.blockchain.ledger.data.LedgerInitSettingData; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Random; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class LedgerInitOperationTest { + + byte[] seed = null; + byte[] csSysSettingBytes = null; + LedgerInitSettingData ledgerInitSettingData = new LedgerInitSettingData(); + + @Before + public void initCfg() { + + DataContractRegistry.register(LedgerInitSetting.class); + DataContractRegistry.register(LedgerInitOperation.class); + + Random rand = new Random(); + + seed = new byte[8]; + rand.nextBytes(seed); + csSysSettingBytes = new byte[64]; + rand.nextBytes(csSysSettingBytes); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setAutoVerifyHash(true); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + + + ledgerInitSettingData.setConsensusSettings(new Bytes(csSysSettingBytes)); + ledgerInitSettingData.setConsensusProvider("cons-provider"); + + ledgerInitSettingData.setLedgerSeed(seed); + + ledgerInitSettingData.setCryptoSetting(cryptoConfig); + } + + @Test + public void test_LedgerInitOperation_ConsensusParticipantData() { + ConsensusParticipantData[] parties = new ConsensusParticipantData[4]; + BlockchainKeyPair[] keys = new BlockchainKeyPair[parties.length]; + for (int i = 0; i < parties.length; i++) { + keys[i] = BlockchainKeyGenerator.getInstance().generate(); + parties[i] = new ConsensusParticipantData(); +// parties[i].setId(i); + parties[i].setAddress(AddressEncoding.generateAddress(keys[i].getPubKey()).toBase58()); + parties[i].setHostAddress(new NetworkAddress("192.168.10." + (10 + i), 10010 + 10 * i)); + parties[i].setName("Participant[" + i + "]"); + parties[i].setPubKey(keys[i].getPubKey()); + } + ConsensusParticipantData[] parties1 = Arrays.copyOf(parties, 4); + + ledgerInitSettingData.setConsensusParticipants(parties1); + + LedgerInitOpTemplate template = new LedgerInitOpTemplate(ledgerInitSettingData); + + byte[] encode = BinaryEncodingUtils.encode(template, LedgerInitOperation.class); + LedgerInitOperation decode = BinaryEncodingUtils.decode(encode); + + for (int i = 0 ; i < template.getInitSetting().getConsensusParticipants().length; i++) { + assertEquals(template.getInitSetting().getConsensusParticipants()[i].getAddress(), decode.getInitSetting().getConsensusParticipants()[i].getAddress()); + assertEquals(template.getInitSetting().getConsensusParticipants()[i].getName(), decode.getInitSetting().getConsensusParticipants()[i].getName()); + assertEquals(template.getInitSetting().getConsensusParticipants()[i].getPubKey(), decode.getInitSetting().getConsensusParticipants()[i].getPubKey()); + + } + assertArrayEquals(template.getInitSetting().getLedgerSeed(), decode.getInitSetting().getLedgerSeed()); + assertArrayEquals(template.getInitSetting().getConsensusSettings().toBytes(), decode.getInitSetting().getConsensusSettings().toBytes()); + assertEquals(template.getInitSetting().getCryptoSetting().getHashAlgorithm(), decode.getInitSetting().getCryptoSetting().getHashAlgorithm()); + assertEquals(template.getInitSetting().getCryptoSetting().getAutoVerifyHash(), decode.getInitSetting().getCryptoSetting().getAutoVerifyHash()); + assertEquals(template.getInitSetting().getConsensusProvider(), decode.getInitSetting().getConsensusProvider()); + + } + + @Test + public void test_LedgerInitOperation_ParticipantCertData() { + ParticipantCertData[] parties = new ParticipantCertData[4]; + BlockchainKeyPair[] keys = new BlockchainKeyPair[parties.length]; + + for (int i = 0; i < parties.length; i++) { + keys[i] = BlockchainKeyGenerator.getInstance().generate(); + parties[i] = new ParticipantCertData(AddressEncoding.generateAddress(keys[i].getPubKey()).toBase58(), "Participant[" + i + "]", keys[i].getPubKey()); + } + + ParticipantCertData[] parties1 = Arrays.copyOf(parties, 4); + + ledgerInitSettingData.setConsensusParticipants(parties1); + + LedgerInitOpTemplate template = new LedgerInitOpTemplate(ledgerInitSettingData); + + byte[] encode = BinaryEncodingUtils.encode(template, LedgerInitOperation.class); + LedgerInitOperation decode = BinaryEncodingUtils.decode(encode); + + for (int i = 0 ; i < template.getInitSetting().getConsensusParticipants().length; i++) { + assertEquals(template.getInitSetting().getConsensusParticipants()[i].getAddress(), decode.getInitSetting().getConsensusParticipants()[i].getAddress()); + assertEquals(template.getInitSetting().getConsensusParticipants()[i].getName(), decode.getInitSetting().getConsensusParticipants()[i].getName()); + assertEquals(template.getInitSetting().getConsensusParticipants()[i].getPubKey(), decode.getInitSetting().getConsensusParticipants()[i].getPubKey()); + + } + assertArrayEquals(template.getInitSetting().getLedgerSeed(), decode.getInitSetting().getLedgerSeed()); + assertArrayEquals(template.getInitSetting().getConsensusSettings().toBytes(), decode.getInitSetting().getConsensusSettings().toBytes()); + assertEquals(template.getInitSetting().getCryptoSetting().getHashAlgorithm(), decode.getInitSetting().getCryptoSetting().getHashAlgorithm()); + assertEquals(template.getInitSetting().getCryptoSetting().getAutoVerifyHash(), decode.getInitSetting().getCryptoSetting().getAutoVerifyHash()); + assertEquals(template.getInitSetting().getConsensusProvider(), decode.getInitSetting().getConsensusProvider()); + } +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerInitSettingTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerInitSettingTest.java new file mode 100644 index 00000000..d171e421 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerInitSettingTest.java @@ -0,0 +1,134 @@ +package test.com.jd.blockchain.ledger; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.ParticipantCertData; +import com.jd.blockchain.ledger.data.ConsensusParticipantData; +import com.jd.blockchain.ledger.data.LedgerInitOpTemplate; +import com.jd.blockchain.ledger.data.LedgerInitSettingData; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.junit.Before; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Random; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +public class LedgerInitSettingTest { + byte[] seed = null; + byte[] csSysSettingBytes = null; + LedgerInitSettingData ledgerInitSettingData = new LedgerInitSettingData(); + LedgerInitOpTemplate template = new LedgerInitOpTemplate(); + + @Before + public void initCfg() { + + DataContractRegistry.register(LedgerInitSetting.class); + Random rand = new Random(); + + seed = new byte[8]; + rand.nextBytes(seed); + csSysSettingBytes = new byte[64]; + rand.nextBytes(csSysSettingBytes); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setAutoVerifyHash(true); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + + + ledgerInitSettingData.setConsensusSettings(new Bytes(csSysSettingBytes)); + ledgerInitSettingData.setConsensusProvider("cons-provider"); + + ledgerInitSettingData.setLedgerSeed(seed); + + ledgerInitSettingData.setCryptoSetting(cryptoConfig); + + + } + + @Test + public void test_ledgerinitsetting_ConsensusParticipantData() { + + ConsensusParticipantData[] parties = new ConsensusParticipantData[4]; + BlockchainKeyPair[] keys = new BlockchainKeyPair[parties.length]; + for (int i = 0; i < parties.length; i++) { + keys[i] = BlockchainKeyGenerator.getInstance().generate(); + parties[i] = new ConsensusParticipantData(); +// parties[i].setId(i); + parties[i].setAddress(AddressEncoding.generateAddress(keys[i].getPubKey()).toBase58()); + parties[i].setHostAddress(new NetworkAddress("192.168.10." + (10 + i), 10010 + 10 * i)); + parties[i].setName("Participant[" + i + "]"); + parties[i].setPubKey(keys[i].getPubKey()); + } + ConsensusParticipantData[] parties1 = Arrays.copyOf(parties, 4); + + ledgerInitSettingData.setConsensusParticipants(parties1); + + byte[] encode = BinaryEncodingUtils.encode(ledgerInitSettingData, LedgerInitSetting.class); + + LedgerInitSetting decode = BinaryEncodingUtils.decode(encode); + + + for (int i = 0 ; i < ledgerInitSettingData.getConsensusParticipants().length; i++) { + assertEquals(ledgerInitSettingData.getConsensusParticipants()[i].getAddress(), decode.getConsensusParticipants()[i].getAddress()); + assertEquals(ledgerInitSettingData.getConsensusParticipants()[i].getName(), decode.getConsensusParticipants()[i].getName()); + assertEquals(ledgerInitSettingData.getConsensusParticipants()[i].getPubKey(), decode.getConsensusParticipants()[i].getPubKey()); + + } + assertArrayEquals(ledgerInitSettingData.getLedgerSeed(), decode.getLedgerSeed()); + assertArrayEquals(ledgerInitSettingData.getConsensusSettings().toBytes(), decode.getConsensusSettings().toBytes()); + assertEquals(ledgerInitSettingData.getCryptoSetting().getHashAlgorithm(), decode.getCryptoSetting().getHashAlgorithm()); + assertEquals(ledgerInitSettingData.getCryptoSetting().getAutoVerifyHash(), decode.getCryptoSetting().getAutoVerifyHash()); + assertEquals(ledgerInitSettingData.getConsensusProvider(), decode.getConsensusProvider()); + + } + +// @Test +// public void test_ledgerinitsetting_ConsensusParticipantConfig() { +// } + + + @Test + public void test_ledgerinitsetting_ParticipantCertData() { + + ParticipantCertData[] parties = new ParticipantCertData[4]; + BlockchainKeyPair[] keys = new BlockchainKeyPair[parties.length]; + + for (int i = 0; i < parties.length; i++) { + keys[i] = BlockchainKeyGenerator.getInstance().generate(); + parties[i] = new ParticipantCertData(AddressEncoding.generateAddress(keys[i].getPubKey()).toBase58(), "Participant[" + i + "]", keys[i].getPubKey()); + } + + ParticipantCertData[] parties1 = Arrays.copyOf(parties, 4); + + ledgerInitSettingData.setConsensusParticipants(parties1); + + byte[] encode = BinaryEncodingUtils.encode(ledgerInitSettingData, LedgerInitSetting.class); + + LedgerInitSetting decode = BinaryEncodingUtils.decode(encode); + + for (int i = 0 ; i < ledgerInitSettingData.getConsensusParticipants().length; i++) { + assertEquals(ledgerInitSettingData.getConsensusParticipants()[i].getAddress(), decode.getConsensusParticipants()[i].getAddress()); + assertEquals(ledgerInitSettingData.getConsensusParticipants()[i].getName(), decode.getConsensusParticipants()[i].getName()); + assertEquals(ledgerInitSettingData.getConsensusParticipants()[i].getPubKey(), decode.getConsensusParticipants()[i].getPubKey()); + + } + assertArrayEquals(ledgerInitSettingData.getLedgerSeed(), decode.getLedgerSeed()); + assertArrayEquals(ledgerInitSettingData.getConsensusSettings().toBytes(), decode.getConsensusSettings().toBytes()); + assertEquals(ledgerInitSettingData.getCryptoSetting().getHashAlgorithm(), decode.getCryptoSetting().getHashAlgorithm()); + assertEquals(ledgerInitSettingData.getCryptoSetting().getAutoVerifyHash(), decode.getCryptoSetting().getAutoVerifyHash()); + assertEquals(ledgerInitSettingData.getConsensusProvider(), decode.getConsensusProvider()); + } +} + + diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java new file mode 100644 index 00000000..8ed3e587 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerManagerTest.java @@ -0,0 +1,220 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import org.bouncycastle.util.io.Streams; +import org.junit.Test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockBody; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionRequestBuilder; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.ContractAccountSet; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.DataAccountSet; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.ledger.core.UserAccount; +import com.jd.blockchain.ledger.core.UserAccountSet; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.ledger.data.ConsensusParticipantData; +import com.jd.blockchain.ledger.data.LedgerInitSettingData; +import com.jd.blockchain.ledger.data.TxBuilder; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class LedgerManagerTest { + + static { + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(UserRegisterOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(BlockBody.class); + } + + private static SignatureFunction signatureFunction = CryptoUtils.sign(CryptoAlgorithm.ED25519); + + @Test + public void testLedgerInit() { + // 创建账本初始化配置; + LedgerInitSetting initSetting = createLedgerInitSetting(); + + // 采用基于内存的 Storage; + MemoryKVStorage storage = new MemoryKVStorage(); + + // 新建账本; + LedgerManager ledgerManager = new LedgerManager(); + LedgerEditor ldgEdt = ledgerManager.newLedger(initSetting, storage); + + // 创建一个模拟的创世交易; + TransactionRequest genesisTxReq = LedgerTestUtils.createTxRequest(null, signatureFunction); + + // 记录交易,注册用户; + LedgerTransactionContext txCtx = ldgEdt.newTransaction(genesisTxReq); + LedgerDataSet ldgDS = txCtx.getDataSet(); + BlockchainKeyPair userKP = BlockchainKeyGenerator.getInstance().generate();; + UserAccount userAccount = ldgDS.getUserAccountSet().register(userKP.getAddress(), userKP.getPubKey()); + userAccount.setProperty("Name", "孙悟空", -1); + userAccount.setProperty("Age", "10000", -1); + + System.out.println("UserAddress=" + userAccount.getAddress()); + + // 提交交易结果; + LedgerTransaction tx = txCtx.commit(TransactionState.SUCCESS); + + assertEquals(genesisTxReq.getTransactionContent().getHash(), tx.getTransactionContent().getHash()); + assertEquals(0, tx.getBlockHeight()); + + // 生成区块; + LedgerBlock genesisBlock = ldgEdt.prepare(); + HashDigest ledgerHash = genesisBlock.getHash(); + + assertEquals(0, genesisBlock.getHeight()); + assertNotNull(genesisBlock.getHash()); + assertNull(genesisBlock.getPreviousHash()); + assertEquals(ledgerHash, genesisBlock.getLedgerHash()); + + // 提交数据,写入存储; + ldgEdt.commit(); + + //重新加载并校验结果; + LedgerManager reloadLedgerManager = new LedgerManager(); + LedgerRepository reloadLedgerRepo = reloadLedgerManager.register(ledgerHash, storage); + + HashDigest genesisHash= reloadLedgerRepo.getBlockHash(0); + assertEquals(ledgerHash, genesisHash); + + LedgerBlock latestBlock = reloadLedgerRepo.getLatestBlock(); + assertEquals(0, latestBlock.getHeight()); + assertEquals(ledgerHash, latestBlock.getHash()); + assertEquals(ledgerHash, latestBlock.getLedgerHash()); + + LedgerEditor editor1 = reloadLedgerRepo.createNextBlock(); + + TxBuilder txBuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair dataKey = BlockchainKeyGenerator.getInstance().generate(); + txBuilder.dataAccounts().register(dataKey.getIdentity()); + TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); + DigitalSignature dgtsign = txReqBuilder.signAsEndpoint(userKP); + TransactionRequest txRequest = txReqBuilder.buildRequest(); + + LedgerTransactionContext txCtx1 = editor1.newTransaction(txRequest); + txCtx1.getDataSet().getDataAccountSet().register(dataKey.getAddress(), dataKey.getPubKey(), null); + txCtx1.commit(TransactionState.SUCCESS); + + LedgerBlock block1 = editor1.prepare(); + editor1.commit(); + assertEquals(1, block1.getHeight()); + assertNotNull(block1.getHash()); + assertEquals(genesisHash, block1.getPreviousHash()); + assertEquals(ledgerHash, block1.getLedgerHash()); + + latestBlock = reloadLedgerRepo.getLatestBlock(); + assertEquals(1, latestBlock.getHeight()); + assertEquals(block1.getHash(), latestBlock.getHash()); + + showStorageKeys(storage); + + reloadLedgerManager = new LedgerManager(); + reloadLedgerRepo = reloadLedgerManager.register(ledgerHash, storage); + latestBlock = reloadLedgerRepo.getLatestBlock(); + assertEquals(1, latestBlock.getHeight()); + assertEquals(block1.getHash(), latestBlock.getHash()); + + DataAccountSet dataAccountSet = reloadLedgerRepo.getDataAccountSet(latestBlock); + UserAccountSet userAccountSet = reloadLedgerRepo.getUserAccountSet(latestBlock); + ContractAccountSet contractAccountSet = reloadLedgerRepo.getContractAccountSet(latestBlock); + + } + + + + private void showStorageKeys(MemoryKVStorage storage) { + // 输出写入的 kv; + System.out.println("------------------- Storage Keys -------------------"); + Object[] keys = Stream.of(storage.getStorageKeySet().toArray(new Bytes[0])).map(p -> p.toString()).sorted((o1, o2) -> o1.compareTo(o2)).toArray(); + int i = 0; + for (Object k : keys) { + i++; + System.out.println(i + ":" + k.toString()); + } + } + + + private LedgerInitSetting createLedgerInitSetting() { + CryptoConfig defCryptoSetting = new CryptoConfig(); + defCryptoSetting.setAutoVerifyHash(true); + defCryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + + LedgerInitSettingData initSetting = new LedgerInitSettingData(); + + initSetting.setLedgerSeed(BytesUtils.toBytes("A Test Ledger seed!", "UTF-8")); + initSetting.setCryptoSetting(defCryptoSetting); + ConsensusParticipantData[] parties = new ConsensusParticipantData[4]; + parties[0] = new ConsensusParticipantData(); + parties[0].setId(0); + parties[0].setName("John"); + CryptoKeyPair kp0 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + parties[0].setPubKey(kp0.getPubKey()); + parties[0].setAddress(AddressEncoding.generateAddress(kp0.getPubKey()).toBase58()); + parties[0].setHostAddress(new NetworkAddress("127.0.0.1", 9000)); + + parties[1] = new ConsensusParticipantData(); + parties[1].setId(1); + parties[1].setName("Mary"); + CryptoKeyPair kp1 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + parties[1].setPubKey(kp1.getPubKey()); + parties[1].setAddress(AddressEncoding.generateAddress(kp1.getPubKey()).toBase58()); + parties[1].setHostAddress(new NetworkAddress("127.0.0.1", 9010)); + + parties[2] = new ConsensusParticipantData(); + parties[2].setId(2); + parties[2].setName("Jerry"); + CryptoKeyPair kp2 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + parties[2].setPubKey(kp2.getPubKey()); + parties[2].setAddress(AddressEncoding.generateAddress(kp2.getPubKey()).toBase58()); + parties[2].setHostAddress(new NetworkAddress("127.0.0.1", 9020)); + + parties[3] = new ConsensusParticipantData(); + parties[3].setId(3); + parties[3].setName("Tom"); + CryptoKeyPair kp3 = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + parties[3].setPubKey(kp3.getPubKey()); + parties[3].setAddress(AddressEncoding.generateAddress(kp3.getPubKey()).toBase58()); + parties[3].setHostAddress(new NetworkAddress("127.0.0.1", 9030)); + + initSetting.setConsensusParticipants(parties); + + return initSetting; + } + +// +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerMetaDataTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerMetaDataTest.java new file mode 100644 index 00000000..4785ab13 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerMetaDataTest.java @@ -0,0 +1,199 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.util.Random; + +import org.junit.Before; +import org.junit.Test; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerAdminAccount; +import com.jd.blockchain.ledger.core.LedgerConfiguration; +import com.jd.blockchain.ledger.core.LedgerMetadata; +import com.jd.blockchain.ledger.core.LedgerSetting; +import com.jd.blockchain.ledger.core.ParticipantCertData; +import com.jd.blockchain.utils.Bytes; + +/** + * Created by zhangshuang3 on 2018/8/31. + */ +public class LedgerMetaDataTest { + byte[] seed = null; + String consensusProvider = "test-provider"; + byte[] consensusSettingBytes = null; + byte[] rawDigestBytes = null; + + @Before + public void initCfg() throws Exception { + Random rand = new Random(); + seed = new byte[8]; + consensusSettingBytes = new byte[8]; + rawDigestBytes = new byte[8]; + rand.nextBytes(seed); + rand.nextBytes(consensusSettingBytes); + rand.nextBytes(rawDigestBytes); + DataContractRegistry.register(LedgerMetadata.class); + DataContractRegistry.register(ParticipantNode.class); + } + + @Test + public void testSerialize_LedgerMetadata() { + // LedgerCodes.METADATA + + // prepare work + // ConsensusConfig consensusConfig = new ConsensusConfig(); + // consensusConfig.setValue(settingValue); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setAutoVerifyHash(true); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + + LedgerConfiguration ledgerConfiguration = new LedgerConfiguration(consensusProvider, + new Bytes(consensusSettingBytes), cryptoConfig); + LedgerAdminAccount.LedgerMetadataImpl ledgerMetadata = new LedgerAdminAccount.LedgerMetadataImpl(); + + ledgerMetadata.setSeed(seed); + ledgerMetadata.setSetting(ledgerConfiguration); + + HashDigest hashDigest = new HashDigest(CryptoAlgorithm.SHA256, rawDigestBytes); + ledgerMetadata.setParticipantsHash(hashDigest); + + // encode and decode + byte[] encodeBytes = BinaryEncodingUtils.encode(ledgerMetadata, LedgerMetadata.class); + LedgerMetadata deLedgerMetaData = BinaryEncodingUtils.decode(encodeBytes); + + // verify start + assertArrayEquals(ledgerMetadata.getSeed(), deLedgerMetaData.getSeed()); + assertEquals(ledgerMetadata.getParticipantsHash(), deLedgerMetaData.getParticipantsHash()); + assertNotEquals(ledgerMetadata.getSetting(), deLedgerMetaData.getSetting()); + + return; + } + + @Test + public void testSerialize_LedgerSetting() { + // LedgerCodes.METADATA_LEDGER_SETTING + Random rand = new Random(); + byte[] csSettingsBytes = new byte[8]; + rand.nextBytes(csSettingsBytes); + String consensusProvider = "testprovider"; + + // ConsensusConfig consensusConfig = new ConsensusConfig(); + // consensusConfig.setValue(settingValue); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setAutoVerifyHash(true); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + + LedgerConfiguration ledgerConfiguration = new LedgerConfiguration(consensusProvider, new Bytes(csSettingsBytes), cryptoConfig); + byte[] encodeBytes = BinaryEncodingUtils.encode(ledgerConfiguration, LedgerSetting.class); + LedgerSetting deLedgerConfiguration = BinaryEncodingUtils.decode(encodeBytes); + // verify start + assertTrue(ledgerConfiguration.getConsensusSetting().equals(deLedgerConfiguration.getConsensusSetting())); + assertEquals(ledgerConfiguration.getCryptoSetting().getAutoVerifyHash(), + deLedgerConfiguration.getCryptoSetting().getAutoVerifyHash()); + assertEquals(ledgerConfiguration.getCryptoSetting().getHashAlgorithm(), + deLedgerConfiguration.getCryptoSetting().getHashAlgorithm()); + + return; + } + + // @Test + // public void testSerialize_ConsensusSetting() { + // //LedgerCodes.METADATA_LEDGER_SETTING_CONSENSUS + // Random rand = new Random(); + // byte[] settingValue = new byte[8]; + // rand.nextBytes(settingValue); + // + // ConsensusConfig consensusConfig = new ConsensusConfig(); + // consensusConfig.setValue(settingValue); + // byte[] encodeBytes = BinaryEncodingUtils.encode(consensusConfig, + // ConsensusSetting.class); + // ConsensusSetting deConsensusConfig = BinaryEncodingUtils.decode(encodeBytes); + // + // //verify start + // assertArrayEquals(consensusConfig.getValue(), deConsensusConfig.getValue()); + // + // return; + // } + + @Test + public void testSerialize_CryptoSetting() { + // LedgerCodes.METADATA_LEDGER_SETTING_CRYPTO + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setAutoVerifyHash(true); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + byte[] encodeBytes = BinaryEncodingUtils.encode(cryptoConfig, CryptoSetting.class); + CryptoSetting deCryptoConfig = BinaryEncodingUtils.decode(encodeBytes); + + // verify start + assertEquals(cryptoConfig.getHashAlgorithm(), deCryptoConfig.getHashAlgorithm()); + assertEquals(cryptoConfig.getAutoVerifyHash(), deCryptoConfig.getAutoVerifyHash()); + return; + } + + @Test + public void testSerialize_ParticipantCert() { + // LedgerCodes.METADATA_PARTICIPANT_CERT + // prepare work + int id = 1; + // String address = "xxxxxxxxxxxxxx"; + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, rawDigestBytes); + // ParticipantInfo info = new ParticipantCertData.ParticipantInfoData(1, "yyy"); + // SignatureDigest signature = new SignatureDigest(CryptoAlgorithm.SM2, + // rawDigestBytes); + String name = "John"; + // NetworkAddress consensusAddress = new NetworkAddress("192.168.1.1", 9001, + // false); + String address = AddressEncoding.generateAddress(pubKey).toBase58(); + ParticipantCertData participantCertData = new ParticipantCertData(address, name, pubKey); + + // encode and decode + byte[] encodeBytes = BinaryEncodingUtils.encode(participantCertData, ParticipantNode.class); + ParticipantNode deParticipantInfoData = BinaryEncodingUtils.decode(encodeBytes); + + // verify start + assertEquals(participantCertData.getAddress(), deParticipantInfoData.getAddress()); + assertEquals(participantCertData.getPubKey(), deParticipantInfoData.getPubKey()); + assertEquals(participantCertData.getName(), deParticipantInfoData.getName()); + // assertEquals(participantCertData.getConsensusAddress().getHost(), + // deParticipantInfoData.getConsensusAddress().getHost()); + // assertEquals(participantCertData.getConsensusAddress().getPort(), + // deParticipantInfoData.getConsensusAddress().getPort()); + // assertEquals(participantCertData.getConsensusAddress().isSecure(), + // deParticipantInfoData.getConsensusAddress().isSecure()); + + return; + } + + // @Test + // public void testSerialize_ParticipantInfo() { + // String name = "yyyy"; + // + // ParticipantCertData.ParticipantInfoData participantInfoData = new + // ParticipantCertData.ParticipantInfoData(1, name); + // byte[] encodeBytes = BinaryEncodingUtils.encode(participantInfoData, + // ParticipantInfo.class); + // ParticipantCertData.ParticipantInfoData deParticipantInfoData = + // BinaryEncodingUtils.decode(encodeBytes, null, + // ParticipantCertData.ParticipantInfoData.class); + // + // //verify start + // assertEquals(participantInfoData.getId(), deParticipantInfoData.getId()); + // assertEquals(participantInfoData.getName(), deParticipantInfoData.getName()); + // + // return; + // } +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java new file mode 100644 index 00000000..d9c80841 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTestUtils.java @@ -0,0 +1,95 @@ +package test.com.jd.blockchain.ledger; + +import java.util.Random; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.impl.TransactionStagedSnapshot; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.ledger.data.TxTemplate; + +public class LedgerTestUtils { + + // private static ThreadLocalRandom rand = ThreadLocalRandom.current(); + + private static Random rand = new Random(); + + + public static TransactionRequest createTxRequest(HashDigest ledgerHash) { + return createTxRequest(ledgerHash, CryptoUtils.sign(CryptoAlgorithm.ED25519)); + } + + public static TransactionRequest createTxRequest(HashDigest ledgerHash, SignatureFunction signatureFunction) { + TxHandle txHandle = new TxHandle(); + + TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); + + CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); + PubKey pubKey = cryptoKeyPair.getPubKey(); + txTemp.users().register(new BlockchainIdentityData(pubKey)); + PreparedTransaction ptx = txTemp.prepare(); + ptx.sign(cryptoKeyPair); + ptx.commit(); + return txHandle.txRequest; + } + + public static TransactionRequest createContractEventTxRequest(HashDigest ledgerHash, + SignatureFunction signatureFunction, String contractAddress, String event, byte[] args) { + TxHandle txHandle = new TxHandle(); + + TxTemplate txTemp = new TxTemplate(ledgerHash, txHandle); + + txTemp.contractEvents().send(contractAddress, event, args); + + CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); + PubKey pubKey = cryptoKeyPair.getPubKey(); + txTemp.users().register(new BlockchainIdentityData(pubKey)); + PreparedTransaction ptx = txTemp.prepare(); + ptx.sign(cryptoKeyPair); + ptx.commit(); + return txHandle.txRequest; + } + + public static TransactionStagedSnapshot generateRandomSnapshot() { + TransactionStagedSnapshot txDataSnapshot = new TransactionStagedSnapshot(); + txDataSnapshot.setAdminAccountHash(generateRandomHash()); + txDataSnapshot.setContractAccountSetHash(generateRandomHash()); + txDataSnapshot.setDataAccountSetHash(generateRandomHash()); + txDataSnapshot.setUserAccountSetHash(generateRandomHash()); + return txDataSnapshot; + } + + public static HashDigest generateRandomHash() { + byte[] data = new byte[64]; + rand.nextBytes(data); + return CryptoUtils.hash(CryptoAlgorithm.SHA256).hash(data); + } + + + public static CryptoSetting createDefaultCryptoSetting() { + CryptoConfig cryptoSetting = new CryptoConfig(); + cryptoSetting.setAutoVerifyHash(true); + cryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + return cryptoSetting; + } + + + private static class TxHandle implements TransactionService { + + private TransactionRequest txRequest; + + @Override + public TransactionResponse process(TransactionRequest txRequest) { + this.txRequest = txRequest; + return null; + } + + } + +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java new file mode 100644 index 00000000..d44e08d9 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/LedgerTransactionDataTest.java @@ -0,0 +1,260 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.LedgerTransactionImplTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午9:48 + * Description: + */ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; + +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.EndpointRequest; +import com.jd.blockchain.ledger.HashObject; +import com.jd.blockchain.ledger.LedgerDataSnapshot; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.NodeRequest; +import com.jd.blockchain.ledger.Transaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.core.impl.LedgerTransactionData; +import com.jd.blockchain.ledger.core.impl.TransactionStagedSnapshot; +import com.jd.blockchain.ledger.data.BlockchainOperationFactory; +import com.jd.blockchain.ledger.data.DigitalSignatureBlob; +import com.jd.blockchain.ledger.data.TxContentBlob; +import com.jd.blockchain.ledger.data.TxRequestMessage; +import com.jd.blockchain.utils.io.ByteArray; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class LedgerTransactionDataTest { + + private LedgerTransactionData data; + + @Before + public void initLedgerTransactionImpl() throws Exception { + DataContractRegistry.register(LedgerTransaction.class); + DataContractRegistry.register(Transaction.class); + DataContractRegistry.register(LedgerDataSnapshot.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(HashObject.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + + TransactionRequest txRequestMessage = initTxRequestMessage(); + + long blockHeight = 9986L; + data = new LedgerTransactionData(blockHeight, txRequestMessage, TransactionState.SUCCESS, initTransactionStagedSnapshot()); + + HashDigest hash = new HashDigest(CryptoAlgorithm.SHA256, "zhangsan".getBytes()); + HashDigest adminAccountHash = new HashDigest(CryptoAlgorithm.SHA256, "lisi".getBytes()); + HashDigest userAccountSetHash = new HashDigest(CryptoAlgorithm.SHA256, "wangwu".getBytes()); + HashDigest dataAccountSetHash = new HashDigest(CryptoAlgorithm.SHA256, "zhaoliu".getBytes()); + HashDigest contractAccountSetHash = new HashDigest(CryptoAlgorithm.SHA256, "sunqi".getBytes()); + + data.setHash(hash); +// data.setBlockHeight(blockHeight); + data.setAdminAccountHash(adminAccountHash); + data.setUserAccountSetHash(userAccountSetHash); + data.setDataAccountSetHash(dataAccountSetHash); + data.setContractAccountSetHash(contractAccountSetHash); + } + + + @Test + public void testSerialize_LedgerTransaction() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, LedgerTransaction.class); + LedgerTransaction resolvedData = BinaryEncodingUtils.decode(serialBytes); + + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getAdminAccountHash(), data.getAdminAccountHash()); + assertEquals(resolvedData.getContractAccountSetHash(), data.getContractAccountSetHash()); + assertEquals(resolvedData.getDataAccountSetHash(), data.getDataAccountSetHash()); + assertEquals(resolvedData.getUserAccountSetHash(), data.getUserAccountSetHash()); + assertEquals(resolvedData.getExecutionState(), data.getExecutionState()); + assertEquals(resolvedData.getHash(), data.getHash()); + assertEquals(resolvedData.getBlockHeight(), data.getBlockHeight()); + + // EndpointSignatures 验证 + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + + // NodeSignatures 验证 + DigitalSignature[] dataNodeSignatures = data.getNodeSignatures(); + DigitalSignature[] resolvedNodeSignatures = resolvedData.getNodeSignatures(); + for (int i = 0; i < dataNodeSignatures.length; i++) { + assertEquals(dataNodeSignatures[i].getPubKey(), resolvedNodeSignatures[i].getPubKey()); + assertEquals(dataNodeSignatures[i].getDigest(), resolvedNodeSignatures[i].getDigest()); + } + + assertEqual(data.getTransactionContent(), resolvedData.getTransactionContent()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_Transaction() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, Transaction.class); + Transaction resolvedData = BinaryEncodingUtils.decode(serialBytes); + + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getExecutionState(), data.getExecutionState()); + assertEquals(resolvedData.getHash(), data.getHash()); + assertEquals(resolvedData.getBlockHeight(), data.getBlockHeight()); + + // EndpointSignatures 验证 + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + + // NodeSignatures 验证 + DigitalSignature[] dataNodeSignatures = data.getNodeSignatures(); + DigitalSignature[] resolvedNodeSignatures = resolvedData.getNodeSignatures(); + for (int i = 0; i < dataNodeSignatures.length; i++) { + assertEquals(dataNodeSignatures[i].getPubKey(), resolvedNodeSignatures[i].getPubKey()); + assertEquals(dataNodeSignatures[i].getDigest(), resolvedNodeSignatures[i].getDigest()); + } + + assertEqual(data.getTransactionContent(), resolvedData.getTransactionContent()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_LedgerDataSnapshot() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, LedgerDataSnapshot.class); + LedgerDataSnapshot resolvedData = BinaryEncodingUtils.decode(serialBytes); + + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getAdminAccountHash(), data.getAdminAccountHash()); + assertEquals(resolvedData.getContractAccountSetHash(), data.getContractAccountSetHash()); + assertEquals(resolvedData.getDataAccountSetHash(), data.getDataAccountSetHash()); + assertEquals(resolvedData.getUserAccountSetHash(), data.getUserAccountSetHash()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_NodeRequest() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, NodeRequest.class); + NodeRequest resolvedData = BinaryEncodingUtils.decode(serialBytes); + + System.out.println("------Assert start ------"); + // EndpointSignatures 验证 + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + + // NodeSignatures 验证 + DigitalSignature[] dataNodeSignatures = data.getNodeSignatures(); + DigitalSignature[] resolvedNodeSignatures = resolvedData.getNodeSignatures(); + for (int i = 0; i < dataNodeSignatures.length; i++) { + assertEquals(dataNodeSignatures[i].getPubKey(), resolvedNodeSignatures[i].getPubKey()); + assertEquals(dataNodeSignatures[i].getDigest(), resolvedNodeSignatures[i].getDigest()); + } + + assertEqual(data.getTransactionContent(), resolvedData.getTransactionContent()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_EndpointRequest() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, EndpointRequest.class); + EndpointRequest resolvedData = BinaryEncodingUtils.decode(serialBytes); + + System.out.println("------Assert start ------"); + // EndpointSignatures 验证 + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + assertEqual(data.getTransactionContent(), resolvedData.getTransactionContent()); + System.out.println("------Assert OK ------"); + } + + private void assertEqual(TransactionContent dataTxContent, TransactionContent resolvedTxContent) { + assertEquals(dataTxContent.getHash(), resolvedTxContent.getHash()); + assertEquals(dataTxContent.getLedgerHash(), resolvedTxContent.getLedgerHash()); +// assertEquals(dataTxContent.getSequenceNumber(), resolvedTxContent.getSequenceNumber()); +// assertEquals(dataTxContent.getSubjectAccount(), resolvedTxContent.getSubjectAccount()); + } + + private TransactionStagedSnapshot initTransactionStagedSnapshot() { + TransactionStagedSnapshot transactionStagedSnapshot = new TransactionStagedSnapshot(); + transactionStagedSnapshot.setAdminAccountHash(new HashDigest(CryptoAlgorithm.SHA256, "zhangsan".getBytes())); + transactionStagedSnapshot.setContractAccountSetHash(new HashDigest(CryptoAlgorithm.SHA256, "lisi".getBytes())); + transactionStagedSnapshot.setDataAccountSetHash(new HashDigest(CryptoAlgorithm.SHA256, "wangwu".getBytes())); + transactionStagedSnapshot.setUserAccountSetHash(new HashDigest(CryptoAlgorithm.SHA256, "zhaoliu".getBytes())); + return transactionStagedSnapshot; + } + + private TxRequestMessage initTxRequestMessage() throws Exception { + TxRequestMessage txRequestMessage = new TxRequestMessage(initTransactionContent()); + + SignatureDigest digest1 = new SignatureDigest(CryptoAlgorithm.ED25519, "zhangsan".getBytes()); + SignatureDigest digest2 = new SignatureDigest(CryptoAlgorithm.ED25519, "lisi".getBytes()); + DigitalSignatureBlob endPoint1 = new DigitalSignatureBlob(new PubKey(CryptoAlgorithm.ED25519, "jd1.com".getBytes()) + , digest1); + DigitalSignatureBlob endPoint2 = new DigitalSignatureBlob(new PubKey(CryptoAlgorithm.ED25519, "jd2.com".getBytes()) + , digest2); + txRequestMessage.addEndpointSignatures(endPoint1); + txRequestMessage.addEndpointSignatures(endPoint2); + + SignatureDigest digest3 = new SignatureDigest(CryptoAlgorithm.ED25519, "wangwu".getBytes()); + SignatureDigest digest4 = new SignatureDigest(CryptoAlgorithm.ED25519, "zhaoliu".getBytes()); + DigitalSignatureBlob node1 = new DigitalSignatureBlob(new PubKey(CryptoAlgorithm.ED25519, "jd3.com".getBytes()) + , digest3); + DigitalSignatureBlob node2 = new DigitalSignatureBlob(new PubKey(CryptoAlgorithm.ED25519, "jd4.com".getBytes()) + , digest4); + txRequestMessage.addNodeSignatures(node1); + txRequestMessage.addNodeSignatures(node2); + + return txRequestMessage; + } + + private TransactionContent initTransactionContent() throws Exception{ + TxContentBlob contentBlob = null; + BlockchainKeyPair id = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + HashDigest ledgerHash = CryptoUtils.hash(CryptoAlgorithm.SHA256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); + BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); + contentBlob = new TxContentBlob(ledgerHash); + contentBlob.setHash(new HashDigest(CryptoAlgorithm.SHA256, "jd.com".getBytes())); +// contentBlob.setSubjectAccount(id.getAddress()); +// contentBlob.setSequenceNumber(1); + DataAccountKVSetOperation kvsetOP = opFactory.dataAccount(id.getAddress()).set("Name", ByteArray.fromString("AAA", "UTF-8"), -1).getOperation(); + contentBlob.addOperation(kvsetOP); + return contentBlob; + } +} \ No newline at end of file diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java new file mode 100644 index 00000000..6a93df07 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleDataSetTest.java @@ -0,0 +1,367 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; +import java.util.Set; + +import org.junit.Test; +import org.springframework.util.StringUtils; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.MerkleDataSet; +import com.jd.blockchain.ledger.core.MerkleProof; +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +public class MerkleDataSetTest { + + /** + * 测试存储的增长; + */ + @Test + public void testStorageIncreasement() { + String keyPrefix = ""; + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + cryptoConfig.setAutoVerifyHash(true); + + MemoryKVStorage storage = new MemoryKVStorage(); + + MerkleDataSet mds = new MerkleDataSet(cryptoConfig, keyPrefix, storage, storage); + mds.setValue("A", "A".getBytes(), -1); + mds.setValue("B", "B".getBytes(), -1); + mds.setValue("C", "C".getBytes(), -1); + + mds.commit(); + + // 1个KV项的存储KEY的数量= 1 + 1(保存SN) + Merkle节点数量; + // 所以:3 项; + // so the expected item count in storage is 10; + int expStorageCount = 10; + assertEquals(expStorageCount, storage.getStorageCount()); + + mds.setValue("B", "B".getBytes(), 0); + mds.setValue("C", "C".getBytes(), 0); + mds.commit(); + + // Version changed only;仅仅增加 merkle 节点,此时 Merkle 树只有 1 层路径节点,因此只更新2个数据节点和 1 + // 个路径节点;(注:版本值是在同一个 key 下按序列保存的); + expStorageCount = expStorageCount + 3; + assertEquals(expStorageCount, storage.getStorageCount()); + + mds.setValue("D", "DValue".getBytes(), -1); + mds.commit(); + + // New key added, include 1 versioning kv, 1 sn key, 2 merkle nodes; +// String[] keys = StringUtils.toStringArray(storage.keySet()); + Bytes[] keys = storage.keySet().toArray(new Bytes[0]); + expStorageCount = expStorageCount + 1 + 1 + 2; + assertEquals(expStorageCount, storage.getStorageCount()); + + // Check rollback function: Add some keys, and then rollback; + long v = mds.setValue("E", "E-values".getBytes(), -1); + assertEquals(v, 0); + String expEValue = new String(mds.getValue("E")); + assertEquals(expEValue, "E-values"); + + v = mds.setValue("F", "F-values".getBytes(), -1); + assertEquals(v, 0); + String expFValue = new String(mds.getValue("F")); + assertEquals(expFValue, "F-values"); + + v = mds.setValue("E", "E-values-1".getBytes(), 0); + assertEquals(v, 1); + expEValue = new String(mds.getValue("E")); + assertEquals(expEValue, "E-values-1"); + + mds.cancel(); + + byte[] bv = mds.getValue("E"); + assertNull(bv); + bv = mds.getValue("F"); + assertNull(bv); + + v = mds.getVersion("E"); + assertEquals(-1, v); + v = mds.getVersion("F"); + assertEquals(-1, v); + + // Expect that states has been recover; + keys = storage.keySet().toArray(new Bytes[0]); + assertEquals(expStorageCount, storage.getStorageCount()); + + // ------ + System.out.println("\r\n------------- storage keys --------------"); + Set storageKeys = storage.getStorageKeySet(); + int i = 0; + for (Bytes k : storageKeys) { + System.out.println(String.format("keys[%s]=%s", i, k)); + i++; + } + } + + @Test + public void testDataReload() { + String keyPrefix = ""; + Random rand = new Random(); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + cryptoConfig.setAutoVerifyHash(true); + + MemoryKVStorage storage = new MemoryKVStorage(); + + MerkleDataSet mds = new MerkleDataSet(cryptoConfig, keyPrefix, storage, storage); + + // 初始的时候没有任何数据,总是返回 null; + VersioningKVEntry verKVEntry = mds.getDataEntry("NULL_KEY"); + byte[] vbytes = mds.getValue("NULL_KEY"); + assertNull(verKVEntry); + assertNull(vbytes); + + Map dataVersions = new HashMap<>(); + Map dataValues = new HashMap<>(); + + Map> history = new LinkedHashMap<>(); + + HashDigest rootHash; + + // generate base data sample; + int count = 1024;// + rand.nextInt(1024); + String key; + byte[] data = new byte[64]; + long v; + MerkleProof proof; + for (int i = 0; i < count; i++) { + key = "data" + i; + rand.nextBytes(data); + v = mds.setValue(key, data, -1); + dataVersions.put(key, v); + dataValues.put(key + "_" + v, data); + assertEquals(v, 0); + } + mds.commit(); + + // Take snapshot; + { + rootHash = mds.getRootHash(); + Map snapshot = new HashMap<>(); + for (int i = 0; i < count; i++) { + key = "data" + i; + + proof = mds.getProof(key); + assertNotNull(proof); + assertEquals(rootHash, proof.getRootHash()); + + KeySnapshot ks = new KeySnapshot(); + ks.proof = proof; + ks.maxVersion = mds.getVersion(key); + + snapshot.put(key, ks); + } + history.put(rootHash, snapshot); + } + + // verify; + { + MerkleDataSet mdsReload = new MerkleDataSet(rootHash, cryptoConfig, keyPrefix, storage, storage, true); + // verify every keys; + Map snapshot = history.get(rootHash); + MerkleProof expProof; + for (int i = 0; i < count; i++) { + key = "data" + i; + proof = mdsReload.getProof(key); + assertNotNull(proof); + assertEquals(rootHash, proof.getRootHash()); + expProof = snapshot.get(key).proof; + assertEquals(expProof.toString(), proof.toString()); + } + } + + // generate multi-version data sample; + long expVer; + long maxVer = 0; + long minIdx = count; + long maxIdx = 0; + for (int t = 0; t < 100; t++) { + int bound = rand.nextInt(500) + 1; + for (int i = rand.nextInt(count); i < count; i = i + rand.nextInt(bound) + 1) { + key = "data" + i; + rand.nextBytes(data); + expVer = dataVersions.get(key); + v = mds.setValue(key, data, expVer); + + assertEquals(v, expVer + 1); + + dataVersions.put(key, v); + + dataValues.put(key + "_" + v, data); + + maxVer = v > maxVer ? v : maxVer; + minIdx = i < minIdx ? i : minIdx; + maxIdx = i > maxIdx ? i : maxIdx; + } + mds.commit(); + + assertNotEquals(rootHash, mds.getRootHash()); + + // Take snapshot; + { + rootHash = mds.getRootHash(); + Map snapshot = new HashMap<>(); + for (int i = 0; i < count; i++) { + key = "data" + i; + + proof = mds.getProof(key); + assertNotNull(proof); + assertEquals(rootHash, proof.getRootHash()); + + KeySnapshot ks = new KeySnapshot(); + ks.proof = proof; + ks.maxVersion = mds.getVersion(key); + snapshot.put(key, ks); + } + history.put(rootHash, snapshot); + } + } + + System.out.println( + String.format("total count=%s; from %s to %s, max version=%s;", count, minIdx, maxIdx, maxVer)); + + { + for (HashDigest hisRootHash : history.keySet()) { + Map snapshot = history.get(hisRootHash); + + MerkleDataSet mdsReload = new MerkleDataSet(hisRootHash, cryptoConfig, keyPrefix, storage, storage, + true); + assertEquals(hisRootHash, mdsReload.getRootHash()); + + // verify every keys; + for (int i = 0; i < count; i++) { + key = "data" + i; + // 最新版本一致; + long expLatestVersion = snapshot.get(key).maxVersion; + long actualLatestVersion = mdsReload.getVersion(key); + assertEquals(expLatestVersion, actualLatestVersion); + + // 数据证明一致; + proof = mdsReload.getProof(key); + assertNotNull(proof); + + MerkleProof expProof = snapshot.get(key).proof; + assertEquals(expProof, proof); + + maxVer = dataVersions.get(key); + assertTrue(actualLatestVersion > -1); + assertTrue(actualLatestVersion <= maxVer); + for (long j = 0; j < actualLatestVersion; j++) { + String keyver = key + "_" + j; + byte[] expValue = dataValues.get(keyver); + byte[] actualValue = mdsReload.getValue(key, j); + assertTrue(BytesUtils.equals(expValue, actualValue)); + } + } + } + } + } + + @Test + public void testInsertSameData() { + String keyPrefix = ""; + Random rand = new Random(); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + cryptoConfig.setAutoVerifyHash(true); + + MemoryKVStorage storage = new MemoryKVStorage(); + + MerkleDataSet mds = new MerkleDataSet(cryptoConfig, keyPrefix, storage, storage); + + // 初始的时候没有任何数据,总是返回 null; + VersioningKVEntry verKVEntry = mds.getDataEntry("NULL_KEY"); + byte[] vbytes = mds.getValue("NULL_KEY"); + assertNull(verKVEntry); + assertNull(vbytes); + + Map dataVersions = new HashMap<>(); + // Map dataValues = new HashMap<>(); + + Map> history = new LinkedHashMap<>(); + + HashDigest rootHash; + + // generate base data sample; + int count = 1024;// + rand.nextInt(1024); + String key; + byte[] data = new byte[64]; + rand.nextBytes(data); + long v; + MerkleProof proof; + for (int i = 0; i < count; i++) { + key = "data" + i; + v = mds.setValue(key, data, -1); + dataVersions.put(key, v); + // dataValues.put(key + "_" + v, data); + assertEquals(v, 0); + } + mds.commit(); + + // Take snapshot; + { + rootHash = mds.getRootHash(); + Map snapshot = new HashMap<>(); + for (int i = 0; i < count; i++) { + key = "data" + i; + + proof = mds.getProof(key); + assertNotNull(proof); + assertEquals(rootHash, proof.getRootHash()); + + KeySnapshot ks = new KeySnapshot(); + ks.proof = proof; + ks.maxVersion = mds.getVersion(key); + + snapshot.put(key, ks); + } + history.put(rootHash, snapshot); + } + + // verify; + { + MerkleDataSet mdsReload = new MerkleDataSet(rootHash, cryptoConfig, keyPrefix, storage, storage, true); + // verify every keys; + Map snapshot = history.get(rootHash); + MerkleProof expProof; + for (int i = 0; i < count; i++) { + key = "data" + i; + proof = mdsReload.getProof(key); + assertNotNull(proof); + assertEquals(rootHash, proof.getRootHash()); + expProof = snapshot.get(key).proof; + assertEquals(expProof.toString(), proof.toString()); + + byte[] value = mdsReload.getValue(key); + assertTrue(BytesUtils.equals(data, value)); + } + } + } + + private static class KeySnapshot { + private MerkleProof proof; + private long maxVersion; + + } +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleTreeTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleTreeTest.java new file mode 100644 index 00000000..7a3b9140 --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/MerkleTreeTest.java @@ -0,0 +1,788 @@ +package test.com.jd.blockchain.ledger; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.TreeMap; + +import org.junit.Test; +import org.mockito.Mockito; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.core.MerkleDataNode; +import com.jd.blockchain.ledger.core.MerkleNode; +import com.jd.blockchain.ledger.core.MerkleProof; +import com.jd.blockchain.ledger.core.MerkleTree; +import com.jd.blockchain.storage.service.utils.ExistancePolicyKVStorageMap; +import com.jd.blockchain.utils.Bytes; + +public class MerkleTreeTest { + + private static String keyPrefix = ""; + + @Test + public void testWriteAndRead() { + + Random rand = new Random(); + + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap kvs1 = new ExistancePolicyKVStorageMap(); + MerkleTree mkt = new MerkleTree(setting, Bytes.fromString(keyPrefix), kvs1); + + // 初始化,按照顺序的序列号加入10条记录; + int count = 18; + byte[] dataBuf = new byte[16]; + MerkleDataNode[] dataNodes = new MerkleDataNode[count]; + for (int i = 0; i < count; i++) { + rand.nextBytes(dataBuf); + long sn = i; + dataNodes[i] = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + assertEquals(sn, dataNodes[i].getSN()); + } + mkt.commit(); + + HashDigest rootHash = mkt.getRootHash(); + assertNotNull(rootHash); + long maxSN = mkt.getMaxSn(); + long dataCount = mkt.getDataCount(); + assertEquals(count, dataCount); + assertEquals(dataCount - 1, maxSN);// 仅仅在采用顺序加入的情况下成立; + + // reload; + mkt = new MerkleTree(rootHash, setting, keyPrefix, kvs1, false); + // 校验是否与之前的一致; + maxSN = mkt.getMaxSn(); + dataCount = mkt.getDataCount(); + assertEquals(count, dataCount); + assertEquals(dataCount - 1, maxSN);// 仅仅在采用顺序加入的情况下成立; + + // 取每一个数据节点 + for (int i = 0; i <= maxSN; i++) { + MerkleDataNode dataNode = mkt.getData(i); + assertEquals(i, dataNode.getSN()); + assertEquals(dataNodes[i].getNodeHash(), dataNode.getNodeHash()); + assertEquals(dataNodes[i].getKey(), dataNode.getKey()); + assertEquals(dataNodes[i].getLevel(), dataNode.getLevel()); + assertEquals(dataNodes[i].getVersion(), dataNode.getVersion()); + } + } + + /** + * 测试以单次提交的方式顺序地插入数据; + */ + @Test + public void testSequenceInsert_OneCommit() { + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap kvs1 = new ExistancePolicyKVStorageMap(); + + // 创建空的的树; + MerkleTree mkt = new MerkleTree(setting, keyPrefix, kvs1); + + // 查询空树的最大序列号,将返回 -1; + long maxSN = mkt.getMaxSn(); + assertEquals(-1, maxSN); + + long sn = 0; + // 加入 4097 条数据记录,预期构成以一颗 4 层 16 叉树; + int count = 4097; + byte[] dataBuf = new byte[16]; + Random rand = new Random(); + for (int i = 0; i < count; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + // 检查节点数; + assertEquals(count, mkt.getDataCount()); + + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(sn - 1, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; + long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + MerkleProof proof = null; + for (int i = 0; i <= maxSN; i++) { + proof = mkt.getProof(i); + assertNotNull(proof); + } + } + + /** + * 测试以多次提交的方式顺序地插入数据; + */ + @Test + public void testSequenceInsert_MultiCommit() { + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap kvs1 = new ExistancePolicyKVStorageMap(); + MerkleTree mkt = new MerkleTree(setting, keyPrefix, kvs1); + + long sn = 0; + // 初始化,加入10条记录,预期目前的树只有一层; + int count = 10; + byte[] dataBuf = new byte[16]; + Random rand = new Random(); + for (int i = 0; i < count; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + assertEquals(count, mkt.getDataCount()); + // 检查最大序列号的正确性; + long maxSN = mkt.getMaxSn(); + assertEquals(sn - 1, maxSN); + + assertEquals(1, mkt.getLevel()); + + // 路径节点 + 数据节点;1 层只有一个路径节点; + long expectedNodes = 1 + count; + assertEquals(11, expectedNodes); + + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + MerkleProof proof = null; + for (int i = 0; i <= maxSN; i++) { + proof = mkt.getProof(i); + assertNotNull(proof); + } + + // -------------------------- + // 再加入 6 条记录;总共 16 条,预期目前的树也只有一层; + for (int i = 0; i < 6; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + count = 16; + assertEquals(count, mkt.getDataCount()); + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(sn - 1, maxSN); + + // 预期只有 1 层; + assertEquals(1, mkt.getLevel()); + + // 路径节点 + 数据节点;1 层只有一个路径节点;新增了1个路径节点,以及新增 6 个数据节点; + // 注:上一次未填充满的路径节点由于哈希变化而重新生成,因此多出 1 条; + expectedNodes = expectedNodes + 1 + 6; + assertEquals(expectedNodes, kvs1.getCount()); + + // -------------------------- + // 再加入 10 条记录;总共 26 条数据,预期目前的树有2层[17-32]; + for (int i = 0; i < 10; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + count = 26; + assertEquals(count, mkt.getDataCount()); + // 预期扩展到 2 层; + assertEquals(2, mkt.getLevel()); + + // 路径节点 + 数据节点;扩展到 2 层, 新增加了2个路径节点(包括:新的根节点和其中1个子节点),以及10个新增的数据节点; + expectedNodes = expectedNodes + 2 + 10; + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + for (int i = 0; i <= maxSN; i++) { + proof = mkt.getProof(i); + assertNotNull(proof); + } + + // ---------------------------------------------------------------- + // 再加入 230 条记录,总共 256 条数据记录,预期构成以一颗满的 2 层 16 叉树; + for (int i = 0; i < 230; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + count = 256; + assertEquals(count, mkt.getDataCount()); + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(sn - 1, maxSN); + + // 预期仍然维持 2 层; + assertEquals(2, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 由于原来的两个未满的路径节点重新计算 hash 而新增加了2个路径节点,新增14个 Level(1) 的路径节点,加上230个新增数据记录; + expectedNodes = expectedNodes + 2 + 14 + 230; + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + for (int i = 0; i <= maxSN; i++) { + proof = mkt.getProof(i); + assertNotNull(proof); + } + + // ---------------------------------------------------------------- + // 再加入 3840 条记录,总共 4096 条数据记录,预期构成以一颗满的 3 层 16 叉树; + for (int i = 0; i < 3840; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + count = 4096; + assertEquals(count, mkt.getDataCount()); + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(sn - 1, maxSN); + + // 预期扩展到 3 层; + assertEquals(3, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 3 层的满16叉树,新增256的路径节点,加上3840个新增数据记录; + expectedNodes = expectedNodes + 256 + 3840; + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + for (int i = 0; i <= maxSN; i++) { + proof = mkt.getProof(i); + assertNotNull(proof); + } + + // ---------------------------------------------------------------- + // 再加入 1 条记录,总共 4097 条数据记录,预期构成以一颗 4 层 16 叉树; + for (int i = 0; i < 1; i++) { + rand.nextBytes(dataBuf); + mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + sn++; + } + mkt.commit(); + + count = 4097; + assertEquals(count, mkt.getDataCount()); + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(sn - 1, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,新增4个路径节点,加上1个新增数据记录; + expectedNodes = expectedNodes + 4 + 1; + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + for (int i = 0; i <= maxSN; i++) { + proof = mkt.getProof(i); + assertNotNull(proof); + } + } + + /** + * 测试以多次提交的方式随机地插入数据; + */ + @Test + public void testRandomInsert_MultiCommit() { + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 保存所有写入的数据节点的 SN-Hash 映射表; + TreeMap dataNodes = new TreeMap<>(); + MerkleNode nd; + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap kvs1 = new ExistancePolicyKVStorageMap(); + MerkleTree mkt = new MerkleTree(setting, keyPrefix, kvs1); + + // 加入 30 条数据记录,分两批各15条分别从序号两端加入,预期构成以一颗 4 层 16 叉树; + int count = 4097; + byte[] dataBuf = new byte[16]; + Random rand = new Random(); + long sn = 0; + for (int i = 0; i < 15; i++) { + rand.nextBytes(dataBuf); + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + sn++; + } + sn = count - 1; + + for (int i = 0; i < 15; i++) { + rand.nextBytes(dataBuf); + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + sn--; + } + mkt.commit(); + + assertEquals(30, mkt.getDataCount()); + // 检查最大序列号的正确性; + long maxSN = mkt.getMaxSn(); + assertEquals(count - 1, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,共9个路径节点,加30个数据节点; + long expectedNodes = 9 + 30; + assertEquals(expectedNodes, kvs1.getCount()); + + // --------------------------------- + // 在 15 - 4081 之间随机插入; + int randomInterval = 4082 - 15; + List snPool = new LinkedList<>(); + for (int i = 0; i < randomInterval; i++) { + snPool.add(15L + i); + } + for (int i = 0; i < randomInterval; i++) { + int selected = rand.nextInt(snPool.size()); + sn = snPool.remove(selected).longValue(); + rand.nextBytes(dataBuf); + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + } + mkt.commit(); + + assertEquals(4097, mkt.getDataCount()); + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(count - 1, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,之前的路径节点全部被重新计算哈希, 等同于在3层满16叉树之上加入1条记录,共273路径节点,加上 sn 为 4096 + // 的数据对于 4个路径节点(由于是上次写入的,4个路径节点中本次只更新了根节点); + expectedNodes = expectedNodes + 273 + 1 + randomInterval; + assertEquals(expectedNodes, kvs1.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + MerkleProof proof = null; + for (Long n : dataNodes.keySet()) { + proof = mkt.getProof(n.longValue()); + assertNotNull(proof); + assertEquals(dataNodes.get(n), proof.getHash(0)); + } + } + + /** + * 测试以单次提交的方式顺序地插入数据; + */ + @Test + public void testDataModify() { + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 保存所有写入的数据节点的 SN-Hash 映射表; + TreeMap dataNodes = new TreeMap<>(); + MerkleNode nd; + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap storage = new ExistancePolicyKVStorageMap(); + + // 创建空的的树; + MerkleTree mkt = new MerkleTree(setting, keyPrefix, storage); + + long sn = 0; + // 加入 4097 条数据记录,预期构成以一颗 4 层 16 叉树; + int count = 4097; + byte[] dataBuf = new byte[16]; + Random rand = new Random(); + for (int i = 0; i < count; i++) { + rand.nextBytes(dataBuf); + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + sn++; + } + mkt.commit(); + + // 检查节点数; + assertEquals(count, mkt.getDataCount()); + + // 检查最大序列号的正确性; + long expectedMaxSN = 4096;// count-1; + long maxSN = mkt.getMaxSn(); + assertEquals(expectedMaxSN, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; + long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; + assertEquals(expectedNodes, storage.getCount()); + + // 覆盖到每一路分支修改数据节点; + int storageDataCountBefore = storage.getCount(); + // maxSn = 4096; + int i; + for (i = 0; i <= maxSN; i += 16) { + rand.nextBytes(dataBuf); + sn = i; + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + } + + mkt.commit(); + // 检查节点数; + assertEquals(count, mkt.getDataCount()); + + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + assertEquals(expectedMaxSN, maxSN); + + // 由于覆盖到每一个分支节点,全部分支节点都重新生成,因此: + // 新增节点数=修改的数据节点数 + 全部分支节点数; + long addCounts = i / 16 + getMaxPathNodeCount(3) + 4; + assertEquals(storageDataCountBefore + addCounts, storage.getCount()); + + // 验证每一个数据节点都产生了存在性证明; + MerkleProof proof = null; + for (Long n : dataNodes.keySet()) { + proof = mkt.getProof(n.longValue()); + assertNotNull(proof); + HashDigest expHash = dataNodes.get(n); + assertEquals(expHash.toBase58(), proof.getHash(0).toBase58()); + } + + } + + /** + * 测试单独修改版本而不变更数据时,是否能够正确地更新 merkle 树;; + */ + @Test + public void testDataVersionModify() { + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 保存所有写入的数据节点的 SN-Hash 映射表; + TreeMap dataNodes = new TreeMap<>(); + MerkleNode nd; + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap storage = new ExistancePolicyKVStorageMap(); + + // 创建空的的树; + MerkleTree mkt = new MerkleTree(setting, keyPrefix, storage); + + long sn = 0; + // 加入 4097 条数据记录,预期构成以一颗 4 层 16 叉树; + int count = 4097; + byte[] dataBuf = new byte[16]; + Random rand = new Random(); + for (int i = 0; i < count; i++) { + rand.nextBytes(dataBuf); + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + sn++; + } + mkt.commit(); + byte[] dataOfMaxSnNode = Arrays.copyOf(dataBuf, dataBuf.length); + + // 检查节点数; + assertEquals(count, mkt.getDataCount()); + + // 检查最大序列号的正确性; + long expectedMaxSN = 4096;// count-1; + long maxSN = mkt.getMaxSn(); + assertEquals(expectedMaxSN, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; + long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; + assertEquals(expectedNodes, storage.getCount()); + + // 仅仅更新最大的 sn 的数据节点的版本(由 0 升级为 1),预期将产生 4 个更新的路径节点和 1 个新的数据节点; + long currNodes = expectedNodes; + mkt.setData(maxSN, "KEY-" + maxSN, 1, dataOfMaxSnNode); + mkt.commit(); + + // 验证; + maxSN = mkt.getMaxSn(); + assertEquals(expectedMaxSN, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期将产生 4 个更新的路径节点和 1 个新的数据节点; + expectedNodes = currNodes + 4 + 1; + assertEquals(expectedNodes, storage.getCount()); + } + + /** + * 测试从存储重新加载 Merkle 树的正确性; + */ + @Test + public void testMerkleReload() { + CryptoSetting setting = Mockito.mock(CryptoSetting.class); + when(setting.getHashAlgorithm()).thenReturn(CryptoAlgorithm.SHA256); + when(setting.getAutoVerifyHash()).thenReturn(true); + + // 保存所有写入的数据节点的 SN-Hash 映射表; + TreeMap dataNodes = new TreeMap<>(); + MerkleNode nd; + + // 测试从空的树开始,顺序增加数据节点; + ExistancePolicyKVStorageMap storage = new ExistancePolicyKVStorageMap(); + + // 创建空的的树; + MerkleTree mkt = new MerkleTree(setting, keyPrefix, storage); + + long sn = 0; + // 加入 4097 条数据记录,预期构成以一颗 4 层 16 叉树; + int count = 4097; + byte[] dataBuf = new byte[16]; + Random rand = new Random(); + for (int i = 0; i < count; i++) { + rand.nextBytes(dataBuf); + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + sn++; + } + mkt.commit(); + + // 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验; + HashDigest r1_rootHash = mkt.getRootHash(); + long r1_dataCount = mkt.getDataCount(); + long r1_maxSN = mkt.getMaxSn(); + long r1_sn1 = r1_maxSN; + String r1_proof1 = mkt.getProof(r1_sn1).toString(); + long r1_sn2 = 1024; + String r1_proof2 = mkt.getProof(r1_sn2).toString(); + + { + // 检查节点数; + assertEquals(count, mkt.getDataCount()); + + // 检查最大序列号的正确性; + long maxSN = mkt.getMaxSn(); + long expectedMaxSN = 4096;// count-1; + assertEquals(expectedMaxSN, maxSN); + + // 预期扩展到 4 层; + assertEquals(4, mkt.getLevel()); + + // 路径节点 + 数据节点; + // 预期扩展为 4 层16叉树,由 3 层满16叉树扩展 1 新分支(4个路径节点)而形成; + long expectedNodes = getMaxPathNodeCount(3) + 4 + 4097; + assertEquals(expectedNodes, storage.getCount()); + } + + // 覆盖到每一路分支修改数据节点; + int storageDataCountBefore = storage.getCount(); + // maxSn = 4096; + long maxSN = mkt.getMaxSn(); + int i; + for (i = 0; i <= maxSN; i += 16) { + rand.nextBytes(dataBuf); + sn = i; + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + } + + mkt.commit(); + + // 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验; + HashDigest r2_rootHash = mkt.getRootHash(); + long r2_dataCount = mkt.getDataCount(); + long r2_maxSN = mkt.getMaxSn(); + long r2_sn1 = r1_sn1; + String r2_proof1 = mkt.getProof(r2_sn1).toString(); + long r2_sn2 = r1_sn2; + String r2_proof2 = mkt.getProof(r2_sn2).toString(); + + { + // 检查节点数; + assertEquals(count, mkt.getDataCount()); + + assertEquals(r1_dataCount, r2_dataCount); + + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + long expectedMaxSN = 4096;// count-1; + assertEquals(expectedMaxSN, maxSN); + + // 由于覆盖到每一个分支节点,全部分支节点都重新生成,因此: + // 新增节点数=修改的数据节点数 + 全部分支节点数; + long addCounts = i / 16 + getMaxPathNodeCount(3) + 4; + assertEquals(storageDataCountBefore + addCounts, storage.getCount()); + } + + // 新插入数据; + final int NEW_INSERTED_COUNT = 18; + for (i = 0; i < NEW_INSERTED_COUNT; i++) { + rand.nextBytes(dataBuf); + sn = maxSN + 1 + i; + nd = mkt.setData(sn, "KEY-" + sn, 0, dataBuf); + dataNodes.put(sn, nd.getNodeHash()); + } + mkt.commit(); + + // 验证每一个数据节点都产生了存在性证明; + MerkleProof proof = null; + for (Long n : dataNodes.keySet()) { + proof = mkt.getProof(n.longValue()); + assertNotNull(proof); + assertEquals(dataNodes.get(n), proof.getHash(0)); + } + + // 记录一次提交的根哈希以及部分节点信息,用于后续的加载校验; + HashDigest r3_rootHash = mkt.getRootHash(); + long r3_maxSN = mkt.getMaxSn(); + long r3_sn1 = r2_sn1; + String r3_proof1 = mkt.getProof(r3_sn1).toString(); + long r3_sn2 = r2_sn2; + String r3_proof2 = mkt.getProof(r3_sn2).toString(); + long r3_sn3 = 4096 + NEW_INSERTED_COUNT - 5; + String r3_proof3 = mkt.getProof(r3_sn3).toString(); + + { + // 检查节点数; + assertEquals(count + NEW_INSERTED_COUNT, mkt.getDataCount()); + + // 检查最大序列号的正确性; + maxSN = mkt.getMaxSn(); + long expectedMaxSN = 4096 + NEW_INSERTED_COUNT;// count-1; + assertEquals(expectedMaxSN, maxSN); + } + + // -------------------- + // 重新从存储加载生成新的 MerkleTree 实例,验证与初始实例的一致性; + // 从第 2 轮提交的 Merkle 根哈希加载; + MerkleTree r1_mkt = new MerkleTree(r1_rootHash, setting, keyPrefix, storage, true); + assertEquals(r1_maxSN, r1_mkt.getMaxSn()); + assertEquals(r1_rootHash, r1_mkt.getRootHash()); + assertEquals(r1_dataCount, r1_mkt.getDataCount()); + assertEquals(r1_proof1, r1_mkt.getProof(r1_sn1).toString()); + assertEquals(r1_proof2, r1_mkt.getProof(r1_sn2).toString()); + + // 从第 2 轮提交的 Merkle 根哈希加载; + // 第 2 轮生成的 Merkle 树是对第 1 轮的数据的全部节点的修改,因此同一个 SN 的节点的证明是不同的; + MerkleTree r2_mkt = new MerkleTree(r2_rootHash, setting, keyPrefix, storage, true); + assertEquals(r1_maxSN, r2_mkt.getMaxSn()); + assertEquals(r1_dataCount, r2_mkt.getDataCount()); + + assertNotEquals(r1_rootHash, r2_mkt.getRootHash()); + assertNotEquals(r1_proof1, r2_mkt.getProof(r1_sn1).toString()); + assertNotEquals(r1_proof2, r2_mkt.getProof(r1_sn2).toString()); + + assertEquals(r2_maxSN, r2_mkt.getMaxSn()); + assertEquals(r2_rootHash, r2_mkt.getRootHash()); + assertEquals(r2_dataCount, r2_mkt.getDataCount()); + assertEquals(r2_proof1, r2_mkt.getProof(r2_sn1).toString()); + assertEquals(r2_proof2, r2_mkt.getProof(r2_sn2).toString()); + + // 从第 3 轮提交的 Merkle 根哈希加载; + // 第 3 轮生成的 Merkle 树是在第 2 轮的数据基础上做新增,因此非新增的同一个 SN 的节点的Merkle证明是相同的; + MerkleTree r3_mkt = new MerkleTree(r3_rootHash, setting, keyPrefix, storage, true); + assertEquals(r2_maxSN + NEW_INSERTED_COUNT, r3_mkt.getMaxSn()); + assertNotEquals(r2_rootHash, r3_mkt.getRootHash()); + assertEquals(r2_dataCount + NEW_INSERTED_COUNT, r3_mkt.getDataCount()); + + assertEquals(r3_maxSN, r3_mkt.getMaxSn()); + assertEquals(r3_rootHash, r3_mkt.getRootHash()); + assertEquals(r3_proof1, r3_mkt.getProof(r3_sn1).toString()); + assertEquals(r3_proof2, r3_mkt.getProof(r3_sn2).toString()); + assertEquals(r3_proof3, r3_mkt.getProof(r3_sn3).toString()); + + // 验证每一个数据节点都产生了存在性证明; + for (Long n : dataNodes.keySet()) { + proof = r3_mkt.getProof(n.longValue()); + assertNotNull(proof); + assertEquals(dataNodes.get(n), proof.getHash(0)); + } + } + + private static int getLevel(long dataCount) { + if (dataCount < 0) { + throw new IllegalArgumentException("The specified data count is negative!"); + } + int l = 1; + while (dataCount > power(MerkleTree.TREE_DEGREE, l)) { + l++; + } + return l; + } + + /** + * 计算整个 Merkle 树的路径节点的最大数量; + * + * @param level + * @return + */ + private static long getMaxPathNodeCount(int level) { + if (level < 1) { + throw new IllegalArgumentException("The specified level is less than 1!"); + } + long count = 0; + for (int i = 0; i < level; i++) { + count += power(MerkleTree.TREE_DEGREE, i); + } + return count; + } + + /** + * 计算 value 的 x 次方; + *

+ * 注:此方法不处理溢出;调用者需要自行规避; + * + * @param value + * @param x + * 大于等于 0 的整数; + * @return + */ + private static long power(long value, int x) { + if (x == 0) { + return 1; + } + long r = value; + for (int i = 1; i < x; i++) { + r *= value; + } + return r; + } +} diff --git a/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java new file mode 100644 index 00000000..7b28c41e --- /dev/null +++ b/source/ledger/ledger-core/src/test/java/test/com/jd/blockchain/ledger/TransactionBatchProcessorTest.java @@ -0,0 +1,392 @@ +//package test.com.jd.blockchain.ledger; +// +//import static org.junit.Assert.assertArrayEquals; +//import static org.junit.Assert.assertEquals; +//import static org.junit.Assert.assertNotNull; +//import static org.junit.Assert.assertTrue; +// +//import java.io.File; +//import java.io.FileInputStream; +//import java.io.IOException; +//import java.io.InputStream; +//import java.net.URISyntaxException; +//import java.net.URL; +//import java.nio.file.Paths; +// +//import org.junit.Before; +//import org.junit.Test; +// +//import com.jd.blockchain.binaryproto.DataContractRegistry; +//import com.jd.blockchain.contract.model.ContractPath; +//import com.jd.blockchain.crypto.CryptoAlgorithm; +//import com.jd.blockchain.crypto.CryptoUtils; +//import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +//import com.jd.blockchain.crypto.hash.HashDigest; +//import com.jd.blockchain.ledger.BlockchainKeyGenerator; +//import com.jd.blockchain.ledger.BlockchainKeyPair; +//import com.jd.blockchain.ledger.ContractCodeDeployOperation; +//import com.jd.blockchain.ledger.ContractEventSendOperation; +//import com.jd.blockchain.ledger.DataAccountKVSetOperation; +//import com.jd.blockchain.ledger.DataAccountRegisterOperation; +//import com.jd.blockchain.ledger.EndpointRequest; +//import com.jd.blockchain.ledger.LedgerBlock; +//import com.jd.blockchain.ledger.LedgerInitSetting; +//import com.jd.blockchain.ledger.LedgerTransaction; +//import com.jd.blockchain.ledger.NodeRequest; +//import com.jd.blockchain.ledger.TransactionBuilder; +//import com.jd.blockchain.ledger.TransactionContent; +//import com.jd.blockchain.ledger.TransactionContentBody; +//import com.jd.blockchain.ledger.TransactionRequest; +//import com.jd.blockchain.ledger.TransactionRequestBuilder; +//import com.jd.blockchain.ledger.TransactionResponse; +//import com.jd.blockchain.ledger.TransactionState; +//import com.jd.blockchain.ledger.UserRegisterOperation; +//import com.jd.blockchain.ledger.core.CryptoConfig; +//import com.jd.blockchain.ledger.core.DataAccountSet; +//import com.jd.blockchain.ledger.core.LedgerDataSet; +//import com.jd.blockchain.ledger.core.LedgerEditor; +//import com.jd.blockchain.ledger.core.LedgerRepository; +//import com.jd.blockchain.ledger.core.LedgerTransactionContext; +//import com.jd.blockchain.ledger.core.TransactionSet; +//import com.jd.blockchain.ledger.core.UserAccount; +//import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; +//import com.jd.blockchain.ledger.core.impl.LedgerManager; +//import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration; +//import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; +//import com.jd.blockchain.ledger.data.AddressEncoding; +//import com.jd.blockchain.ledger.data.ConsensusParticipantData; +//import com.jd.blockchain.ledger.data.LedgerInitSettingData; +//import com.jd.blockchain.ledger.data.TxBuilder; +//import com.jd.blockchain.storage.service.utils.MemoryKVStorage; +// +//import my.utils.Bytes; +//import my.utils.io.BytesUtils; +//import my.utils.net.NetworkAddress; +// +//public class TransactionBatchProcessorTest { +// private HashDigest ledgerHash = null; +// BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainKeyPair dataKey = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainKeyPair sponsorKey = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainKeyPair gatewayKey = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainKeyPair contractKey = BlockchainKeyGenerator.getInstance().generate(); +// TransactionRequest transactionRequest; +// String pubKeyVal = "jd.com"+Thread.currentThread(); +//// String userPubKeyVal = "this is user's pubKey"; +// byte[] chainCode; +// // 保存资产总数的键; +// private static final String KEY_TOTAL = "TOTAL"; +// //第二个参数; +// private static final String KEY_ABC = "abc"; +// // 采用基于内存的 Storage; +// MemoryKVStorage storage = new MemoryKVStorage(); +// +// @Before +// public void setUp(){ +// DataContractRegistry.register(TransactionContent.class); +// DataContractRegistry.register(TransactionContentBody.class); +// DataContractRegistry.register(TransactionRequest.class); +// DataContractRegistry.register(NodeRequest.class); +// DataContractRegistry.register(EndpointRequest.class); +// DataContractRegistry.register(TransactionResponse.class); +// DataContractRegistry.register(UserRegisterOperation.class); +// } +// +//// @After +// public void after() { +// //清理所有使用的临时文件; +// String outputPath = null; +// try { +// outputPath = ContractPath.getOutputPath(); +// }finally { +// deleteDir(new File(outputPath)); +// } +// } +// +// /** +// * 递归删除目录下的所有文件及子目录下所有文件 +// * @param dir 将要删除的文件目录 +// * @return boolean Returns "true" if all deletions were successful. +// * If a deletion fails, the method stops attempting to +// * delete and returns "false". +// */ +// private static boolean deleteDir(File dir) { +// if (dir.isDirectory()) { +// String[] children = dir.list(); +// //递归删除目录中的子目录下 +// for (int i=0; i + 4.0.0 + + + com.jd.blockchain + ledger + 0.8.2.RELEASE + + ledger-model + + + + com.jd.blockchain + binary-proto + ${project.version} + + + com.jd.blockchain + crypto-framework + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + com.jd.blockchain + utils-web + ${project.version} + + + + + + + + + \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountHeader.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountHeader.java new file mode 100644 index 00000000..d382295d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountHeader.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.ACCOUNT_HEADER) +public interface AccountHeader { + + @DataField(order=1, primitiveType = ValueType.BYTES) + Bytes getAddress(); + + @DataField(order=2, primitiveType = ValueType.BYTES) + PubKey getPubKey(); + + @DataField(order=3, primitiveType = ValueType.BYTES) + HashDigest getRootHash(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountImpl.java new file mode 100644 index 00000000..64e3c97e --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountImpl.java @@ -0,0 +1,406 @@ +//package com.jd.blockchain.ledger; +// +//import java.io.ByteArrayOutputStream; +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.OutputStream; +// +//import com.jd.blockchain.ledger.data.HashEncoding; +// +//import my.utils.io.ByteArray; +//import my.utils.io.BytesEncoding; +//import my.utils.io.BytesReader; +//import my.utils.io.BytesUtils; +//import my.utils.io.BytesWriter; +//import my.utils.io.NumberMask; +// +//public class AccountImpl implements BlockchainAccount, BytesWriter, BytesReader { +// public static final long INIT_TX_SEQUENCE_NUMBER = 0; // 初始交易流水号 +// public static final long INIT_MODEL_VERSION = 0; // 初始模型版本号 +// public static final long INIT_VERSION = 0; // 初始版本号 +// public static final long INIT_PRIVILLEGE_VERSION = 0; // 初始权限版本号 +// public static final long INIT_STATE_VERSION = 0; // 初始状态版本号 +// public static final long INIT_CODE_VERSION = 0; // 初始合约版本号 +// +// private BlockchainIdentity identity; +// private ByteArray ledgerHash; // 账本hash +// private long blockHeight; // 账户注册的区块高度 +// private long txSequenceNumber; // 交易流水号 +// private long modelVersion; // 账户模型版本 +// private long version; // 账户版本 +// private ByteArray privilegeHash; // 权限树根hash +// private long privilegeVersion; // 权限版本 +// private AccountStateType stateType; // 状态类型 +// private ByteArray stateHash; // 状态数根hash +// private long stateVersion; // 状态版本 +// private ByteArray code; // 合约代码 +// private long codeVersion; // 合约版本 +// private HashAlgorithm codeHashAlgorithm = HashAlgorithm.SHA256; +// private ByteArray codeHash; +// +// public AccountImpl() { +// } +// +// public AccountImpl(BlockchainIdentity identity, ByteArray ledgerHash, long blockHeight, long txSequenceNumber, +// long modelVersion, long version, ByteArray privilegeHash, long privilegeVersion, AccountStateType stateType, +// ByteArray stateHash, long stateVersion, ByteArray code, long codeVersion) { +// this.identity = identity; +// this.ledgerHash = ledgerHash; +// this.blockHeight = blockHeight; +// this.txSequenceNumber = txSequenceNumber; +// this.modelVersion = modelVersion; +// this.version = version; +// this.privilegeHash = privilegeHash; +// this.privilegeVersion = privilegeVersion; +// this.stateType = stateType; +// this.stateHash = stateHash; +// this.stateVersion = stateVersion; +// this.code = code; +// this.codeVersion = codeVersion; +// } +// +// public AccountImpl(BlockchainIdentity identity, ByteArray ledgerHash, long blockHeight, ByteArray privilegeHash, +// AccountStateType stateType, ByteArray stateHash, ByteArray code) { +// this.identity = identity; +// this.ledgerHash = ledgerHash; +// this.blockHeight = blockHeight; +// this.txSequenceNumber = INIT_TX_SEQUENCE_NUMBER; +// this.modelVersion = INIT_MODEL_VERSION; +// this.version = INIT_VERSION; +// this.privilegeHash = privilegeHash; +// this.privilegeVersion = INIT_PRIVILLEGE_VERSION; +// this.stateType = stateType; +// this.stateHash = stateHash; +// this.stateVersion = INIT_STATE_VERSION; +// this.code = code; +// this.codeVersion = INIT_CODE_VERSION; +// } +// +// @Override +// public void resolvFrom(InputStream in) throws IOException { +// BlockchainIdentity identity = new BlockchainIdentity(); +// identity.resolvFrom(in); +// ByteArray ledgerHash = HashEncoding.read(in); +// long blockHeight = BytesUtils.readLong(in); +// long txSeqNum = BytesUtils.readLong(in); +// long modelVersion = BytesUtils.readLong(in); +// long version = BytesUtils.readLong(in); +// ByteArray privilegeHash = HashEncoding.read(in); +// long privilegeVersion = BytesUtils.readLong(in); +// AccountStateType stateType = AccountStateType.valueOf(BytesUtils.readByte(in)); +// ByteArray stateHash = HashEncoding.read(in); +// long stateVersion = BytesUtils.readLong(in); +// +// ByteArray code = BytesEncoding.readAsByteArray(NumberMask.NORMAL, in); +// long codeVersion = BytesUtils.readLong(in); +// HashAlgorithm codeHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); +// ByteArray codeHash = HashEncoding.read(in); +// +// this.identity = identity; +// this.ledgerHash = ledgerHash; +// this.blockHeight = blockHeight; +// this.txSequenceNumber = txSeqNum; +// this.modelVersion = modelVersion; +// this.version = version; +// this.privilegeHash = privilegeHash; +// this.privilegeVersion = privilegeVersion; +// this.stateType = stateType; +// this.stateHash = stateHash; +// this.stateVersion = stateVersion; +// this.code = code; +// this.codeVersion = codeVersion; +// this.codeHashAlgorithm = codeHashAlgorithm; +// this.codeHash = codeHash; +// } +// +// @Override +// public void writeTo(OutputStream out) throws IOException { +// identity.writeTo(out); +// HashEncoding.write(ledgerHash, out); +// BytesUtils.writeLong(blockHeight, out); +// BytesUtils.writeLong(txSequenceNumber, out); +// BytesUtils.writeLong(modelVersion, out); +// BytesUtils.writeLong(version, out); +// HashEncoding.write(privilegeHash, out); +// BytesUtils.writeLong(privilegeVersion, out); +// BytesUtils.writeByte(stateType.getCODE(), out); +// HashEncoding.write(stateHash, out); +// BytesUtils.writeLong(stateVersion, out); +// +// BytesEncoding.write(code, NumberMask.NORMAL, out); +// BytesUtils.writeLong(codeVersion, out); +// BytesUtils.writeByte(codeHashAlgorithm.getAlgorithm(), out); +// HashEncoding.write(getCodeHash(), out); +// } +// +// /** +// * 地址; +// * +// * @return +// */ +// @Override +// public BlockchainIdentity getAddress() { +// return identity; +// } +// +// /** +// * 账户所属的账本的 hash;
+// *

+// * 注:账本的hash 等同于该账本的创世区块的 hash; +// * +// * @return +// */ +// @Override +// public ByteArray getLedgerHash() { +// return ledgerHash; +// } +// +// /** +// * 注册账户的区块高度;
+// *

+// * 注册此账户的区块高度; +// * +// * @return +// */ +// @Override +// public long getRegisteredHeight() { +// return blockHeight; +// } +// +// /** +// * 交易流水号;
+// *

+// * 账户的交易流水号初始为 0,当账户作为交易的科目账户(SubjectAccount )发起一个交易并被成功执行之后,账户的交易流水号增加1; +// * +// * @return +// */ +// @Override +// public long getTxSquenceNumber() { +// return txSequenceNumber; +// } +// +// /** +// * 账户模型版本;
+// *

+// * 表示构成一个账户结构的属性模型的程序版本号; +// * +// * @return +// */ +// @Override +// public long getModelVersion() { +// return modelVersion; +// } +// +// /** +// * 账户版本;
+// *

+// * 初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;注:交易序号的改变不会导致账户版本的增加; +// * +// * @return +// */ +// @Override +// public long getVersion() { +// return version; +// } +// +// /** +// * 权限 hash;
+// *

+// * 权限树的根hash; +// * +// * @return +// */ +// @Override +// public ByteArray getPrivilegeHash() { +// return privilegeHash; +// } +// +// /** +// * 权限版本;
+// *

+// * 初始为 0, 每次对权限的变更都导致版本号加 1; +// * +// * @return +// */ +// @Override +// public long getPrivilegeVersion() { +// return privilegeVersion; +// } +// +// /** +// * 状态类型;
+// *

+// * 账户的状态类型有3种:空类型(NIL);键值类型;对象类型;参考 {@link AccountStateType} +// * +// * @return +// */ +// @Override +// public AccountStateType getStateType() { +// return stateType; +// } +// +// /** +// * 状态版本;
+// *

+// * 初始为 0,每次对状态的更改都使得状态版本增加1; +// * +// * @return +// */ +// @Override +// public long getStateVersion() { +// return stateVersion; +// } +// +// /** +// * 状态哈希;
+// *

+// * 数据状态的 merkle tree 的根hash; +// * +// * @return +// */ +// @Override +// public ByteArray getStateHash() { +// return stateHash; +// } +// +// /** +// * 合约代码哈希;
+// *

+// * 由“账户地址+合约代码版本号+合约代码内容”生成的哈希; +// * +// * @return +// */ +// @Override +// public ByteArray getCodeHash() { +// if (codeHash == null || codeHash == ByteArray.EMPTY) { +// ByteArrayOutputStream out = new ByteArrayOutputStream(); +// BytesEncoding.write(getAddress().getAddress().getBytes(), NumberMask.SHORT, out); +// BytesUtils.writeLong(codeVersion, out); +// BytesEncoding.write(code, NumberMask.NORMAL, out); +// +// codeHash = HashEncoding.computeHash(out.toByteArray(), codeHashAlgorithm); +// } +// +// return codeHash; +// } +// +// public ByteArray getCode() { +// return code; +// } +// +// /** +// * 代码版本;
+// *

+// * 初始为 0,每次对代码的变更都使版本加 1 ; +// * +// * @return +// */ +// @Override +// public long getCodeVersion() { +// return codeVersion; +// } +// +// public BlockchainIdentity getIdentity() { +// return identity; +// } +// +// public void setIdentity(BlockchainIdentity identity) { +// this.identity = identity; +// } +// +// public void setLedgerHash(ByteArray ledgerHash) { +// this.ledgerHash = ledgerHash; +// } +// +// public long getBlockHeight() { +// return blockHeight; +// } +// +// public void setBlockHeight(long blockHeight) { +// this.blockHeight = blockHeight; +// } +// +// public long getTxSequenceNumber() { +// return txSequenceNumber; +// } +// +// public void setTxSequenceNumber(long txSequenceNumber) { +// this.txSequenceNumber = txSequenceNumber; +// } +// +// public void setModelVersion(long modelVersion) { +// this.modelVersion = modelVersion; +// } +// +// public void setVersion(long version) { +// this.version = version; +// } +// +// public void setPrivilegeHash(ByteArray privilegeHash) { +// this.privilegeHash = privilegeHash; +// } +// +// public void setPrivilegeVersion(long privilegeVersion) { +// this.privilegeVersion = privilegeVersion; +// } +// +// public void setStateType(AccountStateType stateType) { +// this.stateType = stateType; +// } +// +// public void setStateHash(ByteArray stateHash) { +// this.stateHash = stateHash; +// } +// +// public void setStateVersion(long stateVersion) { +// this.stateVersion = stateVersion; +// } +// +// public void setCodeHash(ByteArray codeHash) { +// this.codeHash = codeHash; +// } +// +// public void setCodeVersion(long codeVersion) { +// this.codeVersion = codeVersion; +// } +// +// @Override +// public boolean equals(Object o) { +// if (this == o) +// return true; +// if (!(o instanceof AccountImpl)) +// return false; +// +// AccountImpl account = (AccountImpl) o; +// +// if (getBlockHeight() != account.getBlockHeight()) +// return false; +// if (getTxSequenceNumber() != account.getTxSequenceNumber()) +// return false; +// if (getModelVersion() != account.getModelVersion()) +// return false; +// if (getVersion() != account.getVersion()) +// return false; +// if (getPrivilegeVersion() != account.getPrivilegeVersion()) +// return false; +// if (getStateVersion() != account.getStateVersion()) +// return false; +// if (getCodeVersion() != account.getCodeVersion()) +// return false; +// if (!getIdentity().equals(account.getIdentity())) +// return false; +// if (!getLedgerHash().equals(account.getLedgerHash())) +// return false; +// if (!getPrivilegeHash().equals(account.getPrivilegeHash())) +// return false; +// if (getStateType() != account.getStateType()) +// return false; +// if (!getStateHash().equals(account.getStateHash())) +// return false; +// if (!code.equals(account.code)) +// return false; +// if (codeHashAlgorithm != account.codeHashAlgorithm) +// return false; +// return getCodeHash().equals(account.getCodeHash()); +// } +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountRegisterOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountRegisterOperation.java new file mode 100644 index 00000000..1983349a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/AccountRegisterOperation.java @@ -0,0 +1,38 @@ +//package com.jd.blockchain.ledger; +// +//import my.utils.io.ByteArray; +// +///** +// * @author huanghaiquan +// * +// */ +//public interface AccountRegisterOperation extends BlockchainOperation { +// +// /** +// * 要注册的身份;
+// * +// * 必选; +// * +// * @return +// */ +// BlockchainIdentity getId(); +// +// /** +// * 数据状态的存储类型;
+// * +// * 必选; +// * +// * @return +// */ +// AccountStateType getStateType(); +// +//// /** +//// * 账户的合约代码;
+//// * +//// * 可选; +//// * +//// * @return +//// */ +//// ByteArray getContractCode(); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java new file mode 100644 index 00000000..54a6011e --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockBody.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.BLOCK_BODY) +public interface BlockBody extends LedgerDataSnapshot{ + + @DataField(order=2, primitiveType = ValueType.BYTES) + HashDigest getPreviousHash(); + + @DataField(order=3, primitiveType = ValueType.BYTES) + HashDigest getLedgerHash(); + + @DataField(order=4, primitiveType= ValueType.INT64) + long getHeight(); + + @DataField(order=5, primitiveType = ValueType.BYTES) + HashDigest getTransactionSetHash(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainAccount.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainAccount.java new file mode 100644 index 00000000..cd9f40c5 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainAccount.java @@ -0,0 +1,146 @@ +//package com.jd.blockchain.ledger; +// +//import my.utils.io.ByteArray; +// +//import java.io.Serializable; +// +///** +// * 区块链账户; +// * +// * @author huanghaiquan +// * +// */ +//public interface BlockchainAccount extends Serializable { +// +// /** +// * 地址; +// * +// * @return +// */ +// BlockchainIdentity getAddress(); +// +// /** +// * 账户所属的账本的 hash;
+// * +// * 注:账本的hash 等同于该账本的创世区块的 hash; +// * +// * @return +// */ +// ByteArray getLedgerHash(); +// +// /** +// * 注册账户的区块高度;
+// * +// * 注册此账户的区块高度; +// * +// * @return +// */ +// long getRegisteredHeight(); +// +// /** +// * 交易流水号;
+// * +// * 账户的交易流水号初始为 0,当账户作为交易的科目账户(SubjectAccount )发起一个交易并被成功执行之后,账户的交易流水号增加1; +// * +// * @return +// */ +// long getTxSquenceNumber(); +// +// /** +// * 账户模型版本;
+// * +// * 表示构成一个账户结构的属性模型的程序版本号; +// * +// * @return +// */ +// long getModelVersion(); +// +// /** +// * 账户版本;
+// * +// * 初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;注:交易序号的改变不会导致账户版本的增加; +// * +// * @return +// */ +// long getVersion(); +// +// // /** +// // * 权限设置; +// // * +// // * @return +// // */ +// // PrivilegeSetting getPrivilegeSetting(); +// +// /** +// * 权限 hash;
+// * +// * 权限树的根hash; +// * +// * @return +// */ +// ByteArray getPrivilegeHash(); +// +// /** +// * 权限版本;
+// * +// * 初始为 0, 每次对权限的变更都导致版本号加 1; +// * +// * @return +// */ +// long getPrivilegeVersion(); +// +//// /** +//// * 状态类型;
+//// * +//// * 账户的状态类型有3种:空类型(NIL);键值类型;对象类型;参考 {@link AccountStateType} +//// * +//// * @return +//// */ +//// AccountStateType getStateType(); +// +// /** +// * 状态版本;
+// * +// * 初始为 0,每次对状态的更改都使得状态版本增加1; +// * +// * @return +// */ +// long getStateVersion(); +// +// /** +// * 状态哈希;
+// * +// * 数据状态的 merkle tree 的根hash; +// * +// * @return +// */ +// ByteArray getStateHash(); +// +// // /** +// // * 合约代码;
+// // * +// // * 合约代码是一段代码片段; +// // * +// // * @return +// // */ +// // String getCode(); +// +// /** +// * 合约代码哈希;
+// * +// * 由“账户地址+合约代码版本号+合约代码内容”生成的哈希; +// * +// * @return +// */ +// ByteArray getCodeHash(); +// +// /** +// * 代码版本;
+// * +// * 初始为 0,每次对代码的变更都使版本加 1 ; +// * +// * @return +// */ +// long getCodeVersion(); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainEventType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainEventType.java new file mode 100644 index 00000000..aa7449a0 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainEventType.java @@ -0,0 +1,83 @@ +package com.jd.blockchain.ledger; + +/** + * 区块链事件类型;

+ * + * 每一种事件类型都包含一个事件码;

+ * + * 在一次事件消息中,可以包含多种事件,而且事件之间具有嵌套关系;
+ * + * 例如:

+ * + * 一个区块生成事件 {@link #BLOCK_GENERATED} 含了交易提交事件 + * {@link #TRANSACTION_COMMITED};

+ * + * 交易提交事件 {@link #TRANSACTION_COMMITED} 必然包含账户更新事件 {@link #ACCOUNT_UPDATED};

+ * + * 更进一步,账户更新事件 {@link #ACCOUNT_UPDATED} 也必然包含了权限更新事件 + * {@link #PRIVILEGE_UPDATED}、负载数据更新事件 {@link #PAYLOAD_UPDATED} + * 、合约脚本更新事件{@link #SCRIPT_UPDATED} 、合约脚本执行事件{@link #SCRIPT_INVOKED} 这4种事件中的一种或者多种事件;

+ * + * 这种嵌套关系,表现在事件的编码中是子事件码的比特位中包含了上级事件码; + * + * @author huanghaiquan + * + */ +public enum BlockchainEventType { + + /** + * 生成新区块;
+ * + * 事件码:1 (0x01) + * + */ + BLOCK_GENERATED(1), + + /** + * 成功提交新交易;
+ * + * 事件码:3 (0x03) + */ + TRANSACTION_COMMITED(3), + + /** + * 账户的版本已更新;
+ * + * 事件码:259 (0x103) + */ + ACCOUNT_UPDATED(259), + + /** + * 账户权限已被更新;
+ * + * 事件码:65795 (0x10103) + */ + PRIVILEGE_UPDATED(65795), + + /** + * 账户负载数据已被更新;
+ * + * 事件码:131331 (0x20103) + */ + PAYLOAD_UPDATED(131331), + + /** + * 合约脚本已被更新;
+ * + * 事件码:262403 (0x40103) + */ + SCRIPT_UPDATED(262403), + + /** + * 合约脚本已被调用;
+ * + * 事件码:524547 (0x80103) + */ + SCRIPT_INVOKED(524547); + + public final int CODE; + + private BlockchainEventType(int code) { + this.CODE = code; + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainIdentity.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainIdentity.java new file mode 100644 index 00000000..4b54cd3c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainIdentity.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.BLOCK_CHAIN_IDENTITY) +public interface BlockchainIdentity { + + @DataField(order = 1, primitiveType = ValueType.BYTES) + Bytes getAddress(); + + @DataField(order = 2, primitiveType=ValueType.BYTES) + PubKey getPubKey(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainIdentityData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainIdentityData.java new file mode 100644 index 00000000..212b49e3 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainIdentityData.java @@ -0,0 +1,219 @@ +package com.jd.blockchain.ledger; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Externalizable; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.data.CryptoKeyEncoding; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesReader; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.BytesWriter; +import com.jd.blockchain.utils.io.RuntimeIOException; + +/** + * 区块链身份; + * + * @author huanghaiquan + * + */ +public class BlockchainIdentityData implements BytesWriter, BytesReader, Externalizable, BlockchainIdentity { + + private Bytes address; + + private PubKey pubKey; + + private BlockchainIdentityData() { + } + + public BlockchainIdentityData(PubKey pubKey) { + this.pubKey = pubKey; + this.address = AddressEncoding.generateAddress(pubKey); + } + + public BlockchainIdentityData(CryptoAlgorithm algorithm, ByteArray pubKeyBytes) { + this.pubKey = new PubKey(algorithm, pubKeyBytes.bytes()); + this.address = AddressEncoding.generateAddress(pubKey); + } + + @DConstructor(name = "BlockchainIdentityData") + public BlockchainIdentityData(@FieldSetter(name = "getAddress", type = "String") Bytes address, @FieldSetter(name = "getPubKey", type = "PubKey") PubKey pubKey) { + if (!verifyAddress(address, pubKey)) { + throw new IllegalArgumentException("Blockchain address is mismatch with the pub-key!"); + } + this.address = address; + this.pubKey = pubKey; + } + + public static boolean verifyAddress(Bytes address, PubKey pubKey) { + Bytes addr = AddressEncoding.generateAddress(pubKey); + return addr.equals(address); + } + + @Override + public void resolvFrom(InputStream in) throws IOException { + Bytes addr = AddressEncoding.readAddress(in); + CryptoKey pubKey = CryptoKeyEncoding.readKey(in); + if (!(pubKey instanceof PubKey)) { + throw new IllegalArgumentException( + "Expected public key but private key was resolved from the InputStream!"); + } + this.address = addr; + this.pubKey = (PubKey) pubKey; + } + + @Override + public void writeTo(OutputStream out) throws IOException { + AddressEncoding.writeAddress(address, out); + CryptoKeyEncoding.writeKey(pubKey, out); + } + + /* (non-Javadoc) + * @see com.jd.blockchain.ledger.BlockchainIdentity#getAddress() + */ + @Override + public Bytes getAddress() { + return address; + } + + /* (non-Javadoc) + * @see com.jd.blockchain.ledger.BlockchainIdentity#getPubKey() + */ + @Override + public PubKey getPubKey() { + return pubKey; + } + + public static BlockchainIdentity resolveFrom(ByteArray bytes) { + try { + BlockchainIdentityData id = new BlockchainIdentityData(); + id.resolvFrom(bytes.asInputStream()); + return id; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static ByteArray toBytes(List identities) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesUtils.writeInt(identities.size(), out); + for (BlockchainIdentityData identity : identities) { + identity.writeTo(out); + } + + return ByteArray.wrap(out.toByteArray()); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static List resolveIdentitiesFrom(ByteArray bytes) { + try { + InputStream in = bytes.asInputStream(); + int identitiesLen = BytesUtils.readInt(in); + List identities = new ArrayList<>(); + for (int i = 0; i < identitiesLen; i++) { + BlockchainIdentityData id = new BlockchainIdentityData(); + id.resolvFrom(in); + + identities.add(id); + } + + return identities; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public ByteArray toBytes() { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeTo(out); + return ByteArray.wrap(out.toByteArray()); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + @Override + public int hashCode() { + return address.hashCode(); + } + + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } + if (!(other instanceof BlockchainIdentityData)) { + return false; + } + + BlockchainIdentity identity = (BlockchainIdentity) other; + + if (!getAddress().equals(identity.getAddress())) { + return false; + } + return pubKey.equals(identity.getPubKey()); + } + + /** + * The object implements the writeExternal method to save its contents by + * calling the methods of DataOutput for its primitive values or calling the + * writeObject method of ObjectOutput for objects, strings, and arrays. + * + * @param out + * the stream to write the object to + * @throws IOException + * Includes any I/O exceptions that may occur + * @serialData Overriding methods should use this tag to describe the data + * layout of this Externalizable object. List the sequence of + * element types and, if possible, relate the element to a + * public/protected field and/or method of this Externalizable + * class. + */ + @Override + public void writeExternal(ObjectOutput out) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + writeTo(os); + byte[] bts = os.toByteArray(); + out.writeInt(bts.length); + out.write(bts); + } + + /** + * The object implements the readExternal method to restore its contents by + * calling the methods of DataInput for primitive types and readObject for + * objects, strings and arrays. The readExternal method must read the values in + * the same sequence and with the same types as were written by writeExternal. + * + * @param in + * the stream to read data from in order to restore the object + * @throws IOException + * if I/O errors occur + * @throws ClassNotFoundException + * If the class for an object being restored cannot be found. + */ + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + int len = in.readInt(); + byte[] bts = new byte[len]; + in.readFully(bts); + this.resolvFrom(new ByteArrayInputStream(bts)); + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainKeyGenerator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainKeyGenerator.java new file mode 100644 index 00000000..cdf97c92 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainKeyGenerator.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; + +/** + * 区块链密钥生成器; + * + * @author huanghaiquan + * + */ +public class BlockchainKeyGenerator { + + private BlockchainKeyGenerator() { + } + + public static BlockchainKeyGenerator getInstance() { + return new BlockchainKeyGenerator(); + } + + public BlockchainKeyPair generate() { + return generate(CryptoAlgorithm.ED25519); + } + + public BlockchainKeyPair generate(CryptoAlgorithm signatureAlgorithm) { + CryptoKeyPair cryptoKeyPair = CryptoUtils.sign(signatureAlgorithm).generateKeyPair(); + return new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainKeyPair.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainKeyPair.java new file mode 100644 index 00000000..2a2a7c9a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainKeyPair.java @@ -0,0 +1,46 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.Bytes; + +/** + * 区块链密钥对; + * + * @author huanghaiquan + * + */ +public class BlockchainKeyPair extends CryptoKeyPair { + + private BlockchainIdentity id; + +// public BlockchainKeyPair(CryptoAlgorithm algorithm, ByteArray pubKeyBytes, ByteArray privKeyBytes) { +// this.id = new BlockchainIdentity(algorithm, pubKeyBytes); +// privKey = new PrivKey(algorithm, privKeyBytes.bytes()); +// } + + public BlockchainKeyPair(String address, PubKey pubKey, PrivKey privKey) { + super(pubKey, privKey); + if (pubKey.getAlgorithm() != privKey.getAlgorithm()) { + throw new IllegalArgumentException("The PublicKey's algorithm is different from the PrivateKey's!"); + } + this.id = new BlockchainIdentityData(Bytes.fromBase58(address), pubKey); + } + + public BlockchainKeyPair(PubKey pubKey, PrivKey privKey) { + super(pubKey, privKey); + if (pubKey.getAlgorithm() != privKey.getAlgorithm()) { + throw new IllegalArgumentException("The PublicKey's algorithm is different from the PrivateKey's!"); + } + this.id = new BlockchainIdentityData(pubKey); + } + + public Bytes getAddress() { + return id.getAddress(); + } + + public BlockchainIdentity getIdentity() { + return id; + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainOperation.java new file mode 100644 index 00000000..e17f9b0a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BlockchainOperation.java @@ -0,0 +1,11 @@ +//package com.jd.blockchain.ledger; +// +//import java.io.Serializable; +// +//public interface BlockchainOperation extends Serializable { +// +// OperationType getOperationType(); +// +//// Operation getOperation(); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValue.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValue.java new file mode 100644 index 00000000..c2fec5fc --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValue.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.io.BytesSlice; + +@DataContract(code = TypeCodes.BYTES_VALUE) +public interface BytesValue { + + /** + * 数据类型; + * + * @return + */ + @DataField(order = 0, refEnum = true) + DataType getType(); + + /** + * 数据值的二进制序列; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + BytesSlice getValue(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueImpl.java new file mode 100644 index 00000000..99278adb --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/BytesValueImpl.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.utils.io.BytesSlice; + +/** + * Created by zhangshuang3 on 2018/12/3. + */ +public class BytesValueImpl implements BytesValue{ + DataType type; + BytesSlice slice; + + public BytesValueImpl(DataType type, byte[] bytes) { + this.type = type; + this.slice = new BytesSlice(bytes); + } + + @Override + public DataType getType() { + return this.type; + } + + public void setType(DataType type) { + this.type = type; + } + + @Override + public BytesSlice getValue() { + return this.slice; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CodeDeployOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CodeDeployOperation.java new file mode 100644 index 00000000..2816b952 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CodeDeployOperation.java @@ -0,0 +1,23 @@ +//package com.jd.blockchain.ledger; +// +//import com.jd.blockchain.ledger.data.AccountUpdateOperationBuilder; +// +///** +// * 合约代码部署操作; +// * +// * @author huanghaiquan +// * +// */ +//public interface CodeDeployOperation extends AccountUpdateOperationBuilder { +// +// /** +// * 修改脚本; +// * +// * @param code +// * 合约代码; +// * @param codeVersion +// * 预期的当前的代码的版本;如果指定为 -1,则不进行版本检查; +// */ +// void set(BlockchainIdentity id, String code, long codeVersion); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ConsensusNode.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ConsensusNode.java new file mode 100644 index 00000000..71eb0b26 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ConsensusNode.java @@ -0,0 +1,38 @@ +//package com.jd.blockchain.ledger; +// +//import com.jd.blockchain.base.data.TypeCodes; +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +// +//import my.utils.ValueType; +// +///** +// * 共识节点;
+// * 以地址和端口一起作为唯一标识; +// * +// * @author huanghaiquan +// * +// */ +//@DataContract(code = TypeCodes.METADATA_CONSENSUS_NODE) +//public interface ConsensusNode { +// +// /** +// * 参与者ID; +// * +// * @return +// */ +// @DataField(order = 1, primitiveType = ValueType.INT32) +// int getId(); +// +//// /** +//// * 共识节点的主机地址; +//// * +//// *
+//// * 该节点以此地址参与共识服务; +//// * +//// * @return +//// */ +//// @DataField(order = 2, primitiveType = ValueType.BYTES) +//// NetworkAddress getConsensusAddress(); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ConsensusSetting.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ConsensusSetting.java new file mode 100644 index 00000000..fae75065 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ConsensusSetting.java @@ -0,0 +1,15 @@ +//package com.jd.blockchain.ledger; +// +//import com.jd.blockchain.base.data.TypeCodes; +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +// +//import my.utils.ValueType; +// +//@DataContract(code= TypeCodes.METADATA_CONSENSUS_SETTING) +//public interface ConsensusSetting { +// +// @DataField(order=2, primitiveType= ValueType.BYTES) +// byte[] getValue(); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractCodeDeployOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractCodeDeployOperation.java new file mode 100644 index 00000000..48ab6294 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractCodeDeployOperation.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.TX_OP_CONTRACT_DEPLOY) +public interface ContractCodeDeployOperation extends Operation { + + @DataField(order=2, refContract = true) + BlockchainIdentity getContractID(); + + @DataField(order=3, primitiveType=ValueType.BYTES) + byte[] getChainCode(); + + + /** + * 地址签名; + * + *
+ * 这是合约账户身份 ({@link #getContractID()}) 使用对应的私钥对地址做出的签名; + *
+ * 在注册时将校验此签名与账户地址、公钥是否相匹配,以此保证只有私钥的持有者才能注册相应的合约账户,确保合约账户的唯一性; + * + * @return + */ + @DataField(order=4, refContract = true) + DigitalSignature getAddressSignature(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractEventSendOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractEventSendOperation.java new file mode 100644 index 00000000..dce287b8 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ContractEventSendOperation.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +/** + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.TX_OP_CONTRACT_EVENT_SEND) +public interface ContractEventSendOperation extends Operation { + +// @DataField(order=1, refEnum=true) +// @Override +// default OperationType getType() { +// return OperationType.SEND_CONTRACT_EVENT; +// } + + @DataField(order=2, primitiveType=ValueType.BYTES) + Bytes getContractAddress(); + + @DataField(order=3, primitiveType=ValueType.TEXT) + String getEvent(); + + + @DataField(order=4, primitiveType=ValueType.BYTES) + byte[] getArgs(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CryptoSetting.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CryptoSetting.java new file mode 100644 index 00000000..d00518e9 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/CryptoSetting.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.utils.ValueType; + +/** + * 默克尔树算法相关的配置; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.METADATA_CRYPTO_SETTING) +public interface CryptoSetting { + + /** + * 系统中使用的 Hash 算法;
+ * + * 对于历史数据,如果它未发生更改,则总是按照该数据产生时采用的算法进行校验,即使当时指定的Hash算法和当前的不同;
+ * + * 如果对数据进行了更新,则采用新的 Hash 算法来计算生成完整性证明; + * + * @return + */ + @DataField(order=1, refEnum=true) + public CryptoAlgorithm getHashAlgorithm(); + + /** + * 当有完整性证明的数据被从持久化介质中加载时,是否对其进行完整性校验(重新计算 hash 比对是否一致);
+ * + * 如果为 true ,则自动进行校验,如果校验失败,会引发异常;
+ * + * 注意:开启此选项将对性能会产生负面影响,因此使用者需要在性能和数据安全性之间做出权衡; + * + * @return + */ + @DataField(order=2, primitiveType= ValueType.BOOLEAN) + public boolean getAutoVerifyHash(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java new file mode 100644 index 00000000..868bbd0f --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountKVSetOperation.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.TX_OP_DATA_ACC_SET) +public interface DataAccountKVSetOperation extends Operation{ + + @DataField(order=2, primitiveType=ValueType.BYTES) + Bytes getAccountAddress(); + + @DataField(order=3, list=true, refContract=true) + KVWriteEntry[] getWriteSet(); + + + @DataContract(code=TypeCodes.TX_OP_DATA_ACC_SET_KV) + public static interface KVWriteEntry{ + + @DataField(order=1, primitiveType=ValueType.TEXT) + String getKey(); + + @DataField(order=2, refContract = true) + BytesValue getValue(); + + @DataField(order=3, primitiveType=ValueType.INT64) + long getExpectedVersion(); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountRegisterOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountRegisterOperation.java new file mode 100644 index 00000000..d79ae611 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataAccountRegisterOperation.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +@DataContract(code= TypeCodes.TX_OP_DATA_ACC_REG) +public interface DataAccountRegisterOperation extends Operation { + + @DataField(order=1, refContract = true) + BlockchainIdentity getAccountID(); + + /** + * 地址签名; + * + *
+ * 这是账户身份 ({@link #getAccountID()}) 使用对应的私钥对地址做出的签名; + *
+ * 在注册时将校验此签名与账户地址、公钥是否相匹配,以此保证只有私钥的持有者才能注册数据账户,确保数据账户的唯一性; + * + * @return + */ + @DataField(order=2, refContract = true) + DigitalSignature getAddressSignature(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java new file mode 100644 index 00000000..5bbd832d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DataType.java @@ -0,0 +1,113 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.EnumContract; +import com.jd.blockchain.binaryproto.EnumField; +import com.jd.blockchain.utils.ValueType; + +/** + * 键值操作的数据类型; + * + * @author huanghaiquan + * + */ +@EnumContract(code= TypeCodes.ENUM_TYPE_DATA_TYPE, name = "DataType", decription = "") +public enum DataType { + + /** + * 空; + */ + NIL((byte) 0x00), + + /** + * 布尔型; + */ + BOOLEAN((byte) 0x10), + + /** + * 数值型: + */ + + INT8((byte) 0x11), + + INT16((byte) 0x12), + + INT32((byte) 0x13), + + INT64((byte) 0x14), + + /** + * 日期时间; + */ + DATETIME((byte) 0x15), + + /** + * 文本数据; + */ + TEXT((byte) 0x20), + + /** + * 文本数据; + */ + JSON((byte) 0x21), + + /** + * 文本数据; + */ + XML((byte) 0x22), + + /** + * 二进制数据; + */ + BYTES((byte) 0x40), + + /** + * 大整数; + */ + BIG_INT((byte) 0x41), + + /** + * 图片; + */ + IMG((byte) 0x42), + + /** + * 视频; + */ + VIDEO((byte) 0x43), + + /** + * 位置; + */ + LOCATION((byte) 0x44); + +// /** +// * 引用;
+// * +// * 表示引用区块链系统中的某一个特定的对象,用以下形式的 URI 表示; +// * +// * state://ledger/account/key/version
+// * 或
+// * proof:state://account_merkle_path/key_merkle_path +// * +// * proof:tx:// +// * +// */ +// REFERENCE((byte) 0x80); + @EnumField(type= ValueType.INT8) + public final byte CODE; + + private DataType(byte code) { + this.CODE = code; + } + + public static DataType valueOf(byte code) { + for (DataType dataType : DataType.values()) { + if (dataType.CODE == code) { + return dataType; + } + } + throw new IllegalArgumentException("Unsupported code[" + code + "] of DataType!"); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DigitalSignature.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DigitalSignature.java new file mode 100644 index 00000000..9a4ea62a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DigitalSignature.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; + +/** + * 数字签名; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.DIGITALSIGNATURE) +public interface DigitalSignature extends DigitalSignatureBody { + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DigitalSignatureBody.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DigitalSignatureBody.java new file mode 100644 index 00000000..1b3f26ac --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/DigitalSignatureBody.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 数字签名; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.DIGITALSIGNATURE_BODY) +public interface DigitalSignatureBody { + + /** + * 公钥; + * + * 注:公钥的编码方式中包含了算法标识; + * + * @return + */ + @DataField(order=1, primitiveType = ValueType.BYTES) + PubKey getPubKey(); + + /** + * 摘要; + * + * @return + */ + @DataField(order=2, primitiveType = ValueType.BYTES ) + SignatureDigest getDigest(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/EndpointRequest.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/EndpointRequest.java new file mode 100644 index 00000000..f0a31867 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/EndpointRequest.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code= TypeCodes.REQUEST_ENDPOINT) +public interface EndpointRequest { + + @DataField(order=1, primitiveType = ValueType.BYTES) + HashDigest getHash(); + /** + * 交易内容; + * + * @return + */ + @DataField(order=2, refContract=true) + TransactionContent getTransactionContent(); + + /** + * 终端用户的签名列表; + * + * @return + */ + @DataField(order=3, list=true, refContract=true) + DigitalSignature[] getEndpointSignatures(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/HashAlgorithm.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/HashAlgorithm.java new file mode 100644 index 00000000..48ea876b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/HashAlgorithm.java @@ -0,0 +1,46 @@ +package com.jd.blockchain.ledger; + +/** + * Hash 算法的代码常量; + * + * @author zhaoming9 + * + */ +public enum HashAlgorithm { + + RIPE160((byte) 1), + + SHA256((byte) 2), + + SM3((byte) 4); + + public final byte CODE; + + private HashAlgorithm(byte algorithm) { + CODE = algorithm; + } + + public byte getAlgorithm() { + return CODE; + } + + public static HashAlgorithm valueOf(byte algorithm) { + for (HashAlgorithm hashAlgorithm : HashAlgorithm.values()) { + if (hashAlgorithm.CODE == algorithm) { + return hashAlgorithm; + } + } + throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); + } + + public static void checkHashAlgorithm(HashAlgorithm algorithm) { + switch (algorithm) { + case RIPE160: + break; + case SHA256: + break; + default: + throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); + } + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/HashObject.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/HashObject.java new file mode 100644 index 00000000..1e9fb23c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/HashObject.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; + +/** + * HashObject 由一个哈希摘要唯一地标识对象; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.HASH_OBJECT) +public interface HashObject { + + /** + * 哈希值; + * + * @return + */ + //no need annotation + HashDigest getHash(); + + // /** + // * 哈希算法; + // * + // * @return + // */ + // HashAlgorithm getHashAlgorithm(); + + // /** + // * 进行哈希运算的数据; + // * @return + // */ + // ByteArray getHashData(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataEntry.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataEntry.java new file mode 100644 index 00000000..f4875b9c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataEntry.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.utils.ValueType; + +public interface KVDataEntry { + + /** + * 键名; + * + * @return + */ + String getKey(); + + /** + * 版本; + *

+ * 有效的版本大于等于 0 ; + *

+ * 如果返回 -1 ,则表示此项数据无效; + * + * @return + */ + long getVersion(); + + /** + * 数据类型; + * + * @return + */ + ValueType getType(); + + /** + * 值; + * @return + */ + Object getValue(); +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataObject.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataObject.java new file mode 100644 index 00000000..ee6a1f4d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/KVDataObject.java @@ -0,0 +1,623 @@ +package com.jd.blockchain.ledger; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.util.Date; + +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * KV数据项; + * + *

+ * + * {@link KVDataObject} 被设计为只读对象; + * + * @author huanghaiquan + * + */ +public class KVDataObject implements KVDataEntry { + + private String key; + + private long version; + + private ValueType type; + + private byte[] bytesValue; + + public KVDataObject(String key, long version, ValueType type, byte[] bytesValue) { + this.key = key; + this.type = type; + this.version = version < 0 ? -1 : version; + this.bytesValue = bytesValue; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.KVDataEntry#getKey() + */ + @Override + public String getKey() { + return key; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.KVDataEntry#getVersion() + */ + @Override + public long getVersion() { + return version; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.ledger.KVDataEntry#getType() + */ + @Override + public ValueType getType() { + return type; + } + + @Override + public Object getValue() { + if (bytesValue == null) { + return null; + } + + try { + switch (type) { + case NIL: + return null; + case TEXT: + return new String(bytesValue, "UTF-8"); + case BYTES: + return ByteArray.toHex(bytesValue); + case INT64: + return BytesUtils.readLong(new ByteArrayInputStream(bytesValue)); + case JSON: + return new String(bytesValue, "UTF-8"); + + default: + throw new IllegalStateException("Unsupported value type[" + type + "] to resolve!"); + } + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 是否为空值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#NIL} 时返回 true,其它情况返回 false; + *

+ * + * @return + */ + public boolean isNil() { + return ValueType.NIL == type; + } + + /** + * 字节数组形式的原始内容; + * + * @return + */ + ByteArray bytesArray() { + return ByteArray.wrapReadonly(bytesValue); + } + + /** + * 返回 8 位整数值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#INT8} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public byte tinyValue() { + if (ValueType.INT8 == type) { + return bytesValue[0]; + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.INT8, type)); + } + + /** + * 返回 16 位整数值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#INT16} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public short shortValue() { + if (ValueType.INT16 == type) { + return BytesUtils.toShort(bytesValue, 0); + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.INT16, type)); + } + + /** + * 返回 32 位整数值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#INT32} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public int intValue() { + if (ValueType.INT32 == type) { + return BytesUtils.toInt(bytesValue, 0); + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.INT32, type)); + } + + /** + * 返回 64 位整数值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#INT64} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public long longValue() { + if (ValueType.INT64 == type) { + return BytesUtils.toLong(bytesValue, 0); + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.INT64, type)); + + } + + /** + * 返回大整数值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#BIG_INT} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public BigInteger bigIntValue() { + if (ValueType.BIG_INT == type) { + return new BigInteger(bytesValue); + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.BIG_INT, type)); + } + + /** + * 返回布尔值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#BIG_INT} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public boolean boolValue() { + if (ValueType.BOOLEAN == type) { + return bytesValue[0] != 0; + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.BOOLEAN, type)); + } + + /** + * 返回日期时间值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#DATETIME} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public Date datetimeValue() { + if (ValueType.DATETIME == type) { + long ts = BytesUtils.toLong(bytesValue); + return new Date(ts); + } + throw new IllegalStateException(String.format("Expected type [%s], but [%s]", ValueType.DATETIME, type)); + } + + /** + * 返回大整数值; + *

+ * + * 仅当数据类型 {@link #getType()} 为 {@link ValueType#TEXT} / {@link ValueType#JSON} / + * {@link ValueType#XML} 有效; + *

+ * + * 无效类型将引发 {@link IllegalStateException} 异常; + * + * @return + */ + public String stringValue() { + if (ValueType.TEXT == type || ValueType.JSON == type || ValueType.XML == type) { + try { + return new String(bytesValue, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + throw new IllegalStateException(String.format("Expected type [%s] or [%s] or [%s] , but [%s]", ValueType.TEXT, + ValueType.JSON, ValueType.XML, type)); + } + +// // ---------------- +// public KVDataEntry nextVersionNil() { +// return nilState(key, version + 1); +// } +// +// public KVDataEntry nextVersionBoolean(boolean value) { +// return booleanState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionTiny(byte value) { +// return tinyState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionShort(short value) { +// return shortState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionInt(int value) { +// return intState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionLong(long value) { +// return longState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionDatetime(Date value) { +// return datetimeState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionJson(String value) { +// return jsonState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionXml(String value) { +// return xmlState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionBigInt(BigInteger value) { +// return bigIntState(key, version + 1, value); +// } +// +// public KVDataEntry nextVersionText(boolean encrypted, String value) { +// return textState(key, version + 1, encrypted, value); +// } +// +// public KVDataEntry nextVersionBytes(boolean encrypted, byte[] value) { +// return bytesState(key, version + 1, encrypted, value); +// } +// +// public KVDataEntry nextVersionImage(boolean encrypted, byte[] value) { +// return imageState(key, version + 1, encrypted, value); +// } +// +// public KVDataEntry nextVersionVideo(boolean encrypted, byte[] value) { +// return videoState(key, version + 1, encrypted, value); +// } +// +// public KVDataEntry nextVersionLocation(boolean encrypted, byte[] value) { +// return locationState(key, version + 1, encrypted, value); +// } +// // ---------------- +// +// public KVDataEntry newNil() { +// return nilState(key, version); +// } +// +// public KVDataEntry newBoolean(boolean value) { +// return booleanState(key, version, value); +// } +// +// public KVDataEntry newTiny(byte value) { +// return tinyState(key, version, value); +// } +// +// public KVDataEntry newShort(short value) { +// return shortState(key, version, value); +// } +// +// public KVDataEntry newInt(int value) { +// return intState(key, version, value); +// } +// +// public KVDataObject newLong(long value) { +// return longState(key, version, value); +// } +// +// public KVDataEntry newDatetime(Date value) { +// return datetimeState(key, version, value); +// } +// +// public KVDataEntry newJson(String value) { +// return jsonState(key, version, value); +// } +// +// public KVDataEntry newXml(String value) { +// return xmlState(key, version, value); +// } +// +// public KVDataEntry newBigInt(BigInteger value) { +// return bigIntState(key, version, value); +// } +// +// public KVDataEntry newText(boolean encrypted, String value) { +// return textState(key, version, encrypted, value); +// } +// +// public KVDataEntry newBytes(boolean encrypted, byte[] value) { +// return bytesState(key, version, encrypted, value); +// } +// +// public KVDataEntry newImage(boolean encrypted, byte[] value) { +// return imageState(key, version, encrypted, value); +// } +// +// public KVDataEntry newVideo(boolean encrypted, byte[] value) { +// return videoState(key, version, encrypted, value); +// } +// +// public KVDataEntry newLocation(boolean encrypted, byte[] value) { +// return locationState(key, version, encrypted, value); +// } +// +// // ---------------- +// +// public KVDataEntry newNil(long version) { +// return nilState(key, version); +// } +// +// public KVDataEntry newBoolean(long version, boolean value) { +// return booleanState(key, version, value); +// } +// +// public KVDataEntry newTiny(long version, byte value) { +// return tinyState(key, version, value); +// } +// +// public KVDataEntry newShort(long version, short value) { +// return shortState(key, version, value); +// } +// +// public KVDataEntry newInt(long version, int value) { +// return intState(key, version, value); +// } +// +// public KVDataEntry newLong(long version, long value) { +// return longState(key, version, value); +// } +// +// public KVDataEntry newDatetime(long version, Date value) { +// return datetimeState(key, version, value); +// } +// +// public KVDataEntry newJson(long version, String value) { +// return jsonState(key, version, value); +// } +// +// public KVDataEntry newXml(long version, String value) { +// return xmlState(key, version, value); +// } +// +// public KVDataEntry newBigInt(long version, BigInteger value) { +// return bigIntState(key, version, value); +// } +// +// public KVDataEntry newText(long version, boolean encrypted, String value) { +// return textState(key, version, encrypted, value); +// } +// +// public KVDataEntry newBytes(long version, boolean encrypted, byte[] value) { +// return bytesState(key, version, encrypted, value); +// } +// +// public KVDataEntry newImage(long version, boolean encrypted, byte[] value) { +// return imageState(key, version, encrypted, value); +// } +// +// public KVDataEntry newVideo(long version, boolean encrypted, byte[] value) { +// return videoState(key, version, encrypted, value); +// } +// +// public KVDataEntry newLocation(long version, boolean encrypted, byte[] value) { +// return locationState(key, version, encrypted, value); +// } +// +// // ---------------- +// +// public static KVDataEntry booleanState(String key, boolean value) { +// return booleanState(key, -1, value); +// } +// +// public static KVDataEntry tinyState(String key, byte value) { +// return tinyState(key, -1, value); +// } +// +// public static KVDataEntry shortState(String key, short value) { +// return shortState(key, -1, value); +// } +// +// public static KVDataEntry intState(String key, int value) { +// return intState(key, -1, value); +// } +// +// public static KVDataEntry longState(String key, long value) { +// return longState(key, -1, value); +// } +// +// public static KVDataEntry datetimeState(String key, Date value) { +// return datetimeState(key, -1, value); +// } +// +// public static KVDataEntry jsonState(String key, String value) { +// return jsonState(key, -1, value); +// } +// +// public static KVDataEntry xmlState(String key, String value) { +// return xmlState(key, -1, value); +// } +// +// public static KVDataEntry bigIntState(String key, BigInteger value) { +// return bigIntState(key, -1, value); +// } +// +// public static KVDataObject textState(String key, String value) { +// return textState(key, -1, false, value); +// } +// +// public static KVDataEntry bytesState(String key, byte[] value) { +// return bytesState(key, -1, false, value); +// } +// +// public static KVDataEntry imageState(String key, byte[] value) { +// return imageState(key, -1, false, value); +// } +// +// public static KVDataEntry videoState(String key, byte[] value) { +// return videoState(key, -1, false, value); +// } +// +// public static KVDataEntry locationState(String key, byte[] value) { +// return locationState(key, -1, false, value); +// } +// +// // ---------------- +// +// public static KVDataEntry textState(String key, boolean encrypted, String value) { +// return textState(key, -1, encrypted, value); +// } +// +// public static KVDataEntry bytesState(String key, boolean encrypted, byte[] value) { +// return bytesState(key, -1, encrypted, value); +// } +// +// public static KVDataEntry imageState(String key, boolean encrypted, byte[] value) { +// return imageState(key, -1, encrypted, value); +// } +// +// public static KVDataEntry videoState(String key, boolean encrypted, byte[] value) { +// return videoState(key, -1, encrypted, value); +// } +// +// public static KVDataEntry locationState(String key, boolean encrypted, byte[] value) { +// return locationState(key, -1, encrypted, value); +// } +// +// // ---------------------- +// +// public static KVDataEntry nilState(String key) { +// return new KVDataObject(key, ValueType.NIL, -1, false, BytesUtils.EMPTY_BYTES); +// } +// +// public static KVDataEntry nilState(String key, long version) { +// return new KVDataObject(key, ValueType.NIL, version, false, BytesUtils.EMPTY_BYTES); +// } +// +// public static KVDataEntry booleanState(String key, long version, boolean value) { +// byte[] v = { value ? (byte) 1 : (byte) 0 }; +// return new KVDataObject(key, ValueType.BOOLEAN, version, false, v); +// } +// +// public static KVDataEntry tinyState(String key, long version, byte value) { +// byte[] v = { value }; +// return new KVDataObject(key, ValueType.INT8, version, false, v); +// } +// +// public static KVDataEntry shortState(String key, long version, short value) { +// byte[] v = BytesUtils.toBytes(value); +// return new KVDataObject(key, ValueType.INT16, version, false, v); +// } +// +// public static KVDataEntry intState(String key, long version, int value) { +// byte[] v = BytesUtils.toBytes(value); +// return new KVDataObject(key, ValueType.INT32, version, false, v); +// } +// +// public static KVDataObject longState(String key, long version, long value) { +// byte[] v = BytesUtils.toBytes(value); +// return new KVDataObject(key, ValueType.INT64, version, false, v); +// } +// +// public static KVDataEntry datetimeState(String key, long version, Date value) { +// byte[] v = BytesUtils.toBytes(value.getTime()); +// return new KVDataObject(key, ValueType.DATETIME, version, false, v); +// } +// +// public static KVDataObject textState(String key, long version, boolean encrypted, String value) { +// try { +// byte[] v = value.getBytes("UTF-8"); +// return new KVDataObject(key, ValueType.TEXT, version, encrypted, v); +// } catch (UnsupportedEncodingException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } +// } +// +// public static KVDataEntry jsonState(String key, long version, String value) { +// try { +// byte[] v = value.getBytes("UTF-8"); +// return new KVDataObject(key, ValueType.JSON, version, false, v); +// } catch (UnsupportedEncodingException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } +// } +// +// public static KVDataEntry xmlState(String key, long version, String value) { +// try { +// byte[] v = value.getBytes("UTF-8"); +// return new KVDataObject(key, ValueType.XML, version, false, v); +// } catch (UnsupportedEncodingException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } +// } +// +// public static KVDataEntry bigIntState(String key, long version, BigInteger value) { +// byte[] v = value.toByteArray(); +// return new KVDataObject(key, ValueType.BIG_INT, version, false, v); +// } +// +// public static KVDataEntry bytesState(String key, long version, boolean encrypted, byte[] value) { +// return new KVDataObject(key, ValueType.BYTES, version, encrypted, value); +// } +// +// public static KVDataEntry imageState(String key, long version, boolean encrypted, byte[] value) { +// return new KVDataObject(key, ValueType.IMG, version, encrypted, value); +// } +// +// public static KVDataEntry videoState(String key, long version, boolean encrypted, byte[] value) { +// return new KVDataObject(key, ValueType.VIDEO, version, encrypted, value); +// } +// +// public static KVDataEntry locationState(String key, long version, boolean encrypted, byte[] value) { +// return new KVDataObject(key, ValueType.LOCATION, version, encrypted, value); +// } + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Ledger.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Ledger.java new file mode 100644 index 00000000..073ea143 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Ledger.java @@ -0,0 +1,59 @@ +//package com.jd.blockchain.ledger; +// +//import my.utils.io.ByteArray; +// +//import java.io.Serializable; +// +///** +// * Ledger 账本;
+// * +// * 账本只是一个逻辑上的对象,它是对一条区块的hash链的归纳抽象,而在存储上并没有具体的存在形式,而是具体化为由一个特定的创世区块作为开端的区块 hash +// * 链;
+// * +// * 账本的唯一标识也是其创世区块(GenisisBlock)的 hash;
+// * +// * @author huanghaiquan +// * +// */ +//public interface Ledger extends Serializable { +// +// /** +// * 账本的 hash;
+// * +// * 同时也是账本的唯一,等同于其创世区块(GenisisBlock)的 hash {@link GenesisBlock#getBlockHash()}; +// * +// * @return +// */ +// ByteArray getLedgerHash(); +// +// /** +// * 账本结构版本;
+// * +// * 等同于 {@link Block#getLedgerVersion()}; +// * +// * @return +// */ +// long getLedgerVersion(); +// +// /** +// * 由随机数构成的该账本的创世序列; +// * +// * @return +// */ +// ByteArray getGenesisKey(); +// +// /** +// * 当前最新区块的 hash; +// * +// * @return +// */ +// ByteArray getBlockHash(); +// +// /** +// * 账本的区块高度; +// * +// * @return +// */ +// long getBlockHeight(); +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerBlock.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerBlock.java new file mode 100644 index 00000000..e2c3eecc --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerBlock.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code = TypeCodes.BLOCK) +public interface LedgerBlock extends BlockBody { + + /** + * 区块哈希; + * + *
+ * 这是对 {@link BlockBody} 的哈希;
+ * 对于创世区块而言,区块哈希是 {@link BlockBody#getPreviousHash()} 和 + * {@link BlockBody#getLedgerHash()} 属性均为 null 的情况下进行哈希计算得到的结果; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + HashDigest getHash(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerDataSnapshot.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerDataSnapshot.java new file mode 100644 index 00000000..27265b6f --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerDataSnapshot.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +@DataContract(code=TypeCodes.DATA_SNAPSHOT) +public interface LedgerDataSnapshot { + + @DataField(order=1, primitiveType = ValueType.BYTES) + HashDigest getAdminAccountHash(); + + @DataField(order=2, primitiveType = ValueType.BYTES) + HashDigest getUserAccountSetHash(); + + @DataField(order=3, primitiveType = ValueType.BYTES) + HashDigest getDataAccountSetHash(); + + @DataField(order=4, primitiveType = ValueType.BYTES) + HashDigest getContractAccountSetHash(); + +// HashDigest getUserPrivilegeHash(); + +// HashDigest getDataPrivilegeHash(); + +// HashDigest getContractPrivilegeHash(); + + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerImpl.java new file mode 100644 index 00000000..50e307f5 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerImpl.java @@ -0,0 +1,323 @@ +//package com.jd.blockchain.ledger; +// +//import com.jd.blockchain.ledger.data.HashEncoding; +// +//import my.utils.io.ByteArray; +//import my.utils.io.BytesEncoding; +//import my.utils.io.BytesReader; +//import my.utils.io.BytesUtils; +//import my.utils.io.BytesWriter; +//import my.utils.io.NumberMask; +// +//import java.io.IOException; +//import java.io.InputStream; +//import java.io.OutputStream; +//import java.util.Objects; +// +///** +// * Ledger 实现 +// * +// * @author zhaoming9 +// */ +//public class LedgerImpl implements Ledger, BytesWriter, BytesReader { +// +// private HashAlgorithm ledgerHashAlgorithm = HashAlgorithm.SHA256; // 账本hash算法 +// private ByteArray ledgerHash = ByteArray.EMPTY; // 账本hash +// +// private long blockHeight = 0; // 账本当前高度 +// private long blockVersion = 0; // 账本当前版本 +// +// private HashAlgorithm currentBlockHashAlgorithm = HashAlgorithm.SHA256; // 账本当前区块hash算法 +// private ByteArray currentBlockHash = ByteArray.EMPTY; // 账本当前区块hash +// +// private HashAlgorithm previousBlockHashAlgorithm = HashAlgorithm.SHA256; // 账本前一区块hash算法 +// private ByteArray previousBlockHash = ByteArray.EMPTY; // 账本前一区块hash +// +// private ByteArray accountRoot = ByteArray.EMPTY; // account mpt root hash +// private long accountCount; // 账户数量 +// private long txTotalCount; // 交易数量 +// +// private ByteArray genesisKey=ByteArray.EMPTY; // 创世块随机序列 +// +// public LedgerImpl() { +// } +// +// /** +// * 初始化一个新的账本; +// * @param genesisKey +// */ +// public LedgerImpl(ByteArray genesisKey) { +// this.genesisKey = genesisKey; +// } +// +// /** +// * @param ledgerHashAlgorithm +// * @param ledgerHash +// * @param height +// * @param version +// * @param currentBlockHashAlgorithm +// * @param currentBlockHash +// * @param previousBlockHashAlgorithm +// * @param previousBlockHash +// * @param accountRoot +// * @param accountCount +// * @param txTotalCount +// * @param genesisKey +// */ +// private LedgerImpl(HashAlgorithm ledgerHashAlgorithm, ByteArray ledgerHash, long height, long version, +// HashAlgorithm currentBlockHashAlgorithm, ByteArray currentBlockHash, +// HashAlgorithm previousBlockHashAlgorithm, ByteArray previousBlockHash, +// ByteArray accountRoot, long accountCount, long txTotalCount, ByteArray genesisKey) { +// this.ledgerHashAlgorithm = ledgerHashAlgorithm; +// this.ledgerHash = ledgerHash; +// this.blockHeight = height; +// this.blockVersion = version; +// this.currentBlockHashAlgorithm = currentBlockHashAlgorithm; +// this.currentBlockHash = currentBlockHash; +// this.previousBlockHashAlgorithm = previousBlockHashAlgorithm; +// this.previousBlockHash = previousBlockHash; +// this.accountRoot = accountRoot; +// this.accountCount = accountCount; +// this.txTotalCount = txTotalCount; +// this.genesisKey = genesisKey; +// } +// +// public LedgerImpl(ByteArray ledgerHash, long blockHeight, long blockVersion, ByteArray currentBlockHash, +// ByteArray previousBlockHash, ByteArray accountRoot, long accountCount, long txTotalCount, ByteArray genesisKey) { +// this(HashAlgorithm.SHA256, ledgerHash, blockHeight, blockVersion, HashAlgorithm.SHA256, currentBlockHash, +// HashAlgorithm.SHA256, previousBlockHash, accountRoot, accountCount, txTotalCount, genesisKey); +// } +// +// public LedgerImpl(LedgerImpl ledger) { +// this(ledger.getLedgerHashAlgorithm(), ledger.getLedgerHash(), ledger.getBlockHeight(), ledger.getBlockVersion(), +// ledger.getCurrentBlockHashAlgorithm(), ledger.getCurrentBlockHash(), +// ledger.getPreviousBlockHashAlgorithm(), ledger.getPreviousBlockHash(), +// ledger.getAccountRoot(), ledger.getAccountCount(), ledger.getTxTotalCount(),ledger.getGenesisKey()); +// } +// +// public LedgerImpl nextLedger(ByteArray nextBlockHash, ByteArray accountRoot, long newAccountCnt, long newTxCnt) { +// LedgerImpl nextLedger = new LedgerImpl(this); +// nextLedger.blockHeight+=1; +// nextLedger.previousBlockHash = nextLedger.currentBlockHash; +// nextLedger.currentBlockHash = nextBlockHash; +// nextLedger.accountRoot = accountRoot; +// nextLedger.accountCount += newAccountCnt; +// nextLedger.txTotalCount += newTxCnt; +// +// return nextLedger; +// } +// +// /** +// * 账本的 hash;
+// *

+// * 同时也是账本的唯一,等同于其创世区块(GenisisBlock)的 hash +// * +// * @return +// */ +// @Override +// public ByteArray getLedgerHash() { +// return ledgerHash; +// } +// +// /** +// * 由随机数构成的该账本的创世序列; +// * +// * @return +// */ +// @Override +// public ByteArray getGenesisKey() { +// return genesisKey; +// } +// +// /** +// * 当前最新区块的 hash; +// * +// * @return +// */ +// @Override +// public ByteArray getBlockHash() { +// return currentBlockHash; +// } +// +// public HashAlgorithm getBlockHashAlgorithm() { +// return currentBlockHashAlgorithm; +// } +// +// /** +// * 账本的区块高度; +// * +// * @return +// */ +// @Override +// public long getBlockHeight() { +// return blockHeight; +// } +// +// @Override +// public void resolvFrom(InputStream in) throws IOException { +// HashAlgorithm ledgerHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); +// HashAlgorithm.checkHashAlgorithm(ledgerHashAlgorithm); +// ByteArray ledgerHash = HashEncoding.read(in); +// +// long height = BytesUtils.readLong(in); +// long version = BytesUtils.readLong(in); +// +// HashAlgorithm currentBlockHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); +// HashAlgorithm.checkHashAlgorithm(currentBlockHashAlgorithm); +// ByteArray currentBlockHash = HashEncoding.read(in); +// +// HashAlgorithm previousBlockHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); +// HashAlgorithm.checkHashAlgorithm(previousBlockHashAlgorithm); +// ByteArray previousBlockHash = HashEncoding.read(in); +// +// ByteArray accountHash = HashEncoding.read(in); +// long accountCount = BytesUtils.readLong(in); +// long txTotalCount = BytesUtils.readLong(in); +// ByteArray key = BytesEncoding.readAsByteArray(NumberMask.SHORT, in); +// +// this.ledgerHashAlgorithm = ledgerHashAlgorithm; +// this.ledgerHash = ledgerHash; +// this.blockHeight = height; +// this.blockVersion = version; +// this.currentBlockHashAlgorithm = currentBlockHashAlgorithm; +// this.currentBlockHash = currentBlockHash; +// this.previousBlockHashAlgorithm = previousBlockHashAlgorithm; +// this.previousBlockHash = previousBlockHash; +// this.accountRoot = accountHash; +// this.accountCount = accountCount; +// this.txTotalCount = txTotalCount; +// this.genesisKey = key; +// } +// +// @Override +// public void writeTo(OutputStream out) throws IOException { +// BytesUtils.writeByte(ledgerHashAlgorithm.getAlgorithm(), out); +// HashEncoding.write(ledgerHash, out); +// +// BytesUtils.writeLong(blockHeight, out); +// BytesUtils.writeLong(blockVersion, out); +// +// BytesUtils.writeByte(currentBlockHashAlgorithm.getAlgorithm(), out); +// HashEncoding.write(currentBlockHash, out); +// +// BytesUtils.writeByte(previousBlockHashAlgorithm.getAlgorithm(), out); +// HashEncoding.write(previousBlockHash, out); +// +// HashEncoding.write(accountRoot, out); +// BytesUtils.writeLong(accountCount, out); +// BytesUtils.writeLong(txTotalCount, out); +// BytesEncoding.write(genesisKey, NumberMask.SHORT, out); +// } +// +// public HashAlgorithm getLedgerHashAlgorithm() { +// return ledgerHashAlgorithm; +// } +// +// public void setLedgerHashAlgorithm(HashAlgorithm ledgerHashAlgorithm) { +// this.ledgerHashAlgorithm = ledgerHashAlgorithm; +// } +// +// public void setLedgerHash(ByteArray ledgerHash) { +// this.ledgerHash = ledgerHash; +// } +// +// public void setBlockHeight(long blockHeight) { +// this.blockHeight = blockHeight; +// } +// +// public HashAlgorithm getCurrentBlockHashAlgorithm() { +// return currentBlockHashAlgorithm; +// } +// +// public void setCurrentBlockHashAlgorithm(HashAlgorithm currentBlockHashAlgorithm) { +// this.currentBlockHashAlgorithm = currentBlockHashAlgorithm; +// } +// +// public long getBlockVersion() { +// return blockVersion; +// } +// +// public void setBlockVersion(long blockVersion) { +// this.blockVersion = blockVersion; +// } +// +// public void setGenesisKey(ByteArray genesisKey) { +// this.genesisKey = genesisKey; +// } +// +// public ByteArray getCurrentBlockHash() { +// return currentBlockHash; +// } +// +// public void setCurrentBlockHash(ByteArray currentBlockHash) { +// this.currentBlockHash = currentBlockHash; +// } +// +// public HashAlgorithm getPreviousBlockHashAlgorithm() { +// return previousBlockHashAlgorithm; +// } +// +// public void setPreviousBlockHashAlgorithm(HashAlgorithm previousBlockHashAlgorithm) { +// this.previousBlockHashAlgorithm = previousBlockHashAlgorithm; +// } +// +// public ByteArray getAccountRoot() { +// return accountRoot; +// } +// +// public void setAccountRoot(ByteArray accountRoot) { +// this.accountRoot = accountRoot; +// } +// +// public long getAccountCount() { +// return accountCount; +// } +// +// public void setAccountCount(long accountCount) { +// this.accountCount = accountCount; +// } +// +// public long getTxTotalCount() { +// return txTotalCount; +// } +// +// public void setTxTotalCount(long txTotalCount) { +// this.txTotalCount = txTotalCount; +// } +// +// public ByteArray getPreviousBlockHash() { +// return previousBlockHash; +// } +// +// public void setPreviousBlockHash(ByteArray previousBlockHash) { +// this.previousBlockHash = previousBlockHash; +// } +// +// @Override +// public boolean equals(Object o) { +// if (this == o) return true; +// if (!(o instanceof LedgerImpl)) return false; +// LedgerImpl ledger = (LedgerImpl) o; +// return getBlockHeight() == ledger.getBlockHeight() && +// getBlockVersion() == ledger.getBlockVersion() && +// getLedgerHashAlgorithm() == ledger.getLedgerHashAlgorithm() && +// Objects.equals(getLedgerHash(), ledger.getLedgerHash()) && +// getCurrentBlockHashAlgorithm() == ledger.getCurrentBlockHashAlgorithm() && +// Objects.equals(getCurrentBlockHash(), ledger.getCurrentBlockHash()) && +// getPreviousBlockHashAlgorithm() == ledger.getPreviousBlockHashAlgorithm() && +// Objects.equals(getPreviousBlockHash(), ledger.getPreviousBlockHash()) && +// Objects.equals(getGenesisKey(), ledger.getGenesisKey()); +// } +// +// @Override +// public int hashCode() { +// +// return Objects.hash(getLedgerHashAlgorithm(), getLedgerHash(), getBlockHeight(), getBlockVersion(), getCurrentBlockHashAlgorithm(), getCurrentBlockHash(), getPreviousBlockHashAlgorithm(), getPreviousBlockHash(), getGenesisKey()); +// } +// +// @Override +// public long getLedgerVersion() { +// // TODO Auto-generated method stub +// return 0; +// } +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInfo.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInfo.java new file mode 100644 index 00000000..33600983 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInfo.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.hash.HashDigest; + +public class LedgerInfo { + + /** + * 账本哈希,这是账本的唯一标识; + * + * @return + */ + private HashDigest hash; + + private HashDigest latestBlockHash; + + private long latestBlockHeight; + + public HashDigest getHash() { + return hash; + } + + public void setHash(HashDigest hash) { + this.hash = hash; + } + + public HashDigest getLatestBlockHash() { + return latestBlockHash; + } + + public void setLatestBlockHash(HashDigest latestBlockHash) { + this.latestBlockHash = latestBlockHash; + } + + public long getLatestBlockHeight() { + return latestBlockHeight; + } + + public void setLatestBlockHeight(long latestBlockHeight) { + this.latestBlockHeight = latestBlockHeight; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInitOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInitOperation.java new file mode 100644 index 00000000..9b6a98a6 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInitOperation.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +@DataContract(code= TypeCodes.TX_OP_LEDGER_INIT) +public interface LedgerInitOperation extends Operation{ + + @DataField(order=1, refContract=true) + LedgerInitSetting getInitSetting(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInitSetting.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInitSetting.java new file mode 100644 index 00000000..548ec34f --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerInitSetting.java @@ -0,0 +1,53 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ValueType; + +/** + * 账本初始化配置; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.METADATA_INIT_SETTING) +public interface LedgerInitSetting { + + /** + * 账本的种子; + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + byte[] getLedgerSeed(); + + /** + * 共识参与方的列表; + * + * @return + */ + @DataField(order = 2, list = true, refContract = true) + ParticipantNode[] getConsensusParticipants(); + + /** + * 密码算法配置; + * + * @return + */ + @DataField(order = 3, refContract = true) + CryptoSetting getCryptoSetting(); + + + @DataField(order = 4, primitiveType=ValueType.TEXT) + String getConsensusProvider(); + + /** + * 共识算法配置; + * + * @return + */ + @DataField(order = 5, primitiveType=ValueType.BYTES) + Bytes getConsensusSettings(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerTransaction.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerTransaction.java new file mode 100644 index 00000000..45bc493c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/LedgerTransaction.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; + +/** + * 账本的事务; + * + * TODO: refactor: replace {@link Transaction} to {@link LedgerTransaction} + * + * @author huanghaiquan + * + */ +@DataContract(code=TypeCodes.TX_LEDGER) +public interface LedgerTransaction extends Transaction, LedgerDataSnapshot { + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/MagicNumber.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/MagicNumber.java new file mode 100644 index 00000000..cb053bfd --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/MagicNumber.java @@ -0,0 +1,67 @@ +package com.jd.blockchain.ledger; + +/** + * 魔数表; + * + * @author huanghaiquan + * + */ +public class MagicNumber { + + /** + * JD区块链系统标识的高位,即小写字母 j 的 ASCII; + * + */ + public static final byte JD_HIGH = 0x6A; + + /** + * JD区块链系统标识的低位, 即小写字母 d 的 ASCII; + */ + public static final byte JD_LOW = 0x64; + + /** + * 创世区块标识; + */ + public static final byte GENESIS_BLOCK = 0x00; + + /** + * 子区块标识; + * + * 注:“子区块”是除了“创世区块”之外其它的区块; + */ + public static final byte CHILD_BLOCK = 0x01; + + /** + * 交易内容标识; + */ + public static final byte TX_CONTENT = 0x10; + + /** + * 交易请求标识; + */ + public static final byte TX_REQUEST = 0x11; + + /** + * 交易持久标识; + */ + public static final byte TX_PERSISTENCE = 0x12; + + /** + * 数字签名标识; + */ + public static final byte SIGNATURE = 0x20; + + /** + * 公钥标识; + */ + public static final byte PUB_KEY = 0x21; + + /** + * 私钥标识; + */ + public static final byte PRIV_KEY = 0x22; + + + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/NodeRequest.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/NodeRequest.java new file mode 100644 index 00000000..574f1fcf --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/NodeRequest.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; + +@DataContract(code = TypeCodes.REQUEST_NODE) +public interface NodeRequest extends EndpointRequest { + + + /** + * 接入交易的节点的签名;
+ * + * 注:能够提交交易的节点可以是共识节点或网关节点; + * + * @return + */ + + @DataField(order=1, list=true, refContract=true) + DigitalSignature[] getNodeSignatures(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Operation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Operation.java new file mode 100644 index 00000000..87b8f97b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Operation.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; + +@DataContract(code= TypeCodes.TX_OP) +public interface Operation { + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/OperationArgument.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/OperationArgument.java new file mode 100644 index 00000000..5c2f3b3b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/OperationArgument.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.utils.io.ByteArray; + +/** + * 操作参数; + * + * @author huanghaiquan + * + */ +public interface OperationArgument { + + /** + * 参数类型;
+ * + * @return + */ + byte getKey(); + + /** + * 参数值; + * + * @return + */ + ByteArray getValue(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ParticipantInfo.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ParticipantInfo.java new file mode 100644 index 00000000..903cabfd --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ParticipantInfo.java @@ -0,0 +1,35 @@ +//package com.jd.blockchain.ledger; +// +//import com.jd.blockchain.base.data.TypeCodes; +//import com.jd.blockchain.binaryproto.DataContract; +//import com.jd.blockchain.binaryproto.DataField; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +// +//import my.utils.ValueType; +// +///** +// * 参与方信息; +// * +// * @author huanghaiquan +// * +// */ +//@DataContract(code = TypeCodes.METADATA_PARTICIPANT_INFO) +//public interface ParticipantInfo { +// +// /** +// * 参与者名称; +// * +// * @return +// */ +// @DataField(order = 1, primitiveType = ValueType.TEXT) +// String getName(); +// +// /** +// * 公钥; +// * +// * @return +// */ +// @DataField(order = 2, primitiveType = ValueType.BYTES) +// PubKey getPubKey(); +// +//} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ParticipantNode.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ParticipantNode.java new file mode 100644 index 00000000..6cc9924d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/ParticipantNode.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.ValueType; + +/** + * 参与方节点; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.METADATA_CONSENSUS_PARTICIPANT) +public interface ParticipantNode {// extends ConsensusNode, ParticipantInfo { + + /** + * 节点的顺序编号;
+ * + * 注:此字段并非固定不变的;在序列化和反序列化时不包含此字段; + * + * @return + */ + int getId(); + + /** + * 节点的虚拟地址,根据公钥生成; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.TEXT) + String getAddress(); + + /** + * 参与者名称; + * + * @return + */ + @DataField(order = 2, primitiveType = ValueType.TEXT) + String getName(); + + /** + * 节点消息认证的公钥; + * + * @return + */ + @DataField(order = 3, primitiveType = ValueType.BYTES) + PubKey getPubKey(); +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PreparedTransaction.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PreparedTransaction.java new file mode 100644 index 00000000..b017283e --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PreparedTransaction.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; + +/** + * 已就绪的交易; + * + * @author huanghaiquan + * + */ +public interface PreparedTransaction extends HashObject { + + /** + * 交易内容的 Hash; + * + * @return + */ + @Override + HashDigest getHash(); + + /** + * 交易数据内容;
+ * + *
+ * 如果需要对交易进行外部签名,可以将此数据块发送到外部进行签名; + * + * @return + */ + TransactionContent getTransactionContent(); + + /** + * 对交易进行签名; + * + * @param address + * 签名账户的地址; + * @param privKey + * 签名账户的私钥; + * @return + */ + DigitalSignature sign(CryptoKeyPair keyPair); + + /** + * 加入签名; + * + * @param address + * 签名账户的地址; + * @param digest + * Base64格式的签名摘要; + * @return + */ + void addSignature(DigitalSignature signature); + + /** + * 生成交易请求; + * + */ + TransactionResponse commit(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PrivKey.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PrivKey.java new file mode 100644 index 00000000..343daf3a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PrivKey.java @@ -0,0 +1,44 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.ledger.data.CryptoKeyEncoding; +import com.jd.blockchain.utils.io.ByteArray; + +import java.io.Serializable; + +/** + * 密钥; + * + * @author huanghaiquan + * + */ +//public class PrivKey implements CryptoKey,Serializable { +// +// private CryptoKeyType type; +// +// private ByteArray value; +// +// public PrivKey(CryptoKeyType type, ByteArray value) { +// this.type = type; +// this.value = value; +// } +// +// public CryptoKeyType getType() { +// return type; +// } +// +// public ByteArray getValue() { +// return value; +// } +// +// public ByteArray toBytes() { +// return CryptoKeyEncoding.toBytes(this); +// } +// +// @Override +// public String toString() { +// return toBytes().toString(); +// } +// +// +// +//} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PrivilegeType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PrivilegeType.java new file mode 100644 index 00000000..63e1383d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PrivilegeType.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.ledger; + +/** + * 权限类型; + * + * @author huanghaiquan + * + */ +public enum PrivilegeType { + + /** + * 账户注册; + */ + ACCOUNT_REGISTER(1), + + /** + * 账户权限配置; + */ + PRIVILEGE_CONFIG(2), + + /** + * 状态数据写入; + */ + STATE_WRITE(4), + + /** + * 合约应用部署; + */ + CONTRACT_APP_DEPLOY(8), + + /** + * 合约应用调用; + */ + CONTRACT_APP_INVOKE(16); + + public final int CODE; + + private PrivilegeType(int code) { + this.CODE = code; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PubKey.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PubKey.java new file mode 100644 index 00000000..94407a9e --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/PubKey.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.ledger.data.CryptoKeyEncoding; +import com.jd.blockchain.utils.io.ByteArray; + +/** + * 密钥; + * + * @author huanghaiquan + * + */ +//public class PubKey implements CryptoKey { +// +// private CryptoKeyType type; +// +// private ByteArray value; +// +// public PubKey(CryptoKeyType type, ByteArray value) { +// this.type = type; +// this.value = value; +// } +// +// public CryptoKeyType getType() { +// return type; +// } +// +// public ByteArray getValue() { +// return value; +// } +// +// public ByteArray toBytes() { +// return CryptoKeyEncoding.toBytes(this); +// } +// +// @Override +// public String toString() { +// return toBytes().toString(); +// } +// +// @Override +// public boolean equals(Object o) { +// if (this == o) return true; +// if (!(o instanceof PubKey)) return false; +// +// PubKey pubKey = (PubKey) o; +// +// if (getType() != pubKey.getType()) return false; +// return getValue().equals(pubKey.getValue()); +// } +//} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/SignatureInfo.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/SignatureInfo.java new file mode 100644 index 00000000..60b1532f --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/SignatureInfo.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.ledger; + +public interface SignatureInfo { + + /** + * 签署账户的地址; + * + * @return + */ + String getAddress(); + + /** + * 签名的摘要; + * + * 注:采用Base64编码; + * + * @return + */ + String getDigest(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/StateOpType.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/StateOpType.java new file mode 100644 index 00000000..342b20c3 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/StateOpType.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.ledger; + +/** + * 状态操作类型; + * + * @author huanghaiquan + * + */ +public enum StateOpType { + + /** + * 设置状态值; + */ + SET((byte) 1), + + /** + * 移除状态值; + */ + REMOVE((byte) 0); + + public final byte CODE; + + private StateOpType(byte code) { + this.CODE = code; + } + + public static StateOpType valueOf(byte code) { + for (StateOpType opType : StateOpType.values()) { + if (opType.CODE == code) { + return opType; + } + } + throw new IllegalArgumentException("Unsupported code[" + code + "] of StateOpType!"); + + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java new file mode 100644 index 00000000..a286797e --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/Transaction.java @@ -0,0 +1,48 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; +import com.jd.blockchain.utils.io.ByteArray; + +/** + * Transaction 区块链交易,是被原子执行的操作集合; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.TX) +public interface Transaction extends NodeRequest, HashObject { + + /** + * 交易 Hash; + * + * 这是包含交易内容、签名列表、交易结果的完整性 hash; + * + * @return + */ + @DataField(order=1, primitiveType = ValueType.BYTES) + @Override + HashDigest getHash(); + + /** + * 交易被包含的区块高度; + * + * @return + */ + @DataField(order=2, primitiveType=ValueType.INT64) + long getBlockHeight(); + + /** + * 交易的执行结果; + * + * 值为枚举值 {@link TransactionState#CODE} 之一; + * + * @return + */ + @DataField(order=3, refEnum=true) + TransactionState getExecutionState(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionBuilder.java new file mode 100644 index 00000000..6a0d6916 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionBuilder.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.data.ClientOperator; +import com.jd.blockchain.ledger.data.LedgerInitOperator; + +/** + * 区块链交易模板; + * + * @author huanghaiquan + * + */ +public interface TransactionBuilder extends ClientOperator, LedgerInitOperator { + + HashDigest getLedgerHash(); + + /** + * 完成交易定义,并生成就绪的交易数据;
+ * + * 注:调用此方法后,不能再向当前对象加入更多的操作; + * + * @return + */ + TransactionRequestBuilder prepareRequest(); + + /** + * 生成交易内容; + * @return + */ + TransactionContent prepareContent(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContent.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContent.java new file mode 100644 index 00000000..136ce6e0 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContent.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 交易内容; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.TX_CONTENT) +public interface TransactionContent extends TransactionContentBody, HashObject { + @Override + @DataField(order=1, primitiveType = ValueType.BYTES) + HashDigest getHash(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java new file mode 100644 index 00000000..39a27256 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionContentBody.java @@ -0,0 +1,36 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 交易内容; + * + * @author huanghaiquan + * + */ +@DataContract(code = TypeCodes.TX_CONTENT_BODY) +public interface TransactionContentBody { + + /** + * 执行交易的账本地址; + * + * 注:除了账本的创世交易之外,任何交易的账本地址都不允许为 null; + * + * @return + */ + @DataField(order = 1, primitiveType = ValueType.BYTES) + HashDigest getLedgerHash(); + + /** + * 操作列表; + * + * @return + */ + @DataField(order = 2, list = true, refContract = true, genericContract = true) + Operation[] getOperations(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java new file mode 100644 index 00000000..c88f0792 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionException.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.ledger; + +public class TransactionException extends Exception { + + private static final long serialVersionUID = 3583192000738807503L; + + private TransactionState state; + + public TransactionException(TransactionState state) { + this.state = state; + } + + public TransactionException(TransactionState state, String message) { + super(message); + this.state = state; + } + + public TransactionState getState() { + return state; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRequest.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRequest.java new file mode 100644 index 00000000..7bb56dcb --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRequest.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 交易请求; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.REQUEST) +public interface TransactionRequest extends NodeRequest, HashObject { + + /** + * 交易请求的 hash ;
+ * + * @return + */ + @Override + @DataField(order=1, primitiveType = ValueType.BYTES) + HashDigest getHash(); +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRequestBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRequestBuilder.java new file mode 100644 index 00000000..b32f6417 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRequestBuilder.java @@ -0,0 +1,81 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.hash.HashDigest; + +/** + * 已就绪的交易; + * + * @author huanghaiquan + * + */ +public interface TransactionRequestBuilder extends HashObject { + + /** + * 交易内容的 Hash; + * + * @return + */ + @Override + HashDigest getHash(); + + /** + * 交易数据内容;
+ * + *
+ * 如果需要对交易进行外部签名,可以将此数据块发送到外部进行签名; + * + * @return + */ + TransactionContent getTransactionContent(); + + /** + * 对交易进行签名; + * + * @param address + * 签名账户的地址; + * @param privKey + * 签名账户的私钥; + * @return + */ + DigitalSignature signAsEndpoint(CryptoKeyPair keyPair); + + /** + * 对交易进行签名; + * + * @param address + * 签名账户的地址; + * @param privKey + * 签名账户的私钥; + * @return + */ + DigitalSignature signAsNode(CryptoKeyPair keyPair); + + /** + * 加入签名; + * + * @param address + * 签名账户的地址; + * @param digest + * Base64格式的签名摘要; + * @return + */ + void addEndpointSignature(DigitalSignature signature); + + /** + * 加入签名; + * + * @param address + * 签名账户的地址; + * @param digest + * Base64格式的签名摘要; + * @return + */ + void addNodeSignature(DigitalSignature signature); + + /** + * 生成交易请求; + * + */ + TransactionRequest buildRequest(); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java new file mode 100644 index 00000000..cbd667af --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionRespHandle.java @@ -0,0 +1,92 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.ledger.core.impl.TransactionRespHandle + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/11/13 下午10:57 + * Description: + */ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.hash.HashDigest; + +/** + * + * @author shaozhuguang + * @create 2018/11/13 + * @since 1.0.0 + */ + +public class TransactionRespHandle implements TransactionResponse { + + private TransactionRequest request; + + private TransactionState result; + + private LedgerBlock block; + + private TransactionState globalResult; + + public TransactionRespHandle(TransactionRequest request, TransactionState result, TransactionState globalResult) { + this.request = request; + this.result = result; + this.globalResult = globalResult; + } + + public TransactionRequest getRequest() { + return request; + } + + public void setRequest(TransactionRequest request) { + this.request = request; + } + + public TransactionState getResult() { + return result; + } + + public void setResult(TransactionState result) { + this.result = result; + } + + public LedgerBlock getBlock() { + return block; + } + + public void setBlock(LedgerBlock block) { + this.block = block; + } + + public TransactionState getGlobalResult() { + return globalResult; + } + + public void setGlobalResult(TransactionState globalResult) { + this.globalResult = globalResult; + } + + @Override + public HashDigest getContentHash() { + return this.request.getTransactionContent().getHash(); + } + + @Override + public TransactionState getExecutionState() { + return this.result; + } + + @Override + public HashDigest getBlockHash() { + return this.block == null ? null : this.block.getHash(); + } + + @Override + public long getBlockHeight() { + return this.block == null ? -1 : this.block.getHeight(); + } + + @Override + public boolean isSuccess() { + return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java new file mode 100644 index 00000000..db7e5125 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionResponse.java @@ -0,0 +1,56 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.ValueType; + +/** + * 交易请求 {@link TransactionRequest} 的回复; + * + * @author huanghaiquan + * + */ +@DataContract(code= TypeCodes.TX_RESPONSE) +public interface TransactionResponse { + + /** + * 交易原始内容的哈希; + * + * @return + */ + @DataField(order=1, primitiveType = ValueType.BYTES) + HashDigest getContentHash(); + + /** + * 执行状态; + * + * @return + */ + @DataField(order=2, refEnum=true) + TransactionState getExecutionState(); + + /** + * 交易被纳入的区块哈希; + * + * @return + */ + @DataField(order=3, primitiveType = ValueType.BYTES) + HashDigest getBlockHash(); + + /** + * 交易被纳入的区块高度; + * + *

+ * 如果未生成区块,则返回 -1; + * + * @return + */ + @DataField(order=4, primitiveType=ValueType.INT64) + long getBlockHeight(); + + @DataField(order=5, primitiveType=ValueType.BOOLEAN) + boolean isSuccess(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java new file mode 100644 index 00000000..d19bcff2 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionState.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.EnumContract; +import com.jd.blockchain.binaryproto.EnumField; +import com.jd.blockchain.utils.ValueType; + +/** + * 交易(事务)执行状态; + * + * @author huanghaiquan + * + */ +@EnumContract(code= TypeCodes.ENUM_TYPE_TRANSACTION_STATE) +public enum TransactionState { + + /** + * 成功; + */ + SUCCESS((byte) 0), + + /** + * 共识错误; + */ + CONSENSUS_ERROR((byte) 1), + + /** + * 账本错误; + */ + LEDGER_ERROR((byte) 2), + + /** + * 系统错误; + */ + SYSTEM_ERROR((byte) 0x80), + + /** + * 超时; + */ + TIMEOUT((byte) 0x81); + + @EnumField(type= ValueType.INT8) + public final byte CODE; + + private TransactionState(byte code) { + this.CODE = code; + } + + public static TransactionState valueOf(byte code) { + for (TransactionState tr : values()) { + if (tr.CODE == code) { + return tr; + } + } + throw new IllegalArgumentException("Unsupported transaction result code!"); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionTemplate.java new file mode 100644 index 00000000..c1f3ff95 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/TransactionTemplate.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.data.ClientOperator; + +/** + * 区块链交易模板; + * + * @author huanghaiquan + * + */ +public interface TransactionTemplate extends ClientOperator { + + HashDigest getLedgerHash(); + + /** + * 完成交易定义,并生成就绪的交易数据;
+ * + * 注:调用此方法后,不能再向当前对象加入更多的操作; + * + * @return + */ + PreparedTransaction prepare(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserInfo.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserInfo.java new file mode 100644 index 00000000..5510cdb0 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserInfo.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.crypto.asymmetric.PubKey; + +@DataContract(code= TypeCodes.USER) +public interface UserInfo extends AccountHeader { + + PubKey getDataPubKey(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserInfoSetOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserInfoSetOperation.java new file mode 100644 index 00000000..b9a92317 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserInfoSetOperation.java @@ -0,0 +1,33 @@ +//package com.jd.blockchain.ledger; +// +//import com.jd.blockchain.binaryproto.DataContract; +// +///** +// * @author huanghaiquan +// * +// */ +//@DataContract(code=LedgerCodes.TX_OP_USER_INFO_SET) +//public interface UserInfoSetOperation extends Operation { +// +// @Override +// default OperationType getType() { +// return OperationType.SET_USER_INFO; +// } +// +// String getUserAddress(); +// +// KVEntry[] getPropertiesWriteSet(); +// +// +// @DataContract(code=LedgerCodes.TX_OP_USER_INFO_SET_KV) +// public static interface KVEntry{ +// +// String getKey(); +// +// String getValue(); +// +// long getExpectedVersion(); +// } +// +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserRegisterOperation.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserRegisterOperation.java new file mode 100644 index 00000000..ec0f7372 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/UserRegisterOperation.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.ledger; + +import com.jd.blockchain.base.data.TypeCodes; +import com.jd.blockchain.binaryproto.DataContract; +import com.jd.blockchain.binaryproto.DataField; + +@DataContract(code= TypeCodes.TX_OP_USER_REG) +public interface UserRegisterOperation extends Operation { + +// @Override +// @DataField(order=1, refEnum = true) +// default OperationType getType() { +// return OperationType.REGISTER_DATA_ACCOUNT; +// } + + @DataField(order=2, refContract = true) + BlockchainIdentity getUserID(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/BlockchainOperationFactory.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/BlockchainOperationFactory.java new file mode 100644 index 00000000..527049d6 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/BlockchainOperationFactory.java @@ -0,0 +1,205 @@ +package com.jd.blockchain.ledger.data; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.utils.Bytes; + +public class BlockchainOperationFactory implements ClientOperator, LedgerInitOperator { + + private static final LedgerInitOperationBuilderImpl LEDGER_INIT_OP_BUILDER = new LedgerInitOperationBuilderImpl(); + + private static final UserRegisterOperationBuilderImpl USER_REG_OP_BUILDER = new UserRegisterOperationBuilderImpl(); + + private static final DataAccountRegisterOperationBuilderImpl DATA_ACC_REG_OP_BUILDER = new DataAccountRegisterOperationBuilderImpl(); + + private static final ContractCodeDeployOperationBuilderImpl CONTRACT_CODE_DEPLOY_OP_BUILDER = new ContractCodeDeployOperationBuilderImpl(); + + private static final ContractEventSendOperationBuilderImpl CONTRACT_EVENT_SEND_OP_BUILDER = new ContractEventSendOperationBuilderImpl(); + + private LedgerInitOperationBuilder ledgerInitOpBuilder = new LedgerInitOperationBuilderFilter(); + + private UserRegisterOperationBuilder userRegOpBuilder = new UserRegisterOperationBuilderFilter(); + + private DataAccountRegisterOperationBuilder dataAccRegOpBuilder = new DataAccountRegisterOperationBuilderFilter(); + + private ContractCodeDeployOperationBuilder contractCodeDeployOpBuilder = new ContractCodeDeployOperationBuilderFilter(); + + private ContractEventSendOperationBuilder contractEventSendOpBuilder = new ContractEventSendOperationBuilderFilter(); + + private List operationList = new ArrayList<>(); + + @Override + public LedgerInitOperationBuilder ledgers() { + return ledgerInitOpBuilder; + } + + @Override + public UserRegisterOperationBuilder users() { + return userRegOpBuilder; + } + + @Override + public DataAccountRegisterOperationBuilder dataAccounts() { + return dataAccRegOpBuilder; + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { + return new DataAccountKVSetOperationBuilderFilter(Bytes.fromBase58(accountAddress)); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(Bytes accountAddress) { + return new DataAccountKVSetOperationBuilderFilter(accountAddress); + } + + @Override + public ContractCodeDeployOperationBuilder contracts() { + return contractCodeDeployOpBuilder; + } + + @Override + public ContractEventSendOperationBuilder contractEvents() { + return contractEventSendOpBuilder; + } + + public Collection getOperations() { + // TODO: 合并操作列表中可能的重复操作; + return operationList; + } + + public void clear() { + operationList.clear(); + } + + // --------------------------------- 内部类型 ----------------------------------- + + private class LedgerInitOperationBuilderFilter implements LedgerInitOperationBuilder { + + @Override + public LedgerInitOperation create(LedgerInitSetting initSetting) { + LedgerInitOperation op = LEDGER_INIT_OP_BUILDER.create(initSetting); + operationList.add(op); + return op; + } + + } + + private class UserRegisterOperationBuilderFilter implements UserRegisterOperationBuilder { + + @Override + public UserRegisterOperation register(BlockchainIdentity userID) { + UserRegisterOperation op = USER_REG_OP_BUILDER.register(userID); + operationList.add(op); + return op; + } + + } + + private class DataAccountRegisterOperationBuilderFilter implements DataAccountRegisterOperationBuilder { + + @Override + public DataAccountRegisterOperation register(BlockchainIdentity accountID) { + DataAccountRegisterOperation op = DATA_ACC_REG_OP_BUILDER.register(accountID); + operationList.add(op); + return op; + } + + } + + private class DataAccountKVSetOperationBuilderFilter implements DataAccountKVSetOperationBuilder { + + private DataAccountKVSetOperationBuilder innerBuilder; + + private DataAccountKVSetOperation op; + + public DataAccountKVSetOperationBuilderFilter(Bytes accountAddress) { + innerBuilder = new DataAccountKVSetOperationBuilderImpl(accountAddress); + } + + @Override + public DataAccountKVSetOperation getOperation() { + return innerBuilder.getOperation(); + } + + @Override + public DataAccountKVSetOperationBuilder set(String key, byte[] value, long expVersion) { + innerBuilder.set(key, value, expVersion); + if (op == null) { + op = innerBuilder.getOperation(); + operationList.add(op); + } + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { + innerBuilder.set(key, value, expVersion); + if (op == null) { + op = innerBuilder.getOperation(); + operationList.add(op); + } + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, long value, long expVersion) { + innerBuilder.set(key, value, expVersion); + if (op == null) { + op = innerBuilder.getOperation(); + operationList.add(op); + } + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion) { + innerBuilder.set(key, value, expVersion); + if (op == null) { + op = innerBuilder.getOperation(); + operationList.add(op); + } + return this; + } + + } + + private class ContractCodeDeployOperationBuilderFilter implements ContractCodeDeployOperationBuilder { + + @Override + public ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode) { + ContractCodeDeployOperation op = CONTRACT_CODE_DEPLOY_OP_BUILDER.deploy(id, chainCode); + operationList.add(op); + return op; + } + + } + + private class ContractEventSendOperationBuilderFilter implements ContractEventSendOperationBuilder { + + @Override + public ContractEventSendOperation send(String address, String event, byte[] args) { + ContractEventSendOperation op = CONTRACT_EVENT_SEND_OP_BUILDER.send(address, event, args); + operationList.add(op); + return op; + } + + @Override + public ContractEventSendOperation send(Bytes address, String event, byte[] args) { + ContractEventSendOperation op = CONTRACT_EVENT_SEND_OP_BUILDER.send(address, event, args); + operationList.add(op); + return op; + } + + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ClientOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ClientOperator.java new file mode 100644 index 00000000..9fe9ff4f --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ClientOperator.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.data; + +/** + * 面向客户端的操作; + * + * @author huanghaiquan + * + */ +public interface ClientOperator extends UserOperator, DataAccountOperator, ContractOperator, EventOperator { + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ConsensusParticipantData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ConsensusParticipantData.java new file mode 100644 index 00000000..73049d3a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ConsensusParticipantData.java @@ -0,0 +1,59 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class ConsensusParticipantData implements ParticipantNode { + + private int id; + + private String address; + + private String name; + + private PubKey pubKey; + + private NetworkAddress hostAddress; + + 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 NetworkAddress getConsensusAddress() { + return hostAddress; + } + + public void setHostAddress(NetworkAddress hostAddress) { + this.hostAddress = hostAddress; + } + + public PubKey getPubKey() { + return pubKey; + } + + public void setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + } \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOpTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOpTemplate.java new file mode 100644 index 00000000..865d37d2 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOpTemplate.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; +import com.jd.blockchain.ledger.DigitalSignature; + +public class ContractCodeDeployOpTemplate implements ContractCodeDeployOperation { + static { + DataContractRegistry.register(ContractCodeDeployOperation.class); + } + + private BlockchainIdentity contractID; + + private byte[] chainCode; + + public ContractCodeDeployOpTemplate() { + } + + @DConstructor(name="ContractCodeDeployOpTemplate") + public ContractCodeDeployOpTemplate(@FieldSetter(name="getContractID", type="BlockchainIdentity") BlockchainIdentity contractID, @FieldSetter(name="getChainCode", type="byte[]") byte[] chainCode) { + this.contractID = contractID; + this.chainCode= chainCode; + } + + @Override + public BlockchainIdentity getContractID() { + return contractID; + } + + @Override + public byte[] getChainCode() { + return chainCode; + } + + @Override + public DigitalSignature getAddressSignature() { + // TODO Auto-generated method stub + return null; + } + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOperationBuilder.java new file mode 100644 index 00000000..ee48739c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOperationBuilder.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; + +public interface ContractCodeDeployOperationBuilder { + + /** + * 部署合约; + * + * @param id + * 区块链身份; + * @param chainCode + * 合约应用的字节代码; + * @return + */ + ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOperationBuilderImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOperationBuilderImpl.java new file mode 100644 index 00000000..67921caf --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractCodeDeployOperationBuilderImpl.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; + +public class ContractCodeDeployOperationBuilderImpl implements ContractCodeDeployOperationBuilder{ + + @Override + public ContractCodeDeployOperation deploy(BlockchainIdentity id, byte[] chainCode) { + ContractCodeDeployOpTemplate op = new ContractCodeDeployOpTemplate(id, chainCode); + return op; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOpTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOpTemplate.java new file mode 100644 index 00000000..d7d06d85 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOpTemplate.java @@ -0,0 +1,46 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.utils.Bytes; + +public class ContractEventSendOpTemplate implements ContractEventSendOperation { + static { + DataContractRegistry.register(ContractEventSendOperation.class); + } + + private Bytes contractAddress; + private byte[] args; + private String event; + + public ContractEventSendOpTemplate() { + } + + @DConstructor(name="ContractEventSendOpTemplate") + public ContractEventSendOpTemplate(@FieldSetter(name="getContractAddress", type="Bytes") Bytes contractAddress, + @FieldSetter(name="getEvent", type="String") String event, + @FieldSetter(name="getArgs", type="byte[]") byte[] args) { + this.contractAddress = contractAddress; + this.event = event; + this.args = args; + } + + @Override + public Bytes getContractAddress() { + return contractAddress; + } + + @Override + public String getEvent() { + return event; + } + + @Override + public byte[] getArgs() { + return args; + } + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOperationBuilder.java new file mode 100644 index 00000000..1c3f7721 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOperationBuilder.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.utils.Bytes; + +public interface ContractEventSendOperationBuilder { + + /** + * @param address 合约地址; + * @param event 事件名; + * @param args 事件参数; + * @return + */ + ContractEventSendOperation send(String address, String event, byte[] args); + + /** + * @param address 合约地址; + * @param event 事件名; + * @param args 事件参数; + * @return + */ + ContractEventSendOperation send(Bytes address, String event, byte[] args); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOperationBuilderImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOperationBuilderImpl.java new file mode 100644 index 00000000..fdef8106 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractEventSendOperationBuilderImpl.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.utils.Bytes; + +public class ContractEventSendOperationBuilderImpl implements ContractEventSendOperationBuilder{ + + + @Override + public ContractEventSendOperation send(String address, String event, byte[] args) { + ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(Bytes.fromBase58(address), event, args); + return op; + } + + @Override + public ContractEventSendOperation send(Bytes address, String event, byte[] args) { + ContractEventSendOpTemplate op = new ContractEventSendOpTemplate(address, event, args); + return op; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractOperator.java new file mode 100644 index 00000000..dada75c1 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/ContractOperator.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.data; + +public interface ContractOperator { + + /** + * 部署合约; + * @return + */ + ContractCodeDeployOperationBuilder contracts(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/CryptoKeyEncoding.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/CryptoKeyEncoding.java new file mode 100644 index 00000000..fcf22a26 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/CryptoKeyEncoding.java @@ -0,0 +1,131 @@ +package com.jd.blockchain.ledger.data; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoKey; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.MagicNumber; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.NumberMask; +import com.jd.blockchain.utils.io.RuntimeIOException; + +public class CryptoKeyEncoding { + + /** + * @param key + * @param out + * @return 写入的字节数; + */ + public static int writeKey(CryptoKey key, OutputStream out) { + try { + byte magicNum; + if (key instanceof PubKey) { + magicNum = MagicNumber.PUB_KEY; + } else if (key instanceof PrivKey) { + magicNum = MagicNumber.PRIV_KEY; + } else { + throw new IllegalArgumentException("Unsupported key type[" + key.getClass().getName() + "]!"); + } + + out.write(magicNum); + out.write(key.getAlgorithm().CODE); + + int size = 2;// 已经写入 2 字节; + size += BytesEncoding.write(key.getRawKeyBytes(), NumberMask.SHORT, out); + return size; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * @param in + * @return + * @throws IOException + */ + public static CryptoKey readKey(InputStream in) { + try { + byte magicNum = (byte) in.read(); + if (magicNum != MagicNumber.PUB_KEY && magicNum != MagicNumber.PRIV_KEY) { + throw new IllegalArgumentException("The CryptoKey MagicNumber read from the InputStream is Illegal!"); + } + byte code = (byte) in.read(); + CryptoAlgorithm algorithm = CryptoAlgorithm.valueOf(code); + ByteArray value = BytesEncoding.readAsByteArray(NumberMask.SHORT, in); + + if (magicNum == MagicNumber.PUB_KEY) { + return new PubKey(algorithm, value.bytes()); + } else { + return new PrivKey(algorithm, value.bytes()); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * @param in + * @return + * @throws IOException + */ + public static PubKey readPubKey(InputStream in) { + try { + byte magicNum = (byte) in.read(); + if (magicNum != MagicNumber.PUB_KEY) { + throw new IllegalArgumentException("The PubKey MagicNumber read from the InputStream is Illegal!"); + } + byte code = (byte) in.read(); + CryptoAlgorithm algorithm = CryptoAlgorithm.valueOf(code); + ByteArray value = BytesEncoding.readAsByteArray(NumberMask.SHORT, in); + return new PubKey(algorithm, value.bytes()); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * @param in + * @return + * @throws IOException + */ + public static PrivKey readPrivKey(InputStream in) { + try { + byte magicNum = (byte) in.read(); + if (magicNum != MagicNumber.PRIV_KEY) { + throw new IllegalArgumentException("The PrivKey MagicNumber read from the InputStream is Illegal!"); + } + byte code = (byte) in.read(); + CryptoAlgorithm algorithm = CryptoAlgorithm.valueOf(code); + ByteArray value = BytesEncoding.readAsByteArray(NumberMask.SHORT, in); + return new PrivKey(algorithm, value.bytes()); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static ByteArray toBytes(CryptoKey key) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + writeKey(key, out); + byte[] keyBytes = out.toByteArray(); + return ByteArray.wrap(keyBytes); + } + + public static String toBase64(CryptoKey key) { + return toBytes(key).toBase64(); + } + + public static CryptoKey fromBase64(String hexKeyString) { + return readKey(ByteArray.parseBase64(hexKeyString).asInputStream()); + } + + public static CryptoKey fromBase58(String base58String) { + return readKey(ByteArray.parseBase58(base58String).asInputStream()); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOpTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOpTemplate.java new file mode 100644 index 00000000..3d4c6f30 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOpTemplate.java @@ -0,0 +1,69 @@ +package com.jd.blockchain.ledger.data; + +import java.util.LinkedHashMap; +import java.util.Map; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.utils.Bytes; + +public class DataAccountKVSetOpTemplate implements DataAccountKVSetOperation { + static { + DataContractRegistry.register(DataAccountKVSetOperation.class); + } + + private Bytes accountAddress; + + private Map kvset = new LinkedHashMap<>(); + + public DataAccountKVSetOpTemplate() { + } + + @DConstructor(name="DataAccountKVSetOpTemplate") + public DataAccountKVSetOpTemplate(@FieldSetter(name="getAccountAddress", type="String") Bytes accountAddress) { + this.accountAddress = accountAddress; + } + + public DataAccountKVSetOpTemplate(Bytes accountAddress, Map kvset) { + this.accountAddress = accountAddress; + this.kvset = kvset; + } + + @Override + public Bytes getAccountAddress() { + return accountAddress; + } + + @Override + public KVWriteEntry[] getWriteSet() { + return kvset.values().toArray(new KVWriteEntry[kvset.size()]); + } + + public void setWriteSet(Object[] kvEntries) { + for (Object object : kvEntries) { + KVWriteEntry kvEntry = (KVWriteEntry)object; + set(kvEntry.getKey(), kvEntry.getValue(), kvEntry.getExpectedVersion()); + } + return; + } + + public void set(String key, BytesValue value, long expVersion) { + if (kvset.containsKey(key)) { + throw new IllegalArgumentException("Cann't set the same key repeatly!"); + } + KVData kvdata = new KVData(key, value, expVersion); + kvset.put(key, kvdata); + } + + public void set(KVData kvData) { + if (kvset.containsKey(kvData.getKey())) { + throw new IllegalArgumentException("Cann't set the same key repeatly!"); + } + kvset.put(kvData.getKey(), kvData); + } + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOperationBuilder.java new file mode 100644 index 00000000..42c4fc66 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOperationBuilder.java @@ -0,0 +1,68 @@ +package com.jd.blockchain.ledger.data; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataType; +import com.jd.blockchain.utils.Bytes; + +import java.util.Date; + +public interface DataAccountKVSetOperationBuilder { + + /** + * 数据账户的KV写入操作; + * + * @return + */ + DataAccountKVSetOperation getOperation(); + + /** + * 写入键值; + * + * @param key + * 键; + * @param value + * 值;byte[]格式 + * @param expVersion + * 预期的当前版本;如果版本不匹配,则写入失败; + * @return + */ + DataAccountKVSetOperationBuilder set(String key, byte[] value, long expVersion); + /** + * 写入键值; + * + * @param key + * 键; + * @param value + * 值;String格式 + * @param expVersion + * 预期的当前版本;如果版本不匹配,则写入失败; + * @return + */ + DataAccountKVSetOperationBuilder set(String key, String value, long expVersion); + /** + * 写入键值; + * + * @param key + * 键; + * @param value + * 值;Bytes格式 + * @param expVersion + * 预期的当前版本;如果版本不匹配,则写入失败; + * @return + */ + DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion); + /** + * 写入键值; + * + * @param key + * 键; + * @param value + * 值;long格式 + * @param expVersion + * 预期的当前版本;如果版本不匹配,则写入失败; + * @return + */ + DataAccountKVSetOperationBuilder set(String key, long value, long expVersion); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOperationBuilderImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOperationBuilderImpl.java new file mode 100644 index 00000000..46cb8dd5 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountKVSetOperationBuilderImpl.java @@ -0,0 +1,69 @@ +package com.jd.blockchain.ledger.data; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.BytesValueImpl; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; + +import com.jd.blockchain.ledger.DataType; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +public class DataAccountKVSetOperationBuilderImpl implements DataAccountKVSetOperationBuilder{ + + private DataAccountKVSetOpTemplate operation; + + public DataAccountKVSetOperationBuilderImpl(Bytes accountAddress) { + operation = new DataAccountKVSetOpTemplate(accountAddress); + } + + @Override + public DataAccountKVSetOperation getOperation() { + return operation; + } + + public static boolean isJson(String str) { + boolean result = false; + try { + Object obj=JSON.parse(str); + result = true; + } catch (Exception e) { + result=false; + } + return result; + } + + @Override + public DataAccountKVSetOperationBuilder set(String key, byte[] value, long expVersion) { + BytesValue bytesValue = new BytesValueImpl(DataType.BYTES, value); + operation.set(key, bytesValue, expVersion); + return this; + } + + @Override + public DataAccountKVSetOperationBuilder set(String key, String value, long expVersion) { + BytesValue bytesValue; + if (isJson(value)) { + bytesValue = new BytesValueImpl(DataType.JSON, value.getBytes()); + } + else { + bytesValue = new BytesValueImpl(DataType.TEXT, value.getBytes()); + } + operation.set(key, bytesValue, expVersion); + return this; + } + + @Override + public DataAccountKVSetOperationBuilder set(String key, Bytes value, long expVersion) { + BytesValue bytesValue = new BytesValueImpl(DataType.BYTES, value.toBytes()); + operation.set(key, bytesValue, expVersion); + return this; + } + @Override + public DataAccountKVSetOperationBuilder set(String key, long value, long expVersion) { + BytesValue bytesValue = new BytesValueImpl(DataType.INT64, BytesUtils.toBytes(value)); + operation.set(key, bytesValue, expVersion); + return this; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountOperator.java new file mode 100644 index 00000000..ff08d6cf --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountOperator.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.utils.Bytes; + +public interface DataAccountOperator { + + /** + * 数据账户; + * + * @return + */ + + DataAccountRegisterOperationBuilder dataAccounts(); + + /** + * 写入数据; + * @param accountAddress + * @return + */ + DataAccountKVSetOperationBuilder dataAccount(String accountAddress); + + /** + * 写入数据; + * @param accountAddress + * @return + */ + DataAccountKVSetOperationBuilder dataAccount(Bytes accountAddress); +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOpTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOpTemplate.java new file mode 100644 index 00000000..302d114a --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOpTemplate.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; +import com.jd.blockchain.ledger.DigitalSignature; + +public class DataAccountRegisterOpTemplate implements DataAccountRegisterOperation { + static { + DataContractRegistry.register(DataAccountKVSetOperation.class); + } + + private BlockchainIdentity accountID; + + public DataAccountRegisterOpTemplate() { + } + + @DConstructor(name="DataAccountRegisterOpTemplate") + public DataAccountRegisterOpTemplate(@FieldSetter(name="getAccountID", type="BlockchainIdentity") BlockchainIdentity accountID) { + this.accountID = accountID; + } + + @Override + public BlockchainIdentity getAccountID() { + return accountID; + } + + @Override + public DigitalSignature getAddressSignature() { + // TODO Auto-generated method stub + return null; + } + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOperationBuilder.java new file mode 100644 index 00000000..c6bd23f0 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOperationBuilder.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; + +public interface DataAccountRegisterOperationBuilder { + + /** + * @param id + * @return + */ + DataAccountRegisterOperation register(BlockchainIdentity id); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOperationBuilderImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOperationBuilderImpl.java new file mode 100644 index 00000000..afff51a2 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DataAccountRegisterOperationBuilderImpl.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; + +public class DataAccountRegisterOperationBuilderImpl implements DataAccountRegisterOperationBuilder{ + + @Override + public DataAccountRegisterOperation register(BlockchainIdentity userID) { + return new DataAccountRegisterOpTemplate(userID); + } + + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DigitalSignatureBlob.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DigitalSignatureBlob.java new file mode 100644 index 00000000..47fb64b3 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/DigitalSignatureBlob.java @@ -0,0 +1,117 @@ +package com.jd.blockchain.ledger.data; + + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.MagicNumber; + +/** + * 数字签名的字节块; + * + *

+ * 字节位如下:
+ * [第1字节]:标识数据类型为数字签名的魔数常量 ({@link MagicNumber#SIGNATURE});
+ * 
+ * [第2字节] - [第N字节]: 公钥;
+ * 		注:公钥的值是包含了公钥算法标识和公钥内容的字节码编码;
+ * 
+ * [第N+1字节] - 结束: 摘要;
+ * 
+ * 
+ * + * @author huanghaiquan + * + */ +public class DigitalSignatureBlob implements DigitalSignature { //, Externalizable + + private PubKey pubKey; + + private SignatureDigest digest; + + @Override + public PubKey getPubKey() { + return pubKey; + } + + @Override + public SignatureDigest getDigest() { + return digest; + } + + public DigitalSignatureBlob() { + } + + public DigitalSignatureBlob(PubKey pubKey, SignatureDigest digest) { + this.pubKey = pubKey; + this.digest = digest; + } + + +// @Override +// public void resolvFrom(InputStream in) { +// try { +// byte[] buff = new byte[1]; +// int len = in.read(buff); +// if (len < 1) { +// throw new IllegalArgumentException("No enough bytes was read for the magic number [SIGNATURE]!"); +// } +// if (buff[0] != MagicNumber.SIGNATURE) { +// throw new IllegalArgumentException("Magic number [SIGNATURE] dismatch!"); +// } +// PubKey pk = CryptoKeyEncoding.readPubKey(in); +// ByteArray dg = BytesEncoding.readAsByteArray(NumberMask.SHORT, in); +// this.pubKey = pk; +// this.digest = dg; +// } catch (IOException e) { +// throw new RuntimeIOException(e.getMessage(), e); +// } +// } +// +// @Override +// public void writeTo(OutputStream out) { +// try { +// out.write(MagicNumber.SIGNATURE); +// CryptoKeyEncoding.writeKey(pubKey, out); +// BytesEncoding.write(digest, NumberMask.SHORT, out); +// } catch (IOException e) { +// throw new RuntimeIOException(e.getMessage(), e); +// } +// } +// +// @Override +// public boolean equals(Object o) { +// if (this == o) return true; +// if (!(o instanceof DigitalSignatureBlob)) return false; +// DigitalSignatureBlob that = (DigitalSignatureBlob) o; +// return Objects.equals(getPubKey(), that.getPubKey()) && +// Objects.equals(getDigest(), that.getDigest()); +// } +// +// public byte[] toBytes() { +// ByteArrayOutputStream out = new ByteArrayOutputStream(); +// writeTo(out); +// return out.toByteArray(); +// } +// +// @Override +// public String toString() { +// return toBytes().toString(); +// } +// +// @Override +// public void writeExternal(ObjectOutput out) throws IOException { +// byte[] bts = toBytes(); +// out.writeInt(bts.length); +// out.write(bts, 0, bts.length); +// } +// +// @Override +// public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { +// int size = in.readInt(); +// byte[] bs = new byte[size]; +// in.readFully(bs, 0, size); +// resolvFrom(new ByteArrayInputStream(bs)); +// } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/EventOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/EventOperator.java new file mode 100644 index 00000000..551dc06b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/EventOperator.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.data; + +public interface EventOperator { + + /** + * 部署合约; + * @return + */ + ContractEventSendOperationBuilder contractEvents(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/HashEncoding.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/HashEncoding.java new file mode 100644 index 00000000..72cb0557 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/HashEncoding.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.ledger.data; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.jd.blockchain.ledger.HashAlgorithm; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.NumberMask; +import com.jd.blockchain.utils.security.RipeMD160Utils; +import com.jd.blockchain.utils.security.ShaUtils; + +public class HashEncoding { + public static int write(byte[] hash, OutputStream out) { + BytesEncoding.write(hash, NumberMask.TINY, out); + return hash.length; + } + public static int write(ByteArray hash, OutputStream out) { + BytesEncoding.write(hash, NumberMask.TINY, out); + return hash.size(); + } + + /** + * @param in + * @return + * @throws IOException + */ + public static ByteArray read(InputStream in) throws IOException { + return BytesEncoding.readAsByteArray(NumberMask.TINY, in); + } + + + + public static ByteArray computeHash(ByteArray content, HashAlgorithm algorithm) { + return ByteArray.wrap(hash(content.bytes(), algorithm)); + } + + public static ByteArray computeHash(byte[] contentBytes, HashAlgorithm algorithm) { + return ByteArray.wrap(hash(contentBytes, algorithm)); + } + + public static byte[] hash(byte[] contentBytes, HashAlgorithm algorithm) { + byte[] hashBytes; + switch (algorithm) { + case RIPE160: + hashBytes = RipeMD160Utils.hash(contentBytes); + break; + case SHA256: + hashBytes = ShaUtils.hash_256(contentBytes); + break; + default: + throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); + } + return hashBytes; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/KVData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/KVData.java new file mode 100644 index 00000000..169a8747 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/KVData.java @@ -0,0 +1,39 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.BytesValue; +import com.jd.blockchain.ledger.DataAccountKVSetOperation.KVWriteEntry; + +public class KVData implements KVWriteEntry { + + private String key; + + private BytesValue value; + + private long expectedVersion; + + + @DConstructor(name="KVData") + public KVData(@FieldSetter(name="getKey", type="String") String key, @FieldSetter(name="getValue", type="BytesValue") BytesValue value, @FieldSetter(name="getExpectedVersion", type="long")long expectedVersion) { + this.key = key; + this.value = value; + this.expectedVersion = expectedVersion; + } + + @Override + public String getKey() { + return key; + } + + @Override + public BytesValue getValue() { + return value; + } + + @Override + public long getExpectedVersion() { + return expectedVersion; + } + + } \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOpTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOpTemplate.java new file mode 100644 index 00000000..213dacff --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOpTemplate.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.LedgerInitSetting; + +public class LedgerInitOpTemplate implements LedgerInitOperation { + static { + DataContractRegistry.register(LedgerInitOperation.class); + } + + private LedgerInitSetting initSetting; + + public LedgerInitOpTemplate() { + } + + @DConstructor(name="LedgerInitOpTemplate") + public LedgerInitOpTemplate(@FieldSetter(name="getInitSetting", type="LedgerInitSetting") LedgerInitSetting initSetting) { + this.initSetting = initSetting; + } + + @Override + public LedgerInitSetting getInitSetting() { + return initSetting; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperationBuilder.java new file mode 100644 index 00000000..6b7ff42b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperationBuilder.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.LedgerInitSetting; + +public interface LedgerInitOperationBuilder { + + /** + * 注册; + * + * @param initSetting + * 账本初始化配置; + * @return LedgerInitOperation + */ + LedgerInitOperation create(LedgerInitSetting initSetting); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperationBuilderImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperationBuilderImpl.java new file mode 100644 index 00000000..5cb11dc2 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperationBuilderImpl.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.LedgerInitSetting; + +public class LedgerInitOperationBuilderImpl implements LedgerInitOperationBuilder { + + @Override + public LedgerInitOperation create(LedgerInitSetting initSetting) { + return new LedgerInitOpTemplate(initSetting); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperator.java new file mode 100644 index 00000000..43ee3fbb --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitOperator.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.ledger.data; + +public interface LedgerInitOperator { + + /** + * 注册账户操作; + * + * @return + */ + + LedgerInitOperationBuilder ledgers(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitSettingData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitSettingData.java new file mode 100644 index 00000000..ae40966b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/LedgerInitSettingData.java @@ -0,0 +1,65 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.LedgerInitSetting; + +public class LedgerInitSettingData implements LedgerInitSetting { + + private byte[] ledgerSeed; + + private ParticipantNode[] consensusParticipants; + + private CryptoSetting cryptoSetting; + + private String consensusProvider; + + private Bytes consensusSettings; + + @Override + public byte[] getLedgerSeed() { + return ledgerSeed; + } + + @Override + public ParticipantNode[] getConsensusParticipants() { + return consensusParticipants; + } + + @Override + public CryptoSetting getCryptoSetting() { + return cryptoSetting; + } + + @Override + public Bytes getConsensusSettings() { + return consensusSettings; + } + + public void setLedgerSeed(byte[] ledgerSeed) { + this.ledgerSeed = ledgerSeed; + } + + public void setConsensusParticipants(ParticipantNode[] consensusParticipants) { + this.consensusParticipants = consensusParticipants; + } + + public void setCryptoSetting(CryptoSetting cryptoSetting) { + this.cryptoSetting = cryptoSetting; + } + + public void setConsensusSettings(Bytes consensusSettings) { + this.consensusSettings = consensusSettings; + } + + @Override + public String getConsensusProvider() { + return consensusProvider; + } + + public void setConsensusProvider(String consensusProvider) { + this.consensusProvider = consensusProvider; + } + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/NewLedgerOpBlob.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/NewLedgerOpBlob.java new file mode 100644 index 00000000..cd68cd30 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/NewLedgerOpBlob.java @@ -0,0 +1,37 @@ +//package com.jd.blockchain.ledger.data; +// +//import com.jd.blockchain.ledger.OperationType; +//import my.utils.io.ByteArray; +// +//public class NewLedgerOpBlob { +// +// private ByteArray genesisKey; +// +// public NewLedgerOpBlob() { +// } +// +// public NewLedgerOpBlob(ByteArray genesisKey) { +// this.genesisKey = genesisKey; +// } +// +// public void resolvFrom(OpBlob opBlob) { +//// if (OperationType.NEW_LEDGER.CODE != opBlob.getCode()) { +//// throw new IllegalArgumentException( +//// "Could not resolve operation info due to NEW_LEDGER operation code mismatch!"); +//// } +//// +//// genesisKey = opBlob.getArg(0); +// } +// +// public OpBlob toBlob() { +// // 写入操作码; +// OpBlob opBlob = new OpBlob(); +// opBlob.setOperation(OperationType.NEW_LEDGER.CODE, genesisKey); +// +// return opBlob; +// } +// +// public ByteArray getGenesisKey() { +// return genesisKey; +// } +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PreparedTx.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PreparedTx.java new file mode 100644 index 00000000..68faa219 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PreparedTx.java @@ -0,0 +1,63 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.def.asymmetric.ED25519SignatureFunction; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionRequestBuilder; +import com.jd.blockchain.ledger.TransactionResponse; + +public class PreparedTx implements PreparedTransaction { + + private TransactionRequestBuilder txReqBuilder; + + private TransactionService txProcessor; + + public PreparedTx(TransactionRequestBuilder txReqBuilder, TransactionService txProcessor) { + this.txReqBuilder = txReqBuilder; + this.txProcessor = txProcessor; + } + + @Override + public HashDigest getHash() { + return txReqBuilder.getHash(); + } + + @Override + public TransactionContent getTransactionContent() { + return txReqBuilder.getTransactionContent(); + } + + @Override + public DigitalSignature sign(CryptoKeyPair keyPair) { +// SignatureFunction signatureFunction = new ED25519SignatureFunction(); + SignatureFunction signatureFunction = CryptoUtils.sign(keyPair.getPubKey().getAlgorithm()); + PrivKey privKey = keyPair.getPrivKey(); + byte[] content = BinaryEncodingUtils.encode(getTransactionContent(), TransactionContent.class); + SignatureDigest signatureDigest = signatureFunction.sign(privKey, content); + DigitalSignature signature = new DigitalSignatureBlob(keyPair.getPubKey(), signatureDigest); + addSignature(signature); + return signature; + } + + @Override + public void addSignature(DigitalSignature signature) { + txReqBuilder.addEndpointSignature(signature); + } + + @Override + public TransactionResponse commit() { + TransactionRequest txReq = txReqBuilder.buildRequest(); + // 发起交易请求; + TransactionResponse txResponse = txProcessor.process(txReq); + return txResponse; + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PrivilegeSettingOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PrivilegeSettingOperationBuilder.java new file mode 100644 index 00000000..f65b3cde --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PrivilegeSettingOperationBuilder.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.PrivilegeType; + +/** + * 账户权限设置操作; + * + *
+ * + * 注:默认情况下,在账户被注册时,账户自身会包含在权限设置表中,具有全部的权限;
+ * + * 但这不是必须的,使用者可以根据业务需要,去掉账户自身的权限,并将权限赋予其它的账户,以此实现将区块链账户分别用于表示“角色”和“数据”这两种目的; + * + * @author huanghaiquan + * + */ +public interface PrivilegeSettingOperationBuilder { + + PrivilegeSettingOperationBuilder setThreshhold(PrivilegeType privilege, long threshhold); + + PrivilegeSettingOperationBuilder enable(PrivilegeType privilege, String address, int weight); + + PrivilegeSettingOperationBuilder disable(PrivilegeType privilege, String address); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PubKeyData.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PubKeyData.java new file mode 100644 index 00000000..e7350873 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/PubKeyData.java @@ -0,0 +1,29 @@ +//package com.jd.blockchain.ledger.data; +// +//import com.jd.blockchain.ledger.KeyType; +//import com.jd.blockchain.ledger.PubKey; +// +//import my.utils.io.ByteArray; +// +//public class PubKeyData implements PubKey{ +// +// private KeyType type; +// +// private ByteArray value; +// +// public PubKeyData(KeyType type, ByteArray value) { +// this.type = type; +// this.value = value; +// } +// +// @Override +// public KeyType getType() { +// return type; +// } +// +// @Override +// public ByteArray getValue() { +// return value; +// } +// +//} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/SignatureEncoding.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/SignatureEncoding.java new file mode 100644 index 00000000..78031fb3 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/SignatureEncoding.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.ledger.data; + +import org.springframework.util.Base64Utils; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.ledger.DigitalSignature; + +public class SignatureEncoding { + + public static byte[] encode(DigitalSignature signature) { + return BinaryEncodingUtils.encode(signature, DigitalSignature.class); + } + + public static DigitalSignature decode(byte[] bytesSignature) { + return BinaryEncodingUtils.decode(bytesSignature); + } + + public static DigitalSignature decodeFromBase64(String base64Signature) { + byte[] bytesSignature = Base64Utils.decodeFromUrlSafeString(base64Signature); + return decode(bytesSignature); + } + + public static String encodeToBase64(DigitalSignature signature) { + byte[] bytesSignature = encode(signature); + return Base64Utils.encodeToUrlSafeString(bytesSignature); + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/SignatureUtils.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/SignatureUtils.java new file mode 100644 index 00000000..9dd0a32c --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/SignatureUtils.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.security.Ed25519Utils; + +public class SignatureUtils { + + public static DigitalSignature sign(ByteArray data, BlockchainKeyPair keyPair) { + return sign(data.bytes(), keyPair); + } + + public static DigitalSignature sign(byte[] data, BlockchainKeyPair keyPair) { + // 对交易内容的hash进行签名; + CryptoAlgorithm algorithm = keyPair.getPrivKey().getAlgorithm(); + switch (algorithm) { + case ED25519: + byte[] digest = Ed25519Utils.sign_512(data, keyPair.getPrivKey().getRawKeyBytes()); + DigitalSignatureBlob signature = new DigitalSignatureBlob(keyPair.getPubKey(), new SignatureDigest(digest)); + return signature; + case SM2: + throw new IllegalArgumentException("Unsupported KeyType[" + algorithm + "]!"); +// case CA: +// throw new IllegalArgumentException("Unsupported KeyType[" + keyType + "]!"); + default: + throw new IllegalArgumentException("Unsupported KeyType[" + algorithm + "]!"); + } + } + + public static boolean verify(ByteArray data, DigitalSignature signature) { + return verify(data.bytes(), signature); + } + + public static boolean verify(byte[] data, DigitalSignature signature) { + CryptoAlgorithm algorithm = signature.getPubKey().getAlgorithm(); + switch (algorithm) { + case ED25519: + return Ed25519Utils.verify(data, signature.getPubKey().getRawKeyBytes(), signature.getDigest().toBytes()); + case SM2: + throw new IllegalArgumentException("Unsupported KeyType[" + algorithm + "]!"); +// case CA: +// throw new IllegalArgumentException("Unsupported KeyType[" + keyType + "]!"); + default: + throw new IllegalArgumentException("Unsupported KeyType[" + algorithm + "]!"); + } + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TransactionService.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TransactionService.java new file mode 100644 index 00000000..aa56dbe7 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TransactionService.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; + +public interface TransactionService { + + TransactionResponse process(TransactionRequest txRequest); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxBuilder.java new file mode 100644 index 00000000..fc2680ba --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxBuilder.java @@ -0,0 +1,88 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.TransactionBuilder; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionContentBody; +import com.jd.blockchain.ledger.TransactionRequestBuilder; +import com.jd.blockchain.utils.Bytes; + +public class TxBuilder implements TransactionBuilder { + + static { + DataContractRegistry.register(TransactionContentBody.class); + } + + private BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); + + private CryptoAlgorithm defaultHashAlgorithm = CryptoAlgorithm.SHA256; + + private HashDigest ledgerHash; + + public TxBuilder(HashDigest ledgerHash) { + this.ledgerHash = ledgerHash; + } + + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + + @Override + public TransactionRequestBuilder prepareRequest() { + TransactionContent txContent = prepareContent(); + return new TxRequestBuilder(txContent); + } + + @Override + public TransactionContent prepareContent() { + TxContentBlob txContent = new TxContentBlob(ledgerHash); + txContent.addOperations(opFactory.getOperations()); + + byte[] contentBodyBytes = BinaryEncodingUtils.encode(txContent, TransactionContentBody.class); + HashDigest contentHash = CryptoUtils.hash(defaultHashAlgorithm).hash(contentBodyBytes); + txContent.setHash(contentHash); + + return txContent; + } + + @Override + public LedgerInitOperationBuilder ledgers() { + return opFactory.ledgers(); + } + + @Override + public UserRegisterOperationBuilder users() { + return opFactory.users(); + } + + @Override + public DataAccountRegisterOperationBuilder dataAccounts() { + return opFactory.dataAccounts(); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { + return opFactory.dataAccount(accountAddress); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(Bytes accountAddress) { + return opFactory.dataAccount(accountAddress); + } + + @Override + public ContractCodeDeployOperationBuilder contracts() { + return opFactory.contracts(); + } + + @Override + public ContractEventSendOperationBuilder contractEvents() { + return opFactory.contractEvents(); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxContentBlob.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxContentBlob.java new file mode 100644 index 00000000..d669951f --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxContentBlob.java @@ -0,0 +1,92 @@ +package com.jd.blockchain.ledger.data; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.utils.io.NumberMask; + +/** + * 交易内容的数据块; + *

+ * + * 包含原始交易请求的数据块; + * + * @author huanghaiquan + * + */ +public class TxContentBlob implements TransactionContent { + + /** + * 操作数量的最大值; + */ + public static final int MAX_OP_COUNT = NumberMask.SHORT.MAX_BOUNDARY_SIZE; + + private List operationList = new ArrayList(); + + private HashDigest hash; + + private HashDigest ledgerHash; + + @DConstructor(name ="TxContentBlob") + public TxContentBlob(@FieldSetter(name="getLedgerHash", type="HashDigest") HashDigest ledgerHash) { + this.ledgerHash = ledgerHash; + } + + /** + * 交易内容的哈希值; + */ + @Override + public HashDigest getHash() { + return this.hash; + } + + /** + * 更新交易内容的哈希值; + *

+ * 注:当前对象只充当值对象,不校验指定哈希值的完整性,调用者应该在外部实施完整性校验; + * @param hash + */ + public void setHash(HashDigest hash) { + this.hash = hash; + } + + /** + * 交易请求链的hash + * + * @return + */ + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + public void setLedgerHash(HashDigest ledgerHash) { + this.ledgerHash = ledgerHash; + } + + @Override + public Operation[] getOperations() { + return operationList.toArray(new Operation[operationList.size()]); + } + + public void setOperations(Object[] operations) { + //in array's case ,cast will failed! + for (Object operation : operations) { + Operation op = (Operation)operation; + addOperation(op); + } + } + + public void addOperation(Operation operation) { + operationList.add(operation); + } + + public void addOperations(Collection operations) { + operationList.addAll(operations); + } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxRequestBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxRequestBuilder.java new file mode 100644 index 00000000..ee8232b7 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxRequestBuilder.java @@ -0,0 +1,93 @@ +package com.jd.blockchain.ledger.data; + +import java.util.ArrayList; +import java.util.List; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.NodeRequest; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionRequestBuilder; + +public class TxRequestBuilder implements TransactionRequestBuilder { + + private TransactionContent txContent; + + private List endpointSignatures = new ArrayList<>(); + + private List nodeSignatures = new ArrayList<>(); + + public TxRequestBuilder(TransactionContent txContent) { + this.txContent = txContent; + } + + @Override + public HashDigest getHash() { + return txContent.getHash(); + } + + @Override + public TransactionContent getTransactionContent() { + return txContent; + } + + @Override + public DigitalSignature signAsEndpoint(CryptoKeyPair keyPair) { + DigitalSignature signature = sign(txContent, keyPair); + addEndpointSignature(signature); + return signature; + } + + @Override + public DigitalSignature signAsNode(CryptoKeyPair keyPair) { + DigitalSignature signature = sign(txContent, keyPair); + addNodeSignature(signature); + return signature; + } + + @Override + public void addNodeSignature(DigitalSignature signature) { + nodeSignatures.add(signature); + } + + @Override + public void addEndpointSignature(DigitalSignature signature) { + endpointSignatures.add(signature); + } + + public static DigitalSignature sign(TransactionContent txContent, CryptoKeyPair keyPair) { + SignatureDigest signatureDigest = sign(txContent, keyPair.getPrivKey()); + DigitalSignature signature = new DigitalSignatureBlob(keyPair.getPubKey(), signatureDigest); + return signature; + } + + public static SignatureDigest sign(TransactionContent txContent, PrivKey privKey) { + return CryptoUtils.sign(privKey.getAlgorithm()).sign(privKey, txContent.getHash().toBytes()); + } + + public static boolean verifySignature(TransactionContent txContent, SignatureDigest signDigest, PubKey pubKey) { + return CryptoUtils.sign(signDigest.getAlgorithm()).verify(signDigest, pubKey, txContent.getHash().toBytes()); + } + + @Override + public TransactionRequest buildRequest() { + TxRequestMessage txMessage = new TxRequestMessage(txContent); + txMessage.addEndpointSignatures(endpointSignatures); + txMessage.addNodeSignatures(nodeSignatures); + + byte[] reqBytes = BinaryEncodingUtils.encode(txMessage, NodeRequest.class); + HashDigest reqHash = CryptoUtils.hash(CryptoAlgorithm.SHA256).hash(reqBytes); + txMessage.setHash(reqHash); + + return txMessage; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxRequestMessage.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxRequestMessage.java new file mode 100644 index 00000000..10d3e42b --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxRequestMessage.java @@ -0,0 +1,383 @@ +package com.jd.blockchain.ledger.data; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.utils.Bytes; + +/** + * TxRequestMessage 交易消息; + *

+ * + * TxRequestMessage 表示参与者提交的交易请求,由3部分组成:交易内容、参与者签名、网关节点签名;
+ * + *

+ * 字节位如下:
+ * [第1字节]:标识数据类型为交易请求的魔数常量 ({@link MagicNumber#TX_REQUEST});
+ * 
+ * [第2字节] - [第N字节]: 交易内容;
+ * 
+ * [第N+1字节]: 交易参与者数量(有效值范围 0 - 255);
+ *         注:在单个交易中,参与者的数量总是有限的,对于那些需要更多参与者且数量不确定的场景,可以通过合约来实现把参与者分为多次 TX 提交,最终组合完成一次完整的业务进程;
+ * [第N+2字节] - [第X字节]: 参与者的签名列表;
+ * 
+ * [第X+1字节]:对交易请求的哈希算法的代码;
+ * [第X+2字节] - [第Y字节]:对交易请求的哈希值;针对交易请求中此段之前的全部内容进行哈希计算,包括:交易请求魔数、交易内容、签名者列表、哈希算法代码;
+ * 
+ * [第Y+1字节] - 结束: 网关节点针对交易请求的签名;
+ * 
+ * + * @author huanghaiquan + * + */ +public class TxRequestMessage implements TransactionRequest {// , Externalizable { + + /** + * 交易参与者的个数的最大值; + */ + public static final int MAX_TX_PARTICIPANT_COUNT = 0xFF; + + private HashDigest hash; + + private TransactionContent transactionContent; + + private Map endpointSignatureMap = new LinkedHashMap<>(); + + private Map nodeSignatureMap = new LinkedHashMap<>(); + + // private CryptoAlgorithm defaultHashAlgorithm = CryptoAlgorithm.SHA_256; + +// public TxRequestMessage() { +// } + + static { + DataContractRegistry.register(NodeRequest.class); + } + + @DConstructor(name = "TxRequestMessage") + public TxRequestMessage(@FieldSetter(name = "getTransactionContent", type = "TransactionContent ")TransactionContent txContent) { +// if (!(txContent instanceof BytesWriter)) { +// throw new IllegalArgumentException("The tx content must be instance of BytesWriter!"); +// } + this.transactionContent = txContent; + } + + public TxRequestMessage(TransactionRequest txRequest) { + this.transactionContent = txRequest.getTransactionContent(); + setHash(txRequest.getHash()); + setEndpointSignatures(txRequest.getEndpointSignatures()); + setNodeSignatures(txRequest.getNodeSignatures()); + } + + + @Override + public TransactionContent getTransactionContent() { + return this.transactionContent; + } + + @Override + public DigitalSignature[] getEndpointSignatures() { + return endpointSignatureMap.values().toArray(new DigitalSignature[endpointSignatureMap.size()]); + } + + @Override + public DigitalSignature[] getNodeSignatures() { + return nodeSignatureMap.values().toArray(new DigitalSignature[nodeSignatureMap.size()]); + } + + public void setEndpointSignatures(Object[] endpointSignatures) { + if (endpointSignatures != null) { + for (Object object : endpointSignatures) { + DigitalSignature endpointSignature = (DigitalSignature)object; + addEndpointSignatures(endpointSignature); + } + } + return; + } + + public void setNodeSignatures(Object[] nodeSignatures) { + if (nodeSignatures != null) { + for (Object object : nodeSignatures) { + DigitalSignature nodeSignature = (DigitalSignature)object; + addNodeSignatures(nodeSignature); + } + } + return; + } + + private void doAddEndpointSignature(DigitalSignature signature) { + Bytes address = AddressEncoding.generateAddress(signature.getPubKey()); + if (endpointSignatureMap.containsKey(address)) { + throw new IllegalArgumentException( + String.format("Participant signature of Address[%s] already exist!", address)); + } + endpointSignatureMap.put(address, signature); + } + + /** + * 从参与者签名列表中检查是否包含指定的参与者; + * + * @param userBid + * 参与者的身份; + * @return + */ + public boolean containsEndpointSignature(BlockchainIdentity userBid) { + return endpointSignatureMap.containsKey(userBid.getAddress()); + } + + public boolean containsEndpointSignature(Bytes userAddress) { + return endpointSignatureMap.containsKey(userAddress); + } + + public void addEndpointSignatures(DigitalSignature... signature) { + for (DigitalSignature sign : signature) { + doAddEndpointSignature(sign); + } + } + + public void addEndpointSignatures(List signature) { + for (DigitalSignature sign : signature) { + doAddEndpointSignature(sign); + } + } + + /** + * 从节点签名列表中检查是否包含指定的节点; + * + * @param nodeBid + * 节点的身份; + * @return + */ + public boolean containsNodeSignature(BlockchainIdentity nodeBid) { + return nodeSignatureMap.containsKey(nodeBid.getAddress()); + } + + public boolean containsNodeSignature(Bytes nodeAddress) { + return nodeSignatureMap.containsKey(nodeAddress); + } + + private void doAddNodeSignatures(DigitalSignature signature) { + Bytes address = AddressEncoding.generateAddress(signature.getPubKey()); + if (nodeSignatureMap.containsKey(address)) { + throw new IllegalArgumentException(String.format("Node signature of Address[%s] already exist!", address)); + } + nodeSignatureMap.put(address, signature); + } + + public void addNodeSignatures(DigitalSignature... signature) { + for (DigitalSignature sign : signature) { + doAddNodeSignatures(sign); + } + } + + public void addNodeSignatures(List signature) { + for (DigitalSignature sign : signature) { + doAddNodeSignatures(sign); + } + } + + @Override + public HashDigest getHash() { + return hash; + } + + public void setHash(HashDigest hash) { + this.hash = hash; + } + + // public HashDigest updateHash() { + // return computeHash(this.defaultHashAlgorithm); + // } + + // public HashDigest updateHash(CryptoAlgorithm hashAlgorithm) { + // return computeHash(hashAlgorithm); + // } + // + // private HashDigest computeHash(CryptoAlgorithm hashAlgorithm) { + // byte[] reqBody = getRequestBody(); + // this.hash = CryptoUtils.hash(hashAlgorithm).hash(reqBody); + // return this.hash; + // } + + // @Override + // public void resolvFrom(InputStream in) throws IOException { + // // 解析校验交易请求魔数; + // byte[] buff = new byte[1]; + // int len = in.read(buff, 0, 1); + // if (len < 1) { + // throw new IllegalArgumentException("No bytes was read for the magic number + // [TX_REQUEST]!"); + // } + // if (MagicNumber.TX_REQUEST != buff[0]) { + // throw new IllegalArgumentException("Magic number [TX_REQUEST] dismatch!"); + // } + // + // // 解析交易内容; + // TxContentBlob txContentBlob = new TxContentBlob(); + // txContentBlob.resolvFrom(in); + // + // // 解析参与者签名列表; + // int participantCount = NumberMask.TINY.resolveMaskedNumber(in); + // List partiSignList = new ArrayList<>(); + // for (int i = 0; i < participantCount; i++) { + // DigitalSignatureBlob signature = new DigitalSignatureBlob(); + // signature.resolvFrom(in); + // + // partiSignList.add(signature); + // } + // + // // 解析节点签名列表; + // int nodeCount = NumberMask.TINY.resolveMaskedNumber(in); + // List nodeSignList = new ArrayList<>(); + // for (int i = 0; i < nodeCount; i++) { + // DigitalSignatureBlob nodeSign = new DigitalSignatureBlob(); + // nodeSign.resolvFrom(in); + // nodeSignList.add(nodeSign); + // } + // + // // 解析哈希算法标识符; + // HashAlgorithm hashAlgorithm = HashAlgorithm.valueOf((byte) in.read()); + // + // // 解析原始的哈希; + // ByteArray hash = HashEncoding.read(in); + // + // this.txContent = txContentBlob; + // addParticipantSignatures(partiSignList); + // addNodeSignatures(nodeSignList); + // this.hash = hash; + // + // // 校验原始哈希; + // byte[] bodyBytes = getRequestBody(); + // ByteArray rHash = HashEncoding.computeHash(bodyBytes, hashAlgorithm); + // if (!rHash.equals(hash)) { + // throw new IllegalArgumentException("The hash is not match with request + // content!"); + // } + // } + // + // /** + // * 输出交易请求消息; + // * + // * 注:此方法不会自动重新计算hash;如果消息的内容发生改变后,需要调用主动调用 {@link #updateHash()} 方法重新计算 hash; + // */ + // @Override + // public void writeTo(OutputStream out) throws IOException { + // if (this.hash == null) { + // updateHash(); + // } + // + // buildRequestBody(out); + // + // // 写入 hash 值; + // HashEncoding.write(hash, out); + // } + + // /** + // * 生成请求体,包括:交易请求魔数、交易内容、参与者签名者列表、哈希算法代号; + // * + // * @param out + // * @throws IOException + // */ + // private void buildRequestBody(OutputStream out) throws IOException { + // + // buildParticipantRequest(out); + // + // // 写入节点签名列表; + // NumberMask.TINY.writeMask(nodeSignatureMap.size(), out); + // for (DigitalSignature nodeSignatureBlob : nodeSignatureMap.values()) { + // nodeSignatureBlob.writeTo(out); + // } + // + // // 写入 hash 算法代号; + // out.write(hashAlgorithm.getAlgorithm()); + // } + + // /** + // * 生成参与者的请求数据; + // * + // *
+ // * 参与者的请求数据仅包含“交易请求模数({@link MagicNumber#TX_REQUEST })” + // * “交易内容({@link #getTransactionContent()})” + // * 和“参与者签名列表({@link #getParticipantSignatures()})”三项属性; + // * + // * @param out + // */ + // public void buildParticipantRequest(OutputStream out) { + // try { + // // 写入魔数; + // out.write(MagicNumber.TX_REQUEST); + // + // // 写入交易内容; + // txContent.writeTo(out); + // + // // 写入 1 个字节的参与者签名数量; + // if (participantSignatureMap.size() > MAX_TX_PARTICIPANT_COUNT) { + // throw new IllegalArgumentException("The number of participant signatures is + // out of the max count[" + // + MAX_TX_PARTICIPANT_COUNT + "]!"); + // } + // + // NumberMask.TINY.writeMask(participantSignatureMap.size(), out); + // // 写入参与者签名列表; + // for (DigitalSignature digitalSignatureBlob : + // participantSignatureMap.values()) { + // digitalSignatureBlob.writeTo(out); + // } + // + // } catch (IOException e) { + // throw new RuntimeIOException(e.getMessage(), e); + // } + // } + // + // @Override + // public void writeExternal(ObjectOutput out) throws IOException { + // ByteArrayOutputStream os = new ByteArrayOutputStream(); + // writeTo(os); + // byte[] bts = os.toByteArray(); + // out.writeInt(bts.length); + // out.write(bts); + // } + // + // @Override + // public void readExternal(ObjectInput in) throws IOException, + // ClassNotFoundException { + // int len = in.readInt(); + // byte[] bts = new byte[len]; + // in.readFully(bts); + // this.resolvFrom(new ByteArrayInputStream(bts)); + // } + + // @Override + // public byte[] toBytes() { + // ByteArrayOutputStream out = new ByteArrayOutputStream(); + // try { + // writeTo(out); + // } catch (IOException e) { + // throw new RuntimeIOException(e.getMessage(), e); + // } + // return out.toByteArray(); + // } + + // @Override + // public ByteArray getHashData() { + // return ByteArray.wrap(getRequestBody()); + // } + + // private byte[] getRequestBody() { + // try { + // ByteArrayOutputStream out = new ByteArrayOutputStream(); + // buildRequestBody(out); + // + // return out.toByteArray(); + // } catch (IOException e) { + // throw new RuntimeIOException(e.getMessage(), e); + // } + // } +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxResponseMessage.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxResponseMessage.java new file mode 100644 index 00000000..0434f464 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxResponseMessage.java @@ -0,0 +1,67 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.TransactionResponse; + +/** + * @author huanghaiquan + * + */ +public class TxResponseMessage implements TransactionResponse { + + private HashDigest contentHash; + + private HashDigest blockHash; + + private long blockHeight; + + private TransactionState executionState; + + public TxResponseMessage() { + } + + public TxResponseMessage(HashDigest contentHash) { + this.contentHash = contentHash; + } + + @Override + public HashDigest getContentHash() { + return contentHash; + } + + @Override + public TransactionState getExecutionState() { + return executionState; + } + + public void setExecutionState(TransactionState executionState) { + this.executionState = executionState; + } + + /* (non-Javadoc) + * @see com.jd.blockchain.ledger.TransactionResponse#getBlockHash() + */ + @Override + public HashDigest getBlockHash() { + return blockHash; + } + + public void setBlockHash(HashDigest blockHash) { + this.blockHash = blockHash; + } + + @Override + public long getBlockHeight() { + return blockHeight; + } + public void setBlockHeight(long blockHeight) { + this.blockHeight = blockHeight; + } + + @Override + public boolean isSuccess() { + return blockHash != null & executionState == TransactionState.SUCCESS; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxTemplate.java new file mode 100644 index 00000000..d4df36c4 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/TxTemplate.java @@ -0,0 +1,61 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionRequestBuilder; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.utils.Bytes; + +public class TxTemplate implements TransactionTemplate { + + private TxBuilder txBuilder; + + private TransactionService txService; + + public TxTemplate(HashDigest ledgerHash, TransactionService txService) { + this.txBuilder = new TxBuilder(ledgerHash); + this.txService = txService; + } + + @Override + public HashDigest getLedgerHash() { + return txBuilder.getLedgerHash(); + } + + @Override + public PreparedTransaction prepare() { + TransactionRequestBuilder txReqBuilder = txBuilder.prepareRequest(); + return new PreparedTx(txReqBuilder, txService); + } + + @Override + public UserRegisterOperationBuilder users() { + return txBuilder.users(); + } + + @Override + public DataAccountRegisterOperationBuilder dataAccounts() { + return txBuilder.dataAccounts(); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(String accountAddress) { + return txBuilder.dataAccount(accountAddress); + } + + @Override + public DataAccountKVSetOperationBuilder dataAccount(Bytes accountAddress) { + return txBuilder.dataAccount(accountAddress); + } + + @Override + public ContractCodeDeployOperationBuilder contracts() { + return txBuilder.contracts(); + } + + @Override + public ContractEventSendOperationBuilder contractEvents() { + return txBuilder.contractEvents(); + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserOperator.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserOperator.java new file mode 100644 index 00000000..9053b9fb --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserOperator.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.ledger.data; + +public interface UserOperator { + + /** + * 注册账户操作; + * + * @return + */ + + UserRegisterOperationBuilder users(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOpTemplate.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOpTemplate.java new file mode 100644 index 00000000..b95604a3 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOpTemplate.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.DConstructor; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.binaryproto.FieldSetter; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.UserRegisterOperation; + +public class UserRegisterOpTemplate implements UserRegisterOperation { + + static { + DataContractRegistry.register(UserRegisterOperation.class); + } + + private BlockchainIdentity userID; + + public UserRegisterOpTemplate() { + } + + @DConstructor(name="UserRegisterOpTemplate") + public UserRegisterOpTemplate(@FieldSetter(name="getUserId", type="BlockchainIdentity") BlockchainIdentity userID) { + this.userID = userID; + } + + @Override + public BlockchainIdentity getUserID() { + return userID; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOperationBuilder.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOperationBuilder.java new file mode 100644 index 00000000..8977ce3d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOperationBuilder.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.UserRegisterOperation; + +public interface UserRegisterOperationBuilder { + + /** + * 注册; + * + * @param id + * 区块链身份; + * @param stateType + * 负载类型; + * @return + */ + UserRegisterOperation register(BlockchainIdentity userID); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOperationBuilderImpl.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOperationBuilderImpl.java new file mode 100644 index 00000000..ae7301b9 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/data/UserRegisterOperationBuilderImpl.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.ledger.data; + +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.UserRegisterOperation; + +public class UserRegisterOperationBuilderImpl implements UserRegisterOperationBuilder{ + + @Override + public UserRegisterOperation register(BlockchainIdentity userID) { + return new UserRegisterOpTemplate(userID); + } + + + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchProcess.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchProcess.java new file mode 100644 index 00000000..083b5215 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchProcess.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.ledger.service; + +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionState; + +public interface TransactionBatchProcess { + + /** + * 安排执行指定的交易请求; + * + *

+ * + * 注意:此方法并不表示此交易请求立即得到完整执行,并理解获得最终有效的结果; + * + * 方法返回的 {@link TransactionResponse} 只是一个代理对象,其最终的值需要在整个批处理结果被成功地提交或者取消后才能确定。 + * + * @param request + * 交易请求; + * @return 交易执行回复; + */ + TransactionResponse schedule(TransactionRequest request); + + /** + * 完成本次批量执行;生成待提交的结果; + * + * @return + */ + TransactionBatchResultHandle prepare(); + + TransactionBatchResult cancel(TransactionState errorResult); + + long blockHeight(); + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchResult.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchResult.java new file mode 100644 index 00000000..b1363325 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchResult.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.ledger.service; + +import java.util.Iterator; + +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.TransactionResponse; + +public interface TransactionBatchResult { + + LedgerBlock getBlock(); + + Iterator getResponses(); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchResultHandle.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchResultHandle.java new file mode 100644 index 00000000..2af06531 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionBatchResultHandle.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.ledger.service; + +import com.jd.blockchain.ledger.TransactionState; + +public interface TransactionBatchResultHandle extends TransactionBatchResult{ + + void commit(); + + void cancel(TransactionState errorResult); +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionEngine.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionEngine.java new file mode 100644 index 00000000..b0eab5d9 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/ledger/service/TransactionEngine.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.ledger.service; + +import com.jd.blockchain.crypto.hash.HashDigest; + +public interface TransactionEngine { + + TransactionBatchProcess createNextBatch(HashDigest ledgerHash); + + TransactionBatchProcess getBatch(HashDigest ledgerHash); + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/manage/GatewayIncomingSetting.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/manage/GatewayIncomingSetting.java new file mode 100644 index 00000000..abd25b4d --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/manage/GatewayIncomingSetting.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.manage; + +/** + * 网关接入设置; + * + * @author huanghaiquan + * + */ +public class GatewayIncomingSetting { + + private LedgerIncomingSetting[] ledgers; + + /** + * 所有账本的接入设置; + * + * @return + */ + public LedgerIncomingSetting[] getLedgers() { + return ledgers; + } + + public void setLedgers(LedgerIncomingSetting[] ledgerSettings) { + this.ledgers = ledgerSettings; + } + +} diff --git a/source/ledger/ledger-model/src/main/java/com/jd/blockchain/manage/LedgerIncomingSetting.java b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/manage/LedgerIncomingSetting.java new file mode 100644 index 00000000..ee2ffb81 --- /dev/null +++ b/source/ledger/ledger-model/src/main/java/com/jd/blockchain/manage/LedgerIncomingSetting.java @@ -0,0 +1,102 @@ +package com.jd.blockchain.manage; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; + +/** + * 账本的接入设置; + * + * @author huanghaiquan + * + */ +public class LedgerIncomingSetting { + + private int gatewayId; + + private HashDigest ledgerHash; + + private CryptoSetting cryptoSetting; + + private String providerName; + + /** + * 节点是否已经启动; + */ + private boolean ready; + + /** + * Base64 编码的视图配置; + */ + private String clientSetting; + +// /** +// * Base64 编码的网关配置; +// */ +// private String gatewaySetting; + + public String getClientSetting() { + return clientSetting; + } + + public void setClientSetting(String clientSetting) { + this.clientSetting = clientSetting; + } + +// public String getGatewaySetting() { +// return gatewaySetting; +// } + +// public void setGatewaySetting(String gatewaySetting) { +// this.gatewaySetting = gatewaySetting; +// } + + public boolean isReady() { + return ready; + } + + public void setReady(boolean ready) { + this.ready = ready; + } + + public HashDigest getLedgerHash() { + return ledgerHash; + } + + public void setLedgerHash(HashDigest ledgerHash) { + this.ledgerHash = ledgerHash; + } + + /** + * 在共识网络中给当前请求的网关分配的 ID ; + * + * @return + */ + public int getGatewayId() { + return gatewayId; + } + + public void setGatewayId(int gatewayId) { + this.gatewayId = gatewayId; + } + + /** + * 账本的当前密码配置; + * + * @return + */ + public CryptoSetting getCryptoSetting() { + return cryptoSetting; + } + + public void setCryptoSetting(CryptoSetting cryptoSetting) { + this.cryptoSetting = cryptoSetting; + } + + public String getProviderName() { + return providerName; + } + + public void setProviderName(String providerName) { + this.providerName = providerName; + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/AddressEncodingTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/AddressEncodingTest.java new file mode 100644 index 00000000..60003c9b --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/AddressEncodingTest.java @@ -0,0 +1,37 @@ +package test.com.jd.blockchain.ledger.data; + +import java.util.Random; + +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; + +public class AddressEncodingTest { + + public static void main(String[] args) { + BlockchainKeyPair bkp = BlockchainKeyGenerator.getInstance().generate(); + PubKey pk = bkp.getPubKey(); + byte[] data =new byte[64]; + Random rand = new Random(); + rand.nextBytes(data); + int round = 5; + for (int r = 0; r < round; r++) { + System.out.println("================== round[" + r + "] ==================="); + int count = 100000; + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + AddressEncoding.generateAddress(pk); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("Compute Count=%s; Total time=%s ms; TPS=%.2f", count, elapsedTS, + ((count * 1000.0D) / elapsedTS))); + } + } + + // @Test + // public void testGenerateAddress() { + // fail("Not yet implemented"); + // } + +} diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/BytesEncodingTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/BytesEncodingTest.java new file mode 100644 index 00000000..07bd9c1e --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/BytesEncodingTest.java @@ -0,0 +1,83 @@ +package test.com.jd.blockchain.ledger.data; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import org.junit.Test; + +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.NumberMask; + +public class BytesEncodingTest { + + @Test + public void testReadAndWriteSize() { + assertReadAndWriteSizeOK(0, NumberMask.TINY); + assertReadAndWriteSizeOK(255, NumberMask.TINY); + assertReadAndWriteSizeOK(1, NumberMask.TINY); + assertReadAndWriteSizeOK(128, NumberMask.TINY); + assertReadAndWriteSizeError(256, NumberMask.TINY); + assertReadAndWriteSizeError(Integer.MIN_VALUE, NumberMask.TINY); + assertReadAndWriteSizeError(Integer.MAX_VALUE, NumberMask.TINY); + + assertReadAndWriteSizeOK(0, NumberMask.SHORT); + assertReadAndWriteSizeOK(255, NumberMask.SHORT); + assertReadAndWriteSizeOK(1, NumberMask.SHORT); + assertReadAndWriteSizeOK(128, NumberMask.SHORT); + assertReadAndWriteSizeOK(256, NumberMask.SHORT); + assertReadAndWriteSizeOK(1 << 7, NumberMask.SHORT); + assertReadAndWriteSizeOK((1 << 15) - 1, NumberMask.SHORT); + assertReadAndWriteSizeError((1 << 15) + 1, NumberMask.SHORT); + assertReadAndWriteSizeError(1 << 16, NumberMask.SHORT); + assertReadAndWriteSizeError(Integer.MIN_VALUE, NumberMask.SHORT); + assertReadAndWriteSizeError(Integer.MAX_VALUE, NumberMask.SHORT); + + assertReadAndWriteSizeOK(0, NumberMask.NORMAL); + assertReadAndWriteSizeOK(255, NumberMask.NORMAL); + assertReadAndWriteSizeOK(1, NumberMask.NORMAL); + assertReadAndWriteSizeOK(128, NumberMask.NORMAL); + assertReadAndWriteSizeOK(256, NumberMask.NORMAL); + assertReadAndWriteSizeOK(1 << 7, NumberMask.NORMAL); + assertReadAndWriteSizeOK((1 << 15) - 1, NumberMask.NORMAL); + assertReadAndWriteSizeOK((1 << 15) + 1, NumberMask.NORMAL); + assertReadAndWriteSizeOK(1 << 16, NumberMask.NORMAL); + assertReadAndWriteSizeOK(Short.MAX_VALUE, NumberMask.NORMAL); + assertReadAndWriteSizeOK((1 << 16) - 1, NumberMask.NORMAL); + assertReadAndWriteSizeOK(1 << 24, NumberMask.NORMAL); + assertReadAndWriteSizeOK((1 << 24) - 1, NumberMask.NORMAL); + assertReadAndWriteSizeOK((1 << 30) - 1, NumberMask.NORMAL); + assertReadAndWriteSizeError(1 << 30, NumberMask.NORMAL); + assertReadAndWriteSizeError(Integer.MIN_VALUE, NumberMask.NORMAL); + assertReadAndWriteSizeError(Integer.MAX_VALUE, NumberMask.NORMAL); + } + + private void assertReadAndWriteSizeError(int size, NumberMask mask) { + Exception error = null; + try { + assertReadAndWriteSizeOK(size, mask); + } catch (Exception e) { + error = e; + } + assertNotNull(error); + } + + private void assertReadAndWriteSizeOK(int size, NumberMask mask) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + mask.writeMask(size, out); + + byte[] sizebytes = out.toByteArray(); + + assertTrue(sizebytes.length <= mask.MAX_HEADER_LENGTH); + assertTrue(size <= mask.getBoundarySize(sizebytes.length)); + + ByteArrayInputStream in = new ByteArrayInputStream(sizebytes); + int sizeResolved = mask.resolveMaskedNumber(in); + + assertEquals(size, sizeResolved); + + assertTrue(in.available() == 0); + } + +} diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ContractCodeDeployOpTemplateTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ContractCodeDeployOpTemplateTest.java new file mode 100644 index 00000000..801e744d --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ContractCodeDeployOpTemplateTest.java @@ -0,0 +1,78 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.ContractCodeDeployOpTemplateTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午10:53 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.ContractCodeDeployOpTemplate; +import com.jd.blockchain.utils.io.BytesUtils; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class ContractCodeDeployOpTemplateTest { + + private ContractCodeDeployOpTemplate data; + + @Before + public void initContractCodeDeployOpTemplate() { + DataContractRegistry.register(ContractCodeDeployOperation.class); + DataContractRegistry.register(Operation.class); + String pubKeyVal = "jd.com"; + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); + BlockchainIdentity contractID = new BlockchainIdentityData(pubKey); + byte[] chainCode = "jd-test".getBytes(); + data = new ContractCodeDeployOpTemplate(contractID, chainCode); + } + + @Test + public void testSerialize_ContractCodeDeployOperation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, ContractCodeDeployOperation.class); + ContractCodeDeployOperation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertArrayEquals(resolvedData.getChainCode(), data.getChainCode()); + assertEquals(resolvedData.getContractID().getAddress(), data.getContractID().getAddress()); + assertEquals(resolvedData.getContractID().getPubKey(), data.getContractID().getPubKey()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_Operation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, ContractCodeDeployOperation.class); + ContractCodeDeployOperation resolvedData = BinaryEncodingUtils.decode(serialBytes); + BlockchainIdentity expCodeId = data.getContractID(); + BlockchainIdentity actualCodeId = resolvedData.getContractID(); + + assertEquals(expCodeId.getAddress().toBase58(), actualCodeId.getAddress().toBase58()); + assertEquals(expCodeId.getPubKey().toBase58(), actualCodeId.getPubKey().toBase58()); + assertTrue(BytesUtils.equals(data.getChainCode(), resolvedData.getChainCode())); + + System.out.println("------Assert start ------"); + System.out.println("serialBytesLength=" + serialBytes.length); + System.out.println(resolvedData); + System.out.println("------Assert OK ------"); + } + + // test复杂场景; + +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ContractEventSendOpTemplateTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ContractEventSendOpTemplateTest.java new file mode 100644 index 00000000..ead7b0e5 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ContractEventSendOpTemplateTest.java @@ -0,0 +1,65 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.ContractEventSendOpTemplateTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午10:56 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.data.ContractEventSendOpTemplate; +import com.jd.blockchain.ledger.data.DataAccountKVSetOpTemplate; +import com.jd.blockchain.utils.Bytes; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class ContractEventSendOpTemplateTest { + + private ContractEventSendOpTemplate data; + + @Before + public void initContractEventSendOpTemplate() { + DataContractRegistry.register(ContractEventSendOperation.class); + DataContractRegistry.register(Operation.class); + String contractAddress = "zhangsan-address", event = "zhangsan-event"; + byte[] args = "zhangsan-args".getBytes(); + data = new ContractEventSendOpTemplate(Bytes.fromString(contractAddress), event, args); + } + + @Test + public void testSerialize_ContractEventSendOperation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, ContractEventSendOperation.class); + ContractEventSendOperation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getContractAddress(), data.getContractAddress()); + assertEquals(resolvedData.getEvent(), data.getEvent()); + assertArrayEquals(resolvedData.getArgs(), data.getArgs()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_Operation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, Operation.class); + Operation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + System.out.println(resolvedData); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DataAccountKVSetOpTemplateTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DataAccountKVSetOpTemplateTest.java new file mode 100644 index 00000000..2ad55386 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DataAccountKVSetOpTemplateTest.java @@ -0,0 +1,85 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.DataAccountKVSetOpTemplateTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午10:59 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.ledger.BytesValueImpl; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataType; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.data.DataAccountKVSetOpTemplate; +import com.jd.blockchain.ledger.data.KVData; +import com.jd.blockchain.utils.Bytes; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ +//todo 尚未测试通过,等张爽处理完再测试 +public class DataAccountKVSetOpTemplateTest { + + private DataAccountKVSetOpTemplate data; + + @Before + public void initDataAccountKVSetOpTemplate() { + DataContractRegistry.register(DataAccountKVSetOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + DataContractRegistry.register(Operation.class); + String accountAddress = "zhangsandhakhdkah"; + data = new DataAccountKVSetOpTemplate(Bytes.fromString(accountAddress)); + KVData kvData1 = + new KVData("test1", new BytesValueImpl(DataType.TEXT, "zhangsan".getBytes()), 9999L); + KVData kvData2 = + new KVData("test2", new BytesValueImpl(DataType.TEXT, "lisi".getBytes()), 9990L); + KVData kvData3 = + new KVData("test3", new BytesValueImpl(DataType.TEXT, "wangwu".getBytes()), 1990L); + data.set(kvData1); + data.set(kvData2); + data.set(kvData3); + } + + + @Test + public void testSerialize_DataAccountKVSetOperation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, DataAccountKVSetOperation.class); + DataAccountKVSetOperation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getAccountAddress(), data.getAccountAddress()); + DataAccountKVSetOperation.KVWriteEntry[] resolvedKv = resolvedData.getWriteSet(); + DataAccountKVSetOperation.KVWriteEntry[] dataKv = data.getWriteSet(); + assertEquals(dataKv.length, resolvedKv.length); + for (int i = 0; i < dataKv.length; i++) { + assertEquals(dataKv[i].getKey(), resolvedKv[i].getKey()); + assertArrayEquals(dataKv[i].getValue().getValue().toBytes(), resolvedKv[i].getValue().getValue().toBytes()); + assertEquals(dataKv[i].getValue().getType().CODE, resolvedKv[i].getValue().getType().CODE); + + assertEquals(dataKv[i].getExpectedVersion(), resolvedKv[i].getExpectedVersion()); + } + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_Operation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, Operation.class); + Operation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + System.out.println(resolvedData); + System.out.println("serialBytesLength=" + serialBytes.length); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DataAccountRegisterOpTemplateTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DataAccountRegisterOpTemplateTest.java new file mode 100644 index 00000000..f31fe98c --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DataAccountRegisterOpTemplateTest.java @@ -0,0 +1,66 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.DataAccountRegisterOpTemplateTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午11:03 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.DataAccountRegisterOpTemplate; +import com.jd.blockchain.utils.io.ByteArray; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class DataAccountRegisterOpTemplateTest { + + private DataAccountRegisterOpTemplate data; + + @Before + public void initDataAccountRegisterOpTemplate() { + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(Operation.class); + String pubKeyVal = "jd.com"; + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); + BlockchainIdentity contractID = new BlockchainIdentityData(pubKey); + data = new DataAccountRegisterOpTemplate(contractID); + + } + + @Test + public void testSerialize_DataAccountRegisterOperation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, DataAccountRegisterOperation.class); + DataAccountRegisterOperation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getAccountID().getAddress(), data.getAccountID().getAddress()); + assertEquals(resolvedData.getAccountID().getPubKey(), data.getAccountID().getPubKey()); + + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_Operation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, Operation.class); + Operation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + System.out.println("serialBytesLength=" + serialBytes.length); + System.out.println(resolvedData); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DigitalSignatureBlobTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DigitalSignatureBlobTest.java new file mode 100644 index 00000000..27f6d733 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/DigitalSignatureBlobTest.java @@ -0,0 +1,64 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.DigitalSignatureBlobTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 下午2:12 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.DigitalSignatureBody; +import com.jd.blockchain.ledger.data.DigitalSignatureBlob; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class DigitalSignatureBlobTest { + + private DigitalSignatureBlob data; + + @Before + public void initDigitalSignatureBlob() throws Exception { + DataContractRegistry.register(DigitalSignature.class); + DataContractRegistry.register(DigitalSignatureBody.class); + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, "jd.com".getBytes()); + SignatureDigest digest = new SignatureDigest(CryptoAlgorithm.ED25519, "zhangsan".getBytes()); + data = new DigitalSignatureBlob(pubKey, digest); + } + + @Test + public void testSerialize_DigitalSignature() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, DigitalSignature.class); + DigitalSignature resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getDigest(), data.getDigest()); + assertEquals(resolvedData.getPubKey(), data.getPubKey()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_DigitalSignatureBody() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, DigitalSignatureBody.class); + DigitalSignatureBody resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getDigest(), data.getDigest()); + assertEquals(resolvedData.getPubKey(), data.getPubKey()); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ED25519SignatureTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ED25519SignatureTest.java new file mode 100644 index 00000000..fbadccf0 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/ED25519SignatureTest.java @@ -0,0 +1,51 @@ +package test.com.jd.blockchain.ledger.data; + +import java.util.Random; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.utils.security.Ed25519Utils; + +public class ED25519SignatureTest { + + public static void main(String[] args) { + Random rand = new Random(); + byte[] data = new byte[64]; + rand.nextBytes(data); + + BlockchainKeyPair key = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + + byte[] pubKey = key.getPubKey().getRawKeyBytes(); + byte[] privKey = key.getPrivKey().getRawKeyBytes(); + + int count = 10000; + + System.out.println("=================== do sign test ==================="); + byte[] sign = null; + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + sign = Ed25519Utils.sign_512(data, privKey); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("Siging Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + System.out.println("=================== do verify test ==================="); + for (int r = 0; r < 5; r++) { + System.out.println("------------- round[" + r + "] --------------"); + long startTS = System.currentTimeMillis(); + for (int i = 0; i < count; i++) { + Ed25519Utils.verify(data, pubKey, sign); + } + long elapsedTS = System.currentTimeMillis() - startTS; + System.out.println(String.format("Verifying Count=%s; Elapsed Times=%s; TPS=%.2f", count, elapsedTS, + (count * 1000.00D) / elapsedTS)); + } + + } + +} diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/KVDataTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/KVDataTest.java new file mode 100644 index 00000000..1ee20e8a --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/KVDataTest.java @@ -0,0 +1,54 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.KVDataTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午11:08 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.ledger.BytesValueImpl; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataType; +import com.jd.blockchain.ledger.data.DataAccountKVSetOpTemplate; +import com.jd.blockchain.ledger.data.KVData; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class KVDataTest { + private KVData kvData; + + @Before + public void initKVData() throws Exception { + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + String key = "test-key"; + byte[] value = "test-value".getBytes(); + long expectedVersion = 9999L; + + kvData = new KVData(key, new BytesValueImpl(DataType.BYTES, value), expectedVersion); + } + + @Test + public void testSerialize_KVEntry() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(kvData, DataAccountKVSetOperation.KVWriteEntry.class); + DataAccountKVSetOpTemplate.KVWriteEntry resolvedKvData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedKvData.getKey(), kvData.getKey()); + assertEquals(resolvedKvData.getExpectedVersion(), kvData.getExpectedVersion()); + assertArrayEquals(resolvedKvData.getValue().getValue().toBytes(), kvData.getValue().getValue().toBytes()); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/OpBlobTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/OpBlobTest.java new file mode 100644 index 00000000..0692a3e4 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/OpBlobTest.java @@ -0,0 +1,82 @@ +//package test.com.jd.blockchain.ledger.data; +// +//import static org.junit.Assert.*; +// +//import java.io.IOException; +// +//import org.junit.Test; +// +//import com.jd.blockchain.ledger.StateOpType; +//import com.jd.blockchain.ledger.Operation; +//import com.jd.blockchain.ledger.OperationType; +//import com.jd.blockchain.ledger.data.OpBlob; +// +//import my.utils.io.ByteArray; +//import my.utils.io.BytesUtils; +//import my.utils.security.RandomUtils; +// +//public class OpBlobTest { +// +// /** +// * 验证无子操作的情形; +// * +// * @throws IOException +// */ +// @Test +// public void testSerializeNoSubOP() throws IOException { +// OpBlob op = new OpBlob(); +// byte[] opArg = RandomUtils.generateRandomBytes(16); +// op.setOperation(OperationType.REGISTER_USER.CODE, ByteArray.wrap(opArg)); +// +// byte[] opBytes = BytesUtils.toBytes(op); +// +// assertEquals(OperationType.REGISTER_USER.CODE, opBytes[0]); +// +// OpBlob resolvedOP = new OpBlob(); +// resolvedOP.resolvFrom(ByteArray.wrap(opBytes).asInputStream()); +// +// assertEquals(op.getCode(), resolvedOP.getCode()); +// assertEquals(op.getArgCount(), resolvedOP.getArgCount()); +// assertEquals(0, resolvedOP.getSubOperations().length); +// assertEquals(op.getSubOperations().length, resolvedOP.getSubOperations().length); +// +// } +// +// /** +// * 验证有子操作的情形; +// * +// * @throws IOException +// */ +// @Test +// public void testSerializeWithSubOP() throws IOException { +// OpBlob op = new OpBlob(); +// byte[] opArg = RandomUtils.generateRandomBytes(16); +// op.setOperation(OperationType.REGISTER_USER.CODE, ByteArray.wrap(opArg)); +// +// byte[] subopArg = RandomUtils.generateRandomBytes(16); +// op.addSubOperation(StateOpType.SET, ByteArray.wrap(subopArg)); +// +// byte[] opBytes = BytesUtils.toBytes(op); +// +// assertEquals(OperationType.REGISTER_USER.CODE, opBytes[0]); +// +// OpBlob resolvedOP = new OpBlob(); +// resolvedOP.resolvFrom(ByteArray.wrap(opBytes).asInputStream()); +// +// assertEquals(op.getCode(), resolvedOP.getCode()); +// assertEquals(op.getArgCount(), resolvedOP.getArgCount()); +// +// Operation[] subOps = op.getSubOperations(); +// Operation[] resolvedSubOps = resolvedOP.getSubOperations(); +// assertEquals(1, subOps.length); +// assertEquals(subOps.length, resolvedSubOps.length); +// +// for (int i = 0; i < resolvedSubOps.length; i++) { +// assertEquals(subOps[i].getCode(), resolvedSubOps[i].getCode()); +// assertEquals(1, resolvedSubOps[i].getArgCount()); +// assertEquals(subOps[i].getArgCount(), resolvedSubOps[i].getArgCount()); +// assertEquals(subOps[i].getArg(0), resolvedSubOps[i].getArg(0)); +// } +// } +// +//} diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/SizeHeaderMaskTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/SizeHeaderMaskTest.java new file mode 100644 index 00000000..c1acfb00 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/SizeHeaderMaskTest.java @@ -0,0 +1,40 @@ +package test.com.jd.blockchain.ledger.data; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.jd.blockchain.utils.io.NumberMask; + + +public class SizeHeaderMaskTest { + + @Test + public void testSizeHeaderMask() { + assertTrue(NumberMask.TINY.MAX_HEADER_LENGTH == 1); + assertEquals(256, NumberMask.TINY.getBoundarySize(1)); + + assertTrue(NumberMask.SHORT.MAX_HEADER_LENGTH == 2); + assertEquals(128, NumberMask.SHORT.getBoundarySize(1)); + assertEquals(32768, NumberMask.SHORT.getBoundarySize(2)); + + assertTrue(NumberMask.NORMAL.MAX_HEADER_LENGTH == 4); + assertEquals(64, NumberMask.NORMAL.getBoundarySize(1)); + assertEquals(16384, NumberMask.NORMAL.getBoundarySize(2)); + assertEquals(4194304, NumberMask.NORMAL.getBoundarySize(3)); + assertEquals(1073741824, NumberMask.NORMAL.getBoundarySize(4)); + + //不考虑 long 的情况; +// assertTrue(SizeHeaderMask.LONG.MAX_HEADER_LENGTH == 8); +// assertEquals(32L, SizeHeaderMask.LONG.getBoundarySize((byte)1)); +// assertEquals(8192L, SizeHeaderMask.LONG.getBoundarySize((byte)2)); +// assertEquals(2097152L, SizeHeaderMask.LONG.getBoundarySize((byte)3)); +// assertEquals(536870912L, SizeHeaderMask.LONG.getBoundarySize((byte)4)); +// assertEquals(137438953472L, SizeHeaderMask.LONG.getBoundarySize((byte)5)); +// assertEquals(35184372088832L, SizeHeaderMask.LONG.getBoundarySize((byte)6)); +// assertEquals(9007199254740992L, SizeHeaderMask.LONG.getBoundarySize((byte)7)); +// assertEquals(2305843009213693952L, SizeHeaderMask.LONG.getBoundarySize((byte)8)); + } + +} diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxContentBlobTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxContentBlobTest.java new file mode 100644 index 00000000..4b1ae8c0 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxContentBlobTest.java @@ -0,0 +1,104 @@ +package test.com.jd.blockchain.ledger.data; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.util.UUID; + +import com.jd.blockchain.ledger.*; +import org.junit.Before; +import org.junit.Test; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.data.BlockchainOperationFactory; +import com.jd.blockchain.ledger.data.TxContentBlob; +import com.jd.blockchain.utils.io.ByteArray; + +public class TxContentBlobTest { + + private TxContentBlob contentBlob; + + @Before + public void initTxContentBlob() throws Exception { + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(HashObject.class); + + BlockchainKeyPair id = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + + HashDigest ledgerHash = CryptoUtils.hash(CryptoAlgorithm.SHA256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); + + BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); + + contentBlob = new TxContentBlob(ledgerHash); + + contentBlob.setHash(new HashDigest(CryptoAlgorithm.SHA256, "jd.com".getBytes())); + +// contentBlob.setSubjectAccount(id.getAddress()); +// contentBlob.setSequenceNumber(1); + + DataAccountKVSetOperation kvsetOP = opFactory.dataAccount(id.getAddress()).set("Name", ByteArray.fromString("AAA", "UTF-8"), -1).getOperation(); + contentBlob.addOperation(kvsetOP); + } + + @Test + public void testSerialize_TransactionContentBody() throws IOException,InstantiationException,IllegalAccessException{ + + byte[] bytesContent = BinaryEncodingUtils.encode(contentBlob, TransactionContentBody.class); + TransactionContentBody resolvedContentBlob = BinaryEncodingUtils.decode(bytesContent); + + assertEquals(contentBlob.getLedgerHash(), resolvedContentBlob.getLedgerHash()); +// assertEquals(contentBlob.getSubjectAccount(), resolvedContentBlob.getSubjectAccount()); +// assertEquals(contentBlob.getSequenceNumber(), resolvedContentBlob.getSequenceNumber()); + assertEquals(contentBlob.getOperations().length, resolvedContentBlob.getOperations().length); + + assertEquals(contentBlob.getOperations().length, resolvedContentBlob.getOperations().length); + Operation[] resolvedOperations = resolvedContentBlob.getOperations(); + Operation[] dataOperations = contentBlob.getOperations(); + for (int i = 0; i < dataOperations.length; i++) { + DataAccountKVSetOperation resolvedOperation = (DataAccountKVSetOperation) resolvedOperations[i]; + DataAccountKVSetOperation dataOperation = (DataAccountKVSetOperation) dataOperations[i]; + assertEquals(dataOperation.getAccountAddress(), resolvedOperation.getAccountAddress()); + DataAccountKVSetOperation.KVWriteEntry[] dataKv = dataOperation.getWriteSet(); + DataAccountKVSetOperation.KVWriteEntry[] resolvedKv = resolvedOperation.getWriteSet(); + for (int j = 0; j < dataKv.length; j++) { + assertEquals(dataKv[i].getKey(), resolvedKv[i].getKey()); + assertEquals(dataKv[i].getExpectedVersion(), resolvedKv[i].getExpectedVersion()); + assertArrayEquals(dataKv[i].getValue().getValue().toBytes(), resolvedKv[i].getValue().getValue().toBytes()); + } + } + } + + @Test + public void testSerialize_TransactionContent() throws IOException,InstantiationException,IllegalAccessException{ + + byte[] bytesContent = BinaryEncodingUtils.encode(contentBlob, TransactionContent.class); + TransactionContentBody resolvedContentBlob = BinaryEncodingUtils.decode(bytesContent); + + assertEquals(contentBlob.getLedgerHash(), resolvedContentBlob.getLedgerHash()); +// assertEquals(contentBlob.getSubjectAccount(), resolvedContentBlob.getSubjectAccount()); +// assertEquals(contentBlob.getSequenceNumber(), resolvedContentBlob.getSequenceNumber()); + assertEquals(contentBlob.getOperations().length, resolvedContentBlob.getOperations().length); + + assertEquals(contentBlob.getOperations().length, resolvedContentBlob.getOperations().length); + Operation[] resolvedOperations = resolvedContentBlob.getOperations(); + Operation[] dataOperations = contentBlob.getOperations(); + for (int i = 0; i < dataOperations.length; i++) { + DataAccountKVSetOperation resolvedOperation = (DataAccountKVSetOperation) resolvedOperations[i]; + DataAccountKVSetOperation dataOperation = (DataAccountKVSetOperation) dataOperations[i]; + assertEquals(dataOperation.getAccountAddress(), resolvedOperation.getAccountAddress()); + DataAccountKVSetOperation.KVWriteEntry[] dataKv = dataOperation.getWriteSet(); + DataAccountKVSetOperation.KVWriteEntry[] resolvedKv = resolvedOperation.getWriteSet(); + for (int j = 0; j < dataKv.length; j++) { + assertEquals(dataKv[i].getKey(), resolvedKv[i].getKey()); + assertEquals(dataKv[i].getExpectedVersion(), resolvedKv[i].getExpectedVersion()); + assertArrayEquals(dataKv[i].getValue().getValue().toBytes(), resolvedKv[i].getValue().getValue().toBytes()); + } + } + } +} diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxRequestMessageTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxRequestMessageTest.java new file mode 100644 index 00000000..6649c6b8 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxRequestMessageTest.java @@ -0,0 +1,208 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.TxRequestMessageTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/3 下午3:07 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import static org.junit.Assert.assertEquals; + +import java.util.UUID; + +import org.junit.Before; +import org.junit.Test; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DigitalSignature; +import com.jd.blockchain.ledger.EndpointRequest; +import com.jd.blockchain.ledger.HashObject; +import com.jd.blockchain.ledger.NodeRequest; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.data.BlockchainOperationFactory; +import com.jd.blockchain.ledger.data.DigitalSignatureBlob; +import com.jd.blockchain.ledger.data.TxContentBlob; +import com.jd.blockchain.ledger.data.TxRequestMessage; +import com.jd.blockchain.utils.io.ByteArray; + +/** + * + * @author shaozhuguang + * @create 2018/9/3 + * @since 1.0.0 + */ + +public class TxRequestMessageTest { + + private TxRequestMessage data; + + @Before + public void initTxRequestMessage() throws Exception { + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(HashObject.class); + + data = new TxRequestMessage(initTransactionContent()); + + SignatureDigest digest1 = new SignatureDigest(CryptoAlgorithm.ED25519, "zhangsan".getBytes()); + SignatureDigest digest2 = new SignatureDigest(CryptoAlgorithm.ED25519, "lisi".getBytes()); + DigitalSignatureBlob endPoint1 = new DigitalSignatureBlob( + new PubKey(CryptoAlgorithm.ED25519, "jd1.com".getBytes()), digest1); + DigitalSignatureBlob endPoint2 = new DigitalSignatureBlob( + new PubKey(CryptoAlgorithm.ED25519, "jd2.com".getBytes()), digest2); + data.addEndpointSignatures(endPoint1); + data.addEndpointSignatures(endPoint2); + + SignatureDigest digest3 = new SignatureDigest(CryptoAlgorithm.ED25519, "wangwu".getBytes()); + SignatureDigest digest4 = new SignatureDigest(CryptoAlgorithm.ED25519, "zhaoliu".getBytes()); + DigitalSignatureBlob node1 = new DigitalSignatureBlob(new PubKey(CryptoAlgorithm.ED25519, "jd3.com".getBytes()), + digest3); + DigitalSignatureBlob node2 = new DigitalSignatureBlob(new PubKey(CryptoAlgorithm.ED25519, "jd4.com".getBytes()), + digest4); + data.addNodeSignatures(node1); + data.addNodeSignatures(node2); + + HashDigest hash = new HashDigest(CryptoAlgorithm.SHA256, "sunqi".getBytes()); + data.setHash(hash); + } + + @Test + public void testSerialize_TransactionRequest() { + byte[] serialBytes = BinaryEncodingUtils.encode(data, TransactionRequest.class); + TransactionRequest resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getEndpointSignatures().length, data.getEndpointSignatures().length); + assertEquals(resolvedData.getNodeSignatures().length, data.getNodeSignatures().length); + + // EndpointSignatures 验证 + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + + // NodeSignatures 验证 + DigitalSignature[] dataNodeSignatures = data.getNodeSignatures(); + DigitalSignature[] resolvedNodeSignatures = resolvedData.getNodeSignatures(); + for (int i = 0; i < dataNodeSignatures.length; i++) { + assertEquals(dataNodeSignatures[i].getPubKey(), resolvedNodeSignatures[i].getPubKey()); + assertEquals(dataNodeSignatures[i].getDigest(), resolvedNodeSignatures[i].getDigest()); + } + + TransactionContent dataTxContent = data.getTransactionContent(); + TransactionContent resolvedTxContent = data.getTransactionContent(); + + Operation[] dataOperations = dataTxContent.getOperations(); + Operation[] resolvedOperations = resolvedTxContent.getOperations(); + assertEquals(dataOperations.length, resolvedOperations.length); + for (int i = 0; i < dataOperations.length; i++) { + assertEquals(dataOperations[i], resolvedOperations[i]); + } + assertEqual(dataTxContent, resolvedTxContent); + + assertEquals(resolvedData.getHash(), data.getHash()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_NodeRequest() { + byte[] serialBytes = BinaryEncodingUtils.encode(data, NodeRequest.class); + NodeRequest resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getEndpointSignatures().length, data.getEndpointSignatures().length); + assertEquals(resolvedData.getNodeSignatures().length, data.getNodeSignatures().length); + + // EndpointSignatures 验证 + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + + // NodeSignatures 验证 + DigitalSignature[] dataNodeSignatures = data.getNodeSignatures(); + DigitalSignature[] resolvedNodeSignatures = resolvedData.getNodeSignatures(); + for (int i = 0; i < dataNodeSignatures.length; i++) { + assertEquals(dataNodeSignatures[i].getPubKey(), resolvedNodeSignatures[i].getPubKey()); + assertEquals(dataNodeSignatures[i].getDigest(), resolvedNodeSignatures[i].getDigest()); + } + + TransactionContent dataTxContent = data.getTransactionContent(); + TransactionContent resolvedTxContent = data.getTransactionContent(); + + Operation[] dataOperations = dataTxContent.getOperations(); + Operation[] resolvedOperations = resolvedTxContent.getOperations(); + assertEquals(dataOperations.length, resolvedOperations.length); + for (int i = 0; i < dataOperations.length; i++) { + assertEquals(dataOperations[i], resolvedOperations[i]); + } + assertEqual(dataTxContent, resolvedTxContent); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_EndpointRequest() { + byte[] serialBytes = BinaryEncodingUtils.encode(data, EndpointRequest.class); + EndpointRequest resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getEndpointSignatures().length, data.getEndpointSignatures().length); + DigitalSignature[] dataEndpointSignatures = data.getEndpointSignatures(); + DigitalSignature[] resolvedEndpointSignatures = resolvedData.getEndpointSignatures(); + for (int i = 0; i < dataEndpointSignatures.length; i++) { + assertEquals(dataEndpointSignatures[i].getPubKey(), resolvedEndpointSignatures[i].getPubKey()); + assertEquals(dataEndpointSignatures[i].getDigest(), resolvedEndpointSignatures[i].getDigest()); + } + TransactionContent dataTxContent = data.getTransactionContent(); + TransactionContent resolvedTxContent = data.getTransactionContent(); + + Operation[] dataOperations = dataTxContent.getOperations(); + Operation[] resolvedOperations = resolvedTxContent.getOperations(); + assertEquals(dataOperations.length, resolvedOperations.length); + for (int i = 0; i < dataOperations.length; i++) { + assertEquals(dataOperations[i], resolvedOperations[i]); + } + assertEqual(dataTxContent, resolvedTxContent); + System.out.println("------Assert OK ------"); + } + + private void assertEqual(TransactionContent dataTxContent, TransactionContent resolvedTxContent) { + assertEquals(dataTxContent.getHash(), resolvedTxContent.getHash()); + assertEquals(dataTxContent.getLedgerHash(), resolvedTxContent.getLedgerHash()); + // assertEquals(dataTxContent.getSequenceNumber(), + // resolvedTxContent.getSequenceNumber()); + // assertEquals(dataTxContent.getSubjectAccount(), + // resolvedTxContent.getSubjectAccount()); + } + + private TransactionContent initTransactionContent() throws Exception { + TxContentBlob contentBlob = null; + BlockchainKeyPair id = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + HashDigest ledgerHash = CryptoUtils.hash(CryptoAlgorithm.SHA256) + .hash(UUID.randomUUID().toString().getBytes("UTF-8")); + BlockchainOperationFactory opFactory = new BlockchainOperationFactory(); + contentBlob = new TxContentBlob(ledgerHash); + contentBlob.setHash(new HashDigest(CryptoAlgorithm.SHA256, "jd.com".getBytes())); + // contentBlob.setSubjectAccount(id.getAddress()); + // contentBlob.setSequenceNumber(1); + DataAccountKVSetOperation kvsetOP = opFactory.dataAccount(id.getAddress()) + .set("Name", ByteArray.fromString("AAA", "UTF-8"), -1).getOperation(); + contentBlob.addOperation(kvsetOP); + return contentBlob; + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxResponseMessageTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxResponseMessageTest.java new file mode 100644 index 00000000..26b00df0 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/TxResponseMessageTest.java @@ -0,0 +1,60 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.TxResponseMessageTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/6 上午11:00 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/9/6 + * @since 1.0.0 + */ + +public class TxResponseMessageTest { + + private TxResponseMessage data; + + @Before + public void initTxRequestMessage() throws Exception { + DataContractRegistry.register(TransactionResponse.class); + + HashDigest contentHash = new HashDigest(CryptoAlgorithm.SHA256, "jd-content".getBytes()); + + HashDigest blockHash = new HashDigest(CryptoAlgorithm.SHA256, "jd-block".getBytes()); + + long blockHeight = 9999L; + data = new TxResponseMessage(contentHash); + data.setBlockHash(blockHash); + data.setBlockHeight(blockHeight); + data.setExecutionState(TransactionState.SUCCESS); + } + + @Test + public void testSerialize_TransactionResponse() { + byte[] serialBytes = BinaryEncodingUtils.encode(data, TransactionResponse.class); + TransactionResponse resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(resolvedData.getBlockHash(), data.getBlockHash()); + assertEquals(resolvedData.getBlockHeight(), data.getBlockHeight()); + assertEquals(resolvedData.getContentHash(), data.getContentHash()); + assertEquals(resolvedData.getExecutionState(), data.getExecutionState()); + assertEquals(resolvedData.isSuccess(), data.isSuccess()); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/UserRegisterOpTemplateTest.java b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/UserRegisterOpTemplateTest.java new file mode 100644 index 00000000..c5e717f4 --- /dev/null +++ b/source/ledger/ledger-model/src/test/java/test/com/jd/blockchain/ledger/data/UserRegisterOpTemplateTest.java @@ -0,0 +1,66 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.ledger.data.UserRegisterOpTemplateTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/8/30 上午11:04 + * Description: + */ +package test.com.jd.blockchain.ledger.data; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.ContractEventSendOpTemplate; +import com.jd.blockchain.ledger.data.DataAccountRegisterOpTemplate; +import com.jd.blockchain.ledger.data.UserRegisterOpTemplate; +import com.jd.blockchain.utils.io.ByteArray; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * + * @author shaozhuguang + * @create 2018/8/30 + * @since 1.0.0 + */ + +public class UserRegisterOpTemplateTest { + + private UserRegisterOpTemplate data; + + @Before + public void initUserRegisterOpTemplate() { + DataContractRegistry.register(UserRegisterOperation.class); + DataContractRegistry.register(Operation.class); + String pubKeyVal = "jd.com"; + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); + BlockchainIdentity contractID = new BlockchainIdentityData(pubKey); + data = new UserRegisterOpTemplate(contractID); + } + + @Test + public void testSerialize_UserRegisterOperation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, UserRegisterOperation.class); + UserRegisterOperation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + assertEquals(data.getUserID().getAddress(), resolvedData.getUserID().getAddress()); + assertEquals(data.getUserID().getPubKey(), resolvedData.getUserID().getPubKey()); + System.out.println("------Assert OK ------"); + } + + @Test + public void testSerialize_Operation() throws Exception { + byte[] serialBytes = BinaryEncodingUtils.encode(data, Operation.class); + Operation resolvedData = BinaryEncodingUtils.decode(serialBytes); + System.out.println("------Assert start ------"); + System.out.println("serialBytesLength=" + serialBytes.length); + System.out.println(resolvedData); + System.out.println("------Assert OK ------"); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-rpc/pom.xml b/source/ledger/ledger-rpc/pom.xml new file mode 100644 index 00000000..aded5314 --- /dev/null +++ b/source/ledger/ledger-rpc/pom.xml @@ -0,0 +1,51 @@ + + 4.0.0 + + com.jd.blockchain + ledger + 0.8.3.RELEASE + + ledger-rpc + + + + com.jd.blockchain + ledger-model + ${project.version} + + + com.jd.blockchain + consensus-framework + ${project.version} + + + com.jd.blockchain + binary-proto + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + org.springframework + spring-web + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + false + + + + + \ No newline at end of file diff --git a/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/BinaryMessageConverter.java b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/BinaryMessageConverter.java new file mode 100644 index 00000000..affc2dde --- /dev/null +++ b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/BinaryMessageConverter.java @@ -0,0 +1,112 @@ +package com.jd.blockchain.web.converters; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ClientIdentifications; +import com.jd.blockchain.consensus.action.ActionRequest; +import com.jd.blockchain.consensus.action.ActionResponse; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.utils.io.BytesUtils; + +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.AbstractHttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; + +/** + * 针对二进制对象的序列化和反序列化的 HTTP 消息转换器; + * + * @author huanghaiquan + * + */ +public class BinaryMessageConverter extends AbstractHttpMessageConverter { + + public static final String CONTENT_TYPE_VALUE = "application/bin-obj;charset=UTF-8"; + + static { + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(ContractCodeDeployOperation.class); + DataContractRegistry.register(ContractEventSendOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + + DataContractRegistry.register(ActionRequest.class); + DataContractRegistry.register(ActionResponse.class); + DataContractRegistry.register(ClientIdentifications.class); + DataContractRegistry.register(ClientIdentification.class); + } + + public BinaryMessageConverter() { + super(MediaType.valueOf(CONTENT_TYPE_VALUE)); + } + + @Override + protected boolean supports(Class aClass) { + return true; + } + + @Override + protected boolean canRead(MediaType mediaType) { + if (mediaType == null) { + return false; + } + for (MediaType supportedMediaType : getSupportedMediaTypes()) { + if (supportedMediaType.includes(mediaType)) { + return true; + } + } + return false; + } + + @Override + protected boolean canWrite(MediaType mediaType) { + if (mediaType == null) { + return false; + } else if (MediaType.ALL.equals(mediaType)) { + return true; + } + for (MediaType supportedMediaType : getSupportedMediaTypes()) { + if (supportedMediaType.isCompatibleWith(mediaType)) { + return true; + } + } + return false; + } + + @Override + public Object readInternal(Class clazz, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + InputStream in = inputMessage.getBody(); + byte[] serializeBytes = BytesUtils.readBytes(in); + Object resolvedObj = BinaryEncodingUtils.decode(serializeBytes); + return resolvedObj; + } + + @Override + public void writeInternal(Object t, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + OutputStream out = outputMessage.getBody(); + if (t instanceof TransactionResponse) { + byte[] serializeBytes = BinaryEncodingUtils.encode(t, TransactionResponse.class); + out.write(serializeBytes); + out.flush(); + } + } +} diff --git a/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/BytesInputConverter.java b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/BytesInputConverter.java new file mode 100644 index 00000000..f6a4bf32 --- /dev/null +++ b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/BytesInputConverter.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.gateway.converters.HashDigestInputConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/11 下午5:05 + * Description: + */ +package com.jd.blockchain.web.converters; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.codec.Base58Utils; + +import org.springframework.core.convert.converter.Converter; + +/** + * + * @author shaozhuguang + * @create 2018/9/11 + * @since 1.0.0 + */ + +public class BytesInputConverter implements Converter { + + @Override + public HashDigest convert(String inText) { + byte[] hashBytes = Base58Utils.decode(inText); + return new HashDigest(hashBytes); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/HashDigestInputConverter.java b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/HashDigestInputConverter.java new file mode 100644 index 00000000..97f8c41d --- /dev/null +++ b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/converters/HashDigestInputConverter.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.peer.converters.HashDigestInputConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/27 下午2:51 + * Description: + */ +package com.jd.blockchain.web.converters; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.codec.Base58Utils; + +import org.springframework.core.convert.converter.Converter; + +/** + * + * @author shaozhuguang + * @create 2018/9/27 + * @since 1.0.0 + */ + +public class HashDigestInputConverter implements Converter { + + @Override + public HashDigest convert(String inText) { + byte[] hashBytes = Base58Utils.decode(inText); + return new HashDigest(hashBytes); + } +} \ No newline at end of file diff --git a/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectJsonDeserializer.java b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectJsonDeserializer.java new file mode 100644 index 00000000..dc8cb119 --- /dev/null +++ b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectJsonDeserializer.java @@ -0,0 +1,101 @@ +package com.jd.blockchain.web.serializes; + +import com.alibaba.fastjson.parser.DefaultJSONParser; +import com.alibaba.fastjson.parser.JSONToken; +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.parser.deserializer.JavaBeanDeserializer; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesSlice; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Type; +import java.util.Map; + +public class ByteArrayObjectJsonDeserializer extends JavaBeanDeserializer { + + private ByteArrayObjectJsonDeserializer(Class clazz) { + super(ParserConfig.global, clazz); + } + + public static ByteArrayObjectJsonDeserializer getInstance(Class clazz) { + return new ByteArrayObjectJsonDeserializer(clazz); + } + + @SuppressWarnings("unchecked") + @Override + public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + if (type instanceof Class && clazz.isAssignableFrom((Class) type)) { + String parseText = parser.parseObject(String.class); + byte[] hashBytes = Base58Utils.decode(parseText); + if (clazz == HashDigest.class) { + return (T) new HashDigest(hashBytes); + } else if (clazz == PubKey.class) { + return (T) new HashDigest(hashBytes); + } else if (clazz == SignatureDigest.class) { + return (T) new SignatureDigest(hashBytes); + } else if (clazz == Bytes.class) { + return (T) new Bytes(hashBytes); + } else if (clazz == BytesSlice.class) { + return (T) new BytesSlice(hashBytes); + } + +// else if (clazz == BytesValue.class) { +// ByteArrayObjectJsonSerializer.BytesValueJson valueJson = JSON.parseObject(parseText, ByteArrayObjectJsonSerializer.BytesValueJson.class); +// DataType dataType = valueJson.getType(); +// Object dataVal = valueJson.getValue(); +// byte[] bytes = null; +// switch (dataType) { +// case BYTES: +// bytes = ByteArray.fromHex((String) dataVal); +// break; +// case TEXT: +// bytes = ((String) dataVal).getBytes(); +// break; +// case INT64: +// bytes = BytesUtils.toBytes((Long) dataVal); +// break; +// case JSON: +// bytes = ((String) dataVal).getBytes(); +// break; +// } +// BytesValue bytesValue = new BytesValueImpl(dataType, bytes); +// return (T) bytesValue; +// } + } + return (T) parser.parse(fieldName); + } + + @Override + public Object createInstance(Map map, ParserConfig config) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + if (map == null || map.isEmpty()) { + return null; + } + for (Map.Entry entry : map.entrySet()) { + Object value = entry.getValue(); + if (value instanceof String) { + byte[] hashBytes = Base58Utils.decode((String) value); + if (clazz == HashDigest.class) { + return new HashDigest(hashBytes); + } else if (clazz == PubKey.class) { + return new PubKey(hashBytes); + } else if (clazz == SignatureDigest.class) { + return new SignatureDigest(hashBytes); + } else if (clazz == Bytes.class) { + return new Bytes(hashBytes); + } else if (clazz == BytesSlice.class) { + return new BytesSlice(hashBytes); + } + } + } + return null; + } + + @Override + public int getFastMatchToken() { + return JSONToken.LBRACE; + } +} diff --git a/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectJsonSerializer.java b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectJsonSerializer.java new file mode 100644 index 00000000..4aba18a3 --- /dev/null +++ b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectJsonSerializer.java @@ -0,0 +1,120 @@ +package com.jd.blockchain.web.serializes; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.DataType; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesSlice; + +import java.lang.reflect.Type; + +public class ByteArrayObjectJsonSerializer implements ObjectSerializer { + + private Class clazz; + + private ByteArrayObjectJsonSerializer(Class clazz) { + this.clazz = clazz; + } + + public static ByteArrayObjectJsonSerializer getInstance(Class clazz) { + return new ByteArrayObjectJsonSerializer(clazz); + } + + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) { + if (object.getClass() != clazz) { + serializer.writeNull(); + return; + } + if (object instanceof HashDigest) { + serializer.write(new HashDigestJson(((HashDigest) object).toBase58())); + } else if (object instanceof PubKey) { + serializer.write(new HashDigestJson(((PubKey) object).toBase58())); + } else if (object instanceof SignatureDigest) { + serializer.write(new HashDigestJson(((SignatureDigest) object).toBase58())); + } else if (object instanceof Bytes) { + serializer.write(new HashDigestJson(((Bytes) object).toBase58())); + } else if (object instanceof BytesSlice) { + serializer.write(Base58Utils.encode(((BytesSlice) object).toBytes())); + } + +// else if (object instanceof BytesValue) { +// DataType dataType = ((BytesValue) object).getType(); +// BytesSlice bytesValue = ((BytesValue) object).getValue(); +// Object realVal; +// switch (dataType) { +// case NIL: +// realVal = null; +// break; +// case TEXT: +// realVal = bytesValue.getString(); +// break; +// case BYTES: +// realVal = ByteArray.toHex(bytesValue.toBytes()); +// break; +// case INT32: +// realVal = bytesValue.getInt(); +// break; +// case INT64: +// realVal = bytesValue.getLong(); +// break; +// case JSON: +// realVal = bytesValue.getString(); +// break; +// default: +// realVal = ByteArray.toHex(bytesValue.toBytes()); +// break; +// } +// serializer.write(new BytesValueJson(dataType, realVal)); +// } + } + + private static class HashDigestJson { + + String value; + + public HashDigestJson(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + + public static class BytesValueJson { + + public BytesValueJson(DataType type, Object value) { + this.type = type; + this.value = value; + } + + DataType type; + + Object value; + + public DataType getType() { + return type; + } + + public void setType(DataType type) { + this.type = type; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + } +} diff --git a/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectUtil.java b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectUtil.java new file mode 100644 index 00000000..2623cfe5 --- /dev/null +++ b/source/ledger/ledger-rpc/src/main/java/com/jd/blockchain/web/serializes/ByteArrayObjectUtil.java @@ -0,0 +1,41 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.web.serializes.ByteArrayObjectUtil + * Author: shaozhuguang + * Department: Y事业部 + * Date: 2019/3/27 上午11:23 + * Description: + */ +package com.jd.blockchain.web.serializes; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesSlice; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +/** + * + * @author shaozhuguang + * @create 2019/3/27 + * @since 1.0.0 + */ + +public class ByteArrayObjectUtil { + + public static final Class[] BYTEARRAY_JSON_SERIALIZE_CLASS = new Class[] { + HashDigest.class, + PubKey.class, + SignatureDigest.class, + Bytes.class, + BytesSlice.class}; + + public static void init() { + for (Class byteArrayClass : BYTEARRAY_JSON_SERIALIZE_CLASS) { + JSONSerializeUtils.configSerialization(byteArrayClass, + ByteArrayObjectJsonSerializer.getInstance(byteArrayClass), + ByteArrayObjectJsonDeserializer.getInstance(byteArrayClass)); + } + } +} \ No newline at end of file diff --git a/source/ledger/pom.xml b/source/ledger/pom.xml new file mode 100644 index 00000000..0af7101b --- /dev/null +++ b/source/ledger/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + ledger + pom + + + ledger-model + ledger-core + ledger-rpc + + \ No newline at end of file diff --git a/source/peer/.gitignore b/source/peer/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/peer/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/peer/config/hosts.config b/source/peer/config/hosts.config new file mode 100644 index 00000000..8d5fdf76 --- /dev/null +++ b/source/peer/config/hosts.config @@ -0,0 +1,36 @@ +# 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. + +# This file defines the replicas ids, IPs and ports. +# It is used by the replicas and clients to find connection info +# to the initial replicas. +# The ports defined here are the ports used by clients to communicate +# with the replicas. Additional connections are opened by replicas to +# communicate with each other. This additional connection is opened in the +# next port defined here. For an example, consider the line "0 127.0.0.1 11000". +# That means that clients will open a communication channel to replica 0 in +# IP 127.0.0.1 and port 11000. On startup, replicas with id different than 0 +# will open a communication channel to replica 0 in port 11001. +# The same holds for replicas 1, 2, 3 ... N. + +#server id, address and port (the ids from 0 to n-1 are the service replicas) +0 127.0.0.1 11000 +1 127.0.0.1 11010 +2 127.0.0.1 11020 +3 127.0.0.1 11030 +#4 192.168.151.33 11040 +#5 192.168.151.38 11050 +#6 127.0.0.1 11060 +#7 127.0.0.1 11070 +7001 127.0.0.1 11100 diff --git a/source/peer/config/system.config b/source/peer/config/system.config new file mode 100644 index 00000000..c5bb2fa4 --- /dev/null +++ b/source/peer/config/system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/peer/pom.xml b/source/peer/pom.xml new file mode 100644 index 00000000..476a201c --- /dev/null +++ b/source/peer/pom.xml @@ -0,0 +1,98 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + peer + + + 2.10.0 + 1.7.25 + + + + + com.jd.blockchain + consensus-framework + ${project.version} + + + com.jd.blockchain + ledger-rpc + ${project.version} + + + com.jd.blockchain + consensus-mq + ${project.version} + + + com.jd.blockchain + consensus-bftsmart + ${project.version} + + + com.jd.blockchain + ledger-core + ${project.version} + + + com.jd.blockchain + tools-initializer + ${project.version} + + + com.jd.blockchain + utils-web + ${project.version} + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-security + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + + \ No newline at end of file diff --git a/source/peer/shell/start.bat b/source/peer/shell/start.bat new file mode 100644 index 00000000..ce8a26c8 --- /dev/null +++ b/source/peer/shell/start.bat @@ -0,0 +1 @@ +java -jar peer-0.0.1-SNAPSHOT.jar --spring.config.location=file:config/application.properties diff --git a/source/peer/shell/start.sh b/source/peer/shell/start.sh new file mode 100644 index 00000000..6c015ea4 --- /dev/null +++ b/source/peer/shell/start.sh @@ -0,0 +1 @@ +nohup java -jar peer-0.0.1-SNAPSHOT.jar --spring.config.location=file:config/application.properties & diff --git a/source/peer/shell/stop.sh b/source/peer/shell/stop.sh new file mode 100644 index 00000000..5657192c --- /dev/null +++ b/source/peer/shell/stop.sh @@ -0,0 +1,6 @@ +PID=`ps -ef | grep "java -jar peer-" | grep -v grep | awk '{print $2}'` +PROC_PATH=`ps -ef | grep "java -jar peer-" | grep -v grep | awk '{print $8}'` +echo "Stopping peer[$PID][$PROC_PATH] ......" +kill 9 $PID + +echo "Peer stopped!" diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/ConsensusManage.java b/source/peer/src/main/java/com/jd/blockchain/peer/ConsensusManage.java new file mode 100644 index 00000000..55da341c --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/ConsensusManage.java @@ -0,0 +1,33 @@ +package com.jd.blockchain.peer; + +import com.jd.blockchain.consensus.service.NodeServer; + +/** + * 共识服务管理; + * @author huanghaiquan + * + */ +public interface ConsensusManage { + + /** + * @return + */ + ConsensusRealm[] getRealms(); + + /** + * 启动参与的全部共识域的共识服务; + */ + void runAllRealms(); + + /** + * 启动某个共识服务 + * @param nodeServer + */ + void runRealm(NodeServer nodeServer); + + /** + * 停止共识服务; + */ + void closeAllRealms(); + +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/ConsensusRealm.java b/source/peer/src/main/java/com/jd/blockchain/peer/ConsensusRealm.java new file mode 100644 index 00000000..2beb0b49 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/ConsensusRealm.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.peer; + +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.utils.Bytes; + +/** + * 共识域; + * + * @author huanghaiquan + * + */ +public interface ConsensusRealm { + + /** + * 共识节点列表; + * + * @return + */ + ParticipantNode[] getNodes(); + + /** + * 共识系统配置; + * @return + */ + Bytes getSetting(); + + /** + * 与指定的共识域是否有交集; + * + * @param other + * @return + */ + boolean hasIntersection(ConsensusRealm other); + +} \ No newline at end of file diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/LedgerBindingConfigAware.java b/source/peer/src/main/java/com/jd/blockchain/peer/LedgerBindingConfigAware.java new file mode 100644 index 00000000..90b1666e --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/LedgerBindingConfigAware.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.peer; + +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; + +import java.util.List; + +public interface LedgerBindingConfigAware { + + void setConfig(LedgerBindingConfig config); + + NodeServer setConfig(LedgerBindingConfig config, HashDigest ledgerHash); +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/PeerConfiguration.java b/source/peer/src/main/java/com/jd/blockchain/peer/PeerConfiguration.java new file mode 100644 index 00000000..86b169a4 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/PeerConfiguration.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.peer; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.ComponentScan; + +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +@ComponentScan +public class PeerConfiguration { + +} + diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/PeerManage.java b/source/peer/src/main/java/com/jd/blockchain/peer/PeerManage.java new file mode 100644 index 00000000..300bca6c --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/PeerManage.java @@ -0,0 +1,5 @@ +package com.jd.blockchain.peer; + +public interface PeerManage extends LedgerBindingConfigAware, ConsensusManage{ + +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/PeerServerBooter.java b/source/peer/src/main/java/com/jd/blockchain/peer/PeerServerBooter.java new file mode 100644 index 00000000..8e7811a0 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/PeerServerBooter.java @@ -0,0 +1,183 @@ +package com.jd.blockchain.peer; + +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.utils.ArgumentSet; +import com.jd.blockchain.utils.ConsoleUtils; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; +import java.io.InputStream; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * 节点服务器; + * + * @author huanghaiquan + * + */ +public class PeerServerBooter { + + private static final Log log = LogFactory.getLog(PeerServerBooter.class); + + // 初始化账本绑定配置文件的路径; + public static final String LEDGERBIND_ARG = "-c"; + // 服务地址; + private static final String HOST_ARG = "-h"; + // 服务端口; + private static final String PORT_ARG = "-p"; + // 是否输出调试信息; + private static final String DEBUG_OPT = "-debug"; + + public static String ledgerBindConfigFile; + + public static void main(String[] args) { + PeerServerBooter peerServerBooter = new PeerServerBooter(); + peerServerBooter.handle(args); + } + + public void handle(String[] args){ + LedgerBindingConfig ledgerBindingConfig = null; + ArgumentSet arguments = ArgumentSet.resolve(args, + ArgumentSet.setting().prefix(LEDGERBIND_ARG, HOST_ARG, PORT_ARG).option(DEBUG_OPT)); + boolean debug = false; + try { + ArgumentSet.ArgEntry argLedgerBindConf = arguments.getArg(LEDGERBIND_ARG); + ledgerBindConfigFile = argLedgerBindConf == null ? null : argLedgerBindConf.getValue(); + if (ledgerBindConfigFile == null) { + ConsoleUtils.info("Load build-in default configuration ..."); + ClassPathResource configResource = new ClassPathResource("ledger-binding.conf"); + InputStream in = configResource.getInputStream(); + ledgerBindingConfig = LedgerBindingConfig.resolve(in); + } else { + ConsoleUtils.info("Load configuration,ledgerBindConfigFile position="+ledgerBindConfigFile); + File file = new File(ledgerBindConfigFile); + ledgerBindingConfig = LedgerBindingConfig.resolve(file); + } + String host = null; + ArgumentSet.ArgEntry hostArg = arguments.getArg(HOST_ARG); + if (hostArg != null) { + host = hostArg.getValue(); + } + int port = 0; + ArgumentSet.ArgEntry portArg = arguments.getArg(PORT_ARG); + if (portArg != null) { + try { + port = Integer.parseInt(portArg.getValue()); + } catch (NumberFormatException e) { + // ignore NumberFormatException of port argument; + } + } + + debug = arguments.hasOption(DEBUG_OPT); + + PeerServerBooter booter = new PeerServerBooter(ledgerBindingConfig, host, port); + if(log.isDebugEnabled()){ + log.debug("PeerServerBooter's urls="+ Arrays.toString(((URLClassLoader) booter.getClass().getClassLoader()).getURLs())); + } + booter.start(); + } catch (Exception e) { + ConsoleUtils.error("Error occurred on startup! --%s", e.getMessage()); + if (debug) { + e.printStackTrace(); + } + } + } + + private LedgerBindingConfig ledgerBindingConfig; + private String hostAddress; + private int port; + private Object[] externalBeans; + private volatile ConfigurableApplicationContext appContext; + + public PeerServerBooter(){} + + public PeerServerBooter(LedgerBindingConfig ledgerBindingConfig, String hostAddress, int port, + Object... externalBeans) { + this.ledgerBindingConfig = ledgerBindingConfig; + this.hostAddress = hostAddress; + this.port = port; + this.externalBeans = externalBeans; + } + + public synchronized void start() { + if (appContext != null) { + throw new IllegalStateException("Peer server is running already!"); + } + appContext = startServer(ledgerBindingConfig, hostAddress, port, externalBeans); + } + + public synchronized void close() { + if (appContext == null) { + return; + } + ConfigurableApplicationContext ctx = appContext; + appContext = null; + ctx.close(); + } + + /** + * 启动服务; + * + * @param ledgerBindingConfig + * 账本绑定配置; + * @param hostAddress + * 服务地址;如果为空,则采用默认配置; + * @param port + * 端口地址;如果小于等于 0 ,则采用默认配置; + * @return + */ + private static ConfigurableApplicationContext startServer(LedgerBindingConfig ledgerBindingConfig, + String hostAddress, int port, Object... externalBeans) { + List argList = new ArrayList(); + String argServerAddress = String.format("--server.address=%s", "0.0.0.0"); + argList.add(argServerAddress); +// if (hostAddress != null && hostAddress.length() > 0) { +// String argServerAddress = String.format("--server.address=%s", hostAddress); +// argList.add(argServerAddress); +// } + if (port > 0) { + String argServerPort = String.format("--server.port=%s", port); + argList.add(argServerPort); + } + + String[] args = argList.toArray(new String[argList.size()]); + + SpringApplication app = new SpringApplication(PeerConfiguration.class); + if (externalBeans != null && externalBeans.length > 0) { + app.addInitializers((ApplicationContextInitializer) applicationContext -> { + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + for (Object bean : externalBeans) { + beanFactory.registerSingleton(bean.toString(), bean); + } + }); + } + // 启动 web 服务; + ConfigurableApplicationContext ctx = app.run(args); + + // 建立共识网络; + Map bindingConfigAwares = ctx.getBeansOfType(LedgerBindingConfigAware.class); + for (LedgerBindingConfigAware aware : bindingConfigAwares.values()) { + aware.setConfig(ledgerBindingConfig); + } + ConsensusManage consensusManage = ctx.getBean(ConsensusManage.class); + consensusManage.runAllRealms(); + + return ctx; + } + + public DbConnectionFactory getDBConnectionFactory() { + return appContext.getBean(DbConnectionFactory.class); + } +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/BlockchainConsensusServiceImpl.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/BlockchainConsensusServiceImpl.java new file mode 100644 index 00000000..5f62888f --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/BlockchainConsensusServiceImpl.java @@ -0,0 +1,213 @@ +//package com.jd.blockchain.peer.consensus; +// +//import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +//import com.jd.blockchain.consensus.BatchConsensusListener; +//import com.jd.blockchain.consensus.action.ActionRequest; +//import com.jd.blockchain.consensus.action.ActionRequestData; +//import com.jd.blockchain.consensus.bft.*; +//import com.jd.blockchain.crypto.CryptoAlgorithm; +//import com.jd.blockchain.crypto.CryptoUtils; +//import com.jd.blockchain.ledger.*; +//import com.jd.blockchain.ledger.data.TxContentBlob; +//import com.jd.blockchain.ledger.data.TxRequestMessage; +//import my.utils.io.BytesEncoding; +//import my.utils.io.NumberMask; +//import org.slf4j.Logger; +//import org.slf4j.LoggerFactory; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Service; +// +//import com.jd.blockchain.crypto.hash.HashDigest; +//import com.jd.blockchain.ledger.core.TransactionBatchProcess; +//import com.jd.blockchain.ledger.core.TransactionBatchResultHandle; +//import com.jd.blockchain.ledger.core.TransactionEngine; +//import com.jd.blockchain.sdk.bftsmart.ConsensusTransactionService; +// +//import java.io.ByteArrayOutputStream; +//import java.util.concurrent.*; +//import java.util.concurrent.atomic.AtomicBoolean; +//import java.util.concurrent.atomic.AtomicLong; +// +///** +// * @author huanghaiquan +// * +// */ +//@Service +//public class BlockchainConsensusServiceImpl implements ConsensusTransactionService, BatchConsensusListener { +// +// private static final Logger LOGGER = LoggerFactory.getLogger(BlockchainConsensusServiceImpl.class); +// +// private final ExecutorService sendExecutorService = Executors.newFixedThreadPool(10); +// +// private final ScheduledExecutorService timerEexecutorService = new ScheduledThreadPoolExecutor(1); +// //结块之前接收的消息数 +// private int receiveCounts = 0; +// //区块提交之后,后续第一个交易到达的时间 +// private long initTimeStamp = 0; +// //达到一定的交易数后发送结块共识消息 +// private int TransactionCount = 100; +// //两秒内没有收到新的共识消息,发送结块共识消息,把不足TransactionCount的消息结块入帐本,单位毫秒 +// private long TimeOut = 2000; +// +// private final AtomicLong blockIndex = new AtomicLong(); +// +// private long currBlockIndex = 0L; +// +// private final AtomicBoolean blockFlag = new AtomicBoolean(false); +// +// +// @Autowired +// private TransactionEngine txEngine; +// +// //0.5 version implement +//// @Override +//// public void beforeBatch(byte[] groupId) { +//// // 以“账本哈希”为共识分组ID; +//// HashDigest ledgerHash = new HashDigest(groupId); +//// txEngine.createNextBatch(ledgerHash); +//// +//// LOGGER.info("Create new block to process transaction batch! --[LedgerHash=" + ledgerHash.toBase58() + "]"); +//// } +// +// //0.6 version implement, not used +// @Override +// public void beforeBatch(byte[] groupId) { +//// // 以“账本哈希”为共识分组ID; +//// HashDigest ledgerHash = new HashDigest(groupId); +//// txEngine.createNextBatch(ledgerHash); +//// +//// LOGGER.info("Create new block to process transaction batch! --[LedgerHash=" + ledgerHash.toBase58() + "]"); +// } +// +// /** +// * 处理收到的交易消息,并返回结果; +// */ +// //0.5 version implement +//// @Override +//// public TransactionResponse process(TransactionRequest txRequest) { +//// TransactionBatchProcess txBatchProcess = txEngine.getBatch(txRequest.getTransactionContent().getLedgerHash()); +//// return txBatchProcess.schedule(txRequest); +//// } +// +// //由Leader Peer发送结块通知消息,需要PEER对此消息进行共识,达到共识结点结块的一致性 +// public void notifyCommit(final long currBlockIndex, HashDigest ledgerHash, BftsmartConsensusSetting bftsmartConsensusSetting, BftsmartTopology bftsmartTopology) { +// //if peer is leader, send commit block consensus request +// System.out.println(Thread.currentThread().getId() + " leader run notifyCommit = " + receiveCounts + " TransactionCount = " + TransactionCount); +// if (receiveCounts >= TransactionCount) { +// System.out.println(Thread.currentThread().getId() + "this.blockIndex = " + this.blockIndex.get() + ", currBlockIndex=" + currBlockIndex); +// boolean isAdd = this.blockIndex.compareAndSet(currBlockIndex, currBlockIndex + 1); +// System.out.println(Thread.currentThread().getId() + " leader run isadd = " + isAdd); +// if (isAdd) { +// sendExecutorService.execute(sendBlockMessage(ledgerHash, bftsmartConsensusSetting, bftsmartTopology)); +// } +// } +// } +// +// private Runnable sendBlockMessage(HashDigest ledgerHash, BftsmartConsensusSetting bftsmartConsensusSetting, BftsmartTopology bftsmartTopology) { +// Runnable runnable = () -> sendCommitBlockMessage(ledgerHash, bftsmartConsensusSetting, bftsmartTopology); +// return runnable; +// } +// //发送结块通知消息 +// private void sendCommitBlockMessage(HashDigest ledgerHash, BftsmartConsensusSetting bftsmartConsensusSetting, BftsmartTopology bftsmartTopology) { +// +//// ActionRequestData request = new ActionRequestData(); +//// request.setGroupId(ledgerHash.toBytes()); +//// request.setHandleType("com.jd.blockchain.sdk.bftsmart.ConsensusTransactionService"); +//// request.setHandleMethod("public abstract com.jd.blockchain.ledger.TransactionResponse com.jd.blockchain.sdk.bftsmart.ConsensusTransactionService.process(com.jd.blockchain.ledger.TransactionRequest)"); +//// request.setTransactionType("COMMITBLOCK"); +//// +//// BlockchainKeyPair userKeyPeer = BlockchainKeyGenerator.getInstance().generate(); +//// +//// TxContentBlob txContentBlob = new TxContentBlob(ledgerHash); +//// +//// byte[] reqBytes = BinaryEncodingUtils.encode(txContentBlob, TransactionContent.class); +//// HashDigest reqHash = CryptoUtils.hash(CryptoAlgorithm.SHA256).hash(reqBytes); +//// txContentBlob.setHash(reqHash); +//// +//// TxRequestMessage transactionRequest = new TxRequestMessage(txContentBlob); +//// +//// byte[] encodeBytes = BinaryEncodingUtils.encode(transactionRequest, TransactionRequest.class); +//// +//// ByteArrayOutputStream out = new ByteArrayOutputStream(); +//// BytesEncoding.write(encodeBytes, NumberMask.NORMAL, out); +//// +//// request.setMessageBody(out.toByteArray()); +//// byte[] commandReq = BinaryEncodingUtils.encode(request, ActionRequest.class); +//// +//// BftsmartConsensusClient bftsmartConsensusClient = new BftsmartConsensusClient(0, bftsmartConsensusSetting, bftsmartTopology); +//// bftsmartConsensusClient.invokeOrdered(commandReq); +//// +//// LOGGER.info(String.format("Send notify commit block msg success!")); +// } +// +// //0.6 version implement +// @Override +// public TransactionResponse process(TransactionRequest txRequest) { +//// System.out.println("peer process thread = " + Thread.currentThread().getId()); +//// System.out.println("peer process object = " + this); +// HashDigest ledgerHash = txRequest.getTransactionContent().getLedgerHash(); +// TransactionBatchProcess txBatchProcess = txEngine.getBatch(ledgerHash); +// +// boolean isLeader = BftsmartConsensusUtils.getLeader(); +// BftsmartConsensusSetting bftsmartConsensusSetting =BftsmartConsensusUtils.getSetting(); +// BftsmartTopology bftsmartTopology = BftsmartConsensusUtils.getTopology(); +// +// if (txBatchProcess == null) { +// txBatchProcess = txEngine.createNextBatch(ledgerHash); +// currBlockIndex = txBatchProcess.blockHeight(); +// this.blockIndex.set(currBlockIndex); +// receiveCounts = 0; +// +// if (isLeader) { +// long timerStart = System.currentTimeMillis(); +// System.out.println("first message time = "+timerStart); +// timerEexecutorService.schedule(timeTask(currBlockIndex, ledgerHash, bftsmartConsensusSetting, bftsmartTopology), 500L, TimeUnit.MILLISECONDS); +// } +// } +// receiveCounts++; +// if (isLeader) { +// notifyCommit(currBlockIndex, ledgerHash, bftsmartConsensusSetting, bftsmartTopology); +// } +// return txBatchProcess.schedule(txRequest); +// } +// +// @Override +// public void afterBatch(byte[] groupId, Exception error) { +// HashDigest ledgerHash = new HashDigest(groupId); +// TransactionBatchProcess txBatchProcess = txEngine.getBatch(ledgerHash); +// +// if (error != null) { +// txBatchProcess.cancel(TransactionState.SYSTEM_ERROR); +// LOGGER.error("Error occurred on executing batch transactions, so the new block is canceled! --" + error.getMessage(), error); +// return; +// } +// +// //生成区块; +// TransactionBatchResultHandle batchResultHandle = txBatchProcess.prepare(); +// +// LedgerBlock newBlock = batchResultHandle.getBlock(); +// // TODO: 对新区块进行最后的共识; +// HashDigest blockHash = newBlock.getHash(); +// +// LOGGER.info(String.format( +// "Create new block success! --[LedgerHash=%s][BlockHash=%s][BlockHeigth=%s]", +// ledgerHash.toBase58(), blockHash.toBase58(), newBlock.getHeight())); +// +// // 提交新区块; +// batchResultHandle.commit(); +// +// } +// +// private Runnable timeTask(final long currBlockIndex, HashDigest ledgerHash, BftsmartConsensusSetting bftsmartConsensusSetting, BftsmartTopology bftsmartTopology) { +// Runnable task = () -> { +// // todo +// boolean isAdd = this.blockIndex.compareAndSet(currBlockIndex, currBlockIndex + 1); +// if (isAdd) { +// System.out.println(Thread.currentThread().getId() + " leader run isadd = " + isAdd + " timer send commitblock message ! curren time = " + System.currentTimeMillis()); +// sendCommitBlockMessage(ledgerHash, bftsmartConsensusSetting, bftsmartTopology); +// } +// }; +// return task; +// } +// +// } diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java new file mode 100644 index 00000000..170bd4c6 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher.java @@ -0,0 +1,340 @@ +package com.jd.blockchain.peer.consensus; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.service.TransactionBatchProcess; +import com.jd.blockchain.ledger.service.TransactionBatchResultHandle; +import com.jd.blockchain.ledger.service.TransactionEngine; + +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.consensus.service.StateSnapshot; + + +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author huanghaiquan + * + */ +@Component +public class ConsensusMessageDispatcher implements MessageHandle { + + @Autowired + private TransactionEngine txEngine; + + // todo 可能存在内存溢出的问题 + private final Map realmProcessorMap = new ConcurrentHashMap<>(); + + private final ReentrantLock beginLock = new ReentrantLock(); + + @Override + public String beginBatch(String realmName) { + RealmProcessor realmProcessor = realmProcessorMap.get(realmName); + if (realmProcessor == null) { + try { + beginLock.lock(); + realmProcessor = realmProcessorMap.get(realmName); + if (realmProcessor == null) { + realmProcessor = initRealmProcessor(realmName); + realmProcessorMap.put(realmName, realmProcessor); + } + } finally { + beginLock.unlock(); + } + } + return realmProcessor.newBatchId(); + } + + @Override + public AsyncFuture processOrdered(int messageId, byte[] message, String realmName, String batchId) { + // TODO 要求messageId在同一个批次不重复,但目前暂不验证 + RealmProcessor realmProcessor = realmProcessorMap.get(realmName); + if (realmProcessor == null) { + throw new IllegalArgumentException("RealmName is not init!"); + } + if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { + throw new IllegalArgumentException("BatchId is not begin!"); + } + TransactionRequest txRequest = BinaryEncodingUtils.decode(message); + return realmProcessor.schedule(txRequest); + } + + @Override + public StateSnapshot completeBatch(String realmName, String batchId) { + RealmProcessor realmProcessor = realmProcessorMap.get(realmName); + if (realmProcessor == null) { + throw new IllegalArgumentException("RealmName is not init!"); + } + if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { + throw new IllegalArgumentException("BatchId is not begin!"); + } + return realmProcessor.complete(); + } + + @Override + public void commitBatch(String realmName, String batchId) { + RealmProcessor realmProcessor = realmProcessorMap.get(realmName); + if (realmProcessor == null) { + throw new IllegalArgumentException("RealmName is not init!"); + } + if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { + throw new IllegalArgumentException("BatchId is not begin!"); + } + realmProcessor.commit(); +// realmProcessorMap.remove(realmName); + } + + @Override + public void rollbackBatch(String realmName, String batchId, int reasonCode) { + RealmProcessor realmProcessor = realmProcessorMap.get(realmName); + if (realmProcessor == null) { + throw new IllegalArgumentException("RealmName is not init!"); + } + if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { + throw new IllegalArgumentException("BatchId is not begin!"); + } + realmProcessor.rollback(reasonCode); +// realmProcessorMap.remove(realmName); + } + + @Override + public AsyncFuture processUnordered(byte[] message) { + // TODO Auto-generated method stub + throw new IllegalArgumentException("Not implemented!"); + } + + private RealmProcessor initRealmProcessor(String realmName) { + RealmProcessor realmProcessor = new RealmProcessor(); + byte[] hashBytes = Base58Utils.decode(realmName); + HashDigest ledgerHash = new HashDigest(hashBytes); + realmProcessor.realmName = realmName; + realmProcessor.ledgerHash = ledgerHash; + return realmProcessor; + } + + private final class RealmProcessor { + + private final Lock realmLock = new ReentrantLock(); + + private String currBatchId; + + private final ExecutorService txExecutor = Executors.newSingleThreadExecutor(); + + // todo 暂不处理队列溢出导致的OOM问题 + private final ExecutorService asyncBlExecutor = Executors.newSingleThreadExecutor(); + + private Map> txResponseMap; + + private TransactionBatchResultHandle batchResultHandle; + + private final AtomicLong batchIdIndex = new AtomicLong(); + + private LedgerBlock currBlock; + + private TransactionBatchProcess txBatchProcess; + + HashDigest ledgerHash; + + String realmName; + + public String getRealmName() { + return realmName; + } + + public TransactionBatchProcess getTxBatchProcess() { + return txBatchProcess; + } + + public AtomicLong getBatchIdIndex() { + return batchIdIndex; + } + + public HashDigest getLedgerHash() { + return ledgerHash; + } + + public String getCurrBatchId() { + return currBatchId; + } + + public String newBatchId() { + try { + realmLock.lock(); + if (currBatchId == null) { + currBatchId = getRealmName() + "-" + getBatchIdIndex().getAndIncrement(); + } + if (txResponseMap == null) { + txResponseMap = new ConcurrentHashMap<>(); + } + if (txBatchProcess == null) { + txBatchProcess = txEngine.createNextBatch(ledgerHash); + } + } finally { + realmLock.unlock(); + } + return currBatchId; + } + + public AsyncFuture schedule(TransactionRequest txRequest) { + CompletableAsyncFuture asyncTxResult = new CompletableAsyncFuture<>(); + TransactionResponse resp = getTxBatchProcess().schedule(txRequest); + txResponseMap.put(resp, asyncTxResult); +// txExecutor.execute(() -> { +// TransactionResponse resp = getTxBatchProcess().schedule(txRequest); +// txResponseMap.put(resp, asyncTxResult); +// }); + return asyncTxResult; + } + + public StateSnapshot complete() { + batchResultHandle = getTxBatchProcess().prepare(); + currBlock = batchResultHandle.getBlock(); + long blockHeight = currBlock.getHeight(); + HashDigest blockHash = currBlock.getHash(); + asyncBlExecute(new HashMap<>(txResponseMap), blockHeight, blockHash); + BlockStateSnapshot blockStateSnapshot = new BlockStateSnapshot(blockHeight, blockHash); + return blockStateSnapshot; + +// +// +// CompletableAsyncFuture asyncStateSnapshot = new CompletableAsyncFuture<>(); +// txExecutor.execute(() -> { +// batchResultHandle = getTxBatchProcess().prepare(); +// currBlock = batchResultHandle.getBlock(); +// long blockHeight = currBlock.getHeight(); +// HashDigest blockHash = currBlock.getHash(); +// asyncBlExecute(new HashMap<>(txResponseMap), blockHeight, blockHash); +// BlockStateSnapshot blockStateSnapshot = new BlockStateSnapshot(blockHeight, blockHash); +// asyncStateSnapshot.complete(blockStateSnapshot); +// }); +// return asyncStateSnapshot.get(); + } + + public void commit() { + try { + realmLock.lock(); + if (batchResultHandle == null) { + throw new IllegalArgumentException("BatchResultHandle is null, complete() is not execute !"); + } + batchResultHandle.commit(); + currBatchId = null; + txResponseMap = null; + txBatchProcess = null; + } finally { + realmLock.unlock(); + } + } + + public void rollback(int reasonCode) { + try { + realmLock.lock(); + batchResultHandle.cancel(TransactionState.valueOf((byte)reasonCode)); + } finally { + realmLock.unlock(); + } + } + + private void asyncBlExecute(Map> asyncMap, + long blockHeight, HashDigest blockHash) { + asyncBlExecutor.execute(() -> { + // 填充应答结果 + for (Map.Entry> entry : asyncMap.entrySet()) { + CompletableAsyncFuture asyncResult = entry.getValue(); + TxResponse txResponse = new TxResponse(entry.getKey()); + txResponse.setBlockHeight(blockHeight); + txResponse.setBlockHash(blockHash); + asyncResult.complete(BinaryEncodingUtils.encode(txResponse, TransactionResponse.class)); + } + }); + } + + private final class TxResponse implements TransactionResponse { + + private long blockHeight; + + private HashDigest blockHash; + + private TransactionResponse txResp; + + public TxResponse(TransactionResponse txResp) { + this.txResp = txResp; + } + + public void setBlockHeight(long blockHeight) { + this.blockHeight = blockHeight; + } + + public void setBlockHash(HashDigest blockHash) { + this.blockHash = blockHash; + } + + @Override + public HashDigest getContentHash() { + return this.txResp.getContentHash(); + } + + @Override + public TransactionState getExecutionState() { + return this.txResp.getExecutionState(); + } + + @Override + public HashDigest getBlockHash() { + return this.blockHash; + } + + @Override + public long getBlockHeight() { + return this.blockHeight; + } + + @Override + public boolean isSuccess() { + return this.txResp.isSuccess(); + } + } + + private final class BlockStateSnapshot implements StateSnapshot { + + private long id; + + private byte[] snapshotBytes; + + public BlockStateSnapshot(long id, byte[] snapshotBytes) { + this.id = id; + this.snapshotBytes = snapshotBytes; + } + + public BlockStateSnapshot(long id, HashDigest hash) { + this(id, hash.toBytes()); + } + + @Override + public long getId() { + return id; + } + + @Override + public byte[] getSnapshot() { + return snapshotBytes; + } + } + } +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher2.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher2.java new file mode 100644 index 00000000..7cf37806 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusMessageDispatcher2.java @@ -0,0 +1,474 @@ +//package com.jd.blockchain.peer.consensus; +// +//import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +//import com.jd.blockchain.consensus.event.EventEntity; +//import com.jd.blockchain.consensus.event.EventProducer; +//import com.jd.blockchain.consensus.service.MessageHandle; +//import com.jd.blockchain.consensus.service.StateSnapshot; +//import com.jd.blockchain.crypto.hash.HashDigest; +//import com.jd.blockchain.ledger.LedgerBlock; +//import com.jd.blockchain.ledger.TransactionRequest; +//import com.jd.blockchain.ledger.TransactionResponse; +//import com.jd.blockchain.ledger.TransactionState; +//import com.jd.blockchain.ledger.service.TransactionBatchProcess; +//import com.jd.blockchain.ledger.service.TransactionBatchResultHandle; +//import com.jd.blockchain.ledger.service.TransactionEngine; +//import com.lmax.disruptor.BlockingWaitStrategy; +//import com.lmax.disruptor.EventFactory; +//import com.lmax.disruptor.EventHandler; +//import com.lmax.disruptor.RingBuffer; +//import com.lmax.disruptor.dsl.Disruptor; +//import com.lmax.disruptor.dsl.ProducerType; +//import my.utils.codec.Base58Utils; +//import my.utils.concurrent.AsyncFuture; +//import my.utils.concurrent.CompletableAsyncFuture; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Component; +// +//import java.util.Iterator; +//import java.util.LinkedList; +//import java.util.List; +//import java.util.Map; +//import java.util.concurrent.ConcurrentHashMap; +//import java.util.concurrent.atomic.AtomicLong; +//import java.util.concurrent.locks.ReentrantLock; +// +///** +// * @author huanghaiquan +// * +// */ +//@Component +//public class ConsensusMessageDispatcher2 implements MessageHandle { +// +// @Autowired +// private TransactionEngine txEngine; +// +// private Map realmProcessorMap = new ConcurrentHashMap<>(); +// +// private ReentrantLock beginLock = new ReentrantLock(); +// +// @Override +// public String beginBatch(String realmName) { +// RealmProcessor realmProcessor = realmProcessorMap.get(realmName); +// if (realmProcessor == null) { +// try { +// beginLock.lock(); +// realmProcessor = realmProcessorMap.get(realmName); +// if (realmProcessor == null) { +// realmProcessor = initRealmProcessor(realmName); +// realmProcessorMap.put(realmName, realmProcessor); +// } +// } finally { +// beginLock.unlock(); +// } +// } +// return realmProcessor.newBatchId(); +// } +// +// @Override +// public AsyncFuture processOrdered(int messageId, byte[] message, String realmName, String batchId) { +// // TODO 要求messageId在同一个批次不重复,但目前暂不验证 +// RealmProcessor realmProcessor = realmProcessorMap.get(realmName); +// if (realmProcessor == null) { +// throw new IllegalArgumentException("RealmName is not init!"); +// } +// if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { +// throw new IllegalArgumentException("BatchId is not begin!"); +// } +// TransactionRequest txRequest = BinaryEncodingUtils.decode(message); +// return realmProcessor.schedule(txRequest); +// } +// +// @Override +// public StateSnapshot completeBatch(String realmName, String batchId) { +// RealmProcessor realmProcessor = realmProcessorMap.get(realmName); +// if (realmProcessor == null) { +// throw new IllegalArgumentException("RealmName is not init!"); +// } +// if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { +// throw new IllegalArgumentException("BatchId is not begin!"); +// } +// return realmProcessor.complete(); +// } +// +// @Override +// public void commitBatch(String realmName, String batchId) { +// RealmProcessor realmProcessor = realmProcessorMap.get(realmName); +// if (realmProcessor == null) { +// throw new IllegalArgumentException("RealmName is not init!"); +// } +// if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { +// throw new IllegalArgumentException("BatchId is not begin!"); +// } +// realmProcessor.commit(); +// } +// +// @Override +// public void rollbackBatch(String realmName, String batchId, int reasonCode) { +// RealmProcessor realmProcessor = realmProcessorMap.get(realmName); +// if (realmProcessor == null) { +// throw new IllegalArgumentException("RealmName is not init!"); +// } +// if (!realmProcessor.getCurrBatchId().equalsIgnoreCase(batchId)) { +// throw new IllegalArgumentException("BatchId is not begin!"); +// } +// realmProcessor.rollback(reasonCode); +// } +// +// @Override +// public AsyncFuture processUnordered(byte[] message) { +// // TODO Auto-generated method stub +// throw new IllegalArgumentException("Not implemented!"); +// } +// +// private RealmProcessor initRealmProcessor(String realmName) { +// RealmProcessor realmProcessor = new RealmProcessor(); +// byte[] hashBytes = Base58Utils.decode(realmName); +// HashDigest ledgerHash = new HashDigest(hashBytes); +// realmProcessor.realmName = realmName; +// realmProcessor.ledgerHash = ledgerHash; +// return realmProcessor; +// } +// +// private final class RealmProcessor { +// +// private String currBatchId; +// +//// private final ExecutorService txExecutor = Executors.newFixedThreadPool(4); +// +//// private Map> txResponseMap; +// +// private LinkedList txResponseExtensions; +// +// private TransactionBatchResultHandle batchResultHandle; +// +// private final AtomicLong batchIdIndex = new AtomicLong(); +// +//// private LedgerBlock currBlock; +// +// private TransactionBatchProcess txBatchProcess; +// +// private EventProducer eventProducer; +// +// HashDigest ledgerHash; +// +// String realmName; +// +// public RealmProcessor() { +// BlockEventHandler eventHandler = new BlockEventHandler(); +// Disruptor> disruptor = +// new Disruptor<>(new BlockEventFactory(), +// BlockEventFactory.BUFFER_SIZE, r -> { +// return new Thread(r); +// }, ProducerType.SINGLE, new BlockingWaitStrategy()); +// +// disruptor.handleEventsWith(eventHandler); +// disruptor.start(); +// RingBuffer> ringBuffer = disruptor.getRingBuffer(); +// this.eventProducer = new BlockEventProducer(ringBuffer); +// } +// +// public String getRealmName() { +// return realmName; +// } +// +// public TransactionBatchProcess getTxBatchProcess() { +// return txBatchProcess; +// } +// +// public AtomicLong getBatchIdIndex() { +// return batchIdIndex; +// } +// +// public HashDigest getLedgerHash() { +// return ledgerHash; +// } +// +// public String getCurrBatchId() { +// return currBatchId; +// } +// +// public synchronized String newBatchId() { +// if (currBatchId == null) { +// currBatchId = getRealmName() + "-" + getBatchIdIndex().getAndIncrement(); +// } +// if (txBatchProcess == null) { +// txBatchProcess = txEngine.createNextBatch(ledgerHash); +// } +//// if (txResponseMap == null) { +//// txResponseMap = new ConcurrentHashMap<>(); +//// } +// if (txResponseExtensions == null) { +// txResponseExtensions = new LinkedList<>(); +// } +// return currBatchId; +// } +// +// public AsyncFuture schedule(TransactionRequest txRequest) { +// CompletableAsyncFuture asyncTxResult = new CompletableAsyncFuture<>(); +// TransactionResponse resp = getTxBatchProcess().schedule(txRequest); +// TxResponseExtension extension = new TxResponseExtension(resp, asyncTxResult); +// txResponseExtensions.addFirst(extension); +//// txResponseMap.put(resp, asyncTxResult); +//// txExecutor.execute(() -> { +//// if (txBatchProcess == null) { +//// txBatchProcess = txEngine.createNextBatch(ledgerHash); +//// } +//// TransactionResponse resp = getTxBatchProcess().schedule(txRequest); +//// if (txResponseMap == null) { +//// txResponseMap = new ConcurrentHashMap<>(); +//// } +//// txResponseMap.put(resp, asyncTxResult); +//// }); +// return asyncTxResult; +// } +// +// public StateSnapshot complete() { +//// CompletableAsyncFuture asyncStateSnapshot = new CompletableAsyncFuture<>(); +// batchResultHandle = getTxBatchProcess().prepare(); +// LedgerBlock currBlock = batchResultHandle.getBlock(); +//// List extensions = new ArrayList<>(); +//// Collections.copy(extensions, new ArrayList<>(txResponseExtensions)); +// BlockResponse blockResponse = new BlockResponse(currBlock, new LinkedList<>(txResponseExtensions)); +// this.eventProducer.publish(blockResponse); +//// handleResponse(currBlock, new LinkedList<>(txResponseExtensions)); +// txResponseExtensions = null; +// +//// +//// +//// // 填充应答结果 +//// for (Map.Entry> entry : txResponseMap.entrySet()) { +//// CompletableAsyncFuture asyncResult = entry.getValue(); +//// TxResponse txResponse = new TxResponse(entry.getKey()); +//// txResponse.setBlockHeight(currBlock.getHeight()); +//// txResponse.setBlockHash(currBlock.getHash()); +//// asyncResult.complete(BinaryEncodingUtils.encode(txResponse, TransactionResponse.class)); +//// } +// BlockStateSnapshot blockStateSnapshot = new BlockStateSnapshot(currBlock.getHeight(), currBlock.getHash()); +//// asyncStateSnapshot.complete(blockStateSnapshot); +// +//// txExecutor.execute(() -> { +//// batchResultHandle = getTxBatchProcess().prepare(); +//// currBlock = batchResultHandle.getBlock(); +//// // 填充应答结果 +//// for (Map.Entry> entry : txResponseMap.entrySet()) { +//// CompletableAsyncFuture asyncResult = entry.getValue(); +//// TxResponse txResponse = new TxResponse(entry.getKey()); +//// txResponse.setBlockHeight(currBlock.getHeight()); +//// txResponse.setBlockHash(currBlock.getHash()); +//// asyncResult.complete(BinaryEncodingUtils.encode(txResponse, TransactionResponse.class)); +//// } +//// BlockStateSnapshot blockStateSnapshot = new BlockStateSnapshot(currBlock.getHeight(), currBlock.getHash()); +//// asyncStateSnapshot.complete(blockStateSnapshot); +//// }); +//// return asyncStateSnapshot.get(); +// return blockStateSnapshot; +// } +// +// public void commit() { +// if (batchResultHandle == null) { +// throw new IllegalArgumentException("BatchResultHandle is null, complete() is not execute !"); +// } +// batchResultHandle.commit(); +//// txResponseMap = null; +// currBatchId = null; +// txBatchProcess = null; +// } +// +// public void rollback(int reasonCode) { +// batchResultHandle.cancel(TransactionState.valueOf((byte)reasonCode)); +// } +// +//// private void handleResponse(final LedgerBlock block, final List txResponseExtensions) { +//// txExecutor.execute(() -> { +//// Iterator iterator = txResponseExtensions.iterator(); +//// while(iterator.hasNext()){ +//// TxResponseExtension data = iterator.next(); +//// CompletableAsyncFuture asyncResult = data.getAsyncTxResult(); +//// TxResponse txResponse = new TxResponse(data.getResponse()); +//// txResponse.setBlockHeight(block.getHeight()); +//// txResponse.setBlockHash(block.getHash()); +//// asyncResult.complete(BinaryEncodingUtils.encode(txResponse, TransactionResponse.class)); +//// } +//// }); +//// } +// +// private final class TxResponse implements TransactionResponse { +// +// private long blockHeight; +// +// private HashDigest blockHash; +// +// private TransactionResponse txResp; +// +// public TxResponse(TransactionResponse txResp) { +// this.txResp = txResp; +// } +// +// public void setBlockHeight(long blockHeight) { +// this.blockHeight = blockHeight; +// } +// +// public void setBlockHash(HashDigest blockHash) { +// this.blockHash = blockHash; +// } +// +// @Override +// public HashDigest getContentHash() { +// return this.txResp.getContentHash(); +// } +// +// @Override +// public TransactionState getExecutionState() { +// return this.txResp.getExecutionState(); +// } +// +// @Override +// public HashDigest getBlockHash() { +// return this.blockHash; +// } +// +// @Override +// public long getBlockHeight() { +// return this.blockHeight; +// } +// +// @Override +// public boolean isSuccess() { +// return this.txResp.isSuccess(); +// } +// } +// +// private final class TxResponseExtension { +// +// private TransactionResponse response; +// +// private CompletableAsyncFuture asyncTxResult; +// +// public TxResponseExtension(TransactionResponse response, CompletableAsyncFuture asyncTxResult) { +// this.response = response; +// this.asyncTxResult = asyncTxResult; +// } +// +// public TransactionResponse getResponse() { +// return response; +// } +// +// public void setResponse(TransactionResponse response) { +// this.response = response; +// } +// +// public CompletableAsyncFuture getAsyncTxResult() { +// return asyncTxResult; +// } +// +// public void setAsyncTxResult(CompletableAsyncFuture asyncTxResult) { +// this.asyncTxResult = asyncTxResult; +// } +// } +// +// private final class BlockResponse { +// +// private LedgerBlock block; +// +// private LinkedList txResponseExtensions; +// +// public BlockResponse(LedgerBlock block, LinkedList txResponseExtensions) { +// this.block = block; +// this.txResponseExtensions = txResponseExtensions; +// } +// +// public LedgerBlock getBlock() { +// return block; +// } +// +// public void setBlock(LedgerBlock block) { +// this.block = block; +// } +// +// public LinkedList getTxResponseExtensions() { +// return txResponseExtensions; +// } +// +// public void setTxResponseExtensions(LinkedList txResponseExtensions) { +// this.txResponseExtensions = txResponseExtensions; +// } +// } +// +// private final class BlockStateSnapshot implements StateSnapshot { +// +// private long id; +// +// private byte[] snapshotBytes; +// +// public BlockStateSnapshot(long id, byte[] snapshotBytes) { +// this.id = id; +// this.snapshotBytes = snapshotBytes; +// } +// +// public BlockStateSnapshot(long id, HashDigest hash) { +// this(id, hash.toBytes()); +// } +// +// @Override +// public long getId() { +// return id; +// } +// +// @Override +// public byte[] getSnapshot() { +// return snapshotBytes; +// } +// } +// +// private final class BlockEventHandler implements EventHandler> { +// +// @Override +// public void onEvent(EventEntity event, long sequence, boolean endOfBatch) throws Exception { +// BlockResponse blockResponse = event.getEntity(); +// final LedgerBlock block = blockResponse.getBlock(); +// +// final List txResponseExtensions = blockResponse.getTxResponseExtensions(); +// Iterator iterator = txResponseExtensions.iterator(); +// while(iterator.hasNext()){ +// TxResponseExtension data = iterator.next(); +// CompletableAsyncFuture asyncResult = data.getAsyncTxResult(); +// TxResponse txResponse = new TxResponse(data.getResponse()); +// txResponse.setBlockHeight(block.getHeight()); +// txResponse.setBlockHash(block.getHash()); +// asyncResult.complete(BinaryEncodingUtils.encode(txResponse, TransactionResponse.class)); +// } +// } +// } +// +// private class BlockEventFactory implements EventFactory> { +// +// public static final int BUFFER_SIZE = 64 * 1024; +// +// @Override +// public EventEntity newInstance() { +// return new EventEntity<>(); +// } +// } +// +// private class BlockEventProducer implements EventProducer { +// +// private final RingBuffer> ringBuffer; +// +// public BlockEventProducer(RingBuffer> ringBuffer) { +// this.ringBuffer = ringBuffer; +// } +// +// @Override +// public void publish(BlockResponse entity) { +// long sequence = ringBuffer.next(); +// try { +// EventEntity event = ringBuffer.get(sequence); +// event.setEntity(entity); +// } finally { +// this.ringBuffer.publish(sequence); +// } +// } +// } +// } +//} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusRealmImpl.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusRealmImpl.java new file mode 100644 index 00000000..108365a6 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusRealmImpl.java @@ -0,0 +1,97 @@ +package com.jd.blockchain.peer.consensus; + +import java.util.Arrays; + +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.peer.ConsensusRealm; +import com.jd.blockchain.utils.Bytes; + +public class ConsensusRealmImpl implements ConsensusRealm { + + private ParticipantNode[] nodes; + + private Bytes setting; + + private int hashCode; + + public ConsensusRealmImpl(ParticipantNode[] nodeList) { + this.nodes = nodeList; + String[] addrs = new String[nodes.length]; + int i = 0; + for (ParticipantNode n : nodes) { + addrs[i++] = n.getAddress(); + } + this.hashCode = Arrays.hashCode(addrs); + } + + /* + * (non-Javadoc) + * + * @see + * com.jd.blockchain.peer.consensus.ConsensusRealm#getConsensusParticipants() + */ + @Override + public ParticipantNode[] getNodes() { + return Arrays.copyOf(nodes, nodes.length); + // return participantNodes.toArray(new + // ConsensusParticipant[participantNodes.size()]); + } + + // public void addNode(ConsensusNode participantNode) { + // participantNodes.add(participantNode); + // } + + @Override + public Bytes getSetting() { + return setting; + } + + public void setSetting(Bytes setting) { + this.setting = setting; + } + + /* + * (non-Javadoc) + * + * @see + * com.jd.blockchain.peer.consensus.ConsensusRealm#hasCommon(com.jd.blockchain. + * peer.consensus.ConsensusRealm) + */ + @Override + public boolean hasIntersection(ConsensusRealm otherRealm) { + // in case: different ledger ,same consensus realm + if (this.equals(otherRealm)) { + return true; + } + // in case: consensus realm has intersection + ParticipantNode[] otherNodes = otherRealm.getNodes(); + for (ParticipantNode node : nodes) { + for (ParticipantNode other : otherNodes) { + if (node.getAddress().equals(other.getAddress())) { + return true; + } + } + } + return false; + } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof ConsensusRealmImpl) { + ConsensusRealmImpl realm = (ConsensusRealmImpl) obj; + return this.hashCode() == realm.hashCode(); + } + return false; + } +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusViewDefinition.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusViewDefinition.java new file mode 100644 index 00000000..a56e5ed4 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/ConsensusViewDefinition.java @@ -0,0 +1,93 @@ +package com.jd.blockchain.peer.consensus; + +import java.io.Serializable; +import java.util.ArrayList; + +import com.jd.blockchain.utils.net.NetworkAddress; + +public class ConsensusViewDefinition implements Serializable { + + private static final long serialVersionUID = -201642288565436003L; + + private static int[] EMPTY_IDS = {}; + + private ArrayList nodes = new ArrayList<>(); + + public int getF() { + return (int) ((getNodeCount() - 1) / 3); + } + + /** + * 法定的数量; + * + * @return + */ + public int getN() { + return 3 * getF() + 1; + } + + /** + * 实际的节点数量; + * + * @return + */ + public int getNodeCount() { + return nodes.size(); + } + + /** + * 返回法定数量的初始节点的 ID 列表; + * + * @return + */ + public int[] getQuorumIDs() { + if (getNodeCount() == 0) { + return EMPTY_IDS; + } + int[] ids = new int[getN()]; + for (int i = 0; i < ids.length; i++) { + ids[i] = nodes.get(i).getId(); + } + return ids; + } + + /** + * 加入节点信息; + * + * @param networkAddress + * @return 返回分配给新节点的 ID (ID >= 0);如果节点数量以达到 (3f+1) 的数量; + */ + public synchronized int addNode(NetworkAddress networkAddress) { + for (NodeInfo ninf : nodes) { + if (ninf.getNetworkAddress().equals(networkAddress)) { + throw new IllegalArgumentException("Add node[" + networkAddress.toString() + "] reaptly!"); + } + } + NodeInfo nodeInfo = new NodeInfo(nodes.size(), networkAddress); + nodes.add(nodeInfo); + return nodeInfo.getId(); + } + + public static class NodeInfo implements Serializable { + + private static final long serialVersionUID = -9178639061945239622L; + + private int id; + + private NetworkAddress networkAddress; + + public NodeInfo(int id, NetworkAddress networkAddress) { + this.id = id; + this.networkAddress = networkAddress; + } + + public int getId() { + return id; + } + + public NetworkAddress getNetworkAddress() { + return networkAddress; + } + } + +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/consensus/LedgerStateManager.java b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/LedgerStateManager.java new file mode 100644 index 00000000..b88fc49d --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/consensus/LedgerStateManager.java @@ -0,0 +1,43 @@ +package com.jd.blockchain.peer.consensus; + +import java.io.InputStream; +import java.util.Iterator; + +import com.jd.blockchain.consensus.service.StateMachineReplicate; +import com.jd.blockchain.consensus.service.StateSnapshot; +import org.springframework.stereotype.Component; + +@Component +public class LedgerStateManager implements StateMachineReplicate{ + + @Override + public long getLatestStateID(String realmName) { + // TODO Auto-generated method stub + return 0; + } + + @Override + public StateSnapshot getSnapshot(String realmName, long stateId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Iterator getSnapshots(String realmName, long fromStateId, long toStateId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public InputStream readState(String realmName, long stateId) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setupState(String realmName, StateSnapshot snapshot, InputStream state) { + // TODO Auto-generated method stub + + } + +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/ledger/LedgerConfigurer.java b/source/peer/src/main/java/com/jd/blockchain/peer/ledger/LedgerConfigurer.java new file mode 100644 index 00000000..289e16cf --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/ledger/LedgerConfigurer.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.peer.ledger; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.ledger.core.impl.OperationHandleRegisteration; +import com.jd.blockchain.ledger.core.impl.TransactionEngineImpl; +import com.jd.blockchain.ledger.service.TransactionEngine; + +@Configuration +public class LedgerConfigurer { + + @Bean + public LedgerManager ledgerManager() { + return new LedgerManager(); + } + + @Bean + public TransactionEngine transactionEngine() { + return new TransactionEngineImpl(); + } + + @Bean + public OperationHandleRegisteration operationHandleRegisteration() { + return new DefaultOperationHandleRegisteration(); + } +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/LedgerQueryController.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/LedgerQueryController.java new file mode 100644 index 00000000..5b0e764a --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/LedgerQueryController.java @@ -0,0 +1,420 @@ +package com.jd.blockchain.peer.web; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.ledger.core.impl.LedgerQueryService; +import com.jd.blockchain.sdk.BlockchainQueryService; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.QueryUtil; +import com.jd.blockchain.utils.ValueType; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping(path = "/") +public class LedgerQueryController implements BlockchainQueryService { + + @Autowired + private LedgerService ledgerService; + + @RequestMapping(method = RequestMethod.GET, path = "ledgers") + @Override + public HashDigest[] getLedgerHashs() { + return ledgerService.getLedgerHashs(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}") + @Override + public LedgerInfo getLedger(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + //TODO: 需要配置返回值的 spring MsgQueueMessageDispatcher ,对返回对象仅仅序列化声明的返回值类型的属性,而不是整个对象本身; + LedgerInfo ledgerInfo = new LedgerInfo(); + ledgerInfo.setHash(ledgerHash); + ledgerInfo.setLatestBlockHash(ledger.getLatestBlockHash()); + ledgerInfo.setLatestBlockHeight(ledger.getLatestBlockHeight()); + return ledgerInfo; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/participants") + @Override + public ParticipantNode[] getConsensusParticipants(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerAdministration ledgerAdministration = ledger.getAdminInfo(); + long participantCount = ledgerAdministration.getParticipantCount(); + if (participantCount <= 0) { + return null; + } + ParticipantNode[] participantNodes = ledgerAdministration.getParticipants(); + // 重新封装,处理Proxy的问题 + if (participantNodes != null && participantNodes.length > 0) { + ParticipantNode[] convertNodes = new ParticipantNode[participantNodes.length]; + for (int i = 0, length = participantNodes.length; i < length; i++) { + convertNodes[i] = new ParticipantCertData(participantNodes[i]); + } + return convertNodes; + } + return null; + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}") + @Override + public LedgerBlock getBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + //TODO: 需要配置返回值的 spring MsgQueueMessageDispatcher ,对返回对象仅仅序列化声明的返回值类型的属性,而不是整个对象本身; + return ledger.getBlock(blockHeight); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}") + @Override + public LedgerBlock getBlock(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + //TODO: 需要配置返回值的 spring MsgQueueMessageDispatcher ,对返回对象仅仅序列化声明的返回值类型的属性,而不是整个对象本身; + return ledger.getBlock(blockHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/count") + @Override + public long getTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHeight); + TransactionSet txSet = ledger.getTransactionSet(block); + return txSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/count") + @Override + public long getTransactionCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + TransactionSet txSet = ledger.getTransactionSet(block); + return txSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/count") + @Override + public long getTransactionTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + TransactionSet txSet = ledger.getTransactionSet(block); + return txSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/count") + @Override + public long getDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/count") + @Override + public long getDataAccountCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/count") + @Override + public long getDataAccountTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/count") + @Override + public long getUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/count") + @Override + public long getUserCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/count") + @Override + public long getUserTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/count") + @Override + public long getContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long height) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(height); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/count") + @Override + public long getContractCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getBlock(blockHash); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/count") + @Override + public long getContractTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs") + @Override + public LedgerTransaction[] getTransactions(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHeight") long blockHeight, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock ledgerBlock = ledger.getBlock(blockHeight); + TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); + int lastHeightTxTotalNums = 0; + + if (blockHeight > 0) { + lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(blockHeight - 1)).getTotalCount(); + } + + int currentHeightTxTotalNums = (int)ledger.getTransactionSet(ledger.getBlock(blockHeight)).getTotalCount(); + //取当前高度的增量交易数,在增量交易里进行查找 + int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; + +// if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { +// fromIndex = 0; +// } +// if (count == -1) { +// fromIndex = 0; +// count = currentHeightTxNums; +// } +// if (count > currentHeightTxNums) { +// count = currentHeightTxNums - fromIndex; +// } + int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); + return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0] , indexAndCount[1]); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs") + @Override + public LedgerTransaction[] getTransactions(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "blockHash") HashDigest blockHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock ledgerBlock = ledger.getBlock(blockHash); + long height = ledgerBlock.getHeight(); + TransactionSet transactionSet = ledger.getTransactionSet(ledgerBlock); + int lastHeightTxTotalNums = 0; + + if (height > 0) { + lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); + } + + int currentHeightTxTotalNums = (int)ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); + //取当前块hash的增量交易数,在增量交易里进行查找 + int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; + +// if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { +// fromIndex = 0; +// } +// if (count == -1) { +// fromIndex = 0; +// count = currentHeightTxNums; +// } +// if (count > currentHeightTxNums) { +// count = currentHeightTxNums - fromIndex; +// } + int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); + return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0] , indexAndCount[1]); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/{contentHash}") + @Override + public LedgerTransaction getTransactionByContentHash(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "contentHash")HashDigest contentHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.get(contentHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/txs/state/{contentHash}") + @Override + public TransactionState getTransactionStateByContentHash(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "contentHash") HashDigest contentHash) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + TransactionSet txset = ledger.getTransactionSet(block); + return txset.getTxState(contentHash); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users/address/{address}") + @Override + public UserInfo getUser(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + return userAccountSet.getUser(address); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}") + @Override + public AccountHeader getDataAccount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + return dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + } + + @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, path = "ledgers/{ledgerHash}/accounts/{address}/entries") + @Override + public KVDataEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address, + @RequestParam("keys") String... keys) { + if (keys == null || keys.length == 0) { + return null; + } + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + + KVDataEntry[] entries = new KVDataEntry[keys.length]; + long ver; + for (int i = 0; i < entries.length; i++) { + ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); + if (ver < 0) { + entries[i] = new KVDataObject(keys[i], -1, ValueType.NIL, null); + }else { + byte[] value = dataAccount.getBytes(Bytes.fromString(keys[i]), ver); + BytesValue decodeData = BinaryEncodingUtils.decode(value); + entries[i] = new KVDataObject(keys[i], ver, ValueType.valueOf(decodeData.getType().CODE), decodeData.getValue().toBytes()); + } + } + + return entries; + } + + @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries") + @Override + public KVDataEntry[] getDataEntries(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + + return dataAccount.getDataEntries(fromIndex, count); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries/count") + @Override + public long getDataEntriesTotalCount(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + DataAccount dataAccount = dataAccountSet.getDataAccount(Bytes.fromBase58(address)); + + return dataAccount.getDataEntriesTotalCount(); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts/address/{address}") + @Override + public AccountHeader getContract(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @PathVariable(name = "address") String address) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + return contractAccountSet.getContract(Bytes.fromBase58(address)); + } + + /** + * get more users by fromIndex and count; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/users") + @Override + public AccountHeader[] getUsers(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + UserAccountSet userAccountSet = ledger.getUserAccountSet(block); + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)userAccountSet.getTotalCount()); + return userAccountSet.getAccounts(pages[0],pages[1]); + } + + /** + * get more dataAccounts by fromIndex and count; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/accounts") + @Override + public AccountHeader[] getDataAccounts(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + DataAccountSet dataAccountSet = ledger.getDataAccountSet(block); + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)dataAccountSet.getTotalCount()); + return dataAccountSet.getAccounts(pages[0],pages[1]); + } + + @RequestMapping(method = RequestMethod.GET, path = "ledgers/{ledgerHash}/contracts") + @Override + public AccountHeader[] getContractAccounts(@PathVariable(name = "ledgerHash") HashDigest ledgerHash, + @RequestParam(name = "fromIndex", required = false, defaultValue = "0") int fromIndex, + @RequestParam(name = "count", required = false, defaultValue = "-1") int count) { + LedgerRepository ledger = ledgerService.getLedger(ledgerHash); + LedgerBlock block = ledger.getLatestBlock(); + ContractAccountSet contractAccountSet = ledger.getContractAccountSet(block); + int pages[] = QueryUtil.calFromIndexAndCount(fromIndex,count,(int)contractAccountSet.getTotalCount()); + return contractAccountSet.getAccounts(pages[0],pages[1]); + } + +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/ManagementController.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/ManagementController.java new file mode 100644 index 00000000..9654d66d --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/ManagementController.java @@ -0,0 +1,419 @@ +package com.jd.blockchain.peer.web; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import javax.annotation.PostConstruct; +import javax.annotation.PreDestroy; + +import com.jd.blockchain.ledger.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ClientIdentification; +import com.jd.blockchain.consensus.ClientIdentifications; +import com.jd.blockchain.consensus.ClientIncomingSettings; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.NodeSettings; +import com.jd.blockchain.consensus.action.ActionResponse; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; +import com.jd.blockchain.consensus.mq.server.MsgQueueMessageDispatcher; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.consensus.service.ServerSettings; +import com.jd.blockchain.consensus.service.StateMachineReplicate; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.LedgerAdminAccount; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.manage.GatewayIncomingSetting; +import com.jd.blockchain.manage.LedgerIncomingSetting; +import com.jd.blockchain.peer.ConsensusRealm; +import com.jd.blockchain.peer.LedgerBindingConfigAware; +import com.jd.blockchain.peer.PeerManage; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.web.converters.BinaryMessageConverter; + +/** + * 网关管理服务; + * + * 提供 + * + * @author huanghaiquan + * + */ +@RestController +@RequestMapping(path = "/management") +public class ManagementController implements LedgerBindingConfigAware, PeerManage { + + private static Logger LOGGER = LoggerFactory.getLogger(ManagementController.class); + + public static final String GATEWAY_PUB_EXT_NAME = ".gw.pub"; + + public static final int MIN_GATEWAY_ID = 10000; + + // @Autowired + // private PeerSettings peerSetting; + +// @Autowired +// private ConsensusTransactionService consensusService; + + // private ConsensusPeer consensusReplica; + + @Autowired + private LedgerManage ledgerManager; + + @Autowired + private DbConnectionFactory connFactory; + + // private Map ledgerConns = new + // ConcurrentHashMap<>(); + + private Map ledgerTxConverters = new ConcurrentHashMap<>(); + + private Map ledgerPeers = new ConcurrentHashMap<>(); + private Map ledgerCryptoSettings = new ConcurrentHashMap<>(); + + // private Map nodeRealms = new + // ConcurrentHashMap<>(); + + // private Map ledgerRealms = new + // ConcurrentHashMap<>(); + // private Map ledgerRealmsNoConflict = new + // ConcurrentHashMap<>(); + + private LedgerBindingConfig config; + + @Autowired + private MessageHandle consensusMessageHandler; + + @Autowired + private StateMachineReplicate consensusStateManager; + + // private static int step = 0; + // private static int temp = 0; + + static { + DataContractRegistry.register(LedgerInitOperation.class); + DataContractRegistry.register(LedgerBlock.class); + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(ContractCodeDeployOperation.class); + DataContractRegistry.register(ContractEventSendOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + + DataContractRegistry.register(ActionResponse.class); + + DataContractRegistry.register(BftsmartConsensusSettings.class); + DataContractRegistry.register(BftsmartNodeSettings.class); + + } + + @PostConstruct + private void init() { + + } + + @PreDestroy + private void destroy() { +// DbConnection[] conns = ledgerConns.values().toArray(new DbConnection[ledgerConns.size()]); +// ledgerConns.clear(); +// for (DbConnection conn : conns) { +// try { +// conn.close(); +// } catch (Exception e) { +// // Ignore; +// } +// } + } + + /** + * 接入认证; + * + * @param clientIdentifications + * @return + */ + @RequestMapping(path = "/gateway/auth", method = RequestMethod.POST, consumes = BinaryMessageConverter.CONTENT_TYPE_VALUE) + public GatewayIncomingSetting authenticateGateway(@RequestBody ClientIdentifications clientIdentifications) { + // 去掉不严谨的网关注册和认证逻辑;暂时先放开,不做认证,后续应该在链上注册网关信息,并基于链上的网关信息进行认证; + // by: huanghaiquan; at 2018-09-11 18:34; + // TODO: 实现网关的链上注册与认证机制; + // TODO: 暂时先返回全部账本对应的共识网络配置信息;以账本哈希为 key 标识每一个账本对应的共识域、以及共识配置参数; + if (ledgerPeers.size() == 0 || clientIdentifications == null) { + return null; + } + + ClientIdentification[] identificationArray = clientIdentifications.getClientIdentifications(); + if (identificationArray == null || identificationArray.length <= 0) { + return null; + } + + GatewayIncomingSetting setting = new GatewayIncomingSetting(); + List ledgerIncomingList = new ArrayList(); + + for (HashDigest ledgerHash : ledgerPeers.keySet()) { + + NodeServer peer = ledgerPeers.get(ledgerHash); + + String peerProviderName = peer.getProviderName(); + + ConsensusProvider provider = ConsensusProviders.getProvider(peer.getProviderName()); + + ClientIncomingSettings clientIncomingSettings = null; + for (ClientIdentification authId : identificationArray) { + if (authId.getProviderName() == null || + authId.getProviderName().length() <= 0 || + !authId.getProviderName().equalsIgnoreCase(peerProviderName)) { + continue; + } + try { + clientIncomingSettings = peer.getManageService().authClientIncoming(authId); + break; + } catch (Exception e) { + throw new AuthenticationServiceException(e.getMessage(), e); + } + } + if (clientIncomingSettings == null) { + continue; + } + + byte[] clientIncomingBytes = provider.getSettingsFactory().getIncomingSettingsEncoder() + .encode(clientIncomingSettings); + String base64ClientIncomingSettings = ByteArray.toBase64(clientIncomingBytes); + + LedgerIncomingSetting ledgerIncomingSetting = new LedgerIncomingSetting(); + ledgerIncomingSetting.setLedgerHash(ledgerHash); + ledgerIncomingSetting.setCryptoSetting(ledgerCryptoSettings.get(ledgerHash)); + ledgerIncomingSetting.setClientSetting(base64ClientIncomingSettings); + ledgerIncomingSetting.setProviderName(peerProviderName); + + ledgerIncomingList.add(ledgerIncomingSetting); + + } + setting.setLedgers(ledgerIncomingList.toArray(new LedgerIncomingSetting[ledgerIncomingList.size()])); + return setting; + } + + @Override + public void setConfig(LedgerBindingConfig config) { + // TODO 更新配置;暂时不考虑变化过程的平滑切换问题,后续完善该流程; + // 1、检查账本的数据库配置;a、配置发生变化的账本,建立新的账本库(LedgerRepository)替换旧的实例;b、加入新增加的账本库实例;c、移除已经废弃的账本库; + // 2、完成账本库更改后,读取最新的共识配置信息,更新共识域; + // 3、基于当前共识地址检查共识域;a、启动新增加的共识地址,以及更新相应的共识域关系;c、已经废弃的共识域直接停止; + try { + // remove all existing ledger repositories; + HashDigest[] existingLedgerHashs = ledgerManager.getLedgerHashs(); + for (HashDigest lh : existingLedgerHashs) { + ledgerManager.unregister(lh); + } + HashDigest[] ledgerHashs = config.getLedgerHashs(); + for (HashDigest ledgerHash : ledgerHashs) { + setConfig(config,ledgerHash); +// LedgerBindingConfig.BindingConfig bindingConfig = config.getLedger(ledgerHash); +// DbConnection dbConnNew = connFactory.connect(bindingConfig.getDbConnection().getUri(), +// bindingConfig.getDbConnection().getPassword()); +// LedgerRepository ledgerRepository = ledgerManager.register(ledgerHash, dbConnNew.getStorageService()); +// +// // load provider; +// LedgerAdminAccount ledgerAdminAccount = ledgerRepository.getAdminAccount(); +// String consensusProvider = ledgerAdminAccount.getSetting().getConsensusProvider(); +// ConsensusProvider provider = ConsensusProviders.getProvider(consensusProvider); +// // find current node; +// Bytes csSettingBytes = ledgerAdminAccount.getSetting().getConsensusSetting(); +// ConsensusSettings csSettings = provider.getSettingsFactory().getConsensusSettingsEncoder() +// .decode(csSettingBytes.toBytes()); +// NodeSettings currentNode = null; +// for (NodeSettings nodeSettings : csSettings.getNodes()) { +// if (nodeSettings.getAddress().equals(bindingConfig.getParticipant().getAddress())) { +// currentNode = nodeSettings; +// } +// } +// if (currentNode == null) { +// throw new IllegalArgumentException( +// "Current node is not found from the consensus settings of ledger[" + ledgerHash.toBase58() +// + "]!"); +// } +// ServerSettings serverSettings = provider.getServerFactory().buildServerSettings(ledgerHash.toBase58(), csSettings, currentNode.getAddress()); +// +// NodeServer server = provider.getServerFactory().setupServer(serverSettings, consensusMessageHandler, +// consensusStateManager); +// ledgerPeers.put(ledgerHash, server); +// ledgerCryptoSettings.put(ledgerHash, ledgerAdminAccount.getSetting().getCryptoSetting()); + + } + + // remove duplicate consensus realm,and establish consensus peer and consensus + // realm corresponding relationship + // initBindingConfig(config); + this.config = config; + + } catch (Exception e) { + LOGGER.error("Error occurred on configing LedgerBindingConfig! --" + e.getMessage(), e); + throw new IllegalStateException(e); + } + } + + @Override + public NodeServer setConfig(LedgerBindingConfig config, HashDigest ledgerHash) { + LedgerBindingConfig.BindingConfig bindingConfig = config.getLedger(ledgerHash); + DbConnection dbConnNew = connFactory.connect(bindingConfig.getDbConnection().getUri(), + bindingConfig.getDbConnection().getPassword()); + LedgerRepository ledgerRepository = ledgerManager.register(ledgerHash, dbConnNew.getStorageService()); + + // load provider; + LedgerAdminAccount ledgerAdminAccount = ledgerRepository.getAdminAccount(); + String consensusProvider = ledgerAdminAccount.getSetting().getConsensusProvider(); + ConsensusProvider provider = ConsensusProviders.getProvider(consensusProvider); + // find current node; + Bytes csSettingBytes = ledgerAdminAccount.getSetting().getConsensusSetting(); + ConsensusSettings csSettings = provider.getSettingsFactory().getConsensusSettingsEncoder() + .decode(csSettingBytes.toBytes()); + NodeSettings currentNode = null; + for (NodeSettings nodeSettings : csSettings.getNodes()) { + if (nodeSettings.getAddress().equals(bindingConfig.getParticipant().getAddress())) { + currentNode = nodeSettings; + } + } + if (currentNode == null) { + throw new IllegalArgumentException( + "Current node is not found from the consensus settings of ledger[" + ledgerHash.toBase58() + + "]!"); + } + ServerSettings serverSettings = provider.getServerFactory().buildServerSettings(ledgerHash.toBase58(), csSettings, currentNode.getAddress()); + + NodeServer server = provider.getServerFactory().setupServer(serverSettings, consensusMessageHandler, + consensusStateManager); + ledgerPeers.put(ledgerHash, server); + ledgerCryptoSettings.put(ledgerHash, ledgerAdminAccount.getSetting().getCryptoSetting()); + + return server; + } + + // private void initBindingConfig(LedgerBindingConfig config) { + // boolean intersection = false; + // // to remove intersection consensus realm + // for (HashDigest hashDigest : ledgerRealms.keySet()) { + // ConsensusRealm consensusRealm1i = ledgerRealms.get(hashDigest); + // for (ConsensusRealm consensusRealm1j : ledgerRealms.values()) { + // // avoid compare with myself + // if (consensusRealm1i.equals(consensusRealm1j)) { + // continue; + // } + // if (consensusRealm1i.hasIntersection(consensusRealm1j)) { + // intersection = true; + // break; + // } + // } + // // prompt consensus realm conflict info + // if (intersection == true) { + // ConsoleUtils.info("\r\nconsensus realm intersection with other consensus + // realm\r\n"); + // continue; + // } + // if (intersection == false) { + // // add consensus realm without conflict to ledgerRealmsNoConflict + // ledgerRealmsNoConflict.put(hashDigest, consensusRealm1i); + // + // // String consensusSystemFile = + // config.getLedger(hashDigest).getCsConfigFile(); + // int currentId = config.getLedger(hashDigest).getParticipant().getId(); + // // init consensusSystemConfig; + // ConsensusProperties csProps = + // ConsensusProperties.resolve(consensusRealm1i.getSetting()); + // ConsensusPeer consensusPeer = new ConsensusPeer(consensusRealm1i, currentId, + // consensusService, + // csProps.getProperties()); + // ledgerPeers.put(hashDigest, consensusPeer); + // } + // } // END OF FOR:get ledgerRealmsNoConflict and ledgerPeers + // + // } + + @Override + public ConsensusRealm[] getRealms() { + throw new IllegalStateException("Not implemented!"); + } + + @Override + public void runAllRealms() { + for (NodeServer peer : ledgerPeers.values()) { + runRealm(peer); + } + // try { + // + // // for (ConsensusPeer peer : ledgerPeers.values()) { + // for (Map.Entry entry : ledgerPeers.entrySet()) { + // HashDigest ledgerHash = entry.getKey(); + // ConsensusPeer peer = entry.getValue(); + // // TODO: 多线程启动; + // ConsensusNode[] nodes = peer.getConsensusRealm().getNodes(); + // StringBuilder consensusInfo = new StringBuilder(); + // for (ConsensusNode node : nodes) { + // consensusInfo.append( + // String.format("[%s]-%s; ", node.getAddress(), + // node.getConsensusAddress().toString())); + // } + // LOGGER.debug(String.format("-------- start consensus peer[Id=%s] --Nodes=%s + // -------------", + // peer.getCurrentId(), consensusInfo.toString())); + // peer.start(); + // // 设置消息队列 + // MsgQueueMessageDispatcher messageDispatcher = ledgerTxConverters.get(ledgerHash); + // + // if (messageDispatcher == null) { + // LedgerBindingConfig.BindingConfig bindingConfig = + // this.config.getLedger(ledgerHash); + // MQConnectionConfig mqConnection = bindingConfig.getMqConnection(); + // if (mqConnection != null && mqConnection.getServer() != null) { + // MessageQueueConfig mqConfig = new + // MessageQueueConfig(mqConnection.getServer(), + // mqConnection.getTopic()); + // messageDispatcher = MessageDispatcherFactory.newInstance(mqConfig, peer); + // Executors.newSingleThreadExecutor().execute(messageDispatcher); // 启动监听 + // } + // } + // } + // } catch (Exception e) { + // LOGGER.error("Error occurred on starting all consensus realms! --" + + // e.getMessage(), e); + // throw new IllegalStateException(e.getMessage(), e); + // } + } + + @Override + public void runRealm(NodeServer nodeServer) { + nodeServer.start(); + } + + @Override + public void closeAllRealms() { + for (NodeServer peer : ledgerPeers.values()) { + peer.stop(); + } + } +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerGlobalExceptionHandler.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerGlobalExceptionHandler.java new file mode 100644 index 00000000..eaad2b55 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerGlobalExceptionHandler.java @@ -0,0 +1,48 @@ +package com.jd.blockchain.peer.web; + +import javax.servlet.http.HttpServletRequest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; + +import com.alibaba.fastjson.JSONException; +import com.jd.blockchain.utils.BusinessException; +import com.jd.blockchain.utils.web.model.ErrorCode; +import com.jd.blockchain.utils.web.model.WebResponse; +import com.jd.blockchain.utils.web.model.WebResponse.ErrorMessage; + +/** + * 全局异常处理类 + */ +@ControllerAdvice +public class PeerGlobalExceptionHandler { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + @ExceptionHandler(value = Exception.class) + @ResponseBody + public WebResponse json(HttpServletRequest req, Exception ex) { + String reqURL = req.getRequestURL().insert(0, "[" + req.getMethod() + "] ").toString(); + ErrorMessage message = null; + if (ex instanceof BusinessException) { + logger.error("BusinessException occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() + "] " + + ex.getMessage(), ex); + BusinessException businessException = (BusinessException) ex; + message = new ErrorMessage(businessException.getErrorCode(), businessException.getMessage()); + } else if (ex instanceof JSONException) { + logger.error("JSONException occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() + "] " + + ex.getMessage(), ex); + message = new ErrorMessage(ErrorCode.REQUEST_PARAM_FORMAT_ILLEGAL.getValue(), + ErrorCode.REQUEST_PARAM_FORMAT_ILLEGAL.getDescription()); + } else { + logger.error("Unexpected exception occurred! --[RequestURL=" + reqURL + "][" + ex.getClass().toString() + + "]" + ex.getMessage(), ex); + message = new ErrorMessage(ErrorCode.UNEXPECTED.getValue(), ErrorCode.UNEXPECTED.getDescription(ex.getMessage())); + } + WebResponse responseResult = WebResponse.createFailureResult(message); + return responseResult; + } + +} \ No newline at end of file diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerJsonResponseAdvice.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerJsonResponseAdvice.java new file mode 100644 index 00000000..cc663aa1 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerJsonResponseAdvice.java @@ -0,0 +1,61 @@ +package com.jd.blockchain.peer.web; + +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.StringHttpMessageConverter; +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; + +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import com.jd.blockchain.utils.web.model.JsonWebResponseMessageConverter; +import com.jd.blockchain.utils.web.model.WebResponse; + +@RestControllerAdvice +public class PeerJsonResponseAdvice implements ResponseBodyAdvice { + + @Override + public boolean supports(MethodParameter returnType, Class> converterType) { + if (StringHttpMessageConverter.class == converterType && returnType.getDeclaringClass().getName().startsWith("com.jd")) { + return true; + } + if (JsonWebResponseMessageConverter.class == converterType && returnType.getDeclaringClass().getName().startsWith("com.jd")) { + return true; + } + if (JsonWebResponseMessageConverter.class == converterType && returnType.getDeclaringClass().getName().startsWith("com.jd")) { + return true; + } + if (MappingJackson2HttpMessageConverter.class == converterType + && returnType.getDeclaringClass().getName().startsWith("com.jd")) { + return true; + } + return false; + } + + @Override + public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, + Class> selectedConverterType, ServerHttpRequest request, + ServerHttpResponse response) { + WebResponse result = null; + if (body == null) { + result = WebResponse.createSuccessResult(null); + } + if (body instanceof ResponseEntity) { + return body; + } + // 把返回结果自动转换为 WebResponse; + if (body instanceof WebResponse) { + return body; + } + result = WebResponse.createSuccessResult(body); + if (String.class == returnType.getMethod().getReturnType()) { + return JSONSerializeUtils.serializeToJSON(result); + } + return result; + } + +} \ No newline at end of file diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerTimeTasks.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerTimeTasks.java new file mode 100644 index 00000000..26f2d593 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerTimeTasks.java @@ -0,0 +1,118 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.peer.web.ScheduledTasks + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/7 上午11:12 + * Description: + */ +package com.jd.blockchain.peer.web; + +import com.jd.blockchain.consensus.service.NodeServer; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.peer.ConsensusManage; +import com.jd.blockchain.peer.LedgerBindingConfigAware; +import com.jd.blockchain.peer.PeerServerBooter; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.utils.ArgumentSet; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.io.ClassPathResource; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.InputStream; +import java.util.*; + +/** + * + * @author shaozhuguang + * @create 2019/1/7 + * @since 1.0.0 + */ +@Component +@EnableScheduling +public class PeerTimeTasks implements ApplicationContextAware { + + private ApplicationContext applicationContext; + + @Autowired + private LedgerManage ledgerManager; + + private String ledgerBindConfigFile; + + //每1分钟执行一次 + @Scheduled(cron = "0 */5 * * * * ") + public void updateLedger(){ + System.out.println ("Update Ledger Tasks Start " + new Date()); + try { + LedgerBindingConfig ledgerBindingConfig = loadLedgerBindingConfig(); + + HashDigest[] totalLedgerHashs = ledgerBindingConfig.getLedgerHashs(); + + HashDigest[] existingLedgerHashs = ledgerManager.getLedgerHashs(); + + Set newAddHashs = new HashSet<>(); + + for (HashDigest ledgerHash : totalLedgerHashs) { + boolean isExist = false; + for (HashDigest exist : existingLedgerHashs) { + if (ledgerHash.equals(exist)) { + isExist = true; + break; + } + } + if (!isExist) { + newAddHashs.add(ledgerHash); + } + } + if (!newAddHashs.isEmpty()) { + // 建立共识网络; + Map bindingConfigAwares = applicationContext.getBeansOfType(LedgerBindingConfigAware.class); + List nodeServers = new ArrayList<>(); + for (HashDigest ledgerHash : newAddHashs) { + System.out.printf("newLedger[%s] \r\n", ledgerHash.toBase58()); + for (LedgerBindingConfigAware aware : bindingConfigAwares.values()) { + nodeServers.add(aware.setConfig(ledgerBindingConfig, ledgerHash)); + } + } + // 启动指定NodeServer节点 + ConsensusManage consensusManage = applicationContext.getBean(ConsensusManage.class); + for (NodeServer nodeServer : nodeServers) { + consensusManage.runRealm(nodeServer); + } + } else { + System.out.println("All Ledgers is newest!!!"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + private LedgerBindingConfig loadLedgerBindingConfig() throws Exception { + LedgerBindingConfig ledgerBindingConfig; + ledgerBindConfigFile = PeerServerBooter.ledgerBindConfigFile; + System.out.printf("load ledgerBindConfigFile = %s \r\n", ledgerBindConfigFile); + if (ledgerBindConfigFile == null) { + ClassPathResource configResource = new ClassPathResource("ledger-binding.conf"); + InputStream in = configResource.getInputStream(); + ledgerBindingConfig = LedgerBindingConfig.resolve(in); + } else { + File file = new File(ledgerBindConfigFile); + ledgerBindingConfig = LedgerBindingConfig.resolve(file); + } + return ledgerBindingConfig; + } +} \ No newline at end of file diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerWebSecurityConfiguration.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerWebSecurityConfiguration.java new file mode 100644 index 00000000..1b285b0a --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerWebSecurityConfiguration.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.peer.web; + +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class PeerWebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().permitAll(); + http.csrf().disable(); + } + +} diff --git a/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerWebServerConfigurer.java b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerWebServerConfigurer.java new file mode 100644 index 00000000..f0d52ac5 --- /dev/null +++ b/source/peer/src/main/java/com/jd/blockchain/peer/web/PeerWebServerConfigurer.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.peer.web; + +import java.util.List; + +import com.jd.blockchain.web.converters.BinaryMessageConverter; +import com.jd.blockchain.web.converters.HashDigestInputConverter; + +import com.jd.blockchain.web.serializes.ByteArrayObjectUtil; +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import com.jd.blockchain.utils.web.model.JsonWebResponseMessageConverter; + +@Configuration +public class PeerWebServerConfigurer implements WebMvcConfigurer { + + static { + JSONSerializeUtils.disableCircularReferenceDetect(); + JSONSerializeUtils.configStringSerializer(ByteArray.class); + } + + @Override + public void extendMessageConverters(List> converters) { + int index = converters.size(); + for (int i = 0; i < converters.size(); i++) { + if (converters.get(i) instanceof MappingJackson2HttpMessageConverter) { + index = i; + break; + } + } + converters.add(index, new JsonWebResponseMessageConverter()); + + converters.add(0, new BinaryMessageConverter()); + + initByteArrayJsonSerialize(); + } + + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverter(new HashDigestInputConverter()); + } + + private void initByteArrayJsonSerialize() { + ByteArrayObjectUtil.init(); + } +} diff --git a/source/peer/src/main/resources/banner.txt b/source/peer/src/main/resources/banner.txt new file mode 100644 index 00000000..c39618bd --- /dev/null +++ b/source/peer/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄ +▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░▌ ▐░▌ + ▀▀▀▀▀█░█▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ + ▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌ ▐░▐░▌ +▐░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░░▌ + ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀ + diff --git a/source/peer/src/main/resources/log4j2.xml b/source/peer/src/main/resources/log4j2.xml new file mode 100644 index 00000000..c889aa69 --- /dev/null +++ b/source/peer/src/main/resources/log4j2.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/service/ConsensusViewDefinitionTest.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/service/ConsensusViewDefinitionTest.java new file mode 100644 index 00000000..545a0223 --- /dev/null +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/service/ConsensusViewDefinitionTest.java @@ -0,0 +1,47 @@ +package test.com.jd.blockchain.peer.service; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.jd.blockchain.peer.consensus.ConsensusViewDefinition; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class ConsensusViewDefinitionTest { + + @Test + public void test() { + ConsensusViewDefinition viewDef = new ConsensusViewDefinition(); + + assertEquals(0, viewDef.getNodeCount()); + assertEquals(1, viewDef.getN()); + assertEquals(0, viewDef.getF()); + + viewDef.addNode(new NetworkAddress("localhost", 10001)); + assertEquals(1, viewDef.getNodeCount()); + assertEquals(1, viewDef.getN()); + assertEquals(0, viewDef.getF()); + + viewDef.addNode(new NetworkAddress("localhost", 10002)); + assertEquals(2, viewDef.getNodeCount()); + assertEquals(1, viewDef.getN()); + assertEquals(0, viewDef.getF()); + + viewDef.addNode(new NetworkAddress("localhost", 10003)); + assertEquals(3, viewDef.getNodeCount()); + assertEquals(1, viewDef.getN()); + assertEquals(0, viewDef.getF()); + + viewDef.addNode(new NetworkAddress("localhost", 10004)); + assertEquals(4, viewDef.getNodeCount()); + assertEquals(4, viewDef.getN()); + assertEquals(1, viewDef.getF()); + + viewDef.addNode(new NetworkAddress("localhost", 10005)); + assertEquals(5, viewDef.getNodeCount()); + assertEquals(4, viewDef.getN()); + assertEquals(1, viewDef.getF()); + + } + +} diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/service/LedgerInitCordinatorTest.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/service/LedgerInitCordinatorTest.java new file mode 100644 index 00000000..e45b661b --- /dev/null +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/service/LedgerInitCordinatorTest.java @@ -0,0 +1,151 @@ +//package test.com.jd.blockchain.peer.service; +// +//import com.jd.blockchain.ledger.*; +//import com.jd.blockchain.ledger.service.LedgerService; +//import com.jd.blockchain.ledger.service.impl.LedgerServiceImpl; +//import com.jd.blockchain.peer.service.LedgerInitCordinator; +//import com.jd.blockchain.storage.service.KeyValueStorageService; +//import com.jd.blockchain.storage.service.impl.hashmap.HashMapStorageService; +//import my.utils.net.NetworkAddress; +//import org.junit.After; +//import org.junit.Before; +//import org.junit.Test; +// +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.Collections; +//import java.util.List; +// +//import static org.junit.Assert.*; +// +//public class LedgerInitCordinatorTest { +// +// private LedgerService ledgerService; +// private NetworkAddress localpeer = new NetworkAddress("127.0.0.1", 9000); +// +// @Before +// public void setup() { +// KeyValueStorageService storageService = new HashMapStorageService(); +// ledgerService = new LedgerServiceImpl(storageService); +// } +// +// @After +// public void teardown() { +// ledgerService = null; +// } +// +// @Test +// public void testStart() { +// BlockchainKeyPair genesisAccount = BlockchainKeyGenerator.getInstance().generate(); +// LedgerInitCordinator cordinator = new LedgerInitCordinator(ledgerService, localpeer, genesisAccount.getIdentity()); +// cordinator.start(); +// +// assertEquals(cordinator.getLedgerBuilder().getGenesisSeed(), cordinator.getLedgerDefinition().getGenesisKey()); +// assertEquals(cordinator.getLedgerDefinition().isConfigReady(), false); +// assertEquals(cordinator.getLedgerDefinition().genesisAccountNum(), 1); +// assertEquals(cordinator.getLedgerDefinition().getInitializingOperations().length, 0); +// } +// +// @Test +// public void testJoinLedger() { +// BlockchainKeyPair genesisAccount = BlockchainKeyGenerator.getInstance().generate(); +// LedgerInitCordinator cordinator = new LedgerInitCordinator(ledgerService, localpeer, genesisAccount.getIdentity()); +// cordinator.start(); +// +// NetworkAddress remotepeer1 = new NetworkAddress("127.0.0.1", 9001); +// BlockchainKeyPair remoteAccount1 = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainIdentity[] remoteIdentities1 = new BlockchainIdentity[1]; +// remoteIdentities1[0] = remoteAccount1.getIdentity(); +// +// NetworkAddress remotepeer2 = new NetworkAddress("127.0.0.1", 9002); +// BlockchainKeyPair remoteAccount2 = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainIdentity[] remoteIdentities2 = new BlockchainIdentity[1]; +// remoteIdentities2[0] = remoteAccount2.getIdentity(); +// +// cordinator.joinLedger(cordinator.getLedgerDefinition().getDefinitionId(), remotepeer1, remoteIdentities1, new BlockchainOperation[0]); +// cordinator.joinLedger(cordinator.getLedgerDefinition().getDefinitionId(), remotepeer2, remoteIdentities2, new BlockchainOperation[0]); +// +// assertEquals(cordinator.getLedgerDefinition().genesisAccountNum(), 3); +// assertEquals(cordinator.getLedgerDefinition().getInitializingOperations().length, 0); +// } +// +// @Test +// public void testPrepare() { +// BlockchainKeyPair genesisAccount = BlockchainKeyGenerator.getInstance().generate(); +// LedgerInitCordinator cordinator = new LedgerInitCordinator(ledgerService, localpeer, genesisAccount.getIdentity()); +// cordinator.start(); +// TransactionContent content = cordinator.prepare(cordinator.getLedgerDefinition().getDefinitionId()); +// +// assertEquals(cordinator.getLedgerDefinition().isConfigReady(), true); +// assertEquals(content.getOperations().length, 1); +// } +// +// @Test +// public void testSign() { +// BlockchainKeyPair genesisAccount = BlockchainKeyGenerator.getInstance().generate(); +// LedgerInitCordinator cordinator = new LedgerInitCordinator(ledgerService, localpeer, genesisAccount.getIdentity()); +// cordinator.start(); +// +// NetworkAddress remotepeer1 = new NetworkAddress("127.0.0.1", 9001); +// BlockchainKeyPair remoteAccount1 = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainIdentity[] remoteIdentities1 = new BlockchainIdentity[1]; +// remoteIdentities1[0] = remoteAccount1.getIdentity(); +// +// NetworkAddress remotepeer2 = new NetworkAddress("127.0.0.1", 9002); +// BlockchainKeyPair remoteAccount2 = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainIdentity[] remoteIdentities2 = new BlockchainIdentity[1]; +// remoteIdentities2[0] = remoteAccount2.getIdentity(); +// +// cordinator.joinLedger(cordinator.getLedgerDefinition().getDefinitionId(), remotepeer1, remoteIdentities1, new BlockchainOperation[0]); +// cordinator.joinLedger(cordinator.getLedgerDefinition().getDefinitionId(), remotepeer2, remoteIdentities2, new BlockchainOperation[0]); +// +// TransactionContent content = cordinator.prepare(cordinator.getLedgerDefinition().getDefinitionId()); +// +// DigitalSignature genesisSign = cordinator.sign(genesisAccount); +// DigitalSignature remoteSign1 = cordinator.sign(remoteAccount1); +// DigitalSignature remoteSign2 = cordinator.sign(remoteAccount2); +// +// List signatures = Arrays.asList(cordinator.getLedgerDefinition().genesisSignatures()); +// +// assertEquals(cordinator.getLedgerDefinition().genesisSignatures().length, 3); +// assertEquals(signatures.contains(genesisSign), true); +// assertEquals(signatures.contains(remoteSign1), true); +// assertEquals(signatures.contains(remoteSign2), true); +// assertEquals(cordinator.getLedgerDefinition().isSignatureReady(), true); +// } +// +// @Test +// public void testConsistent() { +// BlockchainKeyPair genesisAccount = BlockchainKeyGenerator.getInstance().generate(); +// LedgerInitCordinator cordinator = new LedgerInitCordinator(ledgerService, localpeer, genesisAccount.getIdentity()); +// cordinator.start(); +// +// NetworkAddress remotepeer1 = new NetworkAddress("127.0.0.1", 9001); +// BlockchainKeyPair remoteAccount1 = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainIdentity[] remoteIdentities1 = new BlockchainIdentity[1]; +// remoteIdentities1[0] = remoteAccount1.getIdentity(); +// +// NetworkAddress remotepeer2 = new NetworkAddress("127.0.0.1", 9002); +// BlockchainKeyPair remoteAccount2 = BlockchainKeyGenerator.getInstance().generate(); +// BlockchainIdentity[] remoteIdentities2 = new BlockchainIdentity[1]; +// remoteIdentities2[0] = remoteAccount2.getIdentity(); +// +// cordinator.joinLedger(cordinator.getLedgerDefinition().getDefinitionId(), remotepeer1, remoteIdentities1, new BlockchainOperation[0]); +// cordinator.joinLedger(cordinator.getLedgerDefinition().getDefinitionId(), remotepeer2, remoteIdentities2, new BlockchainOperation[0]); +// +// TransactionContent content = cordinator.prepare(cordinator.getLedgerDefinition().getDefinitionId()); +// +// cordinator.sign(genesisAccount); +// cordinator.sign(remoteAccount1); +// cordinator.sign(remoteAccount2); +// cordinator.preGenerateLedger(); +// +// cordinator.addConsensusLedgerHash(cordinator.getLedgerDefinition().getDefinitionId(), genesisAccount.getAddress(), cordinator.getLedgerBuilder().getLedger().getBlockHash()); +// +// cordinator.addConsensusLedgerHash(cordinator.getLedgerDefinition().getDefinitionId(), remoteAccount1.getAddress(), cordinator.getLedgerBuilder().getLedger().getBlockHash()); +// +// cordinator.addConsensusLedgerHash(cordinator.getLedgerDefinition().getDefinitionId(), remoteAccount2.getAddress(), cordinator.getLedgerBuilder().getLedger().getBlockHash()); +// +// assertEquals(cordinator.isLedgerConsistent(), true); +// } +//} \ No newline at end of file diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java new file mode 100644 index 00000000..612c97f4 --- /dev/null +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/ControllerTestConfiguration.java @@ -0,0 +1,37 @@ +//package test.com.jd.blockchain.peer.web; +// +//import org.springframework.boot.test.mock.mockito.MockBean; +//import org.springframework.context.annotation.Bean; +//import org.springframework.context.annotation.Configuration; +// +//import com.jd.blockchain.peer.PeerSettings; +//import com.jd.blockchain.peer.service.MessageBroadcaster; +//import com.jd.blockchain.peer.service.PeerKeyStorageService; +// +//@Configuration +//public class ControllerTestConfiguration { +// +// @Bean +// public PeerKeyStorageService peerKeyStorageService() { +// return new PeerKeyStorageServiceImpl(); +// } +// +//// @MockBean +//// private LedgerService ledgerService; +// +// @MockBean +// private MessageBroadcaster msgBroadcaster; // 用于向客户端进行消息通知; +// +// +// @Bean +// public PeerSettings peerSettring() { +// PeerSettings setting = new PeerSettings(); +// PeerSettings.ConsensusSetting consensusSetting = new PeerSettings.ConsensusSetting(); +// consensusSetting.setIp("127.0.0.1"); +// consensusSetting.setPort(9000); +// setting.setConsensus(consensusSetting); +// +// return setting; +// } +// +//} diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingControllerTest.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingControllerTest.java new file mode 100644 index 00000000..c1736b31 --- /dev/null +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingControllerTest.java @@ -0,0 +1,245 @@ +//package test.com.jd.blockchain.peer.web; +// +//import static org.junit.Assert.assertEquals; +//import static org.mockito.Matchers.any; +// +//import java.util.Arrays; +//import java.util.List; +// +//import com.jd.blockchain.ledger.service.impl.LedgerServiceImpl; +//import org.junit.*; +//import org.junit.runner.RunWith; +//import org.junit.runners.MethodSorters; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +//import org.springframework.util.Base64Utils; +// +//import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +//import com.jd.blockchain.ledger.AccountRegisterOperation; +//import com.jd.blockchain.ledger.AccountStateOperation; +//import com.jd.blockchain.ledger.AccountStateType; +//import com.jd.blockchain.ledger.BlockchainIdentity; +//import com.jd.blockchain.ledger.BlockchainKeyGenerator; +//import com.jd.blockchain.ledger.BlockchainKeyPair; +//import com.jd.blockchain.ledger.BlockchainOperation; +//import com.jd.blockchain.ledger.DigitalSignature; +//import com.jd.blockchain.ledger.Ledger; +//import com.jd.blockchain.ledger.TransactionContent; +//import com.jd.blockchain.ledger.data.BlockchainOperationFactory; +//import com.jd.blockchain.ledger.data.SignatureEncoding; +//import com.jd.blockchain.ledger.service.LedgerBuilder; +//import com.jd.blockchain.ledger.service.LedgerService; +//import com.jd.blockchain.peer.PeerSettings; +//import com.jd.blockchain.peer.service.LedgerDefinition; +//import com.jd.blockchain.peer.service.MessageBroadcaster; +//import com.jd.blockchain.peer.service.PeerKeyStorageService; +//import com.jd.blockchain.peer.web.JoinLedgerParameter; +//import com.jd.blockchain.peer.web.LedgerInitializationContext; +//import com.jd.blockchain.peer.web.LedgerInitializingController; +//import com.jd.blockchain.storage.service.impl.hashmap.HashMapStorageService; +// +//import my.utils.io.ByteArray; +//import my.utils.net.NetworkAddress; +//import my.utils.serialize.binary.BinarySerializeUtils; +//import my.utils.serialize.json.JSONSerializeUtils; +// +//@Ignore +//@RunWith(SpringJUnit4ClassRunner.class) +//@SpringBootTest(classes = {ControllerTestConfiguration.class}) +//@FixMethodOrder(MethodSorters.NAME_ASCENDING) +//public class LedgerInitializingControllerTest { +// +// @Autowired +// private PeerKeyStorageService keystoreService; +// +// @Autowired +// private LedgerService ledgerService; +// +// @Autowired +// private PeerSettings peerSettings; +// +// @Autowired +// private MessageBroadcaster msgBroadcaster; // 用于向客户端进行消息通知; +// +// private LedgerInitializingController controller; +// +// @BeforeClass +// public static void setUpBeforeClass() throws Exception { +// } +// +// @AfterClass +// public static void tearDownAfterClass() throws Exception { +// } +// +// private BlockchainKeyPair keyOfP1 = BlockchainKeyGenerator.getInstance().generate(); +// +// @Before +// public void setup() { +// ledgerService = new LedgerServiceImpl(new HashMapStorageService()); +// controller = new LedgerInitializingController(peerSettings, keystoreService, ledgerService, msgBroadcaster); +// +// //when(ledgerService.newLedger()).thenReturn(new LedgerBuilderImpl(ByteArray.wrap("genesisKey".getBytes()), ledgerService)); +// //when(ledgerService.newLedger(ByteArray.wrap("genesisKey".getBytes()))).thenReturn(new LedgerBuilderImpl(ByteArray.wrap("genesisKey".getBytes()), ledgerService)); +// } +// +// @After +// public void after() { +// controller = null; +// } +// +// @Test +// public void testStartNewLedger() { +// LedgerInitializationContext context = controller.startNewLedger("TestParticipantName", PeerKeyStorageServiceImpl.keyOfP1.getAddress()); +// +// assertEquals(context.getLocalAccount(), keystoreService.getBlockchainKey(PeerKeyStorageServiceImpl.keyOfP1.getAddress()).getIdentity()); +// assertEquals(context.getLocalConsensusAddress(), new NetworkAddress(peerSettings.getConsensus().getIp(), peerSettings.getConsensus().getPort())); +// assertEquals(context.getLedgerDefinition().genesisAccountNum(), 1); +// assertEquals(context.getLedgerDefinition().isConfigReady(), false); +// assertEquals(context.getLedgerDefinition().getInitializingOperations().length, 2); +// +// JSONSerializeUtils.disableCircularReferenceDetect(); +// JSONSerializeUtils.configStringSerializer(ByteArray.class); +// System.out.println(JSONSerializeUtils.serializeToJSON(context)); +// } +// +// @Test +// public void testStartJoiningLedger() { +// LedgerInitializationContext context = controller.startNewLedger("TestParticipantName", PeerKeyStorageServiceImpl.keyOfP1.getAddress()); +// +// JoinLedgerParameter parameter = new JoinLedgerParameter(); +// BlockchainKeyPair identity = BlockchainKeyGenerator.getInstance().generate(); +// parameter.setGenesisAccounts(new BlockchainIdentity[]{identity.getIdentity()}); +// +// AccountRegisterOperation regGenesisAccountOP = BlockchainOperationFactory.getInstance().register(identity.getIdentity(), +// AccountStateType.MAP); +// AccountStateOperation initGenesisAccountInfoOP = BlockchainOperationFactory.getInstance() +// .updateState(identity.getAddress()); +// initGenesisAccountInfoOP.putString("NAME", "joinLedger"); +// initGenesisAccountInfoOP.putString("DESCRIPTION", ""); +// +// parameter.setOperations(new BlockchainOperation[]{regGenesisAccountOP, initGenesisAccountInfoOP}); +// parameter.setNetworkAddress(new NetworkAddress("127.0.0.1", 9001)); +// +// LedgerDefinition definition = decodeObject(controller.joinLedger(context.getLedgerDefinition().getDefinitionId(), encodeObject(parameter)), LedgerDefinition.class); +// +// assertEquals(definition.genesisAccountNum(), 2); +// assertEquals(definition.getInitializingOperations().length, 4); +// assertEquals(definition.isConfigReady(), false); +// } +// +// @Test +// public void testPrepare() { +// LedgerInitializationContext context = controller.startNewLedger("TestParticipantName",PeerKeyStorageServiceImpl.keyOfP1.getAddress()); +// +// JoinLedgerParameter parameter = new JoinLedgerParameter(); +// BlockchainIdentity identity = keystoreService.generateNewKey("identity").getIdentity(); +// +// parameter.setGenesisAccounts(new BlockchainIdentity[]{identity}); +// +// AccountRegisterOperation regGenesisAccountOP = BlockchainOperationFactory.getInstance().register(identity, AccountStateType.MAP); +// AccountStateOperation initGenesisAccountInfoOP = BlockchainOperationFactory.getInstance() +// .updateState(identity.getAddress()); +// initGenesisAccountInfoOP.putString("NAME", "joinLedger"); +// initGenesisAccountInfoOP.putString("DESCRIPTION", ""); +// +// parameter.setOperations(new BlockchainOperation[]{regGenesisAccountOP, initGenesisAccountInfoOP}); +// parameter.setNetworkAddress(new NetworkAddress("127.0.0.1", 9001)); +// +// controller.joinLedger(context.getLedgerDefinition().getDefinitionId(), encodeObject(parameter)); +// context = controller.prepareAndSign(); +// +// assertEquals(context.getLedgerDefinition().isConfigReady(), true); +// } +// +// @Test +// public void testAttachSignature() { +// LedgerInitializationContext context = controller.startNewLedger("TestParticipantName", PeerKeyStorageServiceImpl.keyOfP1.getAddress()); +// JoinLedgerParameter parameter = new JoinLedgerParameter(); +// BlockchainIdentity identity = keystoreService.generateNewKey("identity").getIdentity(); +// +// parameter.setGenesisAccounts(new BlockchainIdentity[]{identity}); +// parameter.setOperations(new BlockchainOperation[]{}); +// parameter.setNetworkAddress(new NetworkAddress("127.0.0.1", 9001)); +// +// controller.joinLedger(context.getLedgerDefinition().getDefinitionId(), encodeObject(parameter)); +// context = controller.prepareAndSign(); +// +// LedgerBuilder ledgerBuilder = ledgerService.newLedger(context.getLedgerDefinition().getGenesisKey()); +// ledgerBuilder.addLedgerKeys(context.getLedgerDefinition().genesisIdentites()); +// ledgerBuilder.addInitializeOperations(context.getLedgerDefinition().getInitializingOperations()); +// +// TransactionContent txContent = ledgerBuilder.prepare(); +// context.setTransaction(txContent); +// +// byte[] txContentBytes = BinaryEncodingUtils.encode(txContent, TransactionContent.class); +// DigitalSignature signature = keystoreService.sign(txContentBytes, identity.getAddress()); +// ledgerBuilder.attachSignature(signature); +// +// String base64Signature = SignatureEncoding.encodeToBase64(signature); +// controller.attachSignature(context.getLedgerDefinition().getDefinitionId(), base64Signature); +// +// List signatures = Arrays.asList(context.getLedgerDefinition().genesisSignatures()); +// +// assertEquals(context.getLedgerDefinition().genesisSignatures().length, 2); +// assertEquals(signatures.contains(signature), true); +// assertEquals(context.getLedgerDefinition().isSignatureReady(), true); +// } +// +// @Test +// public void testconsensusLedger() { +// LedgerInitializationContext context = controller.startNewLedger("TestParticipantName", PeerKeyStorageServiceImpl.keyOfP1.getAddress()); +// +// BlockchainIdentity identity = PeerKeyStorageServiceImpl.keyOfP2.getIdentity(); +// JoinLedgerParameter parameter = new JoinLedgerParameter(); +// parameter.setGenesisAccounts(new BlockchainIdentity[]{identity}); +// parameter.setOperations(new BlockchainOperation[]{}); +// parameter.setNetworkAddress(new NetworkAddress("127.0.0.1", 9001)); +// +// controller.joinLedger(context.getLedgerDefinition().getDefinitionId(), encodeObject(parameter)); +// context = controller.prepareAndSign(); +// +// // other peer +// LedgerBuilder builder = ledgerService.newLedger(context.getLedgerDefinition().getGenesisKey()); +// builder.addLedgerKeys(context.getLedgerDefinition().genesisIdentites()); +// builder.addInitializeOperations(context.getLedgerDefinition().getInitializingOperations()); +// +// builder.prepare(); +// String base64Signature = SignatureEncoding.encodeToBase64(builder.sign(PeerKeyStorageServiceImpl.keyOfP2)); +// controller.attachSignature(context.getLedgerDefinition().getDefinitionId(), base64Signature); +// +// // 附加全部的签名列表到本地,生成创世区块,建立账本,并提交账本hash到协调节点进行共识; +// context = controller.getLedgerInitContext(); +// for (DigitalSignature signature1 : context.getLedgerDefinition().genesisSignatures()) { +// builder.attachSignature(signature1); +// } +// +// builder.preCommit(); +// Ledger ledger = builder.getLedger(); +// +// // 向协调节点发送账本hash进行共识; +// controller.consensusLedger(context.getLedgerDefinition().getDefinitionId(), identity.getAddress(), ledger.getLedgerHash().toBase64()); +// +// assertEquals(true, controller.getLedgerInitContext().isConsistent()); +// } +// +// private String encodeObject(Object obj) { +// byte[] bts = BinarySerializeUtils.serialize(obj); +// return Base64Utils.encodeToString(bts); +// } +// +// @SuppressWarnings("unchecked") +// private T decodeObject(String base64Str, Class clazz) { +// byte[] bts = Base64Utils.decodeFromString(base64Str); +// // if (BytesReader.class.isAssignableFrom(clazz)) { +// // BytesReader instance = (BytesReader) BeanUtils.instantiate(clazz); +// // try { +// // instance.resolvFrom(new ByteArrayInputStream(bts)); +// // } catch (IOException e) { +// // throw new IORuntimeException(e.getMessage(), e); +// // } +// // return (T) instance; +// // } +// return (T) BinarySerializeUtils.deserialize(bts); +// } +//} diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java new file mode 100644 index 00000000..6aa8cbdb --- /dev/null +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/LedgerInitializingTest.java @@ -0,0 +1,252 @@ +//package test.com.jd.blockchain.peer.web; +// +//import static org.mockito.Matchers.any; +//import static org.mockito.Matchers.anyInt; +//import static org.mockito.Mockito.mock; +//import static org.mockito.Mockito.spy; +//import static org.mockito.Mockito.when; +// +//import org.junit.After; +//import org.junit.AfterClass; +//import org.junit.Before; +//import org.junit.BeforeClass; +//import org.junit.FixMethodOrder; +//import org.junit.Ignore; +//import org.junit.Test; +//import org.junit.runner.RunWith; +//import org.junit.runners.MethodSorters; +//import org.mockito.invocation.InvocationOnMock; +//import org.mockito.stubbing.Answer; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.boot.test.context.SpringBootTest; +//import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +// +//import com.jd.blockchain.ledger.BlockchainIdentity; +//import com.jd.blockchain.ledger.BlockchainKeyPair; +//import com.jd.blockchain.ledger.DigitalSignature; +//import com.jd.blockchain.ledger.data.SignatureUtils; +//import com.jd.blockchain.ledger.service.LedgerService; +//import com.jd.blockchain.ledger.service.impl.LedgerServiceImpl; +//import com.jd.blockchain.peer.PeerSettings; +//import com.jd.blockchain.peer.service.BlockchainKeyInfo; +//import com.jd.blockchain.peer.service.MessageBroadcaster; +//import com.jd.blockchain.peer.service.PeerKeyStorageService; +//import com.jd.blockchain.peer.web.LedgerInitializationContext; +//import com.jd.blockchain.peer.web.LedgerInitializingController; +//import com.jd.blockchain.peer.web.LedgerInitializingHttpService; +//import com.jd.blockchain.storage.service.impl.redis.RedisStorageService; +// +//import my.utils.io.ByteArray; +//import redis.clients.jedis.Jedis; +// +//@Ignore +//@RunWith(SpringJUnit4ClassRunner.class) +//@SpringBootTest(classes = { ControllerTestConfiguration.class}) +//@FixMethodOrder(MethodSorters.NAME_ASCENDING) +//public class LedgerInitializingTest { +// +// @Autowired +// private PeerKeyStorageService keyStorageService; +// +// @BeforeClass +// public static void setUpBeforeClass() throws Exception { +// } +// +// @AfterClass +// public static void tearDownAfterClass() throws Exception { +// } +// +// @Before +// public void setup() { +// } +// +// @After +// public void after() { +// } +// +// @Test +// public void testStartNewLedger() { +// // 准备上下文; +// ServerContext cordinator = new ServerContext("cordinator", 0, 9000, keyStorageService); +// +// ServerContext participant1 = new ServerContext("participant1", 1, 9001, keyStorageService); +// participant1.setLedgerInitHttpServiceProxy(cordinator.getController()); +// +// ServerContext participant2 = new ServerContext("participant2", 2, 9002, keyStorageService); +// participant2.setLedgerInitHttpServiceProxy(cordinator.getController()); +// +// ServerContext participant3 = new ServerContext("participant3", 3, 9003, keyStorageService); +// participant3.setLedgerInitHttpServiceProxy(cordinator.getController()); +// +// // 协调节点开始新建账本; +// LedgerInitializationContext ctx = cordinator.controller.startNewLedger(cordinator.keyInfo.getName(), +// cordinator.keyInfo.getIdentity().getAddress()); +// String defId = ctx.getLedgerDefinition().getDefinitionId(); +// +// // 参与者加入账本; +// participant1.controller.startJoiningLedger(defId, "localhost", 9000, participant1.keyInfo.getName(), +// participant1.keyInfo.getIdentity().getAddress()); +// participant2.controller.startJoiningLedger(defId, "localhost", 9000, participant2.keyInfo.getName(), +// participant2.keyInfo.getIdentity().getAddress()); +// participant3.controller.startJoiningLedger(defId, "localhost", 9000, participant3.keyInfo.getName(), +// participant3.keyInfo.getIdentity().getAddress()); +// +// // TODO:断言协调节点已经收到了参与者的请求; +// +// +// // 协调节点签署账本; +// cordinator.controller.prepareAndSign(); +// +// // 等待各个参与节点完成签署和生成块; +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// // Swallow InterruptedException; +// } +// +// LedgerInitializationContext cordCtx = cordinator.controller.getLedgerInitContext(); +// LedgerInitializationContext part1Ctx = participant1.controller.getLedgerInitContext(); +// LedgerInitializationContext part2Ctx = participant2.controller.getLedgerInitContext(); +// LedgerInitializationContext part3Ctx = participant3.controller.getLedgerInitContext(); +// +// // 断言各个参与节点都达到了块一致的状态; +// //ssertTrue(cordCtx.isConsistent()); +// +// // 等待各个参与节点完成签署和生成块; +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// // Swallow InterruptedException; +// } +// //assertTrue(part1Ctx.isConsistent()); +// //assertTrue(part2Ctx.isConsistent()); +// //assertTrue(part3Ctx.isConsistent()); +// +// // 断言各个参与节点的块都是真正地一致; +// //assertEquals(cordCtx.getLedger().getLedgerHash(), part1Ctx.getLedger().getLedgerHash()); +// //assertEquals(cordCtx.getLedger().getLedgerHash(), part2Ctx.getLedger().getLedgerHash()); +// //assertEquals(cordCtx.getLedger().getLedgerHash(), part3Ctx.getLedger().getLedgerHash()); +// +// // 协调节点提交; +// cordinator.controller.commitLedger(); +// +// // 等待各个参与节点完成提交; +// try { +// Thread.sleep(5000); +// } catch (InterruptedException e) { +// // Swallow InterruptedException; +// } +// +// // 断言各个参与节点的状态都被重置了; +// cordCtx = cordinator.controller.getLedgerInitContext(); +// part1Ctx = participant1.controller.getLedgerInitContext(); +// part2Ctx = participant2.controller.getLedgerInitContext(); +// part3Ctx = participant3.controller.getLedgerInitContext(); +// +// //assertFalse(cordCtx.isJoined()); +// //assertFalse(part1Ctx.isJoined()); +// //assertFalse(part2Ctx.isJoined()); +// //assertFalse(part3Ctx.isJoined()); +// +// //assertNull(cordCtx.getLedgerDefinition()); +// //assertNull(part1Ctx.getLedgerDefinition()); +// //assertNull(part2Ctx.getLedgerDefinition()); +// //assertNull(part3Ctx.getLedgerDefinition()); +// } +// +// public static class ServerContext { +// +// private PeerKeyStorageService keystoreService; +// +// private LedgerService ledgerService; +// +// private PeerSettings peerSettings; +// +// private MessageBroadcaster msgBroadcaster; // 用于向客户端进行消息通知; +// +// private LedgerInitializingController controller; +// +// //private final BlockchainKeyPair key; +// +// private final BlockchainIdentity identity; +// +// private final BlockchainKeyInfo keyInfo; +// +// private LedgerInitializingHttpService ledgerInitHttpServiceProxy; +// +// public ServerContext(String name, int idx, int port, PeerKeyStorageService keystoreService) { +// Jedis jedis = new Jedis("192.168.151.33", 6379); +// jedis.select(idx); +// jedis.connect(); +// +// this.ledgerService = new LedgerServiceImpl(new RedisStorageService(jedis)); +// this.peerSettings = peerSetting(port); +// this.keystoreService = keystoreService; +// +// this.keyInfo = this.keystoreService.generateNewKey(name); +// this.identity = this.keyInfo.getIdentity(); +// +// //this.key = BlockchainKeyGenerator.getInstance().generate(KeyType.ED25519); +// //this.keyInfo = new BlockchainKeyInfo(); +// //this.keyInfo.setName(name); +// //this.keyInfo.setIdentity(key.getIdentity()); +// +// initTestContext(); +// } +// +// +// private PeerSettings peerSetting(int port) { +// PeerSettings setting = new PeerSettings(); +// PeerSettings.ConsensusSetting consensusSetting = new PeerSettings.ConsensusSetting(); +// consensusSetting.setIp("127.0.0.1"); +// consensusSetting.setPort(port); +// setting.setConsensus(consensusSetting); +// +// return setting; +// } +// +// +// private void initTestContext() { +// //keystoreService = spy(PeerKeyStorageServiceImpl.class); +// //when(keystoreService.getBlockchainKey(key.getAddress())).thenReturn(keyInfo); +// //when(keystoreService.sign(any(), key.getAddress())).then(answerSignature(key)); +// +// msgBroadcaster = mock(MessageBroadcaster.class); +// +// LedgerInitializingController ctrl = new LedgerInitializingController(peerSettings, keystoreService, +// ledgerService, msgBroadcaster); +// this.controller = spy(ctrl); +// +// when(controller.getLedgerInitHttpService(any(), anyInt())).then(new Answer() { +// @Override +// public LedgerInitializingHttpService answer(InvocationOnMock invocationOnMock) throws Throwable { +// return ledgerInitHttpServiceProxy; +// } +// }); +// } +// +// private Answer answerSignature(BlockchainKeyPair keyPair) { +// return new Answer() { +// @Override +// public DigitalSignature answer(InvocationOnMock invocation) throws Throwable { +// Object[] args = invocation.getArguments(); +// ByteArray data = (ByteArray) args[0]; +// return SignatureUtils.sign(data, keyPair); +// } +// }; +// } +// +// public LedgerInitializingController getController() { +// return controller; +// } +// +// public LedgerInitializingHttpService getLedgerInitHttpServiceProxy() { +// return ledgerInitHttpServiceProxy; +// } +// +// public void setLedgerInitHttpServiceProxy(LedgerInitializingHttpService ledgerInitHttpServiceProxy) { +// this.ledgerInitHttpServiceProxy = ledgerInitHttpServiceProxy; +// } +// +// } +//} diff --git a/source/peer/src/test/java/test/com/jd/blockchain/peer/web/PeerKeyStorageServiceImpl.java b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/PeerKeyStorageServiceImpl.java new file mode 100644 index 00000000..5491b2f5 --- /dev/null +++ b/source/peer/src/test/java/test/com/jd/blockchain/peer/web/PeerKeyStorageServiceImpl.java @@ -0,0 +1,140 @@ +//package test.com.jd.blockchain.peer.web; +// +//import java.util.Arrays; +//import java.util.Comparator; +//import java.util.Map; +//import java.util.concurrent.ConcurrentHashMap; +// +//import javax.annotation.PostConstruct; +// +//import com.jd.blockchain.crypto.CryptoAlgorithm; +//import com.jd.blockchain.crypto.asymmetric.PrivKey; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +//import org.springframework.beans.factory.annotation.Autowired; +//import org.springframework.stereotype.Service; +// +//import com.jd.blockchain.ledger.BlockchainIdentity; +//import com.jd.blockchain.ledger.BlockchainKeyGenerator; +//import com.jd.blockchain.ledger.BlockchainKeyPair; +//import com.jd.blockchain.ledger.DigitalSignature; +//import com.jd.blockchain.ledger.data.CryptoKeyEncoding; +//import com.jd.blockchain.ledger.data.SignatureUtils; +//import com.jd.blockchain.peer.PeerSettings; +//import com.jd.blockchain.peer.service.BlockchainKeyInfo; +//import com.jd.blockchain.peer.service.PeerKeyStorageService; +// +//import my.utils.io.ByteArray; +// +//@Service +//public class PeerKeyStorageServiceImpl implements PeerKeyStorageService { +// +// @Autowired +// private PeerSettings settings; +// +// public static BlockchainKeyPair keyOfP1 = BlockchainKeyGenerator.getInstance().generate(); +// public static BlockchainKeyPair keyOfP2 = BlockchainKeyGenerator.getInstance().generate(); +// +// private Map keyStores = new ConcurrentHashMap<>(); +// +// private static String[] encodeToStringLines(BlockchainKeyStore keyStore) { +// return new String[] { keyStore.getKeyInfo().getName(), keyStore.getKeyPair().getAddress(), +// keyStore.getKeyPair().getPubKey().toString(), keyStore.getKeyPair().getPrivKey().toString() }; +// } +// +// private static BlockchainKeyStore decodeFromStringLines(String[] lines) { +// String name = lines[0]; +// String address = lines[1]; +// PubKey pubKey = (PubKey) CryptoKeyEncoding.fromBase58(lines[2]); +// PrivKey privKey = (PrivKey) CryptoKeyEncoding.fromBase58(lines[3]); +// return new BlockchainKeyStore(name, address, pubKey, privKey); +// } +// +// @PostConstruct +// private void init() { +// keyStores.put(keyOfP1.getAddress(), new BlockchainKeyStore("a", keyOfP1)); +// keyStores.put(keyOfP1.getAddress(), new BlockchainKeyStore("b", keyOfP1)); +// } +// +// @Override +// public BlockchainKeyInfo generateNewKey(String name) { +// BlockchainKeyPair keypair = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); +// BlockchainKeyStore keystore = new BlockchainKeyStore(name, keypair); +// +// keyStores.put(keypair.getAddress(), keystore); +// return keystore.getKeyInfo(); +// } +// +// @Override +// public DigitalSignature sign(ByteArray data, String address) { +// BlockchainKeyStore keystore = keyStores.get(address); +// if (keystore == null) { +// throw new IllegalArgumentException("Key not exist!"); +// } +// return SignatureUtils.sign(data, keystore.getKeyPair()); +// } +// +// @Override +// public DigitalSignature sign(byte[] data, String address) { +// BlockchainKeyStore keystore = keyStores.get(address); +// if (keystore == null) { +// throw new IllegalArgumentException("Key not exist!"); +// } +// return SignatureUtils.sign(data, keystore.getKeyPair()); +// } +// +// @Override +// public BlockchainKeyInfo getBlockchainKey(String address) { +// BlockchainKeyStore keystore = keyStores.get(address); +// if (keystore == null) { +// throw new IllegalArgumentException("Key not exist!"); +// } +// return keystore.getKeyInfo(); +// } +// +// @Override +// public BlockchainKeyInfo[] getBlockchainKeys() { +// BlockchainKeyInfo[] keys = new BlockchainKeyInfo[keyStores.size()]; +// int i = 0; +// for (BlockchainKeyStore keystore : keyStores.values()) { +// keys[i] = keystore.getKeyInfo(); +// i++; +// } +// Arrays.sort(keys, new Comparator() { +// @Override +// public int compare(BlockchainKeyInfo o1, BlockchainKeyInfo o2) { +// return o1.getName().compareTo(o2.getName()); +// } +// }); +// return keys; +// } +// +// private static class BlockchainKeyStore { +// private BlockchainKeyInfo keyInfo; +// +// private BlockchainKeyPair keyPair; +// +// public BlockchainKeyStore(String name, String address, PubKey pubKey, PrivKey privKey) { +// keyInfo = new BlockchainKeyInfo(); +// keyInfo.setName(name); +// keyInfo.setIdentity(new BlockchainIdentity(address, pubKey)); +// keyPair = new BlockchainKeyPair(address, pubKey, privKey); +// } +// +// public BlockchainKeyStore(String name, BlockchainKeyPair keypair) { +// keyInfo = new BlockchainKeyInfo(); +// keyInfo.setName(name); +// keyInfo.setIdentity(keypair.getIdentity()); +// this.keyPair = keypair; +// } +// +// public BlockchainKeyInfo getKeyInfo() { +// return keyInfo; +// } +// +// public BlockchainKeyPair getKeyPair() { +// return keyPair; +// } +// +// } +// +//} diff --git a/source/pom.xml b/source/pom.xml new file mode 100644 index 00000000..89f926f5 --- /dev/null +++ b/source/pom.xml @@ -0,0 +1,472 @@ + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.0.6.RELEASE + + + + + + + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + pom + jdchain + + + utils + base + binary-proto + crypto + runtime + ledger + contract + consensus + storage + gateway + peer + sdk + tools + test + deployment + + + + 0.8.1-SNAPSHOT + 0.0.8.RELEASE + 0.6.6.RELEASE + + + 3.3.0 + 1.2.2 + 1.8.8 + + + 1.0.18 + 1.2.2 + 1.2.4 + 3.3.0 + 5.1.37 + + 3.1.0 + 1.2 + + 2.10.0 + 1.7.25 + + 4.12 + 1.10.19 + + 4.5.1 + 9.4.12.v20180830 + 1.2.3 + + 3.3.6 + 3.0.1 + 2.9.0 + 5.15.10 + 3.4.6 + 3.5.12 + 3.5.3 + 1.1.0 + 2.4 + 3.4.2 + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + + + + org.springframework.boot + spring-boot-dependencies + 2.0.6.RELEASE + pom + import + + + + com.jd.blockchain + bft-smart + ${bft-smart.version} + + + + com.jd.blockchain + browser + ${browser.version} + + + + + junit + junit + ${junit.version} + + + org.mockito + mockito-core + ${mockito.version} + test + + + + + javax.servlet + javax.servlet-api + ${servlet.version} + + + javax.servlet + jstl + ${jstl.version} + + + + com.lmax + disruptor + ${disruptor.version} + + + + com.alibaba + fastjson + 1.2.32 + + + + com.github.javaparser + javaparser-core + ${javaparser.version} + + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + slf4j-log4j12 + ${slf4j.version} + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + org.aspectj + aspectjrt + ${aspectj.version} + + + org.aspectj + aspectjweaver + ${aspectj.version} + + + org.aspectj + aspectjtools + ${aspectj.version} + + + + + + org.apache.httpcomponents + httpclient + ${httpclient.version} + + + + org.eclipse.jetty + jetty-server + ${jetty.version} + + + org.eclipse.jetty + jetty-io + ${jetty.version} + + + org.eclipse.jetty + jetty-http + ${jetty.version} + + + org.eclipse.jetty + jetty-util + ${jetty.version} + + + org.eclipse.jetty + jetty-webapp + ${jetty.version} + + + org.eclipse.jetty + jetty-xml + ${jetty.version} + + + org.eclipse.jetty + jetty-security + ${jetty.version} + + + org.eclipse.jetty + jetty-servlet + ${jetty.version} + + + + redis.clients + jedis + ${jedis.version} + + + + org.rocksdb + rocksdbjni + ${rocksdb.version} + + + + io.grpc + grpc-netty + 1.9.0 + + + io.grpc + grpc-protobuf + 1.9.0 + + + io.grpc + grpc-stub + 1.9.0 + + + + org.bouncycastle + bcprov-jdk15on + 1.59 + + + + io.nats + jnats + 2.2.0 + + + net.i2p.crypto + eddsa + + + + + + org.apache.commons + commons-collections4 + 4.1 + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + 1.8 + 1.8 + UTF-8 + false + true + false + false + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + UTF-8 + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-dependency-plugin.version} + + + copy-dependencies + package + + copy-dependencies + + + + ${project.build.directory}/libs + false + false + runtime + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + true + lib + false + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 2.4.1 + + + make-assembly + package + + single + + + + + + + org.apache.maven.plugins + maven-war-plugin + 2.6 + + false + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.10.4 + + + attach-javadocs + package + + jar + + + ${javadoc.opts} + + + + + + + + + + + + + + + + + + + + + + + + + + kr.motd.maven + os-maven-plugin + 1.4.1.Final + + + + + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + https://github.com/blockchain-jd-com/jdchain.git + https://github.com/blockchain-jd-com/jdchain.git + https://github.com/blockchain-jd-com/jdchain.git + + + + + jdchain + git-jdchain@jd.com + https://github.com/blockchain-jd-com/jdchain.git + + + + + + disable-javadoc-doclint + + [1.8,) + + + -Xdoclint:none + + + + + + \ No newline at end of file diff --git a/source/runtime/pom.xml b/source/runtime/pom.xml new file mode 100644 index 00000000..e17377a2 --- /dev/null +++ b/source/runtime/pom.xml @@ -0,0 +1,31 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + runtime + pom + + + runtime-context + runtime-modular + runtime-modular-booter + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/runtime/runtime-context/pom.xml b/source/runtime/runtime-context/pom.xml new file mode 100644 index 00000000..95bf6e1b --- /dev/null +++ b/source/runtime/runtime-context/pom.xml @@ -0,0 +1,19 @@ + + 4.0.0 + + com.jd.blockchain + runtime + 0.8.2.RELEASE + + runtime-context + + + + com.jd.blockchain + utils-common + ${project.version} + + + \ No newline at end of file diff --git a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java new file mode 100644 index 00000000..234b73fe --- /dev/null +++ b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/AbstractModule.java @@ -0,0 +1,108 @@ +package com.jd.blockchain.runtime; + +import java.io.InputStream; +import java.util.concurrent.Callable; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; +import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; + +public abstract class AbstractModule implements Module { + + + protected abstract ClassLoader getModuleClassLoader(); + + @Override + public String getMainClass(){return null;} + + @Override + public Class loadClass(String className) { + try { + return getModuleClassLoader().loadClass(className); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + @Override + public InputStream loadResourceAsStream(String name) { + return getModuleClassLoader().getResourceAsStream(name); + } + + @Override + public void execute(Runnable runnable) { + ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader moduleClassLoader = getModuleClassLoader(); + if (origClassLoader != moduleClassLoader) { + Thread.currentThread().setContextClassLoader(moduleClassLoader); + } + try { + runnable.run(); + } finally { + if (origClassLoader != Thread.currentThread().getContextClassLoader()) { + Thread.currentThread().setContextClassLoader(origClassLoader); + } + } + } + + @Override + public AsyncFuture executeAsync(Runnable runnable) { + ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader moduleClassLoader = getModuleClassLoader(); + if (origClassLoader != moduleClassLoader) { + Thread.currentThread().setContextClassLoader(moduleClassLoader); + } + return CompletableAsyncFuture.runAsync(new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } finally { + if (origClassLoader != Thread.currentThread().getContextClassLoader()) { + Thread.currentThread().setContextClassLoader(origClassLoader); + } + } + } + }); + + } + + @Override + public AsyncFuture callAsync(Callable callable) { + ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader moduleClassLoader = getModuleClassLoader(); + if (origClassLoader != moduleClassLoader) { + Thread.currentThread().setContextClassLoader(moduleClassLoader); + } + return CompletableAsyncFuture.callAsync(new Callable() { + @Override + public V call() throws Exception { + try { + return callable.call(); + } finally { + if (origClassLoader != Thread.currentThread().getContextClassLoader()) { + Thread.currentThread().setContextClassLoader(origClassLoader); + } + } + } + }); + } + + @Override + public V call(Callable callable) { + ClassLoader origClassLoader = Thread.currentThread().getContextClassLoader(); + ClassLoader moduleClassLoader = getModuleClassLoader(); + if (origClassLoader != moduleClassLoader) { + Thread.currentThread().setContextClassLoader(moduleClassLoader); + } + try { + return callable.call(); + } catch (Exception e) { + throw new IllegalStateException(e.getMessage(), e); + } finally { + if (origClassLoader != Thread.currentThread().getContextClassLoader()) { + Thread.currentThread().setContextClassLoader(origClassLoader); + } + } + } + +} \ No newline at end of file diff --git a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/Module.java b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/Module.java new file mode 100644 index 00000000..e2c064cd --- /dev/null +++ b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/Module.java @@ -0,0 +1,39 @@ +package com.jd.blockchain.runtime; + +import java.io.InputStream; +import java.util.concurrent.Callable; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +public interface Module { + + String getName(); + + Class loadClass(String className); + + InputStream loadResourceAsStream(String name); + + String getMainClass(); + +// Module getParent(); + + /** + * Run in this module's ClassLoader context; + * + * @param runnable + */ + void execute(Runnable runnable); + + /** + * Run asynchronize in this module's ClassLoader context; + * + * @param runnable + * @return + */ + AsyncFuture executeAsync(Runnable runnable); + + V call(Callable callable); + + AsyncFuture callAsync(Callable callable); + +} \ No newline at end of file diff --git a/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java new file mode 100644 index 00000000..c161371e --- /dev/null +++ b/source/runtime/runtime-context/src/main/java/com/jd/blockchain/runtime/RuntimeContext.java @@ -0,0 +1,209 @@ +package com.jd.blockchain.runtime; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.jar.Attributes; +import java.util.jar.JarFile; + +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.io.RuntimeIOException; + +public abstract class RuntimeContext { + + public static interface Environment{ + + boolean isProductMode(); + + } + + + private static final Object mutex = new Object(); + private static volatile RuntimeContext runtimeContext; + + public static RuntimeContext get() { + if (runtimeContext == null) { + synchronized (mutex) { + if (runtimeContext == null) { + runtimeContext = new DefaultRuntimeContext(); + } + } + } + return runtimeContext; + } + + protected static void set(RuntimeContext runtimeContext) { + if (RuntimeContext.runtimeContext != null) { + throw new IllegalStateException("RuntimeContext has been setted!"); + } + RuntimeContext.runtimeContext = runtimeContext; + } + + private Map modules = new ConcurrentHashMap<>(); + + public RuntimeContext() { + } + + private File getDynamicModuleJarFile(String name) { + name = name + ".mdl"; + return new File(getRuntimeDir(), name); + } + + public Module getDynamicModule(String name) { + return modules.get(name); + + } + + public List getDynamicModules() { + return new ArrayList<>(modules.values()); + } + + public Module createDynamicModule(String name, byte[] jarBytes) { + Module module = modules.get(name); + if (module != null) { + return module; + } + synchronized (DefaultRuntimeContext.class) { + module = modules.get(name); + if (module != null) { + return module; + } + } + + // Save File to Disk; + File jarFile = getDynamicModuleJarFile(name); + if (jarFile.exists()) { + if (jarFile.isFile()) { + FileUtils.deleteFile(jarFile); + } else { + throw new IllegalStateException("Code storage confliction! --" + jarFile.getAbsolutePath()); + } + } + FileUtils.writeBytes(jarBytes, jarFile); + + try { + URL jarURL = jarFile.toURI().toURL(); + ClassLoader moduleClassLoader = createDynamicModuleClassLoader(jarURL); + + Attributes m = new JarFile(jarFile).getManifest().getMainAttributes(); + String contractMainClass = m.getValue(Attributes.Name.MAIN_CLASS); + module = new DefaultModule(name, moduleClassLoader, contractMainClass); + modules.put(name, module); + + return module; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + + public abstract Environment getEnvironment(); + + protected abstract String getRuntimeDir(); + + protected abstract URLClassLoader createDynamicModuleClassLoader(URL jarURL); + + // ------------------------- inner types -------------------------- + + private static class EnvSettings implements Environment{ + + private boolean productMode; + + @Override + public boolean isProductMode() { + return productMode; + } + + public void setProductMode(boolean productMode) { + this.productMode = productMode; + } + + } + + private static class DefaultModule extends AbstractModule { + + private String name; + + private ClassLoader moduleClassLoader; + + private String MainClass; + + public DefaultModule(String name, ClassLoader cl, String mainClass) { + this.name = name; + this.moduleClassLoader = cl; + this.MainClass = mainClass; + } + + @Override + public String getMainClass() { + return MainClass; + } + + + @Override + public String getName() { + return name; + } + +// @Override +// public Module getParent() { +// return null; +// } + + @Override + protected ClassLoader getModuleClassLoader() { + return moduleClassLoader; + } + + } + + /** + * Default RuntimeContext is a context of that:
+ * all modules are running in a single class loader; + * + * @author huanghaiquan + * + */ + static class DefaultRuntimeContext extends RuntimeContext { + + protected String homeDir; + + protected String runtimeDir; + + protected EnvSettings environment; + + public DefaultRuntimeContext() { + + this.environment = new EnvSettings(); + this.environment.setProductMode(true); + + try { + this.homeDir = new File("./").getCanonicalPath(); + this.runtimeDir = new File(homeDir, "runtime").getAbsolutePath(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + @Override + public Environment getEnvironment() { + return environment; + } + + @Override + protected String getRuntimeDir() { + return runtimeDir; + } + + @Override + protected URLClassLoader createDynamicModuleClassLoader(URL jarURL) { + return new URLClassLoader(new URL[] { jarURL }, RuntimeContext.class.getClassLoader()); + } + + } +} diff --git a/source/runtime/runtime-modular-booter/pom.xml b/source/runtime/runtime-modular-booter/pom.xml new file mode 100644 index 00000000..a02e738c --- /dev/null +++ b/source/runtime/runtime-modular-booter/pom.xml @@ -0,0 +1,11 @@ + + 4.0.0 + + com.jd.blockchain + runtime + 0.8.2.RELEASE + + runtime-modular-booter + \ No newline at end of file diff --git a/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/ArgumentSet.java b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/ArgumentSet.java new file mode 100644 index 00000000..cc2a5102 --- /dev/null +++ b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/ArgumentSet.java @@ -0,0 +1,158 @@ +package com.jd.blockchain.runtime.boot; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ArgumentSet { + +// private static Pattern ARG_PATTERN = Pattern.compile("\\-.+\\={1}.*"); + + private ArgEntry[] args; + + private Map prefixArgs = new HashMap<>(); + + private Set options = new HashSet<>(); + + private ArgumentSet(ArgEntry[] args) { + this.args = args; + for (ArgEntry arg : args) { + if (arg.prefix != null) { + prefixArgs.put(arg.prefix, arg); + } + if (arg.option != null) { + options.add(arg.option); + } + } + } + + public static boolean hasOption(String[] args, String option) { + boolean contains = false; + if (args != null) { + for (String a : args) { + if (option.equalsIgnoreCase(a)) { + contains = true; + break; + } + } + } + return contains; + } + + public static Setting setting() { + return new Setting(); + } + + public static ArgumentSet resolve(String[] args, Setting setting) { + List argEntries = new ArrayList<>(); + for (int i = 0; i < args.length; i++) { + if (setting.prefixes.contains(args[i])) { + ArgEntry ae = new ArgEntry(); + ae.prefix = args[i]; + if (i+1 >= args.length) { + throw new IllegalArgumentException(String.format("缺少 %s 参数!", args[i])); + } + i++; + ae.value = args[i]; + argEntries.add(ae); + continue; + } + + if(setting.options.contains(args[i])){ + ArgEntry ae = new ArgEntry(); + ae.option = args[i]; + argEntries.add(ae); + continue; + } + } + return new ArgumentSet(argEntries.toArray(new ArgEntry[argEntries.size()])); + } + + /** + * 按照原始顺序排列的参数列表; + * + * @return + */ + public ArgEntry[] getArgs() { + return args; + } + + public ArgEntry getArg(String prefix) { + return prefixArgs.get(prefix); + } + + public boolean hasOption(String option) { + return options.contains(option); + } + + /** + * @author huanghaiquan + * + */ + public static class ArgEntry { + + private String prefix; + + private String value; + + private String option; + + /** + * 前缀;
+ * 如果不是前缀参数,则为 null; + * + * @return + */ + public String getPrefix() { + return prefix; + } + + /** + * 参数值;
+ * + * 如果只是选项参数,则返回 null; + * + * @return + */ + public String getValue() { + return value; + } + + /** + * 选项; + * + * @return + */ + public String getOption() { + return option; + } + + } + + public static class Setting { + + private Set prefixes = new HashSet<>(); + + private Set options = new HashSet<>(); + + private Setting() { + } + + + public Setting prefix(String... prefixes) { + this.prefixes.addAll(Arrays.asList(prefixes)); + return this; + } + + public Setting option(String... options) { + this.options.addAll(Arrays.asList(options)); + return this; + } + + } + +} diff --git a/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/ClassPathUtils.java b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/ClassPathUtils.java new file mode 100644 index 00000000..2d8d6051 --- /dev/null +++ b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/ClassPathUtils.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.runtime.boot; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URL; + +public class ClassPathUtils { + public static URL[] loadClassPaths(File dir) { + try { + File[] jars = dir.listFiles(f -> f.getName().endsWith(".jar") && f.isFile()); + URL[] classpaths = new URL[jars.length]; + for (int i = 0; i < classpaths.length; i++) { + classpaths[i] = jars[i].toURI().toURL(); + } + return classpaths; + } catch (MalformedURLException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } +} diff --git a/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/HomeBooter.java b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/HomeBooter.java new file mode 100644 index 00000000..6000f04c --- /dev/null +++ b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/HomeBooter.java @@ -0,0 +1,155 @@ +package com.jd.blockchain.runtime.boot; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.regex.Pattern; + +/** + * 一个模块化代码加载的启动器; + * + * @author huanghaiquan + * + */ +public class HomeBooter { + + public static final String LIBS = "libs"; + public static final String SYSTEM = "system"; + public static final String RUNTIME = "runtime"; + + private static final Pattern ARG_PATTERN = Pattern.compile("^[\\-]\\w+[ ]*(=).*$"); + + private static final String HOME = "-home"; + + private static final String MODE = "-mode"; + private static final String MODE_DEBUG = "debug"; + private static final String MODE_PRODUCT = "product"; + + public static final Pattern MODE_PATTERN = Pattern.compile("^(" + MODE + ")[ ]*(=).*$"); + + /** + * 以多 ClassLoader 方式启动系统; + *

+ * + * 以参数 -home 方式指定系统的 home 目录;
+ * 以参数 -mode=product 指定以生产模式启动,否则以 debug 模式启动; + *

+ * + * 系统 home 目录下,以 libs、systems、runtime 区分3个模块:
+ * libs: 公共库模块;
+ * system: 系统运行模块;
+ * runtime: 运行时动态模块; + * + * @param args + */ + public static HomeContext createHomeContext(String[] args) throws IOException { + Properties settings = resolveSettings(args); + + String mode = settings.getProperty(MODE); + boolean productMode = mode != null && MODE_PRODUCT.equalsIgnoreCase(mode); + + String home = settings.getProperty(HOME); + if (home == null) { + throw new IllegalArgumentException("Miss home dir!"); + } + + List peerArgList = new ArrayList<>(); + for (String a : args) { + if (a.startsWith(HOME) || a.startsWith(MODE)) { + continue; + } + peerArgList.add(a); + } + String[] peerArgs = peerArgList.size() > 0 ? peerArgList.toArray(new String[peerArgList.size()]) : new String[0]; + + File homeDir = new File(home); + if (!homeDir.isDirectory()) { + throw new IllegalArgumentException( + "Home directory don't exist or the path is a file! --" + homeDir.getCanonicalPath()); + } + String homeAbsPath = homeDir.getCanonicalPath(); + File libDir = new File(homeAbsPath, LIBS); + if (!libDir.isDirectory()) { + throw new IllegalArgumentException( + "Libs directory don't exist or the path is a file! --" + libDir.getAbsolutePath()); + } + File systemDir = new File(homeAbsPath, SYSTEM); + if (!systemDir.isDirectory()) { + throw new IllegalArgumentException( + "System directory don't exist or the path is a file! --" + systemDir.getAbsolutePath()); + } + File runtimeDir = new File(homeAbsPath, RUNTIME); + if (runtimeDir.isFile()) { + throw new IllegalArgumentException("Runtime dir path is a file! --" + runtimeDir.getAbsolutePath()); + } + if (!runtimeDir.exists()) { + runtimeDir.mkdirs(); + } + + // 以 ExtClassLoader 作为所有创建的ClassLoader的 Parrent; + ClassLoader extClassLoader = HomeBooter.class.getClassLoader().getParent(); + + URL[] libJars = loadClassPaths(libDir); + showJars("-------- lib jars --------", libJars); + URLClassLoader libClassLoader = new URLClassLoader(libJars, extClassLoader); + + URL[] systemJars = loadClassPaths(systemDir); + showJars("-------- system jars --------", systemJars); + URLClassLoader systemClassLoader = new URLClassLoader(systemJars, libClassLoader); + + return new HomeContext(libClassLoader, systemClassLoader, homeAbsPath, runtimeDir.getAbsolutePath(), + productMode, peerArgs); + } + + private static void showJars(String msg, URL[] jars) { + System.out.println(msg); + int i = 0; + for (URL url : jars) { + System.out.println(i + ": " + url.toString()); + } + System.out.println("-----------------"); + } + + public static URL[] loadClassPaths(File dir) { + try { + File[] jars = dir.listFiles(f -> f.getName().endsWith(".jar") && f.isFile()); + URL[] classpaths = new URL[jars.length]; + for (int i = 0; i < classpaths.length; i++) { + classpaths[i] = jars[i].toURI().toURL(); + } + return classpaths; + } catch (MalformedURLException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 解析参数中以 “-”或者“--”开头的以“=”分隔的键值参数; + * + * @param args + * @return + */ + private static Properties resolveSettings(String[] args) { + Properties prop = new Properties(); + if (args == null || args.length == 0) { + return prop; + } + + for (String arg : args) { + if (ARG_PATTERN.matcher(arg).matches()) { + int i = arg.indexOf("="); + String argName = arg.substring(0, i).trim(); + String argValue = arg.substring(i + 1, arg.length()).trim(); + prop.setProperty(argName, argValue); + } + } + + return prop; + } + +} diff --git a/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/HomeContext.java b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/HomeContext.java new file mode 100644 index 00000000..beae6545 --- /dev/null +++ b/source/runtime/runtime-modular-booter/src/main/java/com/jd/blockchain/runtime/boot/HomeContext.java @@ -0,0 +1,51 @@ +package com.jd.blockchain.runtime.boot; + +public class HomeContext { + + private String homeDir; + + private String runtimeDir; + + private boolean productMode; + + private ClassLoader libsClassLoader; + + private ClassLoader systemClassLoader; + + private String[] startingArgs; + + public HomeContext(ClassLoader libsClassLoader, ClassLoader systemClassLoader, String homeDir, String runtimeDir, + boolean productMode, String[] startingArgs) { + this.libsClassLoader = libsClassLoader; + this.systemClassLoader = systemClassLoader; + this.homeDir = homeDir; + this.runtimeDir = runtimeDir; + this.productMode = productMode; + this.startingArgs = startingArgs; + } + + public String getHomeDir() { + return homeDir; + } + + public String getRuntimeDir() { + return runtimeDir; + } + + public ClassLoader getLibsClassLoader() { + return libsClassLoader; + } + + public ClassLoader getSystemClassLoader() { + return systemClassLoader; + } + + public boolean isProductMode() { + return productMode; + } + + public String[] getStartingArgs() { + return startingArgs; + } + +} diff --git a/source/runtime/runtime-modular/pom.xml b/source/runtime/runtime-modular/pom.xml new file mode 100644 index 00000000..1354ce0a --- /dev/null +++ b/source/runtime/runtime-modular/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + com.jd.blockchain + runtime + 0.8.2.RELEASE + + runtime-modular + + + + com.jd.blockchain + runtime-context + ${project.version} + + + + \ No newline at end of file diff --git a/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/JarsModule.java b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/JarsModule.java new file mode 100644 index 00000000..94b64041 --- /dev/null +++ b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/JarsModule.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.runtime.modular; + +import com.jd.blockchain.runtime.AbstractModule; + +public class JarsModule extends AbstractModule { + + private String name; + + private ClassLoader classLoader; + + public JarsModule(String name, ClassLoader classLoader) { + this.name = name; + this.classLoader = classLoader; + } + + + @Override + public String getName() { + return name; + } + + @Override + protected ClassLoader getModuleClassLoader() { + return classLoader; + } + + +} diff --git a/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/ModularFactory.java b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/ModularFactory.java new file mode 100644 index 00000000..e38b3dd1 --- /dev/null +++ b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/ModularFactory.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.runtime.modular; + +public class ModularFactory { + + /** + * start system; + */ + public static void startSystem(String runtimeDir, boolean productMode, + ClassLoader libClassLoader,String mainClassName, ClassLoader systemClassLoader, String[] args) { + + JarsModule libModule = new JarsModule("LibModule", libClassLoader); + + ModularRuntimeContext runtimeContext = new ModularRuntimeContext(runtimeDir, libModule, productMode); + runtimeContext.register(); + + SystemModule systemModule = new SystemModule(mainClassName, systemClassLoader, runtimeContext); + systemModule.start(args); + } + +} diff --git a/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/ModularRuntimeContext.java b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/ModularRuntimeContext.java new file mode 100644 index 00000000..5b1bc5dc --- /dev/null +++ b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/ModularRuntimeContext.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.runtime.modular; + +import java.net.URL; +import java.net.URLClassLoader; + +import com.jd.blockchain.runtime.RuntimeContext; + +public class ModularRuntimeContext extends RuntimeContext { + + private String runtimeDir; + + private JarsModule libModule; + + private EnvSettings environment; + + public ModularRuntimeContext(String runtimeDir, JarsModule libModule, + boolean productMode) { + this.environment = new EnvSettings(); + this.environment.setProductMode(productMode); + + this.runtimeDir = runtimeDir; + this.libModule = libModule; + } + + void register() { + RuntimeContext.set(this); + } + + @Override + public Environment getEnvironment() { + return environment; + } + + @Override + protected String getRuntimeDir() { + return runtimeDir; + } + + @Override + protected URLClassLoader createDynamicModuleClassLoader(URL jarURL) { + return new URLClassLoader(new URL[] {jarURL}, libModule.getModuleClassLoader()); + } + + // --------------------------- inner types ----------------------------- + + private static class EnvSettings implements Environment { + + private boolean productMode; + + @Override + public boolean isProductMode() { + return productMode; + } + + public void setProductMode(boolean productMode) { + this.productMode = productMode; + } + + } +} diff --git a/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/MuduleClassLoader.java b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/MuduleClassLoader.java new file mode 100644 index 00000000..ad202462 --- /dev/null +++ b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/MuduleClassLoader.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.runtime.modular; + +import java.net.URL; +import java.net.URLClassLoader; + +public class MuduleClassLoader extends URLClassLoader { + private ClassLoader parent; + public MuduleClassLoader(URL[] urls, ClassLoader parent){ + super(urls, parent); + this.parent = parent; + } + + + @Override + public Class loadClass(String name) + throws ClassNotFoundException{ + if (name.equals("com.jd.blockchain.contract.model.ContractEventContext") ){ + return this.parent.loadClass(name); + } + return super.loadClass(name); + + } + +} diff --git a/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/SystemModule.java b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/SystemModule.java new file mode 100644 index 00000000..10aa7edc --- /dev/null +++ b/source/runtime/runtime-modular/src/main/java/com/jd/blockchain/runtime/modular/SystemModule.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.runtime.modular; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import com.jd.blockchain.runtime.AbstractModule; + +public class SystemModule extends AbstractModule { + + private ClassLoader classloader; + + private String mainClassName; + + private ModularRuntimeContext modularRuntimeContext; + + public SystemModule(String mainClassName, ClassLoader classloader, ModularRuntimeContext modularRuntimeContext) { + this.mainClassName = mainClassName; + this.classloader = classloader; + this.modularRuntimeContext = modularRuntimeContext; + } + + @Override + public String getName() { + return "SystemModule"; + } + + public ModularRuntimeContext getModularRuntimeContext() { + return modularRuntimeContext; + } + + @Override + protected ClassLoader getModuleClassLoader() { + return classloader; + } + + public void start(String[] args) { + execute(new Runnable() { + @Override + public void run() { + runMainClass(args); + } + }); + } + + private void runMainClass(String[] args) { + try { + Class mainClass = loadClass(mainClassName); + Method mainMethod = mainClass.getMethod("main", String[].class); + if (!Modifier.isStatic(mainMethod.getModifiers())) { + throw new IllegalArgumentException("Miss static main method in class[" + mainClassName + "]!"); + } + mainMethod.invoke(null, (Object) args); + } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { + throw new IllegalStateException( + String.format("Error occurred on running %s! --%s", mainClassName, e.getMessage()), e); + } + } + + +} diff --git a/source/sdk/pom.xml b/source/sdk/pom.xml new file mode 100644 index 00000000..67a240cc --- /dev/null +++ b/source/sdk/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + sdk + pom + + + sdk-base + + sdk-client + sdk-samples + + + + \ No newline at end of file diff --git a/source/sdk/sdk-base/pom.xml b/source/sdk/sdk-base/pom.xml new file mode 100644 index 00000000..e09d8150 --- /dev/null +++ b/source/sdk/sdk-base/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + + com.jd.blockchain + sdk + 0.8.2.RELEASE + + sdk-base + + + + com.jd.blockchain + ledger-model + ${project.version} + + + + com.jd.blockchain + ledger-rpc + ${project.version} + + + + com.jd.blockchain + consensus-framework + ${project.version} + + + binary-proto + com.jd.blockchain + + + crypto-framework + com.jd.blockchain + + + utils-common + com.jd.blockchain + + + + + + com.jd.blockchain + utils-serialize + ${project.version} + + + utils-common + com.jd.blockchain + + + + + + + \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/AbstractBlockchainServiceFactory.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/AbstractBlockchainServiceFactory.java new file mode 100644 index 00000000..e7293cc6 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/AbstractBlockchainServiceFactory.java @@ -0,0 +1,51 @@ +//package com.jd.blockchain.sdk; +// +//import java.io.Closeable; +// +//import com.jd.blockchain.ledger.CryptoSetting; +//import com.jd.blockchain.ledger.data.TransactionService; +//import com.jd.blockchain.sdk.proxy.BlockchainServiceProxy; +// +///** +// * +// * @author huanghaiquan +// * +// */ +//public abstract class AbstractBlockchainServiceFactory implements Closeable { +// +// private final Object mutex = new Object(); +// +// private volatile BlockchainService blockchainService; +// +//// protected ServiceSetting setting; +// +// private CryptoSetting cryptoSetting; +// +// public AbstractBlockchainServiceFactory() { +// } +// +// public BlockchainService getBlockchainService() { +// if (blockchainService == null) { +// synchronized (mutex) { +// if (blockchainService == null) { +// BlockchainQueryService queryService = getQueryService(setting); +// TransactionService consensusService = getConsensusService(setting); +// blockchainService = createBlockchainService(setting, consensusService, queryService); +// } +// } +// } +// return blockchainService; +// } +// +// protected BlockchainService createBlockchainService(ServiceSetting setting, TransactionService consensusService, BlockchainQueryService queryService) { +// return new BlockchainServiceProxy(consensusService, queryService); +// } +// +// protected abstract BlockchainQueryService getQueryService(ServiceSetting setting); +// +// protected abstract TransactionService getConsensusService(ServiceSetting setting); +// +// @Override +// public abstract void close(); +// +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventHandle.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventHandle.java new file mode 100644 index 00000000..383a72c4 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventHandle.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.sdk; + +/** + * BlockchainEventHandle 维护了一个具体的事件监听实例的状态,提供了在不需要继续监听时进行取消的方法 + * {@link #cancel()}; + * + * @author huanghaiquan + * + */ +public interface BlockchainEventHandle { + + /** + * 要监听的事件类型; + * + * @return + */ + int getFilteredEventTypes(); + + /** + * 要监听的交易;如果为 null,则不进行交易过滤; + * + * @return + */ + String getFilteredTxHash(); + + /** + * 要监听的账户地址;如果为 null,这不进行账户地址过滤; + * + * @return + */ + String getFilteredAccountAddress(); + + /** + * 监听器实例; + * + * @return + */ + BlockchainEventListener getListener(); + + /** + * 取消监听; + */ + void cancel(); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventListener.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventListener.java new file mode 100644 index 00000000..6b87a9cf --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventListener.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.sdk; + +public interface BlockchainEventListener { + + public void onEvent(BlockchainEventMessage eventMessage, BlockchainEventHandle eventHandle); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventMessage.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventMessage.java new file mode 100644 index 00000000..8a1a442d --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventMessage.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.sdk; + +import java.util.BitSet; + +import com.jd.blockchain.ledger.BlockchainEventType; + +public interface BlockchainEventMessage { + + /** + * 事件代码;
+ * + * 事件代码是本次事件的所有类型事件码的按位或的结果;

+ * + * 可以按以下方法检查是否包含某个特定事件:
+ * + * + * ({@link BlockchainEventType#PAYLOAD_UPDATED} & {@link #getEventCode()}) == {@link BlockchainEventType#PAYLOAD_UPDATED} + * + * + * @return + */ + int getEventCode(); + + /** + * 区块高度; + * + * @return + */ + long getLedgerNumber(); + + /** + * 包含本次事件中的所有成功交易的布隆过滤器(BloomFilter)的值; + * + * @return + */ + BitSet getBloomFilterOfTxs(); + + /** + * 包含本次事件中的所有变更账户的布隆过滤器(BloomFilter)的值; + * + * @return + */ + BitSet getBloomFilterOfAccounts(); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventService.java new file mode 100644 index 00000000..34f47d38 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainEventService.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.sdk; + +public interface BlockchainEventService { + + /** + * 注册区块链事件监听器; + * + * @param filteredEventTypes + * 要监听的事件类型; + * @param filteredTxHash + * 要监听的交易;如果为 null,则不进行交易过滤; + * @param filteredAccountAddress + * 要监听的账户地址;如果为 null,这不进行账户地址过滤; + * @param listener + * 监听器实例; + */ + BlockchainEventHandle addBlockchainEventListener(int filteredEventTypes, String filteredTxHash, + String filteredAccountAddress, BlockchainEventListener listener); + +} \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainException.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainException.java new file mode 100644 index 00000000..cb89c49e --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainException.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.sdk; + +public class BlockchainException extends RuntimeException { + + private static final long serialVersionUID = 8228291068740022658L; + + public BlockchainException() { + } + + public BlockchainException(String message) { + super(message); + } + + public BlockchainException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainExtendQueryService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainExtendQueryService.java new file mode 100644 index 00000000..664c0753 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainExtendQueryService.java @@ -0,0 +1,163 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.BlockchainExtendQueryService + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/10/19 上午9:34 + * Description: + */ +package com.jd.blockchain.sdk; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * + * @author shaozhuguang + * @create 2018/10/19 + * @since 1.0.0 + */ + +public interface BlockchainExtendQueryService extends BlockchainQueryService { + + /** + * 获取最新区块 + * + * @param ledgerHash + * 账本Hash + * @return + */ + LedgerBlock getLatestBlock(HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的交易总数(即该区块中交易集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + long getAdditionalTransactionCount(HashDigest ledgerHash, long blockHeight); + + /** + * 获取指定区块Hash中新增的交易总数(即该区块中交易集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + long getAdditionalTransactionCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 获取指定账本最新区块附加的交易数量 + * + * @param ledgerHash + * 账本Hash + * @return + */ + long getAdditionalTransactionCount(HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的数据账户总数(即该区块中数据账户集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + long getAdditionalDataAccountCount(HashDigest ledgerHash, long blockHeight); + + /** + * 获取指定区块Hash中新增的数据账户总数(即该区块中数据账户集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + long getAdditionalDataAccountCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 获取指定账本中附加的数据账户数量 + * + * @param ledgerHash + * 账本Hash + * @return + */ + long getAdditionalDataAccountCount(HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的用户总数(即该区块中用户集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + long getAdditionalUserCount(HashDigest ledgerHash, long blockHeight); + + /** + * 获取指定区块Hash中新增的用户总数(即该区块中用户集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + long getAdditionalUserCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 获取指定账本中新增的用户数量 + * + * @param ledgerHash + * 账本Hash + * @return + */ + long getAdditionalUserCount(HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的合约总数(即该区块中合约集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + long getAdditionalContractCount(HashDigest ledgerHash, long blockHeight); + + /** + * 获取指定区块Hash中新增的合约总数(即该区块中合约集合的数量) + * + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + long getAdditionalContractCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 获取指定账本中新增的合约数量 + * + * @param ledgerHash + * 账本Hash + * @return + */ + long getAdditionalContractCount(HashDigest ledgerHash); + + /** + * get all ledgers count; + */ + int getLedgersCount(); +} \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainQueryService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainQueryService.java new file mode 100644 index 00000000..22a11e1a --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainQueryService.java @@ -0,0 +1,317 @@ +package com.jd.blockchain.sdk; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; + +/** + * 区块链查询器; + * + * @author huanghaiquan + * + */ +public interface BlockchainQueryService { + + /** + * 返回所有的账本的 hash 列表;
+ * + * 注:账本的 hash 既是该账本的创世区块的 hash; + * + * @return 账本 hash 的集合; + */ + HashDigest[] getLedgerHashs(); + + /** + * 获取账本信息; + * + * @param ledgerHash + * @return 账本对象;如果不存在,则返回 null; + */ + LedgerInfo getLedger(HashDigest ledgerHash); + + /** + * 返回当前账本的参与者信息列表 + * + * @param ledgerHash + * @return + */ + ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash); + + /** + * 返回指定账本序号的区块; + * + * @param ledgerHash + * 账本hash; + * @param height + * 高度; + * @return + */ + LedgerBlock getBlock(HashDigest ledgerHash, long height); + + /** + * 返回指定区块hash的区块; + * + * @param ledgerHash + * 账本hash; + * @param blockHash + * 区块hash; + * @return + */ + LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 返回指定高度的区块中记录的交易总数; + * + * @param ledgerHash + * @param height + * @return + */ + long getTransactionCount(HashDigest ledgerHash, long height); + + /** + * 返回指定高度的区块中记录的交易总数; + * + * @param ledgerHash + * @param blockHash + * @return + */ + long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 返回当前账本的交易总数 + * + * @param ledgerHash + * @return + */ + long getTransactionTotalCount(HashDigest ledgerHash); + + /** + * 返回指定高度的区块中记录的数据账户总数 + * + * @param ledgerHash + * @param height + * @return + */ + long getDataAccountCount(HashDigest ledgerHash, long height); + + /** + * 返回指定的区块中记录的数据账户总数 + * + * @param ledgerHash + * @param blockHash + * @return + */ + long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 返回当前账本的数据账户总数 + * + * @param ledgerHash + * @return + */ + long getDataAccountTotalCount(HashDigest ledgerHash); + + /** + * 返回指定高度区块中的用户总数 + * + * @param ledgerHash + * @param height + * @return + */ + long getUserCount(HashDigest ledgerHash, long height); + + /** + * 返回指定区块中的用户总数 + * + * @param ledgerHash + * @param blockHash + * @return + */ + long getUserCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 返回当前账本的用户总数 + * + * @param ledgerHash + * @return + */ + long getUserTotalCount(HashDigest ledgerHash); + + /** + * 返回指定高度区块中的合约总数 + * + * @param ledgerHash + * @param height + * @return + */ + long getContractCount(HashDigest ledgerHash, long height); + + /** + * 返回指定区块中的合约总数 + * + * @param ledgerHash + * @param blockHash + * @return + */ + long getContractCount(HashDigest ledgerHash, HashDigest blockHash); + + /** + * 返回当前账本的合约总数 + * + * @param ledgerHash + * @return + */ + long getContractTotalCount(HashDigest ledgerHash); + + + /** + * 分页返回指定账本序号的区块中的交易列表; + * + * @param ledgerHash + * 账本hash; + * @param height + * 账本高度; + * @param fromIndex + * 开始的记录数; + * @param count + * 本次返回的记录数;
+ * 最小为1,最大值受到系统参数的限制;
+ * 注:通过 {@link #getBlock(String, long)} 方法获得的区块信息中可以得到区块的总交易数 + * {@link Block#getTxCount()}; + * @return + */ + LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count); + + /** + * 分页返回指定账本序号的区块中的交易列表; + * + * @param ledgerHash + * 账本hash; + * @param blockHash + * 账本高度; + * @param fromIndex + * 开始的记录数; + * @param count + * 本次返回的记录数;
+ * 如果参数值为 -1,则返回全部的记录;
+ * 注:通过 {@link #getBlock(String, String)} 方法获得的区块信息中可以得到区块的总交易数 + * {@link Block#getTxCount()}; + * @return + */ + LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count); + + /** + * 根据交易内容的哈希获取对应的交易记录; + * + * @param ledgerHash + * 账本hash; + * @param contentHash + * 交易内容的hash,即交易的 {@link Transaction#getContentHash()} 属性的值; + * @return + */ + LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash); + + /** + * 根据交易内容的哈希获取对应的交易状态; + * + * @param ledgerHash + * 账本hash; + * @param contentHash + * 交易内容的hash,即交易的 {@link Transaction#getContentHash()} 属性的值; + * @return + */ + TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash); + + /** + * 返回用户信息; + * + * @param ledgerHash + * @param address + * @return + */ + UserInfo getUser(HashDigest ledgerHash, String address); + + /** + * 返回数据账户信息; + * + * @param ledgerHash + * @param address + * @return + */ + AccountHeader getDataAccount(HashDigest ledgerHash, String address); + + /** + * 返回数据账户中指定的键的最新值;
+ * + * 返回结果的顺序与指定的键的顺序是一致的;
+ * + * 如果某个键不存在,则返回版本为 -1 的数据项; + * + * @param ledgerHash + * @param address + * @param keys + * @return + */ + KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys); + + /** + * 返回指定数据账户中KV数据的总数;
+ * + * @param ledgerHash + * @param address + * @return + */ + long getDataEntriesTotalCount(HashDigest ledgerHash, String address); + + /** + * 返回数据账户中指定序号的最新值; + * 返回结果的顺序与指定的序号的顺序是一致的;
+ * + * @param ledgerHash + * 账本hash; + * @param address + * 数据账户地址; + * @param fromIndex + * 开始的记录数; + * @param count + * 本次返回的记录数;
+ * 如果参数值为 -1,则返回全部的记录;
+ * @return + */ + KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count); + + /** + * 返回合约账户信息; + * + * @param ledgerHash + * @param address + * @return + */ + AccountHeader getContract(HashDigest ledgerHash, String address); + + /** + * get users by ledgerHash and its range; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count); + + /** + * get data accounts by ledgerHash and its range; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count); + + /** + * get contract accounts by ledgerHash and its range; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count); +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainService.java new file mode 100644 index 00000000..13f78855 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainService.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.sdk; + +/** + * 区块链服务; + * + *
+ * 这是一个门面服务(facade); + * + * @author huanghaiquan + * + */ +public interface BlockchainService extends BlockchainQueryService, BlockchainTransactionService, BlockchainEventService { + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainServiceFactory.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainServiceFactory.java new file mode 100644 index 00000000..cf605b05 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainServiceFactory.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.sdk; + +public interface BlockchainServiceFactory { + + BlockchainService getBlockchainService(); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainTransactionService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainTransactionService.java new file mode 100644 index 00000000..5c9a59c8 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/BlockchainTransactionService.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.sdk; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.TransactionTemplate; + +public interface BlockchainTransactionService { + + /** + * 发起新交易; + * + * @return + */ + TransactionTemplate newTransaction(HashDigest ledgerHash); + +// /** +// * 以指定的科目和流水号发起新交易; +// * +// * @param subjectAccount +// * 交易的科目账户地址; +// * @param sequenceNumber +// * 交易的流水号; +// * @return +// */ +// TransactionTemplate newTransaction(ByteArray ledgerHash, String subjectAccount, long sequenceNumber); + +} \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/DefaultServiceSetting.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/DefaultServiceSetting.java new file mode 100644 index 00000000..59f0b91a --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/DefaultServiceSetting.java @@ -0,0 +1,17 @@ +//package com.jd.blockchain.sdk; +// +//import com.jd.blockchain.crypto.CryptoAlgorithm; +// +//public class DefaultServiceSetting implements ServiceSetting { +// +// public static final DefaultServiceSetting INSTANCE = new DefaultServiceSetting(); +// +// private DefaultServiceSetting() { +// } +// +// @Override +// public CryptoAlgorithm getHashAlgorithm() { +// return CryptoAlgorithm.SHA256; +// } +// +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/LedgerAccessContext.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/LedgerAccessContext.java new file mode 100644 index 00000000..bc4d8c24 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/LedgerAccessContext.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.sdk; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.data.TransactionService; + +public interface LedgerAccessContext { + + HashDigest getLedgerHash(); + + CryptoSetting getCryptoSetting(); + + TransactionService getTransactionService(); + + BlockchainQueryService getQueryService(); + + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/ManagementHttpService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/ManagementHttpService.java new file mode 100644 index 00000000..5d35623f --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/ManagementHttpService.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.sdk; + +import com.jd.blockchain.consensus.ClientIdentifications; +import com.jd.blockchain.manage.GatewayIncomingSetting; + +import com.jd.blockchain.sdk.converters.BinarySerializeRequestConverter; +import com.jd.blockchain.sdk.converters.BinarySerializeResponseConverter; +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.HttpService; +import com.jd.blockchain.utils.http.RequestBody; +import com.jd.blockchain.utils.web.client.WebResponseConverterFactory; + + +@HttpService(path="/management", defaultRequestBodyConverter = BinarySerializeRequestConverter.class, responseConverterFactory=WebResponseConverterFactory.class) +public interface ManagementHttpService { + + @HttpAction(method=HttpMethod.POST, path="/gateway/auth", contentType = BinarySerializeRequestConverter.CONTENT_TYPE_VALUE) + public GatewayIncomingSetting authenticateGateway(@RequestBody ClientIdentifications clientIdentifications) ; + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/PrivilegeSetting.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/PrivilegeSetting.java new file mode 100644 index 00000000..c78ee579 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/PrivilegeSetting.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.sdk; + +import com.jd.blockchain.ledger.PrivilegeType; + +/** + * 权限设置;
+ * + * + * + * @author huanghaiquan + * + */ +public interface PrivilegeSetting { + + String[] getSigners(); + + long getMask(String address); + + boolean isEnable(String address, PrivilegeType privilege); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/ServiceSetting.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/ServiceSetting.java new file mode 100644 index 00000000..4f85a2c4 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/ServiceSetting.java @@ -0,0 +1,9 @@ +//package com.jd.blockchain.sdk; +// +//import com.jd.blockchain.crypto.CryptoAlgorithm; +// +//public interface ServiceSetting { +// +// CryptoAlgorithm getHashAlgorithm(); +// +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/TransactionalScope.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/TransactionalScope.java new file mode 100644 index 00000000..af51d0c7 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/TransactionalScope.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.sdk; + +public interface TransactionalScope { + + void startNewTransaction(Runnable runnable); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/TxCommiter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/TxCommiter.java new file mode 100644 index 00000000..94963e19 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/TxCommiter.java @@ -0,0 +1,11 @@ +//package com.jd.blockchain.sdk; +// +//import com.jd.blockchain.ledger.DigitalSignature; +//import com.jd.blockchain.ledger.TransactionContent; +//import com.jd.blockchain.ledger.TransactionResponse; +// +//public interface TxCommiter { +// +// public TransactionResponse commitTx(TransactionContent txContent, DigitalSignature[] signatures); +// +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/BinarySerializeRequestConverter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/BinarySerializeRequestConverter.java new file mode 100644 index 00000000..92168134 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/BinarySerializeRequestConverter.java @@ -0,0 +1,44 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.converters.BinarySerializeRequestConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/5 下午5:09 + * Description: 序列化请求体 + */ +package com.jd.blockchain.sdk.converters; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.consensus.ClientIdentifications; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.data.TxRequestMessage; +import com.jd.blockchain.utils.http.RequestBodyConverter; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * 序列化请求体 + * @author shaozhuguang + * @create 2018/9/5 + * @since 1.0.0 + */ + +public class BinarySerializeRequestConverter implements RequestBodyConverter { + + public static final String CONTENT_TYPE_VALUE = "application/bin-obj"; + + @Override + public void write(Object param, OutputStream out) throws IOException { + // 使用自定义的序列化方式 + if (param instanceof TransactionRequest) { + byte[] serializeBytes = BinaryEncodingUtils.encode(param, TransactionRequest.class); + out.write(serializeBytes); + out.flush(); + } else if (param instanceof ClientIdentifications) { + byte[] serializeBytes = BinaryEncodingUtils.encode(param, ClientIdentifications.class); + out.write(serializeBytes); + out.flush(); + } + } +} \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/BinarySerializeResponseConverter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/BinarySerializeResponseConverter.java new file mode 100644 index 00000000..163bc33e --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/BinarySerializeResponseConverter.java @@ -0,0 +1,37 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.converters.BinarySerializeResponseConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/5 下午5:22 + * Description: + */ +package com.jd.blockchain.sdk.converters; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.io.BytesUtils; + +import java.io.InputStream; + +/** + * + * @author shaozhuguang + * @create 2018/9/5 + * @since 1.0.0 + */ + +public class BinarySerializeResponseConverter implements ResponseConverter { + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) + throws Exception { + byte[] serializeBytes = BytesUtils.readBytes(responseStream); + Object resolvedObj = BinaryEncodingUtils.decode(serializeBytes); + return resolvedObj; + } + +} \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/HashDigestToStringConverter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/HashDigestToStringConverter.java new file mode 100644 index 00000000..2ac85443 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/HashDigestToStringConverter.java @@ -0,0 +1,30 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: my.utils.http.converters.HashDigestToStringConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/14 下午2:24 + * Description: HashDigest转为字符串 + */ +package com.jd.blockchain.sdk.converters; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.http.StringConverter; + +/** + * HashDigest转为字符串 + * @author shaozhuguang + * @create 2018/9/14 + * @since 1.0.0 + */ + +public class HashDigestToStringConverter implements StringConverter { + + @Override + public String toString(Object param) { + if (param instanceof HashDigest) { + return ((HashDigest) param).toBase58(); + } + return null; + } +} \ No newline at end of file diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/HashDigestsResponseConverter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/HashDigestsResponseConverter.java new file mode 100644 index 00000000..9c766e5e --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/converters/HashDigestsResponseConverter.java @@ -0,0 +1,75 @@ +package com.jd.blockchain.sdk.converters; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.http.converters.JsonResponseConverter; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import com.jd.blockchain.utils.web.client.WebServiceException; +import com.jd.blockchain.utils.web.model.WebResponse; + +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Created by zhangshuang3 on 2018/10/17. + */ +public class HashDigestsResponseConverter implements ResponseConverter { + + private JsonResponseConverter jsonConverter = new JsonResponseConverter(WebResponse.class); + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) throws Exception { + WebResponse response = (WebResponse) jsonConverter.getResponse(request, responseStream, null); + if (response == null) { + return null; + } + if (response.getError() != null) { + throw new WebServiceException(response.getError().getErrorCode(), response.getError().getErrorMessage()); + } + if (response.getData() == null) { + return null; + } + + +// byte[] serializeBytes = BytesUtils.readBytes(responseStream); +// String jsonChar = new String(serializeBytes, "UTF-8"); +// JSONArray jsonArray = JSON.parseArray(jsonChar); +// List hashDigests = new ArrayList<>(); +// for (Object obj : jsonArray) { +// if (obj instanceof JSONObject) { +// String base58Str = ((JSONObject)obj).getString("value"); +// hashDigests.add(new HashDigest(Base58Utils.decode(base58Str))); +// } +// } + return deserialize(response.getData()); + } + + private Object deserialize(Object object) { + List hashDigests = new ArrayList<>(); + if (object instanceof JSONArray) { + JSONArray jsonArray = (JSONArray)object; + for (Object obj : jsonArray) { + if (obj instanceof Map) { + Map objMap = (Map)obj; + String base58Str = objMap.get("value"); + hashDigests.add(new HashDigest(Base58Utils.decode(base58Str))); + } + } + } + return hashDigests.toArray(new HashDigest[hashDigests.size()]); + } +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java new file mode 100644 index 00000000..1b842090 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/BlockchainServiceProxy.java @@ -0,0 +1,174 @@ +package com.jd.blockchain.sdk.proxy; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.ledger.data.TxTemplate; +import com.jd.blockchain.sdk.BlockchainEventHandle; +import com.jd.blockchain.sdk.BlockchainEventListener; +import com.jd.blockchain.sdk.BlockchainQueryService; +import com.jd.blockchain.sdk.BlockchainService; + +public abstract class BlockchainServiceProxy implements BlockchainService { + + + protected abstract TransactionService getTransactionService(HashDigest ledgerHash); + + protected abstract BlockchainQueryService getQueryService(HashDigest ledgerHash); + + @Override + public TransactionTemplate newTransaction(HashDigest ledgerHash) { + return new TxTemplate(ledgerHash, getTransactionService(ledgerHash)); + } + + @Override + public BlockchainEventHandle addBlockchainEventListener(int filteredEventTypes, String filteredTxHash, + String filteredAccountAddress, BlockchainEventListener listener) { + throw new IllegalStateException("Not implemented!"); + } + + @Override + public LedgerInfo getLedger(HashDigest ledgerHash) { + return getQueryService(ledgerHash).getLedger(ledgerHash); + } + + @Override + public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { + return getQueryService(ledgerHash).getConsensusParticipants(ledgerHash); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, long height) { + return getQueryService(ledgerHash).getBlock(ledgerHash, height); + } + + @Override + public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { + return getQueryService(ledgerHash).getBlock(ledgerHash, blockHash); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, long height) { + return getQueryService(ledgerHash).getTransactionCount(ledgerHash, height); + } + + @Override + public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { + return getQueryService(ledgerHash).getTransactionCount(ledgerHash, blockHash); + } + + @Override + public long getTransactionTotalCount(HashDigest ledgerHash) { + return getQueryService(ledgerHash).getTransactionTotalCount(ledgerHash); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, long height) { + return getQueryService(ledgerHash).getDataAccountCount(ledgerHash, height); + } + + @Override + public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { + return getQueryService(ledgerHash).getDataAccountCount(ledgerHash, blockHash); + } + + @Override + public long getDataAccountTotalCount(HashDigest ledgerHash) { + return getQueryService(ledgerHash).getDataAccountTotalCount(ledgerHash); + } + + @Override + public long getUserCount(HashDigest ledgerHash, long height) { + return getQueryService(ledgerHash).getUserCount(ledgerHash, height); + } + + @Override + public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { + return getQueryService(ledgerHash).getUserCount(ledgerHash, blockHash); + } + + @Override + public long getUserTotalCount(HashDigest ledgerHash) { + return getQueryService(ledgerHash).getUserTotalCount(ledgerHash); + } + + @Override + public long getContractCount(HashDigest ledgerHash, long height) { + return getQueryService(ledgerHash).getContractCount(ledgerHash, height); + } + + @Override + public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { + return getQueryService(ledgerHash).getContractCount(ledgerHash, blockHash); + } + + @Override + public long getContractTotalCount(HashDigest ledgerHash) { + return getQueryService(ledgerHash).getContractTotalCount(ledgerHash); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { + return getQueryService(ledgerHash).getTransactions(ledgerHash, height, fromIndex, count); + } + + @Override + public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { + return getQueryService(ledgerHash).getTransactions(ledgerHash, blockHash, fromIndex, count); + } + + @Override + public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + return getQueryService(ledgerHash).getTransactionByContentHash(ledgerHash, contentHash); + } + + @Override + public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { + return getQueryService(ledgerHash).getTransactionStateByContentHash(ledgerHash, contentHash); + } + + @Override + public UserInfo getUser(HashDigest ledgerHash, String address) { + return getQueryService(ledgerHash).getUser(ledgerHash, address); + } + + @Override + public AccountHeader getDataAccount(HashDigest ledgerHash, String address) { + return getQueryService(ledgerHash).getDataAccount(ledgerHash, address); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { + return getQueryService(ledgerHash).getDataEntries(ledgerHash, address, keys); + } + + @Override + public KVDataEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { + return getQueryService(ledgerHash).getDataEntries(ledgerHash, address, fromIndex, count); + } + + @Override + public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { + return getQueryService(ledgerHash).getDataEntriesTotalCount(ledgerHash, address); + } + + @Override + public AccountHeader getContract(HashDigest ledgerHash, String address) { + return getQueryService(ledgerHash).getContract(ledgerHash, address); + } + + @Override + public AccountHeader[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { + return getQueryService(ledgerHash).getUsers(ledgerHash, fromIndex, count); + } + + @Override + public AccountHeader[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { + return getQueryService(ledgerHash).getDataAccounts(ledgerHash, fromIndex, count); + } + + @Override + public AccountHeader[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { + return getQueryService(ledgerHash).getContractAccounts(ledgerHash, fromIndex, count); + } +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/CodeDeployOperationBuilder.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/CodeDeployOperationBuilder.java new file mode 100644 index 00000000..7a524131 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/CodeDeployOperationBuilder.java @@ -0,0 +1,74 @@ +//package com.jd.blockchain.sdk.proxy; +// +//import com.jd.blockchain.ledger.*; +//import com.jd.blockchain.ledger.data.BlockchainOperationFactory; +//import com.jd.blockchain.ledger.data.OpBlob; +//import com.jd.blockchain.ledger.data.PrivilegeSettingOperationBuilder; +//import my.utils.io.ByteArray; +// +//public class CodeDeployOperationBuilder implements CodeDeployOperation { +// +// private TxTemplate txTemp; +// +// public CodeDeployOperationBuilder(TxTemplate txTemp) { +// this.txTemp = txTemp; +// } +// +// /** +// * 修改脚本; +// * +// * @param id +// * @param code 合约代码; +// * @param codeVersion +// */ +// @Override +// public void set(BlockchainIdentity id, String code, long codeVersion) { +// ContractDeployingOperation codeOperation = BlockchainOperationFactory.getInstance().deploy(id, ByteArray.wrap(code.getBytes())); +// +// txTemp.addOperation((OpBlob) codeOperation.getOperation()); +// } +// +// /** +// * 配置特权操作; +// * +// * @param accountAddress 账户地址; +// * @return +// */ +// @Override +// public PrivilegeSettingOperationBuilder configPrivilege(String accountAddress) { +// return null; +// } +// +// /** +// * 执行针对负载类型 {@link AccountStateType}为 {@link AccountStateType#MAP} 的账户操作; +// * +// * @param accountAddress 要操作的账户地址; +// * @return +// */ +// @Override +// public MapStateOperationBuilder updateState(String accountAddress) { +// return null; +// } +// +// /** +// * 执行定义账户的合约脚本的操作; +// * +// * @param accountAddress 要操作的账户地址; +// * @return +// */ +// @Override +// public CodeDeployOperation defineScript(String accountAddress) { +// return null; +// } +// +// /** +// * 执行调用账户的合约脚本的方法的操作; +// * +// * @param accountAddress 要操作的账户地址; +// * @return +// */ +// @Override +// public ScriptInvokingOperation executeScript(String accountAddress) { +// return null; +// } +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/CodeInvokeOperationBuilder.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/CodeInvokeOperationBuilder.java new file mode 100644 index 00000000..8e5958c7 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/CodeInvokeOperationBuilder.java @@ -0,0 +1,73 @@ +//package com.jd.blockchain.sdk.proxy; +// +//import com.jd.blockchain.ledger.CodeDeployOperation; +//import com.jd.blockchain.ledger.ContractEventOperation; +//import com.jd.blockchain.ledger.MapStateOperationBuilder; +//import com.jd.blockchain.ledger.ScriptInvokingOperation; +//import com.jd.blockchain.ledger.data.BlockchainOperationFactory; +//import com.jd.blockchain.ledger.data.OpBlob; +//import com.jd.blockchain.ledger.data.PrivilegeSettingOperationBuilder; +// +//import my.utils.io.ByteArray; +// +//public class CodeInvokeOperationBuilder implements ScriptInvokingOperation { +// private TxTemplate txTemp; +// +// public CodeInvokeOperationBuilder(TxTemplate txTemp) { +// this.txTemp = txTemp; +// } +// +// @Override +// public void invoke(String address, String[] args) { +// ContractEventOperation operation = BlockchainOperationFactory.getInstance().event(address, new ByteArray[] {}); +// +// txTemp.addOperation((OpBlob) operation.getOperation()); +// } +// +// /** +// * 配置特权操作; +// * +// * @param accountAddress +// * 账户地址; +// * @return +// */ +// @Override +// public PrivilegeSettingOperationBuilder configPrivilege(String accountAddress) { +// return null; +// } +// +// /** +// * +// * @param accountAddress +// * 要操作的账户地址; +// * @return +// */ +// @Override +// public MapStateOperationBuilder updateState(String accountAddress) { +// return null; +// } +// +// /** +// * 执行定义账户的合约脚本的操作; +// * +// * @param accountAddress +// * 要操作的账户地址; +// * @return +// */ +// @Override +// public CodeDeployOperation defineScript(String accountAddress) { +// return null; +// } +// +// /** +// * 执行调用账户的合约脚本的方法的操作; +// * +// * @param accountAddress +// * 要操作的账户地址; +// * @return +// */ +// @Override +// public ScriptInvokingOperation executeScript(String accountAddress) { +// return null; +// } +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/HttpBlockchainQueryService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/HttpBlockchainQueryService.java new file mode 100644 index 00000000..319d549c --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/HttpBlockchainQueryService.java @@ -0,0 +1,575 @@ +package com.jd.blockchain.sdk.proxy; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainExtendQueryService; +import com.jd.blockchain.sdk.BlockchainQueryService; + +import com.jd.blockchain.sdk.converters.HashDigestsResponseConverter; +import com.jd.blockchain.utils.http.*; +import com.jd.blockchain.utils.web.client.WebResponseConverterFactory; +import com.jd.blockchain.sdk.converters.HashDigestToStringConverter; + +/** + * 作为内部使用的适配接口,用于声明 HTTP 协议的服务请求; + * + * @author huanghaiquan + * + */ +@HttpService(responseConverterFactory=WebResponseConverterFactory.class) +public interface HttpBlockchainQueryService extends BlockchainExtendQueryService { + + /** + * 返回所有的账本的 hash 列表;
+ * + * 注:账本的 hash 既是该账本的创世区块的 hash; + * + * @return Base64编码的账本 hash 的集合; + */ + @HttpAction(method=HttpMethod.GET, path="ledgers", responseConverter = HashDigestsResponseConverter.class) + @Override + HashDigest[] getLedgerHashs(); + + /** + * 获取账本信息; + * + * @param ledgerHash + * @return 账本对象;如果不存在,则返回 null; + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}") + @Override + LedgerInfo getLedger(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 获取最新区块 + * + * @param ledgerHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/latest") + @Override + LedgerBlock getLatestBlock(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的交易总数(即该区块中交易集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/additional-count") + @Override + long getAdditionalTransactionCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long blockHeight); + + /** + * 获取指定区块Hash中新增的交易总数(即该区块中交易集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/additional-count") + @Override + long getAdditionalTransactionCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 获取指定账本最新区块新增的交易数量 + * @param ledgerHash + * 账本Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/txs/additional-count") + @Override + long getAdditionalTransactionCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的数据账户总数(即该区块中数据账户集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/additional-count") + @Override + long getAdditionalDataAccountCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long blockHeight); + + /** + * 获取指定区块Hash中新增的数据账户总数(即该区块中数据账户集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/additional-count") + @Override + long getAdditionalDataAccountCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 获取指定账本中附加的数据账户数量 + * @param ledgerHash + * 账本Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/accounts/additional-count") + @Override + long getAdditionalDataAccountCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + + /** + * 获取指定区块高度中新增的用户总数(即该区块中用户集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/additional-count") + @Override + long getAdditionalUserCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long blockHeight); + + /** + * 获取指定区块Hash中新增的用户总数(即该区块中用户集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/additional-count") + @Override + long getAdditionalUserCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 获取指定账本中新增的用户数量 + * @param ledgerHash + * 账本Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/users/additional-count") + @Override + long getAdditionalUserCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 获取指定区块高度中新增的合约总数(即该区块中合约集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHeight + * 区块高度 + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/additional-count") + @Override + long getAdditionalContractCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long blockHeight); + + /** + * 获取指定区块Hash中新增的合约总数(即该区块中合约集合的数量) + * @param ledgerHash + * 账本Hash + * @param blockHash + * 区块Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/additional-count") + @Override + long getAdditionalContractCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 获取指定账本中新增的合约数量 + * @param ledgerHash + * 账本Hash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/contracts/additional-count") + @Override + long getAdditionalContractCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 返回指定账本的参与列表 + * + * @param ledgerHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/participants") + @Override + ParticipantNode[] getConsensusParticipants(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 返回指定账本序号的区块; + * + * @param ledgerHash + * 账本hash; + * @param blockHeight + * 高度; + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}") + @Override + LedgerBlock getBlock(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long blockHeight); + + /** + * 返回指定区块hash的区块; + * + * @param ledgerHash + * 账本hash; + * @param blockHash + * 区块hash; + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}") + @Override + LedgerBlock getBlock(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 返回指定高度的区块中记录的交易总数; + * + * @param ledgerHash + * @param height + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs/count") + @Override + long getTransactionCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long height); + + /** + * 返回指定hash的区块中记录的交易总数; + * + * @param ledgerHash + * @param blockHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs/count") + @Override + long getTransactionCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 返回账本的交易总数 + * + * @param ledgerHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/txs/count") + @Override + long getTransactionTotalCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 返回指定账本和区块的数据账户总数 + * + * @param ledgerHash + * @param height + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/accounts/count") + @Override + long getDataAccountCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long height); + + /** + * 返回指定账本和区块的数据账户总数 + * + * @param ledgerHash + * @param blockHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/accounts/count") + @Override + long getDataAccountCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 返回指定账本的数据账户总数 + * + * @param ledgerHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/accounts/count") + @Override + long getDataAccountTotalCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 返回指定账本和区块的用户总数 + * + * @param ledgerHash + * @param height + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/users/count") + @Override + long getUserCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long height); + + /** + * 返回指定账本和区块的用户总数 + * + * @param ledgerHash + * @param blockHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/users/count") + @Override + long getUserCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 返回指定账本的用户总数 + * + * @param ledgerHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/users/count") + @Override + long getUserTotalCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 返回指定账本和区块的合约总数 + * + * @param ledgerHash + * @param height + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/contracts/count") + @Override + long getContractCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long height); + + /** + * 返回指定账本和区块的合约总数 + * + * @param ledgerHash + * @param blockHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/contracts/count") + @Override + long getContractCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash); + + /** + * 返回指定账本的合约总数 + * + * @param ledgerHash + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/contracts/count") + @Override + long getContractTotalCount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash); + + /** + * 分页返回指定账本序号的区块中的交易列表; + * + * @param ledgerHash + * 账本hash; + * @param height + * 账本高度; + * @param fromIndex + * 开始的记录数; + * @param count + * 本次返回的记录数;
+ * 最小为1,最大值受到系统参数的限制;
+ * 注:通过 {@link #getBlock(String, long)} 方法获得的区块信息中可以得到区块的总交易数 + * {@link Block#getTxCount()}; + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/height/{blockHeight}/txs") + @Override + LedgerTransaction[] getTransactions(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHeight") long height, + @RequestParam(name="fromIndex", required = false) int fromIndex, + @RequestParam(name="count", required = false) int count); + + /** + * 分页返回指定账本序号的区块中的交易列表; + * + * @param ledgerHash + * 账本hash; + * @param blockHash + * 账本高度; + * @param fromIndex + * 开始的记录数; + * @param count + * 本次返回的记录数;
+ * 如果参数值为 -1,则返回全部的记录;
+ * 注:通过 {@link #getBlock(String, String)} 方法获得的区块信息中可以得到区块的总交易数 + * {@link Block#getTxCount()}; + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/blocks/hash/{blockHash}/txs") + @Override + LedgerTransaction[] getTransactions(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="blockHash", converter=HashDigestToStringConverter.class) HashDigest blockHash, + @RequestParam(name="fromIndex", required = false) int fromIndex, + @RequestParam(name="count", required = false) int count); + + /** + * 根据交易内容的哈希获取对应的交易记录; + * + * @param ledgerHash + * 账本hash; + * @param contentHash + * 交易内容的hash,即交易的 {@link Transaction#getContentHash()} 属性的值; + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/txs/{contentHash}") + @Override + LedgerTransaction getTransactionByContentHash(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="contentHash", converter=HashDigestToStringConverter.class) HashDigest contentHash); + + /** + * + * 返回交易状态 + * + * @param ledgerHash + * 账本hash; + * @param contentHash + * 交易内容的hash,即交易的 {@link Transaction#getContentHash()} 属性的值; + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/txs/state/{contentHash}") + @Override + TransactionState getTransactionStateByContentHash(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="contentHash", converter=HashDigestToStringConverter.class) HashDigest contentHash); + + /** + * 返回用户信息; + * + * @param ledgerHash + * @param address + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/users/address/{address}") + @Override + UserInfo getUser(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="address") String address); + + /** + * 返回数据账户信息; + * + * @param ledgerHash + * @param address + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/accounts/address/{address}") + @Override + AccountHeader getDataAccount(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="address") String address); + + /** + * 返回数据账户中指定的键的最新值;
+ * + * 返回结果的顺序与指定的键的顺序是一致的;
+ * + * 如果某个键不存在,则返回版本为 -1 的数据项; + * + * @param ledgerHash + * @param address + * @param keys + * @return + */ + @HttpAction(method=HttpMethod.POST, path="ledgers/{ledgerHash}/accounts/{address}/entries") + @Override + KVDataEntry[] getDataEntries(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="address") String address, + @RequestParam(name="keys", array = true) String... keys); + + @HttpAction(method = HttpMethod.POST, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries") + + /** + * 返回数据账户中指定序号的最新值; + * 返回结果的顺序与指定的序号的顺序是一致的;
+ * + * @param ledgerHash + * 账本hash; + * @param address + * 数据账户地址; + * @param fromIndex + * 开始的记录数; + * @param count + * 本次返回的记录数;
+ * 如果参数值为 -1,则返回全部的记录;
+ * @return + */ + @Override + KVDataEntry[] getDataEntries(@PathParam(name = "ledgerHash") HashDigest ledgerHash, + @PathParam(name = "address") String address, + @RequestParam(name = "fromIndex", required = false) int fromIndex, + @RequestParam(name = "count", required = false) int count); + + /** + * 返回指定数据账户中KV数据的总数; + * @param ledgerHash + * @param address + * @return + */ + @HttpAction(method = HttpMethod.GET, path = "ledgers/{ledgerHash}/accounts/address/{address}/entries/count") + @Override + long getDataEntriesTotalCount(@PathParam(name = "ledgerHash") HashDigest ledgerHash, + @PathParam(name = "address") String address); + + /** + * 返回合约账户信息; + * + * @param ledgerHash + * @param address + * @return + */ + @HttpAction(method=HttpMethod.GET, path="ledgers/{ledgerHash}/contracts/address/{address}") + @Override + AccountHeader getContract(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @PathParam(name="address") String address); + + + /** + * get more users by fromIndex and count; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + @HttpAction(method = HttpMethod.GET, path = "ledgers/{ledgerHash}/users") + @Override + AccountHeader[] getUsers(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @RequestParam(name="fromIndex", required = false) int fromIndex, + @RequestParam(name="count", required = false) int count); + + /** + * get data accounts by ledgerHash and its range; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + @HttpAction(method = HttpMethod.GET, path = "ledgers/{ledgerHash}/accounts") + @Override + AccountHeader[] getDataAccounts(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @RequestParam(name="fromIndex", required = false) int fromIndex, + @RequestParam(name="count", required = false) int count); + + /** + * get contract accounts by ledgerHash and its range; + * @param ledgerHash + * @param fromIndex + * @param count + * @return + */ + @HttpAction(method = HttpMethod.GET, path = "ledgers/{ledgerHash}/contracts") + @Override + AccountHeader[] getContractAccounts(@PathParam(name="ledgerHash", converter=HashDigestToStringConverter.class) HashDigest ledgerHash, + @RequestParam(name="fromIndex", required = false) int fromIndex, + @RequestParam(name="count", required = false) int count); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/HttpQueryServiceAdapter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/HttpQueryServiceAdapter.java new file mode 100644 index 00000000..cb44c64f --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/proxy/HttpQueryServiceAdapter.java @@ -0,0 +1,136 @@ +//package com.jd.blockchain.sdk.proxy; +// +//import java.util.Map; +//import java.util.Set; +// +//import org.springframework.util.Base64Utils; +// +//import com.jd.blockchain.ledger.Block; +//import com.jd.blockchain.ledger.BlockchainAccount; +//import com.jd.blockchain.ledger.Ledger; +//import com.jd.blockchain.ledger.StateMap; +//import com.jd.blockchain.ledger.Transaction; +//import com.jd.blockchain.sdk.BlockchainQueryService; +//import com.jd.blockchain.service.LedgerQueryHttpService; +// +//import my.utils.http.agent.HttpServiceAgent; +//import my.utils.http.agent.ServiceEndpoint; +//import my.utils.net.NetworkAddress; +//import my.utils.serialize.binary.BinarySerializeUtils; +// +//public class HttpQueryServiceAdapter implements BlockchainQueryService { +// +// private LedgerQueryHttpService queryHttpService; +// +// public HttpQueryServiceAdapter(NetworkAddress serviceAddress) { +// ServiceEndpoint endpoint = new ServiceEndpoint(serviceAddress); +// this.queryHttpService = HttpServiceAgent.createService(LedgerQueryHttpService.class, endpoint); +// } +// +// @Override +// public String[] getAllLedgerHashs() { +// // TODO Auto-generated method stub +// return null; +// } +// +// @Override +// public Ledger getLedger(String ledgerHash) { +// return decodeObject(queryHttpService.getLedger(ledgerHash), Ledger.class); +// } +// +// @Override +// public Block getBlock(String ledgerHash, long height) { +// return decodeObject(queryHttpService.getBlock(ledgerHash, height), Block.class); +// } +// +// /** +// * 返回指定账本序号的区块; +// * +// * @param ledgerHash +// * 账本hash; +// * @param blockHash +// * @return +// */ +// @Override +// public Block getBlock(String ledgerHash, String blockHash) { +// return decodeObject(queryHttpService.getBlock(ledgerHash, blockHash), Block.class); +// } +// +// @Override +// public Transaction[] getTransactions(String ledgerHash, long height, int fromIndex, int count) { +// return decodeObject(queryHttpService.getBlockTransactions(height), Transaction[].class); +// } +// +// /** +// * 分页返回指定账本序号的区块中的交易列表; +// * +// * @param ledgerHash +// * 账本hash; +// * @param blockHash +// * 账本高度; +// * @param fromIndex +// * 开始的记录数; +// * @param count +// * 本次返回的记录数;
+// * 如果参数值为 -1,则返回全部的记录;
+// * 注:通过 {@link #getBlock(String, String)} 方法获得的区块信息中可以得到区块的总交易数 +// * {@link Block#getTxCount()}; +// * @return +// */ +// @Override +// public Transaction[] getTransactions(String ledgerHash, String blockHash, int fromIndex, int count) { +// return decodeObject(queryHttpService.getBlockTransactions(ledgerHash, blockHash, fromIndex, count), +// Transaction[].class); +// } +// +// @Override +// public Transaction getTransactionByTxHash(String ledgerHash, String txHash) { +// return decodeObject(queryHttpService.getBlockTransactionByTxHash(ledgerHash, txHash), Transaction.class); +// } +// +// @Override +// public Transaction getTransactionByContentHash(String ledgerHash, String contentHash) { +// return decodeObject(queryHttpService.getBlockTransactionByContentHash(ledgerHash, contentHash), +// Transaction.class); +// } +// +// @Override +// public BlockchainAccount getAccount(String ledgerHash, String address) { +// return decodeObject(queryHttpService.getAccount(ledgerHash, address), BlockchainAccount.class); +// } +// +// @Override +// public StateMap getStates(String ledgerHash, String address, Set keys) { +// // TODO Auto-generated method stub +// return null; +// } +// +// public StateMap getState(String ledgerHash, String address, String keys) { +// return decodeObject(queryHttpService.getAccountState(ledgerHash,address, keys), StateMap.class); +// } +// +// @Override +// public StateMap queryObject(String ledgerHash, String address, String condition) { +// // TODO Auto-generated method stub +// return null; +// } +// +// @Override +// public Map queryObject(String ledgerHash, String condition) { +// // TODO Auto-generated method stub +// return null; +// } +// +// @Override +// public boolean containState(String ledgerHash, String address, String key) { +// // TODO Auto-generated method stub +// return false; +// } +// +// @SuppressWarnings("unchecked") +// private T decodeObject(String base64Str, Class clazz) { +// byte[] bts = Base64Utils.decodeFromString(base64Str); +// return (T) BinarySerializeUtils.deserialize(bts); +// } +// +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/ConsensusTransactionService.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/ConsensusTransactionService.java new file mode 100644 index 00000000..9ae5db3b --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/ConsensusTransactionService.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.consensus.ActionMessage; +import com.jd.blockchain.consensus.OrderedAction; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.data.TransactionService; + +/** + * 带共识的交易服务; + * + * @author huanghaiquan + * + */ +public interface ConsensusTransactionService extends TransactionService { + + @OrderedAction(groupIndexer = LedgerGroupIndexer.class, responseConverter = TransactionResponseMessageConverter.class) + @Override + TransactionResponse process( + @ActionMessage(converter = TransactionRequestMessageConverter.class) TransactionRequest txRequest); + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/IncomingInfo.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/IncomingInfo.java new file mode 100644 index 00000000..8d893324 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/IncomingInfo.java @@ -0,0 +1,39 @@ +package com.jd.blockchain.sdk.service;//package com.jd.blockchain.sdk.bftsmart; +// +//import com.jd.blockchain.manage.GatewayIncomingSetting; +// +//import my.utils.net.NetworkAddress; +// +//public class IncomingInfo { +// +// private GatewayIncomingSetting setting; +// +//// private NetworkAddress peerAddress; +// +// /** +// * 接入的 bft 客户端配置; +// * +// * @return +// */ +// public GatewayIncomingSetting getIncomingSetting() { +// return setting; +// } +// +// public void setSetting(GatewayIncomingSetting setting) { +// this.setting = setting; +// } +// +//// /** +//// * 节点地址; +//// * +//// * @return +//// */ +//// public NetworkAddress getPeerAddress() { +//// return peerAddress; +//// } +//// +//// public void setPeerAddress(NetworkAddress peerAddress) { +//// this.peerAddress = peerAddress; +//// } +// +//} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/LedgerGroupIndexer.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/LedgerGroupIndexer.java new file mode 100644 index 00000000..bcce4cd9 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/LedgerGroupIndexer.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.consensus.GroupIndexer; +import com.jd.blockchain.ledger.TransactionRequest; + +public class LedgerGroupIndexer implements GroupIndexer { + + @Override + public byte[] getGroupId(Object[] messageObjects) { + return ((TransactionRequest)messageObjects[0]).getTransactionContent().getLedgerHash().toBytes(); + } + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/NodeSigningAppender.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/NodeSigningAppender.java new file mode 100644 index 00000000..a0eeec6c --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/NodeSigningAppender.java @@ -0,0 +1,80 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.MessageService; +import com.jd.blockchain.consensus.client.ConsensusClient; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.NodeRequest; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.data.DigitalSignatureBlob; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.ledger.data.TxRequestMessage; +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +/** + * {@link NodeSigningAppender} 以装饰者模式实现,为交易请求附加上节点签名; + * + * @author huanghaiquan + * + */ +public class NodeSigningAppender implements TransactionService { + + static { + DataContractRegistry.register(NodeRequest.class); + } + +// private TransactionService consensusService; + + private MessageService messageService; + + private ConsensusClient consensusClient; + + private CryptoKeyPair nodeKeyPair; + + private CryptoAlgorithm hashAlgorithm; + + public NodeSigningAppender(CryptoAlgorithm hashAlgorithm, CryptoKeyPair nodeKeyPair, ConsensusClient consensusClient) { + this.hashAlgorithm = hashAlgorithm; + this.nodeKeyPair = nodeKeyPair; + this.consensusClient = consensusClient; + } + +// public NodeSigningAppender(CryptoAlgorithm hashAlgorithm, TransactionService reallyService, CryptoKeyPair nodeKeyPair) { +// this.hashAlgorithm = hashAlgorithm; +// this.consensusService = reallyService; +// this.nodeKeyPair = nodeKeyPair; +// } + + public NodeSigningAppender init() { + consensusClient.connect(); + messageService = consensusClient.getMessageService(); + return this; + } + + @Override + public TransactionResponse process(TransactionRequest txRequest) { + TxRequestMessage txMessage = new TxRequestMessage(txRequest); + + // 生成网关签名; + byte[] endpointRequestBytes = BinaryEncodingUtils.encode(txMessage, TransactionRequest.class); + + CryptoAlgorithm signAlgorithm = nodeKeyPair.getPrivKey().getAlgorithm(); + SignatureDigest signDigest = CryptoUtils.sign(signAlgorithm).sign(nodeKeyPair.getPrivKey(), endpointRequestBytes); + txMessage.addNodeSignatures(new DigitalSignatureBlob(nodeKeyPair.getPubKey(), signDigest)); + + // 计算交易哈希; + byte[] nodeRequestBytes = BinaryEncodingUtils.encode(txMessage, TransactionRequest.class); + HashDigest txHash = CryptoUtils.hash(hashAlgorithm).hash(nodeRequestBytes); + txMessage.setHash(txHash); + + AsyncFuture result = messageService.sendOrdered(BinaryEncodingUtils.encode(txMessage, TransactionRequest.class)); + + return BinaryEncodingUtils.decode(result.get()); + } +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/PeerBlockchainServiceFactory.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/PeerBlockchainServiceFactory.java new file mode 100644 index 00000000..b21c5ef3 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/PeerBlockchainServiceFactory.java @@ -0,0 +1,341 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.consensus.*; +import com.jd.blockchain.consensus.client.ClientFactory; +import com.jd.blockchain.consensus.client.ClientSettings; +import com.jd.blockchain.consensus.client.ConsensusClient; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.manage.GatewayIncomingSetting; +import com.jd.blockchain.manage.LedgerIncomingSetting; +import com.jd.blockchain.sdk.*; +import com.jd.blockchain.sdk.proxy.HttpBlockchainQueryService; +import com.jd.blockchain.utils.http.agent.HttpServiceAgent; +import com.jd.blockchain.utils.http.agent.ServiceConnection; +import com.jd.blockchain.utils.http.agent.ServiceConnectionManager; +import com.jd.blockchain.utils.http.agent.ServiceEndpoint; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.utils.security.AuthenticationException; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.Closeable; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class PeerBlockchainServiceFactory implements BlockchainServiceFactory, Closeable { + + private static Logger LOGGER = LoggerFactory.getLogger(PeerBlockchainServiceFactory.class); + + private static final Map peerBlockchainServiceFactories = new ConcurrentHashMap<>(); + + private static final Map peerManageServices = new ConcurrentHashMap<>(); + + private static final Map accessContextMap = new ConcurrentHashMap<>(); + + private ServiceConnectionManager httpConnectionManager; + + private PeerServiceProxy peerServiceProxy; + + + /** + * @param httpConnectionManager + * Http请求管理器; + * @param accessAbleLedgers + * 可用账本列表; + */ + protected PeerBlockchainServiceFactory(ServiceConnectionManager httpConnectionManager, + LedgerAccessContextImpl[] accessAbleLedgers) { + this.httpConnectionManager = httpConnectionManager; + this.peerServiceProxy = new PeerServiceProxy(accessAbleLedgers); + } + + public void addLedgerAccessContexts(LedgerAccessContextImpl[] accessContexts) { + this.peerServiceProxy.addLedgerAccessContexts(accessContexts); + } + + @Override + public BlockchainService getBlockchainService() { + return peerServiceProxy; + } + + /** + * 返回交易服务; + * + *
+ * + * 返回的交易服务聚合了该节点绑定的多个账本的交易服务,并根据交易请求中指定的目标账本选择相应的交易服务进行转发; + * + * @return + */ + public TransactionService getTransactionService() { + return peerServiceProxy; + } + + /** + * 连接到指定的共识节点; + * + * @param peerAddr + * 提供对网关接入认证的节点的认证地址列表;
+ * 按列表的先后顺序连接节点进行认证,从第一个成功通过的节点请求整个区块链网络的拓扑配置,并建立起和整个区块链网络的连接;
+ * 此参数指定的节点列表可以是整个区块链网络的全部节点的子集,而不必包含所有节点; + * + * @return 区块链服务工厂实例; + */ + public static PeerBlockchainServiceFactory connect(CryptoKeyPair gatewayKey, NetworkAddress peerAddr, List peerProviders) { + + if (peerProviders == null || peerProviders.isEmpty()) { + throw new AuthenticationException("No peer Provider was set!"); + } + ClientIdentificationsProvider authIdProvider = authIdProvider(gatewayKey, peerProviders); + + GatewayIncomingSetting incomingSetting = auth(peerAddr, authIdProvider); + + if (incomingSetting == null) { + throw new AuthenticationException("No peer was succeed authenticating from!"); + } + + PeerBlockchainServiceFactory factory = null; + + ServiceConnectionManager httpConnectionManager; + + PeerManageService peerManageService; + + if (peerBlockchainServiceFactories.containsKey(peerAddr)) { + factory = peerBlockchainServiceFactories.get(peerAddr); + httpConnectionManager = factory.httpConnectionManager; + } else { + httpConnectionManager = new ServiceConnectionManager(); + } + + if (peerManageServices.containsKey(peerAddr)) { + peerManageService = peerManageServices.get(peerAddr); + } else { + ServiceConnection httpConnection = httpConnectionManager.create(new ServiceEndpoint(peerAddr)); + peerManageService = new PeerManageService(httpConnection, + HttpServiceAgent.createService(HttpBlockchainQueryService.class, + httpConnection, null)); + peerManageServices.put(peerAddr, peerManageService); + } + + LedgerIncomingSetting[] ledgerSettings = incomingSetting.getLedgers(); + // 判断当前节点对应账本是否一致 + List needInitSettings = new ArrayList<>(); + for (LedgerIncomingSetting setting : ledgerSettings) { + HashDigest currLedgerHash = setting.getLedgerHash(); + if (!accessContextMap.containsKey(currLedgerHash)) { + needInitSettings.add(setting); + } + } + + if (!needInitSettings.isEmpty()) { + LedgerAccessContextImpl[] accessAbleLedgers = new LedgerAccessContextImpl[needInitSettings.size()]; + BlockchainQueryService queryService = peerManageService.getQueryService(); + + for (int i = 0; i < needInitSettings.size(); i++) { + LedgerIncomingSetting ledgerSetting = needInitSettings.get(i); + String providerName = ledgerSetting.getProviderName(); + ConsensusProvider provider = ConsensusProviders.getProvider(providerName); + byte[] clientSettingBytes = ByteArray.fromBase64(ledgerSetting.getClientSetting()); + + ClientIncomingSettings clientIncomingSettings = provider.getSettingsFactory().getIncomingSettingsEncoder().decode(clientSettingBytes); + ClientFactory clientFactory = provider.getClientFactory(); + ClientSettings clientSettings = clientFactory.buildClientSettings(clientIncomingSettings); + ConsensusClient consensusClient = clientFactory.setupClient(clientSettings); + + TransactionService autoSigningTxProcService = enableGatewayAutoSigning(gatewayKey, + ledgerSetting.getCryptoSetting(), consensusClient); + + LedgerAccessContextImpl accCtx = new LedgerAccessContextImpl(); + accCtx.ledgerHash = ledgerSetting.getLedgerHash(); + accCtx.cryptoSetting = ledgerSetting.getCryptoSetting(); + accCtx.queryService = queryService; + accCtx.txProcService = autoSigningTxProcService; + accCtx.consensusClient = consensusClient; + + accessAbleLedgers[i] = accCtx; + + accessContextMap.put(accCtx.ledgerHash, accCtx); + } + if (factory == null) { + factory = new PeerBlockchainServiceFactory(httpConnectionManager, + accessAbleLedgers); + peerBlockchainServiceFactories.put(peerAddr, factory); + } else { + factory.addLedgerAccessContexts(accessAbleLedgers); + } +// PeerBlockchainServiceFactory factory = new PeerBlockchainServiceFactory(httpConnectionManager, +// accessAbleLedgers); + } + + + +// ServiceConnectionManager httpConnectionManager = new ServiceConnectionManager(); +// ServiceConnection httpConnection = httpConnectionManager.create(new ServiceEndpoint(peerAddr)); +// BlockchainQueryService queryService = HttpServiceAgent.createService(HttpBlockchainQueryService.class, +// httpConnection, null); +// +// LedgerIncomingSetting[] ledgerSettings = incomingSetting.getLedgers(); +// +// LedgerAccessContextImpl[] accessAbleLedgers = new LedgerAccessContextImpl[ledgerSettings.length]; +// for (int i = 0; i < ledgerSettings.length; i++) { +// LedgerIncomingSetting ledgerSetting = ledgerSettings[i]; +// String providerName = ledgerSetting.getProviderName(); +// ConsensusProvider provider = ConsensusProviders.getProvider(providerName); +// byte[] clientSettingBytes = ByteArray.fromBase64(ledgerSetting.getClientSetting()); +// +// ClientIncomingSettings clientIncomingSettings = provider.getSettingsFactory().getIncomingSettingsEncoder().decode(clientSettingBytes); +// ClientFactory clientFactory = provider.getClientFactory(); +// ClientSettings clientSettings = clientFactory.buildClientSettings(clientIncomingSettings); +// ConsensusClient consensusClient = clientFactory.setupClient(clientSettings); +// +// TransactionService autoSigningTxProcService = enableGatewayAutoSigning(gatewayKey, +// ledgerSetting.getCryptoSetting(), consensusClient); +// +// +// LedgerAccessContextImpl accCtx = new LedgerAccessContextImpl(); +// accCtx.ledgerHash = ledgerSetting.getLedgerHash(); +// accCtx.cryptoSetting = ledgerSetting.getCryptoSetting(); +// accCtx.queryService = queryService; +// accCtx.txProcService = autoSigningTxProcService; +// accCtx.consensusClient = consensusClient; +// +// accessAbleLedgers[i] = accCtx; +// +// accessContextMap.put(accCtx.ledgerHash, accCtx); +// } +// +// PeerBlockchainServiceFactory factory = new PeerBlockchainServiceFactory(httpConnectionManager, +// accessAbleLedgers); + return factory; + } + + private static GatewayIncomingSetting auth(NetworkAddress peerAuthAddr, ClientIdentifications authIds) { + try { + ManagementHttpService gatewayMngService = getGatewayManageService(peerAuthAddr); + + // 接入认证,获得接入配置; + // 传递网关账户地址及签名; + GatewayIncomingSetting incomingSetting = gatewayMngService.authenticateGateway(authIds); + return incomingSetting; + } catch (Exception e) { + LOGGER.warn("Cann't authenticate gateway incoming from peer[" + peerAuthAddr.toString() + "]!--" + + e.getMessage(), e); + return null; + } + } + + private static ManagementHttpService getGatewayManageService(NetworkAddress peer) { + ServiceEndpoint peerServer = new ServiceEndpoint(peer.getHost(), peer.getPort(), false); + ManagementHttpService gatewayMngService = HttpServiceAgent.createService(ManagementHttpService.class, + peerServer); + return gatewayMngService; + } + + /** + * 启用网关自动签名; + * + * @param nodeKeyPair + * @param cryptoSetting + * @return + */ + private static TransactionService enableGatewayAutoSigning(CryptoKeyPair nodeKeyPair, CryptoSetting cryptoSetting, + ConsensusClient consensusClient) { + NodeSigningAppender signingAppender = new NodeSigningAppender(cryptoSetting.getHashAlgorithm(), + nodeKeyPair, consensusClient); + return signingAppender.init(); + } + + @Override + public void close() { + try { + for (Map.Entry entry : accessContextMap.entrySet()) { + LedgerAccessContextImpl ctx = entry.getValue(); + ctx.consensusClient.close(); + } + httpConnectionManager.close(); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + private static ClientIdentificationsProvider authIdProvider(CryptoKeyPair gatewayKey, List peerProviders) { + ClientIdentificationsProvider authIdProvider = new ClientIdentificationsProvider(); + for (String peerProvider : peerProviders) { + ConsensusProvider provider = ConsensusProviders.getProvider(peerProvider); + ClientFactory clientFactory = provider.getClientFactory(); + ClientIdentification authId = clientFactory.buildAuthId(gatewayKey); + authIdProvider.add(authId); + } + return authIdProvider; + } + + private static class LedgerAccessContextImpl implements LedgerAccessContext { + + private HashDigest ledgerHash; + + private CryptoSetting cryptoSetting; + + private TransactionService txProcService; + + private BlockchainQueryService queryService; + + private ConsensusClient consensusClient; + + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + + @Override + public CryptoSetting getCryptoSetting() { + return cryptoSetting; + } + + @Override + public TransactionService getTransactionService() { + return txProcService; + } + + @Override + public BlockchainQueryService getQueryService() { + return queryService; + } + + } + + private static final class PeerManageService { + + public PeerManageService(ServiceConnection httpConnection, BlockchainQueryService queryService) { + this.httpConnection = httpConnection; + this.queryService = queryService; + } + + ServiceConnection httpConnection; + + BlockchainQueryService queryService; + + public ServiceConnection getHttpConnection() { + return httpConnection; + } + + public void setHttpConnection(ServiceConnection httpConnection) { + this.httpConnection = httpConnection; + } + + public BlockchainQueryService getQueryService() { + return queryService; + } + + public void setQueryService(BlockchainQueryService queryService) { + this.queryService = queryService; + } + } + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/PeerServiceProxy.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/PeerServiceProxy.java new file mode 100644 index 00000000..07085535 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/PeerServiceProxy.java @@ -0,0 +1,91 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.data.TransactionService; +import com.jd.blockchain.sdk.BlockchainException; +import com.jd.blockchain.sdk.BlockchainQueryService; +import com.jd.blockchain.sdk.LedgerAccessContext; +import com.jd.blockchain.sdk.proxy.BlockchainServiceProxy; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +/** + * 对共识节点的区块链服务代理; + * + * @author huanghaiquan + * + */ +public class PeerServiceProxy extends BlockchainServiceProxy implements TransactionService { + + private final Lock accessLock = new ReentrantLock(); + + /** + * 许可的账本以及交易列表; + */ + private Map ledgerAccessContexts; + + public PeerServiceProxy(LedgerAccessContext[] accessAbleLedgers) { + this.ledgerAccessContexts = new HashMap<>(); + for (LedgerAccessContext lac : accessAbleLedgers) { + if (ledgerAccessContexts.containsKey(lac.getLedgerHash())) { + throw new IllegalArgumentException( + String.format("Ledger repeatly! --[LedgerHash=%s]", lac.getLedgerHash().toBase58())); + } + ledgerAccessContexts.put(lac.getLedgerHash(), lac); + } + } + + public void addLedgerAccessContexts(LedgerAccessContext[] accessAbleLedgers) { + try { + accessLock.lock(); + if (this.ledgerAccessContexts == null) { + throw new IllegalArgumentException("LedgerAccessContexts is null, you need init first !!!"); + } + for (LedgerAccessContext lac : accessAbleLedgers) { + if (!ledgerAccessContexts.containsKey(lac.getLedgerHash())) { + ledgerAccessContexts.put(lac.getLedgerHash(), lac); + } + } + } finally { + accessLock.unlock(); + } + } + + @Override + protected TransactionService getTransactionService(HashDigest ledgerHash) { + return getLedgerAccessContext(ledgerHash).getTransactionService(); + } + + @Override + protected BlockchainQueryService getQueryService(HashDigest ledgerHash) { + return getLedgerAccessContext(ledgerHash).getQueryService(); + } + + private LedgerAccessContext getLedgerAccessContext(HashDigest ledgerHash) { + LedgerAccessContext lac = ledgerAccessContexts.get(ledgerHash); + if (lac == null) { + throw new BlockchainException("Unsupported access ledger[" + ledgerHash.toBase58() + "] !"); + } + return lac; + } + + @Override + public HashDigest[] getLedgerHashs() { + return ledgerAccessContexts.keySet().toArray(new HashDigest[ledgerAccessContexts.size()]); + } + + /** + * 处理网关的交易转发; + */ + @Override + public TransactionResponse process(TransactionRequest txRequest) { + TransactionService targetTxService = getTransactionService(txRequest.getTransactionContent().getLedgerHash()); + return targetTxService.process(txRequest); + } +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/TransactionRequestMessageConverter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/TransactionRequestMessageConverter.java new file mode 100644 index 00000000..f150cf91 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/TransactionRequestMessageConverter.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.consensus.BinaryMessageConverter; +import com.jd.blockchain.ledger.TransactionRequest; + +public class TransactionRequestMessageConverter implements BinaryMessageConverter { + + @Override + public byte[] encode(Object message) { + return BinaryEncodingUtils.encode(message, TransactionRequest.class); + } + + @Override + public Object decode(byte[] messageBytes) { + return BinaryEncodingUtils.decode(messageBytes); + } + +} diff --git a/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/TransactionResponseMessageConverter.java b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/TransactionResponseMessageConverter.java new file mode 100644 index 00000000..3748c865 --- /dev/null +++ b/source/sdk/sdk-base/src/main/java/com/jd/blockchain/sdk/service/TransactionResponseMessageConverter.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.sdk.service; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.consensus.BinaryMessageConverter; +import com.jd.blockchain.ledger.TransactionResponse; + +public class TransactionResponseMessageConverter implements BinaryMessageConverter { + + @Override + public byte[] encode(Object message) { + return BinaryEncodingUtils.encode(message, TransactionResponse.class); + } + + @Override + public Object decode(byte[] messageBytes) { + return BinaryEncodingUtils.decode(messageBytes); + } + +} diff --git a/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java b/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java new file mode 100644 index 00000000..b6235493 --- /dev/null +++ b/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/BlockchainServiceProxyTest.java @@ -0,0 +1,95 @@ +package test.com.jd.blockchain.sdk.proxy; + +import static org.junit.Assert.assertEquals; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.verify; + +public class BlockchainServiceProxyTest { + +// private static class ArgCaptorMatcher extends CustomMatcher { +// +// private T arg; +// +// public ArgCaptorMatcher() { +// super("OK"); +// } +// +// @Override +// public boolean matches(Object item) { +// this.arg = (T) item; +// return true; +// } +// +// public T getArg() { +// return arg; +// } +// } +// +// @Test +// public void testRegisterAccount() throws IOException { +// +// BlockchainKeyPair gatewayAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// BlockchainKeyPair sponsorAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// BlockchainKeyPair subjectAccount = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// long sequenceNumber = 110; +// +// ArgCaptorMatcher txReqCaptor = new ArgCaptorMatcher<>(); +// +// TransactionService consensusService = Mockito.mock(TransactionService.class); +// BlockchainQueryService queryService = Mockito.mock(BlockchainQueryService.class); +// +// HashDigest txContentHash =CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); +// TxResponseMessage expectedResponse = new TxResponseMessage(txContentHash); +// expectedResponse.setExecutionState(ExecutionState.SUCCESS); +// +// when(consensusService.process(argThat(txReqCaptor))).thenReturn(expectedResponse); +// +// HashDigest ledgerHash = CryptoUtils.hash(CryptoAlgorithm.SHA_256).hash(UUID.randomUUID().toString().getBytes("UTF-8")); +// +// BlockchainTransactionService serviceProxy = new BlockchainServiceProxy(consensusService, queryService); +// +// TransactionTemplate txTemplate = serviceProxy.newTransaction(ledgerHash); +// txTemplate.setSubject(subjectAccount.getAddress(), sequenceNumber); +// +// BlockchainKeyPair regAccountKeyPair = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// AccountStateType stateType = AccountStateType.MAP; +// txTemplate.users().register(regAccountKeyPair.getIdentity(), stateType); +// +// PreparedTransaction prepTx = txTemplate.prepare(); +// HashDigest txHash = prepTx.getHash(); +// prepTx.sign(sponsorAccount); +// +// TransactionResponse result = prepTx.commit(); +// +// // 验证; +// // 仅被提交一次; +// verify(consensusService, times(1)).process(any()); +// +// assertEquals(ExecutionState.SUCCESS, result.getExecutionState()); +// +// // 验证内容; +// TransactionRequest resolvedTxRequest = txReqCaptor.getArg(); +// +// TransactionContent resolvedTxContent = resolvedTxRequest.getTransactionContent(); +// +// assertEquals(txHash, resolvedTxContent.getHash()); +// +// assertEquals(subjectAccount.getAddress(), resolvedTxContent.getSubjectAccount()); +// assertEquals(sequenceNumber, resolvedTxContent.getSequenceNumber()); +// +// +// Operation[] resolvedOps = resolvedTxContent.getOperations(); +// assertEquals(1, resolvedOps.length); +// Operation resolvedOP = resolvedOps[0]; +//// assertEquals(OperationType.REGISTER_USER.CODE, resolvedOP.getCode()); +// +// UserRegisterOpTemplate accRegOP = new UserRegisterOpTemplate(); +// accRegOP.resolvFrom((OpBlob) resolvedOP); +// +// assertEquals(regAccountKeyPair.getAddress(), accRegOP.getId().getAddress()); +// assertEquals(regAccountKeyPair.getPubKey().getType(), accRegOP.getId().getPubKey().getType()); +// assertEquals(regAccountKeyPair.getPubKey().getValue(), accRegOP.getId().getPubKey().getValue()); +// assertEquals(stateType, accRegOP.getStateType()); +// } + +} diff --git a/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/TxMessageTest.java b/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/TxMessageTest.java new file mode 100644 index 00000000..650b6fd2 --- /dev/null +++ b/source/sdk/sdk-base/src/test/java/test/com/jd/blockchain/sdk/proxy/TxMessageTest.java @@ -0,0 +1,64 @@ +package test.com.jd.blockchain.sdk.proxy; + +//public class TxMessageTest { +// +// @Test +// public void testSerialize() throws IOException { +// BlockchainKeyPair id = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// BlockchainKeyPair id1 = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// BlockchainKeyPair id2 = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// +// TxContentBlob contentBlob = new TxContentBlob(); +// +// contentBlob.setSubjectAccount(id.getAddress()); +// contentBlob.setSequenceNumber(1); +// +// OpBlob op1 = new OpBlob(); +// op1.setOperation(OperationType.REGISTER_USER.CODE, ByteArray.parseString("AAA", "UTF-8")); +// contentBlob.addOperation(op1); +// +// TxRequestMessage txMsg = new TxRequestMessage(contentBlob); +// +// ByteArray mockedDigest = ByteArray.wrap(RandomUtils.generateRandomBytes(32)); +// DigitalSignatureBlob signature = new DigitalSignatureBlob(id.getPubKey(), mockedDigest); +// txMsg.addEndpointSignatures(signature); +// +// ByteArray mockedDigest1 = ByteArray.wrap(RandomUtils.generateRandomBytes(32)); +// DigitalSignatureBlob signature1 = new DigitalSignatureBlob(id1.getPubKey(), mockedDigest1); +// txMsg.addEndpointSignatures(signature1); +// +// ByteArray mockedDigest2 = ByteArray.wrap(RandomUtils.generateRandomBytes(32)); +// DigitalSignatureBlob signature2 = new DigitalSignatureBlob(id2.getPubKey(), mockedDigest2); +// txMsg.addNodeSignatures(signature2); +// +// // 输出; +// byte[] msgBytes = BinaryEncodingUtils.encode(txMsg, TransactionRequest.class); +// +// assertEquals(MagicNumber.TX_REQUEST, msgBytes[0]); +// +//// TxRequestMessage resolvedTxMsg = new TxRequestMessage(); +//// resolvedTxMsg.resolvFrom(ByteArray.wrap(msgBytes).asInputStream()); +// TxRequestMessage resolvedTxMsg = BinaryEncodingUtils.decode(msgBytes, null, TxRequestMessage.class); +// +// assertEquals(txMsg.getTransactionContent().getSubjectAccount(), +// resolvedTxMsg.getTransactionContent().getSubjectAccount()); +// assertEquals(txMsg.getTransactionContent().getHash(), resolvedTxMsg.getTransactionContent().getHash()); +// assertEquals(txMsg.getHash(), resolvedTxMsg.getHash()); +// +// DigitalSignature[] pSignatures = txMsg.getEndpointSignatures(); +// DigitalSignature[] resolvedPSignatures = txMsg.getEndpointSignatures(); +// assertEquals(pSignatures.length, resolvedPSignatures.length); +// for (int i = 0; i < resolvedPSignatures.length; i++) { +// assertEquals(pSignatures[i].getPubKey().getType(), resolvedPSignatures[i].getPubKey().getType()); +// assertEquals(pSignatures[i].getPubKey().getValue(), resolvedPSignatures[i].getPubKey().getValue()); +// assertEquals(pSignatures[i].getDigest(), resolvedPSignatures[i].getDigest()); +// } +// +// assertEquals(txMsg.getNodeSignatures()[0].getPubKey().getType(), +// resolvedTxMsg.getNodeSignatures()[0].getPubKey().getType()); +// assertEquals(txMsg.getNodeSignatures()[0].getPubKey().getValue(), +// resolvedTxMsg.getNodeSignatures()[0].getPubKey().getValue()); +// assertEquals(txMsg.getNodeSignatures()[0].getDigest(), resolvedTxMsg.getNodeSignatures()[0].getDigest()); +// } +// +//} diff --git a/source/sdk/sdk-client/pom.xml b/source/sdk/sdk-client/pom.xml new file mode 100644 index 00000000..14b5c23c --- /dev/null +++ b/source/sdk/sdk-client/pom.xml @@ -0,0 +1,18 @@ + + 4.0.0 + + com.jd.blockchain + sdk + 0.8.2.RELEASE + + sdk-client + + + + com.jd.blockchain + sdk-base + ${project.version} + + + \ No newline at end of file diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/Command.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/Command.java new file mode 100644 index 00000000..8b07376e --- /dev/null +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/Command.java @@ -0,0 +1,100 @@ +//package com.jd.blockchain.client; +// +//import java.util.HashMap; +//import java.util.Map; +// +//public class Command { +// +// private PeerClient peerClient; +// +// private Map executors; +// +// public Command(PeerClient peerClient) { +// this.peerClient = peerClient; +// this.executors = new HashMap<>(); +// executors.put("set", new SetCmd()); +// executors.put("get", new GetCmd()); +// executors.put("remove", new RemoveCmd()); +// executors.put("keys", new KeysCmd()); +// executors.put("contain", new ContainCmd()); +// } +// +// public void execute(String cmd) { +// String[] cmdArgs = cmd.split(" "); +// if (cmdArgs.length == 0) { +// System.out.println("Illegal input!!"); +// return; +// } +// +// String command = cmdArgs[0].trim(); +// String[] args = new String[cmdArgs.length - 1]; +// for (int i = 1; i < cmdArgs.length; i++) { +// args[i - 1] = cmdArgs[i].trim(); +// } +// +// CmdExecutor executor = executors.get(command); +// executor.execute(args); +// } +// +// private static interface CmdExecutor { +// +// public void execute(String[] args); +// +// } +// +// private class SetCmd implements CmdExecutor { +// @Override +// public void execute(String[] args) { +// if (args.length < 2) { +// System.out.println("SET command require 2 args!"); +// return; +// } +// String result = peerClient.set(args[0], args[1]); +// System.out.println("ok. --[" + result + "]"); +// } +// } +// +// private class GetCmd implements CmdExecutor { +// @Override +// public void execute(String[] args) { +// if (args.length < 1) { +// System.out.println("GET command require 1 args!"); +// return; +// } +// String result = peerClient.get(args[0]); +// System.out.println("ok. --[" + result + "]"); +// } +// } +// +// private class RemoveCmd implements CmdExecutor { +// @Override +// public void execute(String[] args) { +// if (args.length < 1) { +// System.out.println("REMOVE command require 1 args!"); +// return; +// } +// String result = peerClient.remove(args[0]); +// System.out.println("ok. --[" + result + "]"); +// } +// } +// +// private class KeysCmd implements CmdExecutor { +// @Override +// public void execute(String[] args) { +// String[] result = peerClient.getKeys(); +// System.out.println("ok. --[" + result + "]"); +// } +// } +// +// private class ContainCmd implements CmdExecutor { +// @Override +// public void execute(String[] args) { +// if (args.length < 1) { +// System.out.println("CONTAIN command require 1 args!"); +// return; +// } +// boolean result = peerClient.contain(args[0]); +// System.out.println("ok. --[" + result + "]"); +// } +// } +//} diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/PeerClient.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/PeerClient.java new file mode 100644 index 00000000..bcc762e7 --- /dev/null +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/PeerClient.java @@ -0,0 +1,75 @@ +//package com.jd.blockchain.client; +// +//import java.io.ByteArrayInputStream; +//import java.io.IOException; +//import java.io.ObjectInputStream; +// +//import com.jd.blockchain.ledger.Operation; +// +//import bftsmart.tom.AsynchServiceProxy; +// +//public class PeerClient { +// +// private AsynchServiceProxy peerProxy; +// +// public PeerClient(int clientId) { +// this.peerProxy = new AsynchServiceProxy(clientId); +// } +// +// public void close(){ +// peerProxy.close(); +// } +// +// public String set(String key, String value){ +// Operation.SetOP setOP = new Operation.SetOP(key, value); +// byte[] reply = peerProxy.invokeOrdered(setOP.toBytes()); +// +// return resolveResult(reply, String.class); +// } +// +// +// public String remove(String key){ +// Operation.RemoveOP removeOP = new Operation.RemoveOP(key); +// byte[] reply = peerProxy.invokeOrdered(removeOP.toBytes()); +// +// return resolveResult(reply, String.class); +// } +// +// public String get(String key){ +// Operation.GetOP getOP = new Operation.GetOP(key); +// byte[] reply = peerProxy.invokeUnordered(getOP.toBytes()); +// +// return resolveResult(reply, String.class); +// } +// +// public String[] getKeys(){ +// Operation.KeysOP keysOP = new Operation.KeysOP(); +// byte[] reply = peerProxy.invokeUnordered(keysOP.toBytes()); +// +// return resolveResult(reply, String[].class); +// } +// +// public boolean contain(String key){ +// Operation.ContainOP containOP = new Operation.ContainOP(key); +// byte[] reply = peerProxy.invokeUnordered(containOP.toBytes()); +// +// return resolveResult(reply, Boolean.class); +// } +// +// @SuppressWarnings("unchecked") +// public static T resolveResult(byte[] bytes, Class resultType){ +// if (bytes == null || bytes.length == 0) { +// return null; +// } +// try { +// ByteArrayInputStream bi = new ByteArrayInputStream(bytes); +// ObjectInputStream in = new ObjectInputStream(bi); +// Object op = in.readObject(); +// return (T) op; +// } catch (ClassNotFoundException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } catch (IOException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } +// } +//} diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/PeerClientDemo.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/PeerClientDemo.java new file mode 100644 index 00000000..cad4ceb0 --- /dev/null +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/client/PeerClientDemo.java @@ -0,0 +1,35 @@ +//package com.jd.blockchain.client; +// +//import java.io.Console; +// +//public class PeerClientDemo { +// +// public static void main(String[] args) { +// int clientId; +// if (args.length == 0) { +//// System.out.println("No client id !!!"); +//// return; +// clientId = 7; +// }else{ +// clientId = Integer.parseInt(args[0]); +// } +// +// PeerClient client = new PeerClient(clientId); +// Command cmd = new Command(client); +// System.out.println("---------------- Client["+clientId+"] started -----------------"); +// +// do { +// System.out.println(">>"); +// Console console = System.console(); +// String op = console.readLine(); +// if ("exit".equalsIgnoreCase(op)) { +// break; +// } +// cmd.execute(op); +// } while (true); +//// +// System.out.println("Client exist!"); +// client.close(); +// } +// +//} diff --git a/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java new file mode 100644 index 00000000..2d75f985 --- /dev/null +++ b/source/sdk/sdk-client/src/main/java/com/jd/blockchain/sdk/client/ClientOperationUtil.java @@ -0,0 +1,273 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.client.ClientOperationUtil + * Author: shaozhuguang + * Department: Y事业部 + * Date: 2019/3/27 下午4:12 + * Description: + */ +package com.jd.blockchain.sdk.client; + +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.*; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.io.BytesSlice; +import org.apache.commons.codec.binary.Base64; + +import java.lang.reflect.Field; + +/** + * + * @author shaozhuguang + * @create 2019/3/27 + * @since 1.0.0 + */ + +public class ClientOperationUtil { + + public static Operation read(Operation operation) { + + try { + // Class + Class clazz = operation.getClass(); + Field field = clazz.getSuperclass().getDeclaredField("h"); + field.setAccessible(true); + Object object = field.get(operation); + if (object instanceof JSONObject) { + JSONObject jsonObject = (JSONObject) object; + if (jsonObject.containsKey("accountID")) { + return convertDataAccountRegisterOperation(jsonObject); + } else if (jsonObject.containsKey("userID")) { + return convertUserRegisterOperation(jsonObject); + } else if (jsonObject.containsKey("contractID")) { + return convertContractCodeDeployOperation(jsonObject); + } else if (jsonObject.containsKey("writeSet")) { + return convertDataAccountKVSetOperation(jsonObject); + } else if (jsonObject.containsKey("initSetting")) { + return convertLedgerInitOperation(jsonObject); + } else if (jsonObject.containsKey("contractAddress")) { + return convertContractEventSendOperation(jsonObject); + } + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + return null; + } + + public static Object readValueByBytesValue(BytesValue bytesValue) { + DataType dataType = bytesValue.getType(); + BytesSlice saveVal = bytesValue.getValue(); + Object showVal; + switch (dataType) { + case BYTES: + // return hex + showVal = HexUtils.encode(saveVal.getBytesCopy()); + break; + case TEXT: + case JSON: + showVal = saveVal.getString(); + break; + case INT64: + showVal = saveVal.getLong(); + break; + default: + showVal = HexUtils.encode(saveVal.getBytesCopy()); + break; + } + return showVal; + } + + public static DataAccountRegisterOperation convertDataAccountRegisterOperation(JSONObject jsonObject) { + JSONObject account = jsonObject.getJSONObject("accountID"); + return new DataAccountRegisterOpTemplate(blockchainIdentity(account)); + } + + public static DataAccountKVSetOperation convertDataAccountKVSetOperation(JSONObject jsonObject) { + // 写入集合处理 + JSONArray writeSetObj = jsonObject.getJSONArray("writeSet"); + JSONObject accountAddrObj = jsonObject.getJSONObject("accountAddress"); + String addressBase58 = accountAddrObj.getString("value"); + Bytes address = Bytes.fromBase58(addressBase58); + + DataAccountKVSetOpTemplate kvOperation = new DataAccountKVSetOpTemplate(address); + for (int i = 0; i + 4.0.0 + + com.jd.blockchain + sdk + 0.8.2.RELEASE + + sdk-samples + + + + com.jd.blockchain + sdk-client + ${project.version} + + + com.jd.blockchain + contract-model + ${project.version} + + + com.jd.blockchain + tools-initializer + ${project.version} + + + + com.jd.blockchain + ledger-rpc + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + + + + + + + + \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract.java new file mode 100644 index 00000000..8b343649 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContract.java @@ -0,0 +1,39 @@ +package com.jd.blockchain.contract.samples; + +import com.jd.blockchain.contract.model.Contract; +import com.jd.blockchain.contract.model.ContractEvent; + +/** + * 示例:一个“资产管理”智能合约; + * + * @author huanghaiquan + * + */ +@Contract +public interface AssetContract { + + /** + * 发行资产; + * + * @param amount + * 新发行的资产数量; + * @param assetHolderAddress + * 新发行的资产的持有账户; + */ + @ContractEvent(name = "issue-asset") + void issue(long amount, String assetHolderAddress); + + /** + * 转移资产 + * + * @param fromAddress + * 转出账户; + * @param toAddress + * 转入账户; + * @param amount + * 转移的资产数额; + */ + @ContractEvent(name = "transfer-asset") + void transfer(String fromAddress, String toAddress, long amount); + +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContractImpl.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContractImpl.java new file mode 100644 index 00000000..c1b888f6 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/contract/samples/AssetContractImpl.java @@ -0,0 +1,188 @@ +package com.jd.blockchain.contract.samples; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.jd.blockchain.contract.model.ContractException; +import com.jd.blockchain.contract.model.ContractEventContext; +import com.jd.blockchain.contract.model.EventProcessingAwire; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.KVDataObject; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 示例:一个“资产管理”智能合约的实现; + * + * 注: 1、实现 EventProcessingAwire 接口以便合约实例在运行时可以从上下文获得合约生命周期事件的通知; 2、实现 + * AssetContract 接口定义的合约方法; + * + * @author huanghaiquan + * + */ +public class AssetContractImpl implements EventProcessingAwire, AssetContract { + // 资产管理账户的地址; + private static final String ASSET_ADDRESS = "2njZBNbFQcmKd385DxVejwSjy4driRzf9Pk"; + // 保存资产总数的键; + private static final String KEY_TOTAL = "TOTAL"; + // 合约事件上下文; + private ContractEventContext eventContext; + + /** + * ------------------- 定义可以由外部用户通过提交“交易”触发的调用方法 ------------------ + */ + + @Override + public void issue(long amount, String assetHolderAddress) { + checkAllOwnersAgreementPermission(); + + // 新发行的资产数量; + if (amount < 0) { + throw new ContractException("The amount is negative!"); + } + if (amount == 0) { + return; + } + + // 查询当前值; + KVDataEntry[] kvEntries = eventContext.getLedger().getDataEntries(currentLedgerHash(), ASSET_ADDRESS, KEY_TOTAL, + assetHolderAddress); + + // 计算资产的发行总数; + KVDataObject currTotal = (KVDataObject) kvEntries[0]; + long newTotal = currTotal.longValue() + amount; + eventContext.getLedger().dataAccount(ASSET_ADDRESS).set(KEY_TOTAL, BytesUtils.toBytes(newTotal), + currTotal.getVersion()); + + // 分配到持有者账户; + KVDataObject holderAmount = (KVDataObject) kvEntries[1]; + long newHodlerAmount = holderAmount.longValue() + amount; + eventContext.getLedger().dataAccount(ASSET_ADDRESS).set(assetHolderAddress, BytesUtils.toBytes(newHodlerAmount), + holderAmount.getVersion()).set("K2", (byte[])null, -1).set("k3", (byte[])null, 3); + + } + + @Override + public void transfer(String fromAddress, String toAddress, long amount) { + // if (amount < 0) { + // throw new ContractError("The amount is negative!"); + // } + // if (amount == 0) { + // return; + // } + // + // //校验“转出账户”是否已签名; + // checkSignerPermission(fromAddress); + // + // // 查询现有的余额; + // Set keys = new HashSet<>(); + // keys.add(fromAddress); + // keys.add(toAddress); + // StateMap origBalances = + // eventContext.getLedger().getStates(currentLedgerHash(), ASSET_ADDRESS, keys); + // KVDataObject fromBalance = origBalances.get(fromAddress); + // KVDataObject toBalance = origBalances.get(toAddress); + // + // //检查是否余额不足; + // if ((fromBalance.longValue() - amount) < 0) { + // throw new ContractError("Insufficient balance!"); + // } + // + // // 把数据的更改写入到账本; + // SimpleStateMap newBalances = new SimpleStateMap(origBalances.getAccount(), + // origBalances.getAccountVersion(), + // origBalances.getStateVersion()); + // KVDataObject newFromBalance = fromBalance.newLong(fromBalance.longValue() - + // amount); + // KVDataObject newToBalance = toBalance.newLong(toBalance.longValue() + + // amount); + // newBalances.setValue(newFromBalance); + // newBalances.setValue(newToBalance); + // + // eventContext.getLedger().updateState(ASSET_ADDRESS).setStates(newBalances); + } + + // ------------------------------------------------------------- + // ------------------- 定义只在合约内部调用的方法 ------------------ + // ------------------------------------------------------------- + + /** + * 只有全部的合约拥有者同意才能通过校验; + */ + private void checkAllOwnersAgreementPermission() { + Set owners = eventContext.getContracOwners(); + Set requestors = eventContext.getTxSigners(); + if (requestors.size() != owners.size()) { + throw new ContractException("Permission Error! -- The requestors is not exactlly being owners!"); + } + + Map ownerMap = new HashMap<>(); + for (BlockchainIdentity o : owners) { + ownerMap.put(o.getAddress().toBase58(), o); + } + for (BlockchainIdentity r : requestors) { + if (!ownerMap.containsKey(r.getAddress())) { + throw new ContractException("Permission Error! -- No agreement of all owners!"); + } + } + } + + private HashDigest currentLedgerHash() { + return eventContext.getCurrentLedgerHash(); + } + + /** + * 校验指定的账户是否签署了当前交易; + * + * @param address + */ + private void checkSignerPermission(String address) { + Set requestors = eventContext.getTxSigners(); + for (BlockchainIdentity r : requestors) { + if (r.getAddress().equals(address)) { + return; + } + } + throw new ContractException("Permission Error! -- No signature !"); + } + + // ---------------------------------------------------------- + // ------------------- 在运行时捕捉上下文事件 ------------------ + // ---------------------------------------------------------- + + /* + * (non-Javadoc) + * + * @see + * com.jd.blockchain.contract.model.EventProcessingAwire#beforeEvent(com.jd. + * blockchain.contract.model.ContractEventContext) + */ + @Override + public void beforeEvent(ContractEventContext eventContext) { + this.eventContext = eventContext; + } + + /* + * (non-Javadoc) + * + * @see com.jd.blockchain.contract.model.EventProcessingAwire#postEvent(com.jd. + * blockchain.contract.model.ContractEventContext, + * com.jd.blockchain.contract.model.ContractError) + */ + @Override + public void postEvent(ContractEventContext eventContext, ContractException error) { + this.eventContext = null; + } + + @Override + public void postEvent(ContractException error) { + + } + + @Override + public void postEvent() { + + } +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java new file mode 100644 index 00000000..4f097128 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Contract.java @@ -0,0 +1,159 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +/** + * 演示合约执行的过程; + * + * @author huanghaiquan + * + */ +public class SDKDemo_Contract { + + public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + + public static AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + /** + * 演示合约执行的过程; + */ + public static void demoContract() { + // 账本地址; + String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; + // 节点地址列表; + NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), + new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), + new NetworkAddress("192.168.10.13", 8080) }; + + // 网关客户端编号; + int gatewayId = 1001; + // 客户端的认证账户; + // String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; + // String privKey = "safefsd32q34vdsvs"; + + // 创建服务代理; + 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(); + + HashDigest ledgerHash = getLedgerHash(); + // 发起交易; + TransactionTemplate txTemp = service.newTransaction(ledgerHash); + + // -------------------------------------- + // 一个贸易账户,贸易结算后的利润将通过一个合约账户来执行利润分配; + // 合约账户被设置为通用的账户,不具备对贸易结算账户的直接权限; + // 只有当前交易发起人具备对贸易账户的直接权限,当交易发起人对交易进行签名之后,权限被间接传递给合约账户; + String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; + // 处理利润分成的通用业务逻辑的合约账户; + String profitDistributionContract = "AAdfe4346fHhefe34fwf343kaeER4678RT=="; + + // 收益人账户; + String receiptorAccount1 = "MMMEy902jkjjJJDkshreGeasdfassdfajjf=="; + String receiptorAccount2 = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; + // 资产编码; + String assetKey = "RMB-ASSET"; + // 此次待分配利润; + long profit = 1000000; + + // 备注信息; + Remark remark = new Remark(); + String remarkJSON = JSONSerializeUtils.serializeToJSON(remark); + + // 合约代码的参数表; + ByteArray[] args = {}; + // 调用合约代码的分配操作; + // txTemp.deployContract().deploy(identity, appByteCodes); + + // todo args暂时无数据,尚未确定填入什么 + txTemp.contractEvents().send(commerceAccount, "trans-asset", null); +// txTemp.invokeContract().send(commerceAccount, "trans-asset", args); + // -------------------------------------- + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); + + // 使用私钥进行签名; + CryptoKeyPair keyPair = getSponsorKey(); + prepTx.sign(keyPair); + + // 提交交易; + prepTx.commit(); + } + + private static HashDigest getLedgerHash() { + // TODO Init ledger hash; + return null; + } + + /** + * 交易发起人的私钥;
+ * + * 注:私钥由调用方在本地保管和使用; + * + * @return + */ + private static CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } + + /** + * 商品信息; + * + * @author huanghaiquan + * + */ + public static class Remark { + + private String code; + + private String name; + + private String venderAddress; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVenderAddress() { + return venderAddress; + } + + public void setVenderAddress(String venderAddress) { + this.venderAddress = venderAddress; + } + + } + +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_DataAccount.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_DataAccount.java new file mode 100644 index 00000000..24fe6a56 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_DataAccount.java @@ -0,0 +1,82 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.CryptoKeyEncoding; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class SDKDemo_DataAccount { + + public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + + public static AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + /** + * 生成一个区块链用户,并注册到区块链; + */ + public static void registerDataAccount() { + // 区块链共识域; + String realm = "SUPPLY_CHAIN_ALLIANCE"; + // 节点地址列表; + NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), + new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), + new NetworkAddress("192.168.10.13", 8080) }; + + // 网关客户端编号; + int gatewayId = 1001; + // 账本地址; + String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; + // 客户端的认证账户; + String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; + String privKey = "safefsd32q34vdsvs"; + // 创建服务代理; + 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); + BlockchainTransactionService service = serviceFactory.getBlockchainService(); + + HashDigest ledgerHash = getLedgerHash(); + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = service.newTransaction(ledgerHash); + + // -------------------------------------- + // 在本地产生要注册的账户的秘钥; + BlockchainKeyGenerator generator = BlockchainKeyGenerator.getInstance(); + + BlockchainKeyPair dataAccount = generator.generate(CryptoAlgorithm.ED25519); + + txTemp.dataAccounts().register(dataAccount.getIdentity()); + + // -------------------------------------- + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + CryptoKeyPair keyPair = getSponsorKey(); + prepTx.sign(keyPair); + + // 提交交易; + prepTx.commit(); + } + + private static HashDigest getLedgerHash() { + // TODO Init ledger hash; + return null; + } + + private static CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } + +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_EventListener.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_EventListener.java new file mode 100644 index 00000000..935431ec --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_EventListener.java @@ -0,0 +1,74 @@ +//package com.jd.blockchain.sdk.samples; +// +//import com.jd.blockchain.ledger.BlockchainEventType; +//import com.jd.blockchain.ledger.BlockchainKeyGenerator; +//import com.jd.blockchain.ledger.BlockchainKeyPair; +//import com.jd.blockchain.ledger.CryptoKeyType; +//import com.jd.blockchain.ledger.StateMap; +//import com.jd.blockchain.sdk.BlockchainEventHandle; +//import com.jd.blockchain.sdk.BlockchainEventListener; +//import com.jd.blockchain.sdk.BlockchainEventMessage; +//import com.jd.blockchain.sdk.BlockchainService; +//import com.jd.blockchain.sdk.client.BlockchainServiceFactory; +// +//import my.utils.net.NetworkAddress; +// +///** +// * 演示监听区块链事件的过程; +// * +// * @author huanghaiquan +// * +// */ +//public class SDKDemo_EventListener { +// +// public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// +// /** +// * 演示合约执行的过程; +// */ +// public static void demoContract() { +// // 区块链共识域; +// String realm = "SUPPLY_CHAIN_ALLIANCE"; +// // 节点地址列表; +// NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), +// new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), +// new NetworkAddress("192.168.10.13", 8080) }; +// +// // 网关客户端编号; +// int gatewayId = 1001; +// // 账本地址; +// String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; +// // 客户端的认证账户; +// String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; +// String privKey = "safefsd32q34vdsvs"; +// // 创建服务代理; +// final String GATEWAY_IP = "127.0.0.1"; +// final int GATEWAY_PORT = 80; +// final boolean SECURE = false; +// BlockchainServiceFactory serviceFactory = BlockchainServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, +// CLIENT_CERT); +// BlockchainService service = serviceFactory.getBlockchainService(); +// +// // 监听账户变动; +// String walletAccount = "MMMEy902jkjjJJDkshreGeasdfassdfajjf=="; +// service.addBlockchainEventListener(BlockchainEventType.PAYLOAD_UPDATED.CODE, null, walletAccount, +// new BlockchainEventListener() { +// @Override +// public void onEvent(BlockchainEventMessage eventMessage, BlockchainEventHandle eventHandle) { +// // 钱包余额; +// StateMap balancePayload = service.getState(walletAccount, "RMB-ASSET","name"); +// Long balance = (Long) balancePayload.get("RMB-ASSET").longValue(); +// if (balance != null) { +// // notify balance change; +// } else { +// // wallet is empty and isn't listened any more; +// eventHandle.cancel(); +// } +// } +// }); +// +// // 销毁服务代理; +// serviceFactory.close(); +// } +// +//} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_InsertData.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_InsertData.java new file mode 100644 index 00000000..485b1d5d --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_InsertData.java @@ -0,0 +1,130 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * 演示数据写入的调用过程; + * + * @author huanghaiquan + * + */ +public class SDKDemo_InsertData { + + public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + + public static AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + /** + * 演示数据写入的调用过程; + */ + public static void insertData() { + // 区块链共识域; + String realm = "SUPPLY_CHAIN_ALLIANCE"; + // 节点地址列表; + NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), + new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), + new NetworkAddress("192.168.10.13", 8080) }; + + // 网关客户端编号; + int gatewayId = 1001; + // 账本地址; + String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; + // 客户端的认证账户; + String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; + String privKey = "safefsd32q34vdsvs"; + // 创建服务代理; + 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(); + + 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(); + } + + private static HashDigest getLedgerHash() { + // TODO Init ledger hash; + return null; + } + + private static CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } + + /** + * 商品信息; + * + * @author huanghaiquan + * + */ + public static class Commodity { + + private String code; + + private String name; + + private String venderAddress; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getVenderAddress() { + return venderAddress; + } + + public void setVenderAddress(String venderAddress) { + this.venderAddress = venderAddress; + } + + } + +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Params.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Params.java new file mode 100644 index 00000000..59c6bcfc --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Params.java @@ -0,0 +1,45 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.samples.SDKDemo_Params + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/10/18 下午2:16 + * Description: + */ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.tools.keygen.KeyGenCommand; + +/** + * + * @author shaozhuguang + * @create 2018/10/18 + * @since 1.0.0 + */ + +public class SDKDemo_Params { + public static final String PASSWORD = "abc"; + + public static final String[] PUB_KEYS = { "endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna", + "endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ", + "endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R", + "endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR" }; + + public static final String[] PRIV_KEYS = { + "177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY", + "177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2", + "177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN", + "177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J" }; + + public static PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + public static PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + public static PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + public static PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + public static PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + public static PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + public static PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + public static PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_PrivilegeSetting.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_PrivilegeSetting.java new file mode 100644 index 00000000..ea85a11e --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_PrivilegeSetting.java @@ -0,0 +1,75 @@ +package com.jd.blockchain.sdk.samples; + +public class SDKDemo_PrivilegeSetting { + +// public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoKeyType.ED25519); +// +// /** +// * 生成一个区块链账户,并注册到区块链; +// */ +// public static void registerAccount() { +// // 区块链共识域; +// String realm = "SUPPLY_CHAIN_ALLIANCE"; +// // 节点地址列表; +// NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), +// new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), +// new NetworkAddress("192.168.10.13", 8080) }; +// +// // 网关客户端编号; +// int gatewayId = 1001; +// // 账本地址; +// String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; +// // 客户端的认证账户; +// String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; +// String privKey = "safefsd32q34vdsvs"; +// // 创建服务代理; +// final String GATEWAY_IP = "127.0.0.1"; +// final int GATEWAY_PORT = 80; +// final boolean SECURE = false; +// BlockchainServiceFactory serviceFactory = BlockchainServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, +// CLIENT_CERT); +// BlockchainTransactionService service = serviceFactory.getBlockchainService(); +// +// HashDigest ledgerHash = getLedgerHash(); +// // 在本地定义注册账号的 TX; +// TransactionTemplate txTemp = service.newTransaction(ledgerHash); +// +// // -------------------------------------- +// // 配置账户的权限; +// String walletAccount = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; +// String user1 = "MMMEy902jkjjJJDkshreGeasdfassdfajjf=="; +// String user2 = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; +// // 配置: +// // “状态数据的写入权限”的阈值为 100; +// // 需要 user1、user2 两个账户的联合签名才能写入; +// // 当前账户仅用于表示一个业务钱包,禁止自身的写入权限,只能由业务角色的账户才能操作; +// +// +// +// txTemp.configPrivilege(walletAccount).setThreshhold(PrivilegeType.STATE_WRITE, 100) +// .enable(PrivilegeType.STATE_WRITE, user1, 50).enable(PrivilegeType.STATE_WRITE, user2, 50) +// .disable(PrivilegeType.STATE_WRITE, walletAccount); +// // -------------------------------------- +// +// // TX 准备就绪; +// PreparedTransaction prepTx = txTemp.prepare(); +// +// // 使用私钥进行签名; +// BlockchainKeyPair sponsorKey = getSponsorKey(); +// prepTx.sign(sponsorKey); +// +// // 提交交易; +// prepTx.commit(); +// } +// +// private static HashDigest getLedgerHash() { +// // TODO Init ledger hash; +// return null; +// } +// +// private static BlockchainKeyPair getSponsorKey() { +// // TODO Auto-generated method stub +// return null; +// } + +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Query.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Query.java new file mode 100644 index 00000000..7a680ac8 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_Query.java @@ -0,0 +1,87 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerTransaction; +import com.jd.blockchain.ledger.Transaction; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * 演示区块链信息查询的过程; + * + * @author huanghaiquan + * + */ +public class SDKDemo_Query { + + public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + + public static final HashDigest LEDGER_HASH = CryptoUtils.hash(CryptoAlgorithm.SHA256) + .hash("xkxjcioewfqwe".getBytes()); + + /** + * 演示合约执行的过程; + */ + public static void demoContract() { + // 区块链共识域; + String realm = "SUPPLY_CHAIN_ALLIANCE"; + // 节点地址列表; + NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), + new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), + new NetworkAddress("192.168.10.13", 8080) }; + + // 网关客户端编号; + int gatewayId = 1001; // 客户端的认证账户; + // 账本地址; + String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; + String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; + String privKey = "safefsd32q34vdsvs"; + // 创建服务代理; + 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(); + + // 查询区块信息; + // 区块高度; + 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); + + // 根据交易的 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(); + +// boolean exist = service.containState(LEDGER_HASH, commerceAccount, "x003"); + + // 按条件查询; + // 1、从保存会员信息的账户地址查询; +// String condition = "female = true AND age > 18 AND address.city = 'beijing'"; +// String memberInfoAccountAddress = "kkf2io39823jfIjfiIRWKQj30203fx=="; +// StateMap memberInfo = service.queryObject(LEDGER_HASH, memberInfoAccountAddress, condition); +// +// // 2、从保存会员信息的账户地址查询; +// Map memberInfoWithAccounts = service.queryObject(LEDGER_HASH, condition); + } + +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterAccount.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterAccount.java new file mode 100644 index 00000000..283b8fc4 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterAccount.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.ConsoleUtils; + +public class SDKDemo_RegisterAccount { + + public static void main(String[] args) { + + String GATEWAY_IPADDR = "127.0.0.1"; + int GATEWAY_PORT = 8081; + if (args != null && args.length == 2) { + GATEWAY_IPADDR = args[0]; + GATEWAY_PORT = Integer.parseInt(args[1]); + } + + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + + BlockchainKeyPair CLIENT_CERT = new BlockchainKeyPair(SDKDemo_Params.pubKey0, SDKDemo_Params.privkey0); + boolean SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, + CLIENT_CERT); + BlockchainService service = serviceFactory.getBlockchainService(); + + HashDigest[] ledgerHashs = service.getLedgerHashs(); + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = service.newTransaction(ledgerHashs[0]); + + //existed signer + CryptoKeyPair keyPair = new BlockchainKeyPair(SDKDemo_Params.pubKey1, SDKDemo_Params.privkey1); + + BlockchainKeyPair dataAcount = BlockchainKeyGenerator.getInstance().generate(); + + // 注册 + txTemp.dataAccounts().register(dataAcount.getIdentity()); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + ConsoleUtils.info("register dataaccount complete, result is [%s]", transactionResponse.isSuccess()); + } +} diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterTest.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterTest.java new file mode 100644 index 00000000..3701e6fa --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterTest.java @@ -0,0 +1,39 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.samples.SDKDemo_RegisterUser + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/10/18 下午2:00 + * Description: 注册用户 + */ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.ConsoleUtils; + +/** + * 注册用户 + * @author shaozhuguang + * @create 2018/10/18 + * @since 1.0.0 + */ + +public class SDKDemo_RegisterTest { + public static void main(String[] args) { + + if (args != null) { + if (args[0].equals("user")) { + SDKDemo_RegisterUser.main(null); + } else if (args[0].equals("dataaccount")) { + SDKDemo_RegisterAccount.main(null); + } + } + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterUser.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterUser.java new file mode 100644 index 00000000..fe5c18bc --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegisterUser.java @@ -0,0 +1,79 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.samples.SDKDemo_RegisterUser + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/10/18 下午2:00 + * Description: 注册用户 + */ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.ConsoleUtils; + +/** + * 注册用户 + * @author shaozhuguang + * @create 2018/10/18 + * @since 1.0.0 + */ + +public class SDKDemo_RegisterUser { + public static void main(String[] args) { + + String GATEWAY_IPADDR = "127.0.0.1"; + int GATEWAY_PORT = 8081; + if (args != null && args.length == 2) { + GATEWAY_IPADDR = args[0]; + GATEWAY_PORT = Integer.parseInt(args[1]); + } + + // 注册相关class + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + + PrivKey privKey = SDKDemo_Params.privkey1; + PubKey pubKey = SDKDemo_Params.pubKey1; + + BlockchainKeyPair CLIENT_CERT = new BlockchainKeyPair(SDKDemo_Params.pubKey0, SDKDemo_Params.privkey0); + + boolean SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, + CLIENT_CERT); + BlockchainService service = serviceFactory.getBlockchainService(); + + HashDigest[] ledgerHashs = service.getLedgerHashs(); + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = service.newTransaction(ledgerHashs[0]); + + //existed signer + CryptoKeyPair keyPair = new BlockchainKeyPair(pubKey, privKey); + + BlockchainKeyPair user = BlockchainKeyGenerator.getInstance().generate(); + + // 注册 + txTemp.users().register(user.getIdentity()); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + ConsoleUtils.info("register user complete, result is [%s]", transactionResponse.isSuccess()); + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_User.java b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_User.java new file mode 100644 index 00000000..e6bb4e81 --- /dev/null +++ b/source/sdk/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_User.java @@ -0,0 +1,96 @@ +package com.jd.blockchain.sdk.samples; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class SDKDemo_User { + + public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(); + + public static AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + /** + * 生成一个区块链用户,并注册到区块链; + */ + public static void registerUser() { + // 区块链共识域; + String realm = "SUPPLY_CHAIN_ALLIANCE"; + // 节点地址列表; + NetworkAddress[] peerAddrs = { new NetworkAddress("192.168.10.10", 8080), + new NetworkAddress("192.168.10.11", 8080), new NetworkAddress("192.168.10.12", 8080), + new NetworkAddress("192.168.10.13", 8080) }; + + // 网关客户端编号; + int gatewayId = 1001; + // 账本地址; + String ledgerAddress = "ffkjhkeqwiuhivnsh3298josijdocaijsda=="; + // 客户端的认证账户; + String clientAddress = "kkjsafieweqEkadsfaslkdslkae998232jojf=="; + String privKey = "safefsd32q34vdsvs"; + // 创建服务代理; + 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); + BlockchainTransactionService service = serviceFactory.getBlockchainService(); + + HashDigest ledgerHash = getLedgerHash(); + + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = service.newTransaction(ledgerHash); + + // -------------------------------------- + // 配置账户的权限; + String walletAccount = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; + String user1 = "MMMEy902jkjjJJDkshreGeasdfassdfajjf=="; + String user2 = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; + // 配置: + // “状态数据的写入权限”的阈值为 100; + // 需要 user1、user2 两个账户的联合签名才能写入; + // 当前账户仅用于表示一个业务钱包,禁止自身的写入权限,只能由业务角色的账户才能操作; + + String userPubKeyStr = "Kjfe8832hfa9jjjJJDkshrFjksjdlkfj93F=="; + + // 在本地产生要注册的账户的秘钥; + //BlockchainKeyGenerator generator = BlockchainKeyGenerator.getInstance(); + //BlockchainKeyPair user = generator.generate(CryptoKeyType.PUB_KEY); + + 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(); + } + + private static HashDigest getLedgerHash() { + // TODO Init ledger hash; + return null; + } + + private static CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } + +} diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_BatchInsertData_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_BatchInsertData_Test_.java new file mode 100644 index 00000000..933cf1ff --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_BatchInsertData_Test_.java @@ -0,0 +1,101 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.codec.Base58Utils; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_BatchInsertData_Test_ { + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + private AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + @Before + public void init() { + CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8000; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect( + GATEWAY_IPADDR, GATEWAY_PORT, SECURE, CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void batchInsertData_Test() { + HashDigest ledgerHash = service.getLedgerHashs()[0]; + // 在本地定义TX模板 + TransactionTemplate txTemp = service.newTransaction(ledgerHash); + + // -------------------------------------- + // 将商品信息写入到指定的账户中; + // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; + String dataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; + + String key1 = "jd_key1"; + byte[] val1 = "www.jd.com".getBytes(); + + String key2 = "jd_key2"; + byte[] val2 = "www.jd.com".getBytes(); + + // 版本号根据实际情况进行调整 + txTemp.dataAccount(dataAccount).set(key1, val1, -1); + txTemp.dataAccount(dataAccount).set(key2, val2, -1); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + CryptoKeyPair keyPair = getSponsorKey(); + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + assertTrue(transactionResponse.isSuccess()); + } + + private CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_ContractDeploy_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_ContractDeploy_Test_.java new file mode 100644 index 00000000..41b30fdd --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_ContractDeploy_Test_.java @@ -0,0 +1,102 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.utils.io.FileUtils; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_ContractDeploy_Test_ { + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + private String CONTRACT_FILE = null; + + private AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + @Before + public void init() { + CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8000; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, + CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void contractDeploy_Test() { + HashDigest ledgerHash = service.getLedgerHashs()[0]; + // 在本地定义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()); + } + + private CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_ContractExec_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_ContractExec_Test_.java new file mode 100644 index 00000000..ab6b8df3 --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_ContractExec_Test_.java @@ -0,0 +1,104 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_ContractExec_Test_ { + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + private AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + @Before + public void init() { + CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8000; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect( + GATEWAY_IPADDR, GATEWAY_PORT, SECURE, CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void contractExec_Test() { + HashDigest ledgerHash = getLedgerHash(); + // 在本地定义TX模板 + TransactionTemplate txTemp = service.newTransaction(ledgerHash); + + // 合约地址 + String contractAddressBase58 = ""; + + // Event + String event = ""; + + // args(注意参数的格式) + byte[] args = "20##30##abc".getBytes(); + + + // 提交合约执行代码 + txTemp.contractEvents().send(contractAddressBase58, event, args); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 生成私钥并使用私钥进行签名; + CryptoKeyPair keyPair = getSponsorKey(); + + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + assertTrue(transactionResponse.isSuccess()); + } + + private HashDigest getLedgerHash() { + HashDigest ledgerHash = new HashDigest(CryptoAlgorithm.SHA256, "jd-gateway".getBytes()); + return ledgerHash; + } + + + private CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_DataAccount_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_DataAccount_Test_.java new file mode 100644 index 00000000..3be8eb27 --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_DataAccount_Test_.java @@ -0,0 +1,89 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_DataAccount_Test_ { + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + private AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + @Before + public void init() { + CLIENT_CERT = new BlockchainKeyPair(SDK_GateWay_KeyPair_Para.pubKey0, SDK_GateWay_KeyPair_Para.privkey0); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8081; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, + CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void registerDataAccount_Test() { + HashDigest[] ledgerHashs = service.getLedgerHashs(); + // 在本地定义TX模板 + TransactionTemplate txTemp = service.newTransaction(ledgerHashs[0]); + + //existed signer + CryptoKeyPair keyPair = new BlockchainKeyPair(SDK_GateWay_KeyPair_Para.pubKey1, SDK_GateWay_KeyPair_Para.privkey1); + + BlockchainKeyPair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + + // 注册 + txTemp.dataAccounts().register(dataAccount.getIdentity()); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + + assertTrue(transactionResponse.isSuccess()); + } + + private SignatureFunction getSignatureFunction() { + return asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_InsertData_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_InsertData_Test_.java new file mode 100644 index 00000000..668b540c --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_InsertData_Test_.java @@ -0,0 +1,98 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.SignatureFunction; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.data.TxResponseMessage; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.BlockchainTransactionService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.sdk.samples.SDKDemo_InsertData; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_InsertData_Test_ { + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + private AsymmetricCryptography asymmetricCryptography = new AsymmtricCryptographyImpl(); + + @Before + public void init() { + CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8000; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, + CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void insertData_Test() { + HashDigest ledgerHash = service.getLedgerHashs()[0]; + // 在本地定义TX模板 + TransactionTemplate txTemp = service.newTransaction(ledgerHash); + + // -------------------------------------- + // 将商品信息写入到指定的账户中; + // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; + String dataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; + + String dataKey = "jd_code"; + + byte[] dataVal = "www.jd.com".getBytes(); + + txTemp.dataAccount(dataAccount).set(dataKey, dataVal, -1); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + CryptoKeyPair keyPair = getSponsorKey(); + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + assertTrue(transactionResponse.isSuccess()); + } + + private CryptoKeyPair getSponsorKey() { + SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); + return signatureFunction.generateKeyPair(); + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java new file mode 100644 index 00000000..94dddf05 --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_KeyPair_Para.java @@ -0,0 +1,34 @@ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.tools.keygen.KeyGenCommand; + +/** + * Created by zhangshuang3 on 2018/10/17. + */ +public class SDK_GateWay_KeyPair_Para { + public static final String PASSWORD = "abc"; + + public static final String[] PUB_KEYS = { "endPsK36imXrY66pru6ttZ8dZ3TynWekmdqoM1K7ZRRoRBBiYVzM", + "endPsK36jQE1uYpdVRSnwQXVYhgAMWTaMJiAqii7URiULoBDLUUN", + "endPsK36fc7FSecKAJCJdFhTejbPHMLaGcihJVQCv95czCq4tW5n", + "endPsK36m1grx8mkTMgh8XQHiiaNzajdC5hkuqP6pAuLmMbYkzd4" }; + + public static final String[] PRIV_KEYS = { + "177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X", + "177gjwQwTdXthkutDKVgKwiq6wWfLWYuxhji1U2N1C5MzqLRWCLZXo3i2g4vpfcEAQUPG8H", + "177gjvLHUjxvAWsqVcGgV8eHgVNBvJZYDfpP9FLjTouR1gEJNiamYu1qjTNDh18XWyLg8or", + "177gk2VtYeGbK5TS2xWhbSZA4BsT9Xj5Fb8hqCzxzgbojVVcqaDSFFrFPsLbZBx7rszyCNy" }; + + public static PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + public static PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + public static PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + public static PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + public static PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + public static PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + public static PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + public static PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + +} diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Query_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Query_Test_.java new file mode 100644 index 00000000..f68ed9ae --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Query_Test_.java @@ -0,0 +1,240 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.client.ClientOperationUtil; +import org.apache.commons.codec.binary.Hex; +import org.junit.Before; +import org.junit.Test; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.AsymmetricCryptography; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.crypto.impl.AsymmtricCryptographyImpl; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; + + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_Query_Test_ { + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + @Before + public void init() { + CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(CryptoAlgorithm.ED25519); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8081; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, + CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void query_Test() { + + // Get First Ledger + HashDigest ledgerHash = service.getLedgerHashs()[0]; + System.out.println("ledgerHash=" + ledgerHash.toBase58()); + + // Show Ledger Info + LedgerInfo ledgerInfo = service.getLedger(ledgerHash); + + // Get highest block height + long latestBlockHeight = ledgerInfo.getLatestBlockHeight(); + + // Get highest block hash + HashDigest latestBlockHash = ledgerInfo.getLatestBlockHash(); + + System.out.println("latestBlockHeight=" + latestBlockHeight); + + System.out.println("latestBlockHash=" + latestBlockHash.toBase58()); + + System.out.println("LedgerHash=" + ledgerInfo.getHash().toBase58()); + + // Get newest block + LedgerBlock latestBlock = service.getBlock(ledgerHash, latestBlockHeight); + + System.out.println("latestBlock.Hash=" + latestBlock.getHash()); + + // Get total contract size + long count = service.getContractCount(ledgerHash, latestBlockHeight); + + System.out.println("contractCount=" + count); + + count = service.getContractCount(ledgerHash, latestBlockHash); + + System.out.println("contractCount=" + count); + + if (count != 0) { + AccountHeader[] accountHeaders = service.getContractAccounts(ledgerHash, 0, (int)count); + for (AccountHeader accountHeader : accountHeaders) { + String contractAddress = accountHeader.getAddress().toBase58(); + System.out.println("Contract address = " + contractAddress); + // Get one contract by contract address + AccountHeader contract = service.getContract(ledgerHash, contractAddress); + } + } + + // Get other block info + LedgerBlock block = service.getBlock(ledgerHash, latestBlockHeight - 1); + System.out.println("block.Hash=" + block.getHash()); + + // Get Total DataAccount Size + count = service.getDataAccountCount(ledgerHash, latestBlockHeight); + + System.out.println("dataAccountCount=" + count); + + count = service.getDataAccountCount(ledgerHash, latestBlockHash); + + System.out.println("dataAccountCount=" + count); + + String queryDataAccountAddress = null; + + if (count > 0) { + AccountHeader[] accountHeaders = service.getDataAccounts(ledgerHash, 0, (int)count); + for (AccountHeader accountHeader : accountHeaders) { + String dataAccountAddress = accountHeader.getAddress().toBase58(); + System.out.println("DataAccount address = " + dataAccountAddress); + // Get one Data Account by address + AccountHeader dataAccount = service.getDataAccount(ledgerHash, dataAccountAddress); + queryDataAccountAddress = dataAccountAddress; + } + } + + // Get total transaction size + count = service.getTransactionCount(ledgerHash, latestBlockHash); + System.out.println("transactionCount=" + count); + + count = service.getTransactionCount(ledgerHash, latestBlockHeight); + System.out.println("transactionCount=" + count); + + // Get transaction list + LedgerTransaction[] txList = service.getTransactions(ledgerHash, 0, 0, 100); + for (LedgerTransaction ledgerTransaction : txList) { + System.out.println("transaction.executionState=" + ledgerTransaction.getExecutionState()); +// System.out.println("transaction.hash=" + ledgerTransaction.getHash().toBase58()); + TransactionContent txContent = ledgerTransaction.getTransactionContent(); + System.out.println("transactionContent.hash=" + txContent.getHash().toBase58()); + 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(); + System.out.println("register account = " + blockchainIdentity.getAddress().toBase58()); + } else if (operation instanceof UserRegisterOperation) { + UserRegisterOperation uro = (UserRegisterOperation) operation; + BlockchainIdentity blockchainIdentity = uro.getUserID(); + System.out.println("register user = " + blockchainIdentity.getAddress().toBase58()); + } else if (operation instanceof LedgerInitOperation) { + + LedgerInitOperation ledgerInitOperation = (LedgerInitOperation)operation; + LedgerInitSetting ledgerInitSetting = ledgerInitOperation.getInitSetting(); + + System.out.println(Hex.encodeHexString(ledgerInitSetting.getLedgerSeed())); + System.out.println(ledgerInitSetting.getConsensusProvider()); + System.out.println(ledgerInitSetting.getConsensusSettings().toBase58()); + + ParticipantNode[] participantNodes = ledgerInitSetting.getConsensusParticipants(); + if (participantNodes != null && participantNodes.length > 0) { + for (ParticipantNode participantNode : participantNodes) { + System.out.println("participantNode.id=" + participantNode.getId()); + System.out.println("participantNode.name=" + participantNode.getName()); + System.out.println("participantNode.address=" + participantNode.getAddress()); + System.out.println("participantNode.pubKey=" + participantNode.getPubKey().toBase58()); + } + } + + } else if (operation instanceof ContractCodeDeployOperation) { + ContractCodeDeployOperation ccdo = (ContractCodeDeployOperation) operation; + BlockchainIdentity blockchainIdentity = ccdo.getContractID(); + System.out.println("deploy contract = " + blockchainIdentity.getAddress()); + } else if (operation instanceof ContractEventSendOperation) { + ContractEventSendOperation ceso = (ContractEventSendOperation) operation; + System.out.println("event = " + ceso.getEvent()); + System.out.println("execute contract address = " + ceso.getContractAddress().toBase58()); + } else if (operation instanceof DataAccountKVSetOperation) { + DataAccountKVSetOperation.KVWriteEntry[] kvWriteEntries = + ((DataAccountKVSetOperation) operation).getWriteSet(); + if (kvWriteEntries != null && kvWriteEntries.length > 0) { + for (DataAccountKVSetOperation.KVWriteEntry kvWriteEntry : kvWriteEntries) { + System.out.println("writeSet.key=" + kvWriteEntry.getKey()); + BytesValue bytesValue = kvWriteEntry.getValue(); + DataType dataType = bytesValue.getType(); + Object showVal = ClientOperationUtil.readValueByBytesValue(bytesValue); + System.out.println("writeSet.value=" + showVal); + System.out.println("writeSet.type=" + dataType); + System.out.println("writeSet.version=" + kvWriteEntry.getExpectedVersion()); + } + } + } + } + } + } + + // Get txs by block height + txList = service.getTransactions(ledgerHash, latestBlockHash, 0, 100); + for (LedgerTransaction ledgerTransaction : txList) { + System.out.println("ledgerTransaction.Hash=" + ledgerTransaction.getHash()); + } + + + // Get total ParticipantNode array + ParticipantNode[] participants = service.getConsensusParticipants(ledgerHash); + for (ParticipantNode participant : participants) { + System.out.println("participant.name=" + participant.getName()); +// System.out.println(participant.getConsensusAddress()); +// System.out.println("participant.host=" + participant.getConsensusAddress().getHost()); + System.out.println("participant.getPubKey=" + participant.getPubKey()); + System.out.println("participant.getKeyType=" + participant.getPubKey().getKeyType()); + System.out.println("participant.getRawKeyBytes=" + participant.getPubKey().getRawKeyBytes()); + System.out.println("participant.algorithm=" + participant.getPubKey().getAlgorithm()); + } + + // Get total kvs + KVDataEntry[] kvData = service.getDataEntries(ledgerHash, queryDataAccountAddress, 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()); + + // Get one kvData by key + KVDataEntry[] kvDataEntries = service.getDataEntries(ledgerHash, + queryDataAccountAddress, kvDatum.getKey()); + + for (KVDataEntry kv : kvDataEntries) { + System.out.println("kv.key=" + kv.getKey()); + System.out.println("kv.version=" + kv.getVersion()); + System.out.println("kv.type=" + kv.getType()); + System.out.println("kv.value=" + kv.getValue()); + } + } + } + } +} \ No newline at end of file diff --git a/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_User_Test_.java b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_User_Test_.java new file mode 100644 index 00000000..2a571f85 --- /dev/null +++ b/source/sdk/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_User_Test_.java @@ -0,0 +1,84 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.sdk.test.SDK_GateWay_InsertData_Test + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/4 上午11:06 + * Description: 插入数据测试 + */ +package test.com.jd.blockchain.sdk.test; + +import com.jd.blockchain.crypto.asymmetric.*; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; + +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertTrue; + +/** + * 插入数据测试 + * @author shaozhuguang + * @create 2018/9/4 + * @since 1.0.0 + */ + +public class SDK_GateWay_User_Test_ { + + private PrivKey privKey; + + private PubKey pubKey; + + private BlockchainKeyPair CLIENT_CERT = null; + + private String GATEWAY_IPADDR = null; + + private int GATEWAY_PORT; + + private boolean SECURE; + + private BlockchainService service; + + @Before + public void init() { + + privKey = SDK_GateWay_KeyPair_Para.privkey1; + pubKey = SDK_GateWay_KeyPair_Para.pubKey1; + + CLIENT_CERT = new BlockchainKeyPair(SDK_GateWay_KeyPair_Para.pubKey0, SDK_GateWay_KeyPair_Para.privkey0); + GATEWAY_IPADDR = "127.0.0.1"; + GATEWAY_PORT = 8081; + SECURE = false; + GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect( + GATEWAY_IPADDR, GATEWAY_PORT, SECURE, CLIENT_CERT); + service = serviceFactory.getBlockchainService(); + } + + @Test + public void registerUser_Test() { + HashDigest[] ledgerHashs = service.getLedgerHashs(); + // 在本地定义TX模板 + TransactionTemplate txTemp = service.newTransaction(ledgerHashs[0]); + + //existed signer + CryptoKeyPair keyPair = new BlockchainKeyPair(pubKey, privKey); + + BlockchainKeyPair user = BlockchainKeyGenerator.getInstance().generate(); + + // 注册 + txTemp.users().register(user.getIdentity()); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + prepTx.sign(keyPair); + + // 提交交易; + TransactionResponse transactionResponse = prepTx.commit(); + assertTrue(transactionResponse.isSuccess()); + } +} \ No newline at end of file diff --git a/source/storage/pom.xml b/source/storage/pom.xml new file mode 100644 index 00000000..30d5b132 --- /dev/null +++ b/source/storage/pom.xml @@ -0,0 +1,29 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + storage + pom + + storage-service + storage-redis + storage-rocksdb + storage-composite + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/storage/storage-composite/pom.xml b/source/storage/storage-composite/pom.xml new file mode 100644 index 00000000..975e29d7 --- /dev/null +++ b/source/storage/storage-composite/pom.xml @@ -0,0 +1,38 @@ + + 4.0.0 + + com.jd.blockchain + storage + 0.8.2.RELEASE + + storage-composite + + + + com.jd.blockchain + storage-service + ${project.version} + + + com.jd.blockchain + storage-redis + ${project.version} + + + com.jd.blockchain + storage-rocksdb + ${project.version} + + + + + + \ No newline at end of file diff --git a/source/storage/storage-composite/src/main/java/com/jd/blockchain/storage/service/impl/composite/CompositeConnectionFactory.java b/source/storage/storage-composite/src/main/java/com/jd/blockchain/storage/service/impl/composite/CompositeConnectionFactory.java new file mode 100644 index 00000000..d2f223e7 --- /dev/null +++ b/source/storage/storage-composite/src/main/java/com/jd/blockchain/storage/service/impl/composite/CompositeConnectionFactory.java @@ -0,0 +1,76 @@ +package com.jd.blockchain.storage.service.impl.composite; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.storage.service.impl.redis.JedisConnection; +import com.jd.blockchain.storage.service.impl.redis.RedisConnectionFactory; +import com.jd.blockchain.storage.service.impl.rocksdb.RocksDBConnection; +import com.jd.blockchain.storage.service.impl.rocksdb.RocksDBConnectionFactory; +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; +import com.jd.blockchain.storage.service.utils.MemoryDBConn; +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; + + +public class CompositeConnectionFactory implements DbConnectionFactory { + + private static final RocksDBConnectionFactory rocksDBConnFactory = new RocksDBConnectionFactory(); + + private static final RedisConnectionFactory redisConnFactory = new RedisConnectionFactory(); + + private static final MemoryDBConnFactory memoryConnFactory = new MemoryDBConnFactory(); + + private static final String CONN_PREFIX_REDIS = "redis://"; + + private static final String CONN_PREFIX_ROCKSDB = "rocksdb://"; + + private static final String CONN_PREFIX_MEMORY = "memory://"; + + private final Map connections = new ConcurrentHashMap<>(); + + @Override + public DbConnection connect(String dbUri) { + return connect(dbUri, null); + } + + @Override + public DbConnection connect(String dbConnectionString, String password) { + if (!dbConnectionString.startsWith(CONN_PREFIX_REDIS) && + !dbConnectionString.startsWith(CONN_PREFIX_ROCKSDB) && + !dbConnectionString.startsWith(CONN_PREFIX_MEMORY)){ + throw new IllegalArgumentException("Illegal format of composite db connection string!"); + } + + if (dbConnectionString.startsWith(CONN_PREFIX_REDIS)) { + return redisConnFactory.connect(dbConnectionString, password); + } else if (dbConnectionString.startsWith(CONN_PREFIX_ROCKSDB)) { + return rocksDBConnFactory.connect(dbConnectionString, password); + } else if (dbConnectionString.startsWith(CONN_PREFIX_MEMORY)) { + return memoryConnFactory.connect(dbConnectionString, password); + } + return null; + } + + @Override + public void close() { + for (DbConnection dbConnection : connections.values()) { + if (dbConnection.getClass().equals(JedisConnection.class)) { + ((JedisConnection)dbConnection).close(); + } + else if (dbConnection.getClass().equals(RocksDBConnection.class)) { + ((RocksDBConnection)dbConnection).dbClose(); + } + else if (dbConnection.getClass().equals(MemoryDBConn.class)) { + ((MemoryDBConn)dbConnection).close(); + } + } + connections.clear(); + } + + @Override + public boolean support(String scheme) { + return rocksDBConnFactory.support(scheme) || redisConnFactory.support(scheme) || memoryConnFactory.support(scheme); + } +} diff --git a/source/storage/storage-composite/src/main/java/com/jd/blockchain/storage/service/impl/composite/CompositeStorageConfiguration.java b/source/storage/storage-composite/src/main/java/com/jd/blockchain/storage/service/impl/composite/CompositeStorageConfiguration.java new file mode 100644 index 00000000..da0fb4ce --- /dev/null +++ b/source/storage/storage-composite/src/main/java/com/jd/blockchain/storage/service/impl/composite/CompositeStorageConfiguration.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.storage.service.impl.composite; + +import com.jd.blockchain.storage.service.DbConnectionFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +/** + * Created by zhangshuang3 on 2018/11/26. + */ +@Configuration +@ComponentScan +public class CompositeStorageConfiguration { + @ConditionalOnMissingBean + @Bean + public DbConnectionFactory compositeConnectionFactory() { + return new CompositeConnectionFactory(); + } +} diff --git a/source/storage/storage-composite/src/main/resources/META-INF/spring.factories b/source/storage/storage-composite/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..fd709f72 --- /dev/null +++ b/source/storage/storage-composite/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.jd.blockchain.storage.service.impl.composite.CompositeStorageConfiguration \ No newline at end of file diff --git a/source/storage/storage-composite/src/test/java/test/com/jd/blockchain/storage/service/impl/composite/CompositeConnectionFactoryTest.java b/source/storage/storage-composite/src/test/java/test/com/jd/blockchain/storage/service/impl/composite/CompositeConnectionFactoryTest.java new file mode 100644 index 00000000..ddc779e7 --- /dev/null +++ b/source/storage/storage-composite/src/test/java/test/com/jd/blockchain/storage/service/impl/composite/CompositeConnectionFactoryTest.java @@ -0,0 +1,112 @@ +package test.com.jd.blockchain.storage.service.impl.composite; +import static org.junit.Assert.*; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import com.jd.blockchain.storage.service.impl.rocksdb.RocksDBConnectionFactory; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; + +import org.junit.Test; + +import java.io.File; +import java.util.regex.Pattern; + +public class CompositeConnectionFactoryTest { + + public static final Pattern URI_PATTER_REDIS = Pattern + .compile("^\\w+\\://(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\:\\d+(/\\d*(/.*)*)?$"); + + @Test + public void testRedisConnectionString() { + String connStr = "redis://192.168.86.130:6379/"; + boolean match = URI_PATTER_REDIS.matcher(connStr).matches(); + assertTrue(match); + + connStr = "redis://192.168.86.131:6379/"; + match = URI_PATTER_REDIS.matcher(connStr).matches(); + assertTrue(match); + + connStr = "redis://192.168.86.132:6379/"; + match = URI_PATTER_REDIS.matcher(connStr).matches(); + assertTrue(match); + + connStr = "redis://192.168.86.133:6379/"; + match = URI_PATTER_REDIS.matcher(connStr).matches(); + assertTrue(match); + } + + @Test + public void testRocksDbConnect() { + String dbUri = initEmptyDB("rocksdb_storage_test"); + long expectedVersion; + try (CompositeConnectionFactory dbConnFactory = new CompositeConnectionFactory()) { +// try (CompositeConnectionFactory dbConnFactory = CompositeConnectionFactory.getInstance()) { + DbConnection conn = dbConnFactory.connect(dbUri); + VersioningKVStorage verStorage = conn.getStorageService().getVersioningKVStorage(); + ExPolicyKVStorage exStorage = conn.getStorageService().getExPolicyKVStorage(); + + expectedVersion = test(verStorage); + + test(exStorage); + } + } + private String initEmptyDB(String name) { + String currDir = FileUtils.getCurrentDir(); + String dbDir = new File(currDir, name + ".db").getAbsolutePath(); + FileUtils.deleteFile(dbDir); + String dbURI = "rocksdb://" + dbDir; + return dbURI; + } + private long test(VersioningKVStorage verStorage) { + String key = "k1"; + long v = verStorage.getVersion(Bytes.fromString(key)); + assertEquals(-1, v); + byte[] data = verStorage.get(Bytes.fromString(key), -1); + assertNull(data); + data = verStorage.get(Bytes.fromString(key), 0); + assertNull(data); + data = verStorage.get(Bytes.fromString(key), 1); + assertNull(data); + + data = BytesUtils.toBytes("data"); + v = verStorage.set(Bytes.fromString(key), data, -1); + assertEquals(0, v); + v = verStorage.set(Bytes.fromString(key), data, -1); + assertEquals(-1, v); + v = verStorage.set(Bytes.fromString(key), data, 0); + assertEquals(1, v); + return v; + } + + private void test(ExPolicyKVStorage exStorage) { + String key = "kex"; + assertFalse(exStorage.exist(Bytes.fromString(key))); + + byte[] data = exStorage.get(Bytes.fromString(key)); + assertNull(data); + + data = BytesUtils.toBytes("data"); + assertFalse(exStorage.set(Bytes.fromString(key), data, ExPolicyKVStorage.ExPolicy.EXISTING)); + + assertTrue(exStorage.set(Bytes.fromString(key), data, ExPolicyKVStorage.ExPolicy.NOT_EXISTING)); + assertTrue(exStorage.exist(Bytes.fromString(key))); + + assertFalse(exStorage.set(Bytes.fromString(key), data, ExPolicyKVStorage.ExPolicy.NOT_EXISTING)); + + assertTrue(exStorage.set(Bytes.fromString(key), data, ExPolicyKVStorage.ExPolicy.EXISTING)); + assertTrue(exStorage.set(Bytes.fromString(key), data, ExPolicyKVStorage.ExPolicy.EXISTING)); + assertFalse(exStorage.set(Bytes.fromString(key), data, ExPolicyKVStorage.ExPolicy.NOT_EXISTING)); + + assertTrue(exStorage.exist(Bytes.fromString(key))); + + byte[] reloadData = exStorage.get(Bytes.fromString(key)); + assertTrue(BytesUtils.equals(data, reloadData)); + } + + +} diff --git a/source/storage/storage-redis/pom.xml b/source/storage/storage-redis/pom.xml new file mode 100644 index 00000000..abf14fb0 --- /dev/null +++ b/source/storage/storage-redis/pom.xml @@ -0,0 +1,42 @@ + + 4.0.0 + + com.jd.blockchain + storage + 0.8.2.RELEASE + + storage-redis + + + + com.jd.blockchain + storage-service + ${project.version} + + + com.jd.blockchain + utils-common + ${project.version} + + + org.springframework.boot + spring-boot + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + redis.clients + jedis + + + + \ No newline at end of file diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/JedisConnection.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/JedisConnection.java new file mode 100644 index 00000000..0494b53f --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/JedisConnection.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.KVStorageService; + +import redis.clients.jedis.JedisPool; + +public class JedisConnection implements DbConnection { + + private JedisPool jedisPool; + + private RedisStorageService storage; + + public JedisConnection(JedisPool jedisPool) { + this.jedisPool = jedisPool; + this.storage = new RedisStorageService(jedisPool); + } + + @Override + public void close() { + jedisPool.close(); + } + + @Override + public KVStorageService getStorageService() { + return storage; + } + +} diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/JedisProperties.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/JedisProperties.java new file mode 100644 index 00000000..8536df26 --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/JedisProperties.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.storage.service.impl.redis; + +public class JedisProperties { + + private String host; + + private int port = 6379; + + private int db = 0; + + 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; + } + + public int getDb() { + return db; + } + + public void setDb(int db) { + this.db = db; + } + +} diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisConnectionFactory.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisConnectionFactory.java new file mode 100644 index 00000000..95e7db3a --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisConnectionFactory.java @@ -0,0 +1,83 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import java.net.URI; +import java.util.regex.Pattern; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; + +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.Protocol; + +public class RedisConnectionFactory implements DbConnectionFactory { + + public static final Pattern URI_PATTER = Pattern + .compile("^\\w+\\://(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\:\\d+(/\\d*(/.*)*)?$"); + // public static final Pattern URI_PATTER = Pattern + // .compile("^\\w+\\://(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})\\:\\d+(/\\d+)?(/.*)*$"); + + @Override + public DbConnection connect(String dbUri) { + return connect(dbUri, null); + } + + @Override + public DbConnection connect(String dbConnectionString, String password) { + + URI dbUri = URI.create(dbConnectionString); + if (!(dbUri.getScheme().equalsIgnoreCase("redis"))) { + throw new IllegalArgumentException( + String.format("Not supported db connection string with scheme \"%s\"!", dbUri.getScheme())); + } + + JedisPoolConfig config = new JedisPoolConfig(); + config.setMaxTotal(500); + config.setMaxIdle(500); + config.setMinIdle(50); + config.setMaxWaitMillis(1000 * 30); + config.setTestOnBorrow(false); + + String host = dbUri.getHost(); + int port = dbUri.getPort(); + int dbId = retriveDbIdFromPath(dbUri.getPath()); + JedisPool pool = new JedisPool(config, host, port, Protocol.DEFAULT_TIMEOUT, password, dbId, false); + return new JedisConnection(pool); + } + + /** + * 从 URI 路径检索数据库 ID ;
+ * 预期路径参数的样式为“/{id}”开头,如果忽略路径之后加入的其它节;
+ * 如果没有定义数据库ID,或者不符合样式,则返回默认的数据库ID ({@link Protocol#DEFAULT_DATABASE}); + * + * @param uriPath + * @return + */ + private int retriveDbIdFromPath(String uriPath) { + if (uriPath == null || uriPath.length() == 0) { + return Protocol.DEFAULT_DATABASE; + } + int secondIndex = uriPath.indexOf('/', 1); + String idStr; + if (secondIndex < 0) { + idStr = uriPath.substring(1).trim(); + } else { + idStr = uriPath.substring(1, secondIndex).trim(); + } + int dbId = Integer.parseInt(idStr); + if (dbId < 0) { + return Protocol.DEFAULT_DATABASE; + } + return dbId; + } + + @Override + public boolean support(String scheme) { + return RedisConsts.URI_SCHEME.equalsIgnoreCase(scheme); + } + + @Override + public void close() { + // TODO:  未实现连接池的关闭; + } +} diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisConsts.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisConsts.java new file mode 100644 index 00000000..922ccdcc --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisConsts.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import redis.clients.util.SafeEncoder; + +public interface RedisConsts { + + public static final String URI_SCHEME = "redis"; + + public static final String OK = "OK"; + + public static final String OK_MULTI = "+OK"; + + byte[] XX = SafeEncoder.encode("XX"); + + byte[] NX = SafeEncoder.encode("NX"); + +} diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisExPolicyStorage.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisExPolicyStorage.java new file mode 100644 index 00000000..6a5880f2 --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisExPolicyStorage.java @@ -0,0 +1,67 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.utils.Bytes; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.util.SafeEncoder; + +public class RedisExPolicyStorage implements ExPolicyKVStorage { + + private JedisPool jedisPool; + + public RedisExPolicyStorage(JedisPool jedisPool) { + this.jedisPool = jedisPool; + } + + @Override + public byte[] get(Bytes key) { + try (Jedis jedis = jedisPool.getResource()) { +// byte[] keyBytes = SafeEncoder.encode(key); +// byte[] valueBytes = jedis.get(keyBytes); + byte[] valueBytes = jedis.get(key.toBytes()); + return valueBytes; + } + } + + @Override + public boolean exist(Bytes key) { + try (Jedis jedis = jedisPool.getResource()) { +// byte[] keyBytes = SafeEncoder.encode(key); +// return jedis.exists(keyBytes); + return jedis.exists(key.toBytes()); + } + } + + @Override + public boolean set(Bytes key, byte[] value, ExPolicy ex) { + try (Jedis jedis = jedisPool.getResource()) { + byte[] nxxx; + switch (ex) { + case EXISTING: + nxxx = RedisConsts.XX; + break; + case NOT_EXISTING: + nxxx = RedisConsts.NX; + break; + default: + throw new IllegalArgumentException("Unsupported ExPolicy[" + ex.toString() + "]!"); + } +// byte[] keyBytes = SafeEncoder.encode(key); +// String retn = jedis.set(keyBytes, value, nxxx); + String retn = jedis.set(key.toBytes(), value, nxxx); + return RedisConsts.OK.equalsIgnoreCase(retn); + } + } + + @Override + public void batchBegin() { + // un support!!! + } + + @Override + public void batchCommit() { + // un support!!! + } +} \ No newline at end of file diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisStorageConfiguration.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisStorageConfiguration.java new file mode 100644 index 00000000..ae5a6090 --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisStorageConfiguration.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +import com.jd.blockchain.storage.service.DbConnectionFactory; + +@Configuration +@ComponentScan +public class RedisStorageConfiguration { + +// @Autowired +// private JedisProperties jedisProps; + +// @Bean +// public Jedis jedis() { +// Jedis jedis = new Jedis(jedisProps.getHost(), jedisProps.getPort()); +// jedis.connect(); +// jedis.select(jedisProps.getDb()); +// return jedis; +// } + + @ConditionalOnMissingBean + @Bean + public DbConnectionFactory redisConnectionFactory() { + return new RedisConnectionFactory(); + } + +} diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisStorageService.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisStorageService.java new file mode 100644 index 00000000..787b77c5 --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisStorageService.java @@ -0,0 +1,39 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.KVStorageService; +import com.jd.blockchain.storage.service.VersioningKVStorage; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; + +public class RedisStorageService implements KVStorageService { + + private RedisExPolicyStorage exStorage; + + private RedisVerioningStorage verStorage; + + private JedisPool jedisPool; + + public RedisStorageService(JedisPool jedisPool) { + this.jedisPool = jedisPool; + this.exStorage = new RedisExPolicyStorage(jedisPool); + this.verStorage = new RedisVerioningStorage(jedisPool); + } + + @Override + public ExPolicyKVStorage getExPolicyKVStorage() { + return exStorage; + } + + @Override + public VersioningKVStorage getVersioningKVStorage() { + return verStorage; + } + + public void clearDB() { + try (Jedis jedis = jedisPool.getResource()) { + jedis.flushDB(); + } + } +} diff --git a/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisVerioningStorage.java b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisVerioningStorage.java new file mode 100644 index 00000000..773b3748 --- /dev/null +++ b/source/storage/storage-redis/src/main/java/com/jd/blockchain/storage/service/impl/redis/RedisVerioningStorage.java @@ -0,0 +1,135 @@ +package com.jd.blockchain.storage.service.impl.redis; + +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; + +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.util.SafeEncoder; + +/** + * 基于 Redis 实现的版本化KV存储; + * + *

+ * + * 版本化KV存储要求实现几个关键特性:
+ * 1、每个Key都有唯一的版本序列,版本序列从 0 开始;
+ * 2、对 Key 值的每一次更改都导致版本增长 1 ;
+ * 3、对“写入”和“版本增长”两个操作一起是原子性的;
+ * 4、“版本增长”必须以 1 顺序递增,不允许跳空递增;
+ * + *

+ * 由于 Redis 缺少事务特性,并且无法很好地自定义组合的原子操作( MULTI-EXEC 不适用于集群模式下),所以在此实现中只能做到前面 3 + * 点。
+ * 第 4 点由调用方(账本操作层)在调用前做了版本校验,所以从整个系统来看也可以保证以上的4点要求。 + * + * @author huanghaiquan + * + */ +public class RedisVerioningStorage implements VersioningKVStorage { + + private JedisPool jedisPool; + + public RedisVerioningStorage(JedisPool jedisPool) { + this.jedisPool = jedisPool; + } + + @Override + public long getVersion(Bytes key) { + try (Jedis jedis = jedisPool.getResource()) { +// byte[] keyBytes = SafeEncoder.encode(key); +// return jedis.hlen(keyBytes) - 1; + return jedis.hlen(key.toBytes()) - 1; + } + } + + @Override + public VersioningKVEntry getEntry(Bytes key, long version) { + byte[] value = get(key, version); + if (value == null) { + return null; + } + return new VersioningKVData(key, version, value); + } + + @Override + public byte[] get(Bytes key, long version) { + try (Jedis jedis = jedisPool.getResource()) { + long ver = version; + if (ver < 0) { + //查询最新; + ver = getVersion(key); + } + if (ver < 0) { + return null; + } +// byte[] keyBytes = SafeEncoder.encode(key); +// byte[] verBytes = encodeVersionKey(ver); +// byte[] value = jedis.hget(keyBytes, verBytes); + byte[] verBytes = encodeVersionKey(ver); + byte[] value = jedis.hget(key.toBytes(), verBytes); + return value; + } + } + + @Override + public long set(Bytes key, byte[] value, long version) { + try (Jedis jedis = jedisPool.getResource()) { +// byte[] keyBytes = SafeEncoder.encode(key); + long ver = version < 0 ? 0 : version + 1; + byte[] verBytes = encodeVersionKey(ver); + // 如果不存在,则写入;由于 Redis 特性的限制,此处无法原子性地校验是 version 参数否存在跳空增长, + // 默认在外部调用已经校验了 version 为最新版本; + Long r = jedis.hsetnx(key.toBytes(), verBytes, value); + return r.longValue() == 0 ? -1 : ver; + } + } + + private byte[] encodeVersionKey(long version) { + return SafeEncoder.encode("" + version); + } + + @Override + public void batchBegin() { + // un support!!! + } + + @Override + public void batchCommit() { + // un support!!! + } + + + private static class VersioningKVData implements VersioningKVEntry{ + + private Bytes key; + + private long version; + + private byte[] value; + + public VersioningKVData(Bytes key, long version, byte[] value) { + this.key = key; + this.version = version; + this.value = value; + } + + @Override + public Bytes getKey() { + return key; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public byte[] getValue() { + return value; + } + + } + +} \ No newline at end of file diff --git a/source/storage/storage-redis/src/main/resources/META-INF/spring.factories b/source/storage/storage-redis/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..c5a24b7e --- /dev/null +++ b/source/storage/storage-redis/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.jd.blockchain.storage.service.impl.redis.RedisStorageConfiguration \ No newline at end of file diff --git a/source/storage/storage-redis/src/test/java/test/com/jd/blockchain/storage/service/impl/redis/RedisConnectionFactoryTest.java b/source/storage/storage-redis/src/test/java/test/com/jd/blockchain/storage/service/impl/redis/RedisConnectionFactoryTest.java new file mode 100644 index 00000000..3f3be4ed --- /dev/null +++ b/source/storage/storage-redis/src/test/java/test/com/jd/blockchain/storage/service/impl/redis/RedisConnectionFactoryTest.java @@ -0,0 +1,32 @@ +package test.com.jd.blockchain.storage.service.impl.redis; +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.jd.blockchain.storage.service.impl.redis.RedisConnectionFactory; + +public class RedisConnectionFactoryTest { + + @Test + public void testConnectionString() { + String connStr = "redis://192.168.1.2:6379/1"; + boolean match = RedisConnectionFactory.URI_PATTER.matcher(connStr).matches(); + + connStr = "redis://192.168.1.2:6379/"; + match = RedisConnectionFactory.URI_PATTER.matcher(connStr).matches(); + assertTrue(match); + + connStr = "redis://192.168.1.2:6379"; + match = RedisConnectionFactory.URI_PATTER.matcher(connStr).matches(); + assertTrue(match); + + connStr = "redis://192.168.1.2:6379/33/kkew"; + match = RedisConnectionFactory.URI_PATTER.matcher(connStr).matches(); + assertTrue(match); + + connStr = "redis://192.168.1.2:6379/kkf/"; + match = RedisConnectionFactory.URI_PATTER.matcher(connStr).matches(); + assertFalse(match); + } + +} diff --git a/source/storage/storage-redis/src/test/java/test/com/jd/blockchain/storage/service/impl/redis/Test.java b/source/storage/storage-redis/src/test/java/test/com/jd/blockchain/storage/service/impl/redis/Test.java new file mode 100644 index 00000000..56828f51 --- /dev/null +++ b/source/storage/storage-redis/src/test/java/test/com/jd/blockchain/storage/service/impl/redis/Test.java @@ -0,0 +1,88 @@ +package test.com.jd.blockchain.storage.service.impl.redis; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.impl.redis.RedisConnectionFactory; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +import redis.clients.jedis.Jedis; + +public class Test { + + public static void main(String[] args) { + + Bytes key = Bytes.fromString("test111"); + try (Jedis jedis = new Jedis("192.168.151.33", 6379)) { + jedis.connect(); + byte[] kbytes = key.toBytes();// BytesUtils.toBytes(key, "UTF-8"); + byte[] valueBytes = jedis.get(kbytes); + String value = BytesUtils.toString(valueBytes, "UTF-8"); + System.out.println(String.format("%s=%s", key, value)); + } + System.out.println("================================="); + + System.out.println("================= test expolicy storage ================="); + + RedisConnectionFactory connFactory = new RedisConnectionFactory(); + try(DbConnection conn = connFactory.connect("redis://192.168.151.33:6379")){ + ExPolicyKVStorage exKVStorage = conn.getStorageService().getExPolicyKVStorage(); + byte[] valueBytes = exKVStorage.get(key); + String value = BytesUtils.toString(valueBytes, "UTF-8"); + System.out.println(String.format("%s=%s", key, value)); + + System.out.println(String.format("%s=%s", key, value)); + boolean success = exKVStorage.set(key, BytesUtils.toBytes("New Value by ExPolicyStorage interface..."), ExPolicy.NOT_EXISTING); + System.out.println("update key when NX " + (success ? "success" : "fail")); + success = exKVStorage.set(key, BytesUtils.toBytes("New Value by ExPolicyStorage interface..."), ExPolicy.EXISTING); + System.out.println("update key when XX " + (success ? "success" : "fail")); + + valueBytes = exKVStorage.get(key); + value = BytesUtils.toString(valueBytes, "UTF-8"); + System.out.println(String.format("Retrieve... %s=%s", key, value)); + }catch(Exception ex) { + ex.printStackTrace(); + } + System.out.println("================================="); + + try(DbConnection conn = connFactory.connect("redis://192.168.151.33:6379/0")){ + ExPolicyKVStorage exKVStorage = conn.getStorageService().getExPolicyKVStorage(); + byte[] valueBytes = exKVStorage.get(key); + String value = BytesUtils.toString(valueBytes, "UTF-8"); + System.out.println(String.format("%s=%s", key, value)); + }catch(Exception ex) { + ex.printStackTrace(); + } + System.out.println("================================="); + + System.out.println("================= test versioning storage ================="); + Bytes addr =Bytes.fromString("User001"); + try(DbConnection conn = connFactory.connect("redis://192.168.151.33:6379/0")){ + VersioningKVStorage verStorage = conn.getStorageService().getVersioningKVStorage(); + long version = verStorage.getVersion(addr); + System.out.println(String.format("Version of key[%s]=%s", addr, version)); + byte[] v1 = BytesUtils.toBytes("value-" + version); + version = verStorage.set(addr, v1, version); + System.out.println(String.format("Update[%s] to V1, version=%s", addr, version)); + byte[] v2 = BytesUtils.toBytes("value-" + version); + version = verStorage.set(addr, v2, version); + System.out.println(String.format("Update[%s] to V2, version=%s", addr, version)); + byte[] v3 = BytesUtils.toBytes("value-" + version); + version = verStorage.set(addr, v3, version); + System.out.println(String.format("Update[%s] to V3, version=%s", addr, version)); + + version = verStorage.getVersion(addr); + System.out.println(String.format("Now the latest version of key[%s]=%s", addr, version)); + for (int i = 0; i <=version; i++) { + String value = BytesUtils.toString(verStorage.get(addr, i)); + System.out.println(String.format("The version[%s] value of key[%s] is :%s", i, addr, value)); + } + }catch(Exception ex) { + ex.printStackTrace(); + } + System.out.println("================================="); + } + +} diff --git a/source/storage/storage-rocksdb/pom.xml b/source/storage/storage-rocksdb/pom.xml new file mode 100644 index 00000000..9ccef320 --- /dev/null +++ b/source/storage/storage-rocksdb/pom.xml @@ -0,0 +1,40 @@ + + 4.0.0 + + com.jd.blockchain + storage + 0.8.2.RELEASE + + storage-rocksdb + + + + com.jd.blockchain + storage-service + ${project.version} + + + + com.jd.blockchain + utils-common + ${project.version} + + + + org.rocksdb + rocksdbjni + + + + org.apache.commons + commons-collections4 + + + + \ No newline at end of file diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/demo/rocksdb/RocksDBDemo.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/demo/rocksdb/RocksDBDemo.java new file mode 100644 index 00000000..61425ad1 --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/demo/rocksdb/RocksDBDemo.java @@ -0,0 +1,286 @@ +package com.jd.blockchain.storage.service.demo.rocksdb; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveAction; + +import org.rocksdb.Options; +import org.rocksdb.RocksDB; +import org.rocksdb.RocksDBException; +import org.rocksdb.TransactionDB; +import org.rocksdb.TransactionDBOptions; + +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.security.ShaUtils; + +public class RocksDBDemo { + + private static String DB_PATH = "/Users/huanghaiquan/Documents/jd/projects/prototype/source/storage/storage-rocksdb/target/myrocks.db"; + private static String DB_0_PATH = "/Users/huanghaiquan/Documents/jd/projects/prototype/source/storage/storage-rocksdb/target/myrocks_0.db"; + private static String DB_1_PATH = "/Users/huanghaiquan/Documents/jd/projects/prototype/source/storage/storage-rocksdb/target/myrocks_1.db"; + + static { + RocksDB.loadLibrary(); + } + + private static Random rand = new Random(); + + + private static String initEmptyDB(String name) { + String currDir = FileUtils.getCurrentDir(); + String dbDir = new File(currDir, name + ".db").getAbsolutePath(); + FileUtils.deleteFile(dbDir); + return dbDir; + +// String dbURI = "rocksdb://" + dbDir; +// return dbURI; + + } + + + public static void main_test(String[] args) { + + DB_PATH = initEmptyDB("myrocks"); + + // the Options class contains a set of configurable DB options + // that determines the behaviour of the database. + try (final Options options = new Options(); ) { + options.setCreateIfMissing(true); + // a factory method that returns a RocksDB instance + try (final RocksDB db = RocksDB.open(options, DB_PATH)) { + // try (final TransactionDB db = + // TransactionDB.open(options, txnDbOptions, DB_PATH)) { + + // try (final RocksDB db0 = RocksDB.open(options, DB_0_PATH); + // final RocksDB db1 = RocksDB.open(options, DB_1_PATH)) { +// RocksDB[] dbs = { db0, db1 }; +// ConsoleUtils.info("simple test in db0"); +// simple_test(db0); +// ConsoleUtils.info("simple test in db1"); +// simple_test(db1); + + RocksDB[] dbs = { db }; + simple_test(db); + + ConsoleUtils.info("Then, I will do performance test..."); + + int count = 1000000; + int valueSize = 1024; + String keyPrefix = System.currentTimeMillis() + "_" + rand.nextInt() + "_"; + + ConsoleUtils.info("Synchronize writting ..."); + perf_writing_test(dbs, count, keyPrefix, valueSize, false); + + ConsoleUtils.info("Parallel writting ..."); + perf_writing_test(dbs, count, keyPrefix, valueSize, true); + + ConsoleUtils.info("Reading ..."); + perf_reading_test(dbs, count, keyPrefix, valueSize); + } + }catch( + + Exception e) + { + // do some error handling + e.printStackTrace(); + } + + } + + private static void simple_test(RocksDB db) throws RocksDBException { + String strKey = "hello"; + byte[] key = BytesUtils.toBytes(strKey); + byte[] value = db.get(key); + if (value == null) { + ConsoleUtils.info("Key[%s] doesn't exist! I will create it automactic.", strKey); + value = new byte[32]; + rand.nextBytes(value); + db.put(key, value); + } else { + ConsoleUtils.info("Key[%s] has been found! It's value is [%s].", strKey, Base58Utils.encode(value)); + } + } + + private static void perf_reading_test(RocksDB[] dbs, int count, String keyPrefix, int valueSize) + throws RocksDBException { + byte[] value = new byte[valueSize]; + + long startTs = System.currentTimeMillis(); + + byte[] key; + RocksDB db; + for (int i = 0; i < count; i++) { + key = BytesUtils.toBytes(keyPrefix + i); + db = dbs[i % dbs.length]; + int len = db.get(key, value); + if (len != value.length) { + throw new IllegalStateException(String.format( + "The size of value reloaded from rocksdb is out of expectation. [expected=%s][actual=%s]", + value.length, len)); + } + } + long elapsedTs = System.currentTimeMillis() - startTs; + + double tps = count * 1000.0 / elapsedTs; + ConsoleUtils.info( + "============= perf_reading_test : total keys = %s; tps= %.2f; elapsed millis = %s; value bytes = %s; ", + count, tps, elapsedTs, value.length); + } + + private static void perf_writing_test(RocksDB[] dbs, int count, String keyPrefix, int valueSize, boolean parallel) + throws RocksDBException { + byte[] value = new byte[valueSize]; + rand.nextBytes(value); + + long startTs = System.currentTimeMillis(); + + if (parallel) { + parallel_write(dbs, count, value, keyPrefix); + } else { + sync_write(dbs, count, value, keyPrefix); + } + + long elapsedTs = System.currentTimeMillis() - startTs; + + double tps = count * 1000.0 / elapsedTs; + ConsoleUtils.info( + "============= perf_writing_test [parallel=%s]: total keys = %s; tps= %.2f; elapsed millis = %s; value bytes = %s; ", + parallel, count, tps, elapsedTs, value.length); + } + + private static void sync_write(RocksDB[] dbs, int count, byte[] value, String keyPrefix) throws RocksDBException { + byte[] key; + if (dbs.length > 16) { + throw new IllegalArgumentException("Too many dbs!"); + } + //  先按默认 2 个 db 进行分片处理; + RocksDB db; + for (int i = 0; i < count; i++) { + key = BytesUtils.toBytes(keyPrefix + i); + db = dbs[i % dbs.length]; + db.put(key, value); + } + } + + private static void parallel_write(RocksDB[] dbs, int count, byte[] value, String keyPrefix) + throws RocksDBException { + // WriteTask task = new WriteTask(0, count, keyPrefix, value, dbs); + + StepWriteTask task1 = new StepWriteTask(0, count, keyPrefix, value, dbs[0], 2); + StepWriteTask task2 = new StepWriteTask(1, count, keyPrefix, value, dbs[0], 2); + + ForkJoinPool.commonPool().execute(task1); + ForkJoinPool.commonPool().execute(task2); + task1.join(); + task2.join(); + } + + private static class WriteTask extends RecursiveAction { + + private static final long serialVersionUID = -2609085082223653954L; + + private static int THRESHOLD = 2000; + + private String keyPrefix; + + private int offset; + + private int count; + + private byte[] value; + + private RocksDB[] dbs; + + public WriteTask(int offset, int count, String keyPrefix, byte[] value, RocksDB[] dbs) { + this.offset = offset; + this.count = count; + this.keyPrefix = keyPrefix; + this.value = value; + this.dbs = dbs; + } + + @Override + protected void compute() { + if (count > THRESHOLD) { + // List tasks = new LinkedList<>(); + // for (int i = 0; i < count;) { + // int c = Math.min(THRESHOLD, count - i); + // WriteTask task = new WriteTask(offset + i, c, keyPrefix, value, db); + // tasks.add(task); + // i += c; + // } + // ForkJoinTask.invokeAll(tasks); + + int count1 = count / 2; + int count2 = count - count1; + WriteTask task1 = new WriteTask(offset, count1, keyPrefix, value, dbs); + WriteTask task2 = new WriteTask(offset + count1, count2, keyPrefix, value, dbs); + ForkJoinTask.invokeAll(task1, task2); + } else { + byte[] key; + RocksDB db; + + List tasks = new LinkedList<>(); + for (int i = 0; i < dbs.length; i++) { + StepWriteTask stepTask = new StepWriteTask(offset + i, count, keyPrefix, value, dbs[i], dbs.length); + tasks.add(stepTask); + } + ForkJoinTask.invokeAll(tasks); + // for (int i = 0; i < count; i++) { + // key = BytesUtils.toBytes(keyPrefix + (offset + i)); + // db = dbs[(offset + i) % dbs.length]; + // db.put(key, value); + // } + } + } + + } + + private static class StepWriteTask extends RecursiveAction { + + private static final long serialVersionUID = -6046220943323359514L; + + private String keyPrefix; + + private int offset; + + private int step; + + private int count; + + private byte[] value; + + private RocksDB db; + + public StepWriteTask(int offset, int count, String keyPrefix, byte[] value, RocksDB db, int step) { + this.offset = offset; + this.count = count; + this.keyPrefix = keyPrefix; + this.value = value; + this.db = db; + this.step = step; + } + + @Override + protected void compute() { + try { + byte[] key; + for (int i = 0; i < count;) { + key = BytesUtils.toBytes(keyPrefix + (offset + i)); + db.put(key, value); + i += step; + } + } catch (RocksDBException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + } + +} diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/demo/rocksdb/RocksDBStoragePerformanceTest.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/demo/rocksdb/RocksDBStoragePerformanceTest.java new file mode 100644 index 00000000..f7d34989 --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/demo/rocksdb/RocksDBStoragePerformanceTest.java @@ -0,0 +1,92 @@ +package com.jd.blockchain.storage.service.demo.rocksdb; + +import java.io.File; +import java.util.Random; + +import org.bouncycastle.util.Arrays; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.storage.service.KVStorageService; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.impl.rocksdb.RocksDBConnectionFactory; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.io.FileUtils; + +public class RocksDBStoragePerformanceTest { + + public static void main(String[] args) { + + String uri = initEmptyDB("perf_rocksdb_storage_test"); + try (RocksDBConnectionFactory connFactory = new RocksDBConnectionFactory()) { + DbConnection conn = connFactory.connect(uri); + KVStorageService storageService = conn.getStorageService(); + + int count = 1000000; + int size = 1024; + Bytes keyPrefix = Bytes.fromString("KEY-" + System.currentTimeMillis() + "_"); + test_versioning_writing("test_versioning_writing", keyPrefix, count, size, + storageService.getVersioningKVStorage()); + Bytes exKeyPrefix = Bytes.fromString("EX").concat(keyPrefix); + test_existance_writing("test_existance_writing", exKeyPrefix, count, size, + storageService.getExPolicyKVStorage()); + } + + } + + private static void test_versioning_writing(String name, Bytes keyPrefix, int count, int valueSize, + VersioningKVStorage storage) { + byte[] value = new byte[valueSize]; + new Random().nextBytes(value); + long startTs = System.currentTimeMillis(); + + Bytes key; + for (int i = 0; i < count; i++) { + key = keyPrefix.concat(Bytes.fromInt(i)); + value = Arrays.copyOf(value, value.length); + long v = storage.set(key, value, -1); + if (v < 0) { + throw new IllegalStateException(String.format( + "The size of value reloaded from rocksdb is out of expectation. [expected=%s][actual=%s]", 0, + v)); + } + } + long elapsedTs = System.currentTimeMillis() - startTs; + + double tps = count * 1000.0 / elapsedTs; + ConsoleUtils.info("============= [%s] : total keys = %s; tps= %.2f; elapsed millis = %s; value bytes = %s; ", + name, count, tps, elapsedTs, value.length); + } + + private static void test_existance_writing(String name, Bytes keyPrefix, int count, int valueSize, + ExPolicyKVStorage storage) { + byte[] value = new byte[valueSize]; + new Random().nextBytes(value); + long startTs = System.currentTimeMillis(); + + Bytes key; + for (int i = 0; i < count; i++) { + key = keyPrefix.concat(Bytes.fromInt(i)); + boolean success = storage.set(key, value, ExPolicy.NOT_EXISTING); + if (!success) { + throw new IllegalStateException(String.format("Key already exist! --key=%s", key)); + } + } + long elapsedTs = System.currentTimeMillis() - startTs; + + double tps = count * 1000.0 / elapsedTs; + ConsoleUtils.info("============= [%s] : total keys = %s; tps= %.2f; elapsed millis = %s; value bytes = %s; ", + name, count, tps, elapsedTs, value.length); + } + + private static String initEmptyDB(String name) { + String currDir = FileUtils.getCurrentDir(); + String dbDir = new File(currDir, name + ".db").getAbsolutePath(); + FileUtils.deleteFile(dbDir); + String dbURI = "rocksdb://" + dbDir; + return dbURI; + } + +} diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/KVWritingCache.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/KVWritingCache.java new file mode 100644 index 00000000..b15f20bd --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/KVWritingCache.java @@ -0,0 +1,68 @@ +package com.jd.blockchain.storage.service.impl.rocksdb; + +import java.io.Closeable; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.LinkedBlockingQueue; + +import org.rocksdb.RocksDB; + +import com.jd.blockchain.utils.io.BytesUtils; + +public class KVWritingCache implements Closeable { + + private Map cache = new ConcurrentHashMap<>(); + + private LinkedBlockingQueue tasks = new LinkedBlockingQueue<>(1000000); + + private volatile boolean running; + + private RocksDB db; + + private Thread thrd; + + public KVWritingCache(RocksDB db) { + this.db = db; + thrd = new Thread(new Runnable() { + @Override + public void run() { + running = true; + doWritingTask(); + } + }, "KVWritingCache-Thread"); + thrd.setContextClassLoader(Thread.currentThread().getContextClassLoader()); + thrd.start(); + } + + public byte[] get(String key) { + return cache.get(key); + } + + public void set(String key, byte[] value) { + cache.put(key, value); + tasks.add(key); + } + + private void doWritingTask() { + while (running) { + dbWrite(); + } + } + + private void dbWrite() { + try { + String key = tasks.take(); + byte[] value = cache.remove(key); + byte[] keyBytes = BytesUtils.toBytes(key); + db.put(keyBytes, value); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void close() { + running = false; + thrd.interrupt(); + } +} diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBConnection.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBConnection.java new file mode 100644 index 00000000..6f3cb19e --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBConnection.java @@ -0,0 +1,55 @@ +package com.jd.blockchain.storage.service.impl.rocksdb; + +import java.io.IOException; + +import org.rocksdb.Options; +import org.rocksdb.RocksDB; +import org.rocksdb.RocksDBException; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.KVStorageService; + +public class RocksDBConnection implements DbConnection { + + private Options options; + + private RocksDB db; + + private RocksDBStorageService storage; + + public RocksDBConnection(String dbPath, Options options) { + try { + this.db = RocksDB.open(options, dbPath); + } catch (RocksDBException e) { + throw new IllegalStateException(e.getMessage(), e); + } + this.storage = new RocksDBStorageService(db); + } + + @Override + public void close() throws IOException { + // 假的释放; + // TODO: 采用引用计数器进行优化; + // db.close(); + } + + @Override + public KVStorageService getStorageService() { + return storage; + } + + public void dbClose() { + Options options = this.options; + this.options = null; + RocksDB db = this.db; + this.db = null; + + if (options != null) { + options.close(); + } + if (db != null) { + db.close(); + } + } + +} diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBConnectionFactory.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBConnectionFactory.java new file mode 100644 index 00000000..d0e82d2c --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBConnectionFactory.java @@ -0,0 +1,124 @@ +package com.jd.blockchain.storage.service.impl.rocksdb; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.regex.Pattern; + +import javax.annotation.PreDestroy; + +import org.rocksdb.*; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import org.rocksdb.util.SizeUnit; + +public class RocksDBConnectionFactory implements DbConnectionFactory { + + static { + RocksDB.loadLibrary(); + } + + public static final String URI_SCHEME = "rocksdb"; + + public static final Pattern URI_PATTER = Pattern + .compile("^\\w+\\://(/)?\\w+(/.*)*$"); + + private Map connections = new ConcurrentHashMap<>(); + + @Override + public DbConnection connect(String dbUri) { + return connect(dbUri, null); + } + + @Override + public synchronized DbConnection connect(String dbConnectionString, String password) { + if (!URI_PATTER.matcher(dbConnectionString).matches()) { + throw new IllegalArgumentException("Illegal format of rocksdb connection string!"); + } + URI dbUri = URI.create(dbConnectionString); + if (!support(dbUri.getScheme())) { + throw new IllegalArgumentException( + String.format("Not supported db connection string with scheme \"%s\"!", dbUri.getScheme())); + } + + String uriHead = URI_SCHEME + "://"; + int beginIndex = dbConnectionString.indexOf(uriHead); + String dbPath = dbConnectionString.substring(beginIndex + uriHead.length()); + if (!dbPath.startsWith(File.separator)) { + dbPath = File.separator + dbPath; + } + + RocksDBConnection conn = connections.get(dbPath); + if (conn != null) { + return conn; + } + + Options options = initOptions(); + + conn = new RocksDBConnection(dbPath, options); + connections.put(dbPath, conn); + + return conn; + } + + + @Override + public boolean support(String scheme) { + return URI_SCHEME.equalsIgnoreCase(scheme); + } + + @PreDestroy + @Override + public void close() { + RocksDBConnection[] conns = connections.values().toArray(new RocksDBConnection[connections.size()]); + connections.clear(); + for (RocksDBConnection conn : conns) { + conn.dbClose(); + } + } + + 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 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; + } + +} diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBExPolicyStorage.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBExPolicyStorage.java new file mode 100644 index 00000000..113db171 --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBExPolicyStorage.java @@ -0,0 +1,55 @@ +package com.jd.blockchain.storage.service.impl.rocksdb; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.utils.Bytes; + +public class RocksDBExPolicyStorage implements ExPolicyKVStorage { + + private RocksDBVersioningStorage versioningStorage; + + public RocksDBExPolicyStorage(RocksDBVersioningStorage versioningStorage) { + this.versioningStorage = versioningStorage; + } + + @Override + public byte[] get(Bytes key) { + return versioningStorage.get(key, 0); + } + + @Override + public boolean exist(Bytes key) { + long ver = versioningStorage.getVersion(key); + if (ver < 1) { + return ver == 0; + } + throw new IllegalStateException( + "The version of keys managed by this RocksDBExPolicyStorage is great than expected max value '0'."); + } + + @Override + public boolean set(Bytes key, byte[] value, ExPolicy ex) { + switch (ex) { + case EXISTING: + if (exist(key)) { + versioningStorage.dbSetData(key, value, 0); + return true; + } + return false; + case NOT_EXISTING: + long v = versioningStorage.set(key, value, -1); + return v == 0; + default: + throw new IllegalArgumentException("Unsupported ExPolicy[" + ex.toString() + "]!"); + } + } + + @Override + public void batchBegin() { + versioningStorage.batchBegin(); + } + + @Override + public void batchCommit() { + versioningStorage.batchCommit(); + } +} \ No newline at end of file diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBStorageService.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBStorageService.java new file mode 100644 index 00000000..db7b673c --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBStorageService.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.storage.service.impl.rocksdb; + +import org.rocksdb.RocksDB; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.KVStorageService; +import com.jd.blockchain.storage.service.VersioningKVStorage; + +public class RocksDBStorageService implements KVStorageService { + + private ExPolicyKVStorage exStorage; + + private VersioningKVStorage verStorage; + + public RocksDBStorageService(RocksDB db) { + this.verStorage = new RocksDBVersioningStorage(db); + this.exStorage = new RocksDBExPolicyStorage(new RocksDBVersioningStorage(db)); + } + + @Override + public ExPolicyKVStorage getExPolicyKVStorage() { + return exStorage; + } + + @Override + public VersioningKVStorage getVersioningKVStorage() { + return verStorage; + } + +} diff --git a/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBVersioningStorage.java b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBVersioningStorage.java new file mode 100644 index 00000000..978fd8c5 --- /dev/null +++ b/source/storage/storage-rocksdb/src/main/java/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBVersioningStorage.java @@ -0,0 +1,258 @@ +package com.jd.blockchain.storage.service.impl.rocksdb; + +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.locks.ReentrantLock; + +import org.apache.commons.collections4.map.LRUMap; +import org.rocksdb.*; + +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 基于 Redis 实现的版本化KV存储; + * + *

+ * + * 版本化KV存储要求实现几个关键特性:
+ * 1、每个Key都有唯一的版本序列,版本序列从 0 开始;
+ * 2、对 Key 值的每一次更改都导致版本增长 1 ;
+ * 3、对“写入”和“版本增长”两个操作一起是原子性的;
+ * 4、“版本增长”必须以 1 顺序递增,不允许跳空递增;
+ * + *

+ * 由于 Redis 缺少事务特性,并且无法很好地自定义组合的原子操作( MULTI-EXEC 不适用于集群模式下),所以在此实现中只能做到前面 3 + * 点。
+ * 第 4 点由调用方(账本操作层)在调用前做了版本校验,所以从整个系统来看也可以保证以上的4点要求。 + * + * @author huanghaiquan + * + */ +public class RocksDBVersioningStorage implements VersioningKVStorage { + + private final ThreadLocal writeBatchThreadLocal = new ThreadLocal<>(); + + private static Bytes VERSION_PREFIX = Bytes.fromString("V"); + + private static Bytes DATA_PREFIX = Bytes.fromString("D"); + + private final ReentrantLock lock = new ReentrantLock(); + + private final WriteOptions writeOptions = new WriteOptions(); + + private final ReadOptions readOptions = new ReadOptions() + .setFillCache(true) + .setVerifyChecksums(false) + ; + // put、get操作都在当前对象中,处理过程已加锁,不再需要线程安全对象 + private Map versions = new LRUMap<>(1024 * 128); +// private Map versions = new LRUMap<>(1024); + + private RocksDB db; + + public RocksDBVersioningStorage(RocksDB db) { + this.db = db; + } + + protected static Bytes encodeVersionKey(Bytes dataKey) { + return VERSION_PREFIX.concat(dataKey); + } + + protected static Bytes encodeDataKey(Bytes dataKey, long version) { + return DATA_PREFIX.concat(Bytes.fromLong(version)).concat(dataKey); + } + + private byte[] dbGet(Bytes key) { + try { + byte[] keyBytes = key.toBytes(); + return db.get(readOptions, keyBytes); + } catch (RocksDBException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + protected void dbSet(Bytes key, byte[] value) { + byte[] keyBytes = key.toBytes(); + + WriteBatch writeBatch = writeBatchThreadLocal.get(); + if (writeBatch != null) { + // 表示批量 + try { + writeBatch.put(keyBytes, value); + } catch (RocksDBException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } else { + try { + this.db.put(keyBytes, value); + } catch (RocksDBException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + } + + @Override + public long getVersion(Bytes key) { + return innerGetVersion(key).get(); + } + + protected AtomicLong innerGetVersion(Bytes key) { + AtomicLong ver; + try { + lock.lock(); + ver = versions.get(key); + if (ver == null) { + Bytes vkey = encodeVersionKey(key); + byte[] verBytes = dbGet(vkey); + if (verBytes == null) { + // TODO: 未处理无效 key 的释放; + ver = new AtomicLong(-1); + } else { + long v = BytesUtils.toLong(verBytes); + if (v < 0) { + throw new IllegalStateException( + String.format("Illegal format of version bytes in rocks db! --[key=%s]", key)); + } + ver = new AtomicLong(v); + } + versions.put(key, ver); + } + } finally { + lock.unlock(); + } + return ver; + } + + @Override + public VersioningKVEntry getEntry(Bytes key, long version) { + byte[] value = get(key, version); + if (value == null) { + return null; + } + return new VersioningKVData(key, version, value); + } + + @Override + public byte[] get(Bytes key, long version) { + long latestVersion = getVersion(key); + if (latestVersion < 0) { + return null; + } + if (version > latestVersion) { + return null; + } + long targetVersion = version < 0 ? latestVersion : version; + Bytes dKey = encodeDataKey(key, targetVersion); + byte[] value = dbGet(dKey); + return value; + } + + @Override + public synchronized long set(Bytes key, byte[] value, long version) { + AtomicLong ver = innerGetVersion(key); + long newVer = version + 1; + if (ver.compareAndSet(version, newVer)) { + // updateIfLatest 为 false 有利于同一个 key 的多版本并发写入; + dbSetToVersion(key, value, newVer); + return newVer; + } + + return -1; + } + + /** + * 向数据库更新键值到指定版本;
+ * + * 操作将把最新的版本号以及对应的数据存储到数据库;
+ * + * @param key + * 要写入的键; + * @param value + * 要写入的值; + * @param version + * 要写入的新版本号;将做并发检查,如果指定的值已经不是最新版本,则不会写入到数据库; + * @return 返回最新的版本号; + */ + private long dbSetToVersion(Bytes key, byte[] value, long version) { + long latestVersion; + try { + lock.lock(); + // 同步地写入版本号; + // 判断此版本是否已被并发更新为更高的值;如果版本已经不一致,则当前值已不必写入,避免数据库中新版本被错误地覆盖为低的版本; + latestVersion = innerGetVersion(key).get(); + if (version == latestVersion) { + Bytes vkey = encodeVersionKey(key); + byte[] verBytes = BytesUtils.toBytes(version); + dbSet(vkey, verBytes); + } + } finally { + lock.unlock(); + } + // 写入数据; + dbSetData(key, value, version); + return latestVersion; + } + + protected void dbSetData(Bytes key, byte[] value, long version) { + // 写入数据; + Bytes dkey = encodeDataKey(key, version); + dbSet(dkey, value); + } + + @Override + public void batchBegin() { + writeBatchThreadLocal.set(new WriteBatch()); + } + + @Override + public void batchCommit() { + WriteBatch writeBatch = writeBatchThreadLocal.get(); + if (writeBatch != null) { + writeBatch(writeBatch); + writeBatchThreadLocal.remove(); + } + } + + private void writeBatch(WriteBatch writeBatch) { + try { + db.write(writeOptions, writeBatch); + } catch (RocksDBException e) { + throw new IllegalStateException(e.getMessage(), e); + } finally { + writeBatch.close(); + } + } + + private static class VersioningKVData implements VersioningKVEntry { + + private Bytes key; + + private long version; + + private byte[] value; + + public VersioningKVData(Bytes key, long version, byte[] value) { + this.key = key; + this.version = version; + this.value = value; + } + + @Override + public Bytes getKey() { + return key; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public byte[] getValue() { + return value; + } + } +} \ No newline at end of file diff --git a/source/storage/storage-rocksdb/src/test/java/test/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBStorageTest.java b/source/storage/storage-rocksdb/src/test/java/test/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBStorageTest.java new file mode 100644 index 00000000..c930d961 --- /dev/null +++ b/source/storage/storage-rocksdb/src/test/java/test/com/jd/blockchain/storage/service/impl/rocksdb/RocksDBStorageTest.java @@ -0,0 +1,136 @@ +package test.com.jd.blockchain.storage.service.impl.rocksdb; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; + +import org.junit.Test; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.impl.rocksdb.RocksDBConnectionFactory; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; + +public class RocksDBStorageTest { + + @Test + public void test() { + String dbUri = initEmptyDB("rocksdb_storage_test"); + long expectedVersion; + try (DbConnectionFactory dbConnFactory = new RocksDBConnectionFactory();) { + DbConnection conn = dbConnFactory.connect(dbUri); + VersioningKVStorage verStorage = conn.getStorageService().getVersioningKVStorage(); + ExPolicyKVStorage exStorage = conn.getStorageService().getExPolicyKVStorage(); + + expectedVersion = test(verStorage); + + test(exStorage); + } + + try (DbConnectionFactory dbConnFactory = new RocksDBConnectionFactory();) { + DbConnection conn = dbConnFactory.connect(dbUri); + VersioningKVStorage verStorage = conn.getStorageService().getVersioningKVStorage(); + ExPolicyKVStorage exStorage = conn.getStorageService().getExPolicyKVStorage(); + + testAfterReload(verStorage, expectedVersion); + + testAfterReload(exStorage); + } + + } + + private void test(ExPolicyKVStorage exStorage) { + Bytes key = Bytes.fromString("kex"); + assertFalse(exStorage.exist(key)); + + byte[] data = exStorage.get(key); + assertNull(data); + + data = BytesUtils.toBytes("data"); + assertFalse(exStorage.set(key, data, ExPolicy.EXISTING)); + + assertTrue(exStorage.set(key, data, ExPolicy.NOT_EXISTING)); + assertTrue(exStorage.exist(key)); + + assertFalse(exStorage.set(key, data, ExPolicy.NOT_EXISTING)); + + assertTrue(exStorage.set(key, data, ExPolicy.EXISTING)); + assertTrue(exStorage.set(key, data, ExPolicy.EXISTING)); + assertFalse(exStorage.set(key, data, ExPolicy.NOT_EXISTING)); + + assertTrue(exStorage.exist(key)); + + byte[] reloadData = exStorage.get(key); + assertTrue(BytesUtils.equals(data, reloadData)); + } + + private void testAfterReload(ExPolicyKVStorage exStorage) { + Bytes key = Bytes.fromString("kex"); + assertTrue(exStorage.exist(key)); + + byte[] data = exStorage.get(key); + assertNotNull(data); + + assertEquals("data", BytesUtils.toString(data)); + + assertFalse(exStorage.set(key, data, ExPolicy.NOT_EXISTING)); + assertFalse(exStorage.set(key, data, ExPolicy.NOT_EXISTING)); + + assertTrue(exStorage.set(key, data, ExPolicy.EXISTING)); + assertTrue(exStorage.set(key, data, ExPolicy.EXISTING)); + assertFalse(exStorage.set(key, data, ExPolicy.NOT_EXISTING)); + + assertTrue(exStorage.exist(key)); + + byte[] reloadData = exStorage.get(key); + assertEquals("data", BytesUtils.toString(data)); + } + + private long test(VersioningKVStorage verStorage) { + Bytes key = Bytes.fromString("k1"); + long v = verStorage.getVersion(key); + assertEquals(-1, v); + byte[] data = verStorage.get(key, -1); + assertNull(data); + data = verStorage.get(key, 0); + assertNull(data); + data = verStorage.get(key, 1); + assertNull(data); + + data = BytesUtils.toBytes("data"); + v = verStorage.set(key, data, -1); + assertEquals(0, v); + v = verStorage.set(key, data, -1); + assertEquals(-1, v); + v = verStorage.set(key, data, 0); + assertEquals(1, v); + return v; + } + + private void testAfterReload(VersioningKVStorage verStorage, long expectedVersion) { + Bytes key = Bytes.fromString("k1"); + long v = verStorage.getVersion(key); + assertEquals(expectedVersion, v); + byte[] data = verStorage.get(key, -1); + String strData = BytesUtils.toString(data); + assertEquals("data", strData); + } + + private String initEmptyDB(String name) { + String currDir = FileUtils.getCurrentDir(); + String dbDir = new File(currDir, name + ".db").getAbsolutePath(); + FileUtils.deleteFile(dbDir); + String dbURI = "rocksdb://" + dbDir; + return dbURI; + } + +} diff --git a/source/storage/storage-service/pom.xml b/source/storage/storage-service/pom.xml new file mode 100644 index 00000000..255bd759 --- /dev/null +++ b/source/storage/storage-service/pom.xml @@ -0,0 +1,19 @@ + + 4.0.0 + + com.jd.blockchain + storage + 0.8.2.RELEASE + + storage-service + + + + com.jd.blockchain + utils-common + ${project.version} + + + \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/BatchStorageService.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/BatchStorageService.java new file mode 100644 index 00000000..3815a2fe --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/BatchStorageService.java @@ -0,0 +1,23 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.storage.service.BatchStorageService + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/5 下午4:41 + * Description: + */ +package com.jd.blockchain.storage.service; + +/** + * + * @author shaozhuguang + * @create 2018/12/5 + * @since 1.0.0 + */ + +public interface BatchStorageService { + + void batchBegin(); + + void batchCommit(); +} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/DbConnection.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/DbConnection.java new file mode 100644 index 00000000..67ea6eff --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/DbConnection.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.storage.service; + +import java.io.Closeable; + +public interface DbConnection extends Closeable { + + KVStorageService getStorageService(); + +} diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/DbConnectionFactory.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/DbConnectionFactory.java new file mode 100644 index 00000000..aed40c19 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/DbConnectionFactory.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.storage.service; + +import java.io.Closeable; + +public interface DbConnectionFactory extends Closeable { + + /** + * 是否支持指定 scheme 的连接字符串; + * @param scheme + * @return + */ + boolean support(String scheme); + + DbConnection connect(String dbConnectionString); + + DbConnection connect(String dbConnectionString, String password); + + @Override + void close(); +} diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/ExPolicyKVStorage.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/ExPolicyKVStorage.java new file mode 100644 index 00000000..469ea308 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/ExPolicyKVStorage.java @@ -0,0 +1,94 @@ +package com.jd.blockchain.storage.service; + +import com.jd.blockchain.utils.Bytes; + +/** + * 支持存在性策略更新的 KV 存储; + * + * @author huanghaiquan + * + */ +public interface ExPolicyKVStorage extends BatchStorageService{ + +// /** +// * 返回“键”对应的“值”;
+// * 如果“键”不存在,则返回 null; +// * +// * @param key +// * @return +// */ +// byte[] get(String key); +// +// /** +// * 如果满足指定的存在性策略,则创建/更新指定的“键-值”; +// * +// * @param key +// * 键; +// * @param value +// * 值; +// * @param ex +// * 如果指定 {@link ExPolicy#EXISTING} ,则只有键存在时才更新;
+// * 如果指定 {@link ExPolicy#NOT_EXISTING} ,则只有键不存在时才更新/创建; +// * @return 如果符合存在性策略,并执行了创建/更新操作,则返回 true,否则返回 false; +// */ +// boolean set(String key, byte[] value, ExPolicy ex); +// +// /** +// * 指定的 key 是否存在; +// * +// * @param key +// * @return +// */ +// boolean exist(String key); + + /** + * 返回“键”对应的“值”;
+ * 如果“键”不存在,则返回 null; + * + * @param key + * @return + */ + byte[] get(Bytes key); + + /** + * 如果满足指定的存在性策略,则创建/更新指定的“键-值”; + * + * @param key + * 键; + * @param value + * 值; + * @param ex + * 如果指定 {@link ExPolicy#EXISTING} ,则只有键存在时才更新;
+ * 如果指定 {@link ExPolicy#NOT_EXISTING} ,则只有键不存在时才更新/创建; + * @return 如果符合存在性策略,并执行了创建/更新操作,则返回 true,否则返回 false; + */ + boolean set(Bytes key, byte[] value, ExPolicy ex); + + /** + * 指定的 key 是否存在; + * + * @param key + * @return + */ + boolean exist(Bytes key); + + /** + * 存在性策略; + * + * @author huanghaiquan + * + */ + public static enum ExPolicy { + + /** + * 已存在; + */ + EXISTING, + + /** + * 不存在; + */ + NOT_EXISTING + } + +} diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/KVStorageService.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/KVStorageService.java new file mode 100644 index 00000000..94b2d7df --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/KVStorageService.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.storage.service; + +public interface KVStorageService { + + ExPolicyKVStorage getExPolicyKVStorage(); + + VersioningKVStorage getVersioningKVStorage(); + +} diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVEntry.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVEntry.java new file mode 100644 index 00000000..a18f6740 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVEntry.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.storage.service; + +import com.jd.blockchain.utils.Bytes; + +/** + * 版本化的键值数据项; + * + * @author huanghaiquan + * + */ +public interface VersioningKVEntry { + +// String getKey(); + Bytes getKey(); + + long getVersion(); + + byte[] getValue(); + +} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java new file mode 100644 index 00000000..e6a00854 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/VersioningKVStorage.java @@ -0,0 +1,139 @@ +package com.jd.blockchain.storage.service; + +import com.jd.blockchain.utils.Bytes; + +/** + * Versioning Key-Value Storage + *

+ * + * One key in this storage has a version attribute, which starts from zero and + * increased by 1 on it's value being updated. + * + *
+ * The writing of a key must be specified a explict integer of it's latest + * version at the executing moment. + * + * + * @author huanghaiquan + * + */ +public interface VersioningKVStorage extends BatchStorageService { + +// /** +// * Return the latest version entry associated the specified key; +// * +// * If the key doesn't exist, then return -1; +// * +// * @param key +// * @return +// */ +// long getVersion(String key); +// +// /** +// * Return the specified verson's entry;
+// * +// * It will return the latest one if the version is -1;
+// * +// * It will return null if the key or version not exist. +// * +// * @param key +// * @param version +// * @return +// */ +// VersioningKVEntry getEntry(String key, long version); +// +// /** +// * Return the specified verson's value;
+// * +// * If the specified version of key doesn't exist, then return null;
+// * +// * If the version is specified to -1, then return the latest version's +// * value;
+// * +// * @param key +// * @param version +// * @return +// */ +// byte[] get(String key, long version); +// +// /** +// * Update the value of the key;
+// * +// * If key exist, and the specified version equals to latest , then the value is +// * updated and version is increased by 1;
+// * If key not exist, and the specified version is -1, then the value will be +// * created and initialized it's version by 0;
+// * +// * @param key +// * the key; +// * @param value +// * the new value to update if expected version match the actual +// * version; +// * @param version +// * the latest version expected; +// * @return The latest version entry after setting.
+// * If the version checking fail, or concurrent confliction occur, then +// * return -1 as indication.
+// */ +// long set(String key, byte[] value, long version); +// + + /** + * Return the latest version entry associated the specified key; + * + * If the key doesn't exist, then return -1; + * + * @param key + * @return + */ + long getVersion(Bytes key); + + /** + * Return the specified verson's entry;
+ * + * It will return the latest one if the version is -1;
+ * + * It will return null if the key or version not exist. + * + * @param key + * @param version + * @return + */ + VersioningKVEntry getEntry(Bytes key, long version); + + /** + * Return the specified verson's value;
+ * + * If the specified version of key doesn't exist, then return null;
+ * + * If the version is specified to -1, then return the latest version's + * value;
+ * + * @param key + * @param version + * @return + */ + byte[] get(Bytes key, long version); + + /** + * Update the value of the key;
+ * + * If key exist, and the specified version equals to latest , then the value is + * updated and version is increased by 1;
+ * If key not exist, and the specified version is -1, then the value will be + * created and initialized it's version by 0;
+ * + * @param key + * the key; + * @param value + * the new value to update if expected version match the actual + * version; + * @param version + * the latest version expected; + * @return The latest version entry after setting.
+ * If the version checking fail, or concurrent confliction occur, then + * return -1 as indication.
+ */ + long set(Bytes key, byte[] value, long version); + +} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/BufferedKVStorage.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/BufferedKVStorage.java new file mode 100644 index 00000000..7b292248 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/BufferedKVStorage.java @@ -0,0 +1,580 @@ +package com.jd.blockchain.storage.service.utils; + +import java.util.ArrayList; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveTask; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.Transactional; + +/** + * {@link BufferedKVStorage} 缓冲写入的KV存储;
+ * + * @author huanghaiquan + * + */ +public class BufferedKVStorage implements VersioningKVStorage, ExPolicyKVStorage, Transactional { + + private static int MAX_PARALLEL_DB_WRITE_SIZE = 500; + static { + String strSize = System.getProperty("max-parallel-dbwrite-size"); + if (strSize != null) { + try { + MAX_PARALLEL_DB_WRITE_SIZE = Integer.parseInt(strSize); + } catch (NumberFormatException e) { + // 忽略格式错误; + e.printStackTrace(); + } + } + System.out.println("------ [[ max-parallel-dbwrite-size=" + MAX_PARALLEL_DB_WRITE_SIZE + " ]] ------"); + } + + private boolean parallel; + + private VersioningKVStorage origVersioningStorage; + + private ExPolicyKVStorage origExistanceStorage; + + /** + * 版本化KV数据的缓冲区; + */ + private ConcurrentHashMap versioningCache = new ConcurrentHashMap<>(); + private Object versioningMutex = new Object(); + + /** + * 存在性KV数据的缓冲区; + */ + private ConcurrentHashMap existanceCache = new ConcurrentHashMap<>(); + private Object existanceMutex = new Object(); + + /** + * 创建实例; + * + * @param origExPolicyStorage + * 原始的存储; + * @param origVersioningStorage + * 原始的存储; + * @param parallel + * 是否并行写入; + */ + public BufferedKVStorage(ExPolicyKVStorage origExPolicyStorage, VersioningKVStorage origVersioningStorage, + boolean parallel) { + this.origExistanceStorage = origExPolicyStorage; + this.origVersioningStorage = origVersioningStorage; + this.parallel = parallel; + } + + @Override + public long getVersion(Bytes key) { + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + return origVersioningStorage.getVersion(key); + } + return ws.getLatestVersion(); + } + + @Override + public VersioningKVEntry getEntry(Bytes key, long version) { + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + return origVersioningStorage.getEntry(key, version); + } + long latestVersion = ws.getStartingVersion(); + if (version <= latestVersion) { + // 值未 + return origVersioningStorage.getEntry(key, version < 0 ? latestVersion : version); + } + if (version > ws.getLatestVersion()) { + return null; + } + // 返回缓冲的新数据;注:这些数据尚未提交到依赖的底层存储; + return ws.getEntry(version); + } + + @Override + public byte[] get(Bytes key, long version) { + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + return origVersioningStorage.get(key, version); + } + long latestVersion = ws.getStartingVersion(); + if (version <= latestVersion) { + // 值未 + return origVersioningStorage.get(key, version < 0 ? latestVersion : version); + } + if (version > ws.getLatestVersion()) { + return null; + } + // 返回缓冲的新数据;注:这些数据尚未提交到依赖的底层存储; + return ws.get(version); + } + + @Override + public long set(Bytes key, byte[] value, long version) { + if (value == null) { + throw new IllegalArgumentException("Value is null!"); + } + if (version < -1) { + version = -1; + } + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + long latestVersion = origVersioningStorage.getVersion(key); + if (version < latestVersion) { + return -1; + } + synchronized (versioningMutex) { + ws = versioningCache.get(key); + if (ws == null) { + if (version == latestVersion) { + ws = new VersioningWritingSet(key, latestVersion, value); + versioningCache.put(key, ws); + return version + 1; + } + // 指定的版本不是最新版本; + return -1; + } + // 存在并发写,退出同步之后由该 key 的 VersioningWritingSet 来控制写入; + } + } + return ws.set(value, version); + } + + /** + * 输出已缓冲的所有写入数据到原始存储,并清空缓冲区; + */ + public void flush() { + if (parallel) { + parallelFlush(); + } else { + syncFlush(); + } + + clear(); + } + + private void parallelFlush() { + // 不必在“版本”和“存在性”这两类存储接口之间保证向下写入的顺序,也不必保证不同 key 向下写入的顺序; + ParallelVersioningWritingTask versioningWritingTask = null; + if (versioningCache.size() > 0) { + VersioningWritingSet[] wss = versioningCache.values() + .toArray(new VersioningWritingSet[versioningCache.size()]); + versioningWritingTask = new ParallelVersioningWritingTask(wss, 0, wss.length, origVersioningStorage); + ForkJoinPool.commonPool().execute(versioningWritingTask); + } + + ParallelExistanceWritingTask existanceWritingTask = null; + if (existanceCache.size() > 0) { + ExistanceWritingSet[] wss = existanceCache.values().toArray(new ExistanceWritingSet[existanceCache.size()]); + existanceWritingTask = new ParallelExistanceWritingTask(wss, 0, wss.length, origExistanceStorage); + ForkJoinPool.commonPool().execute(existanceWritingTask); + } + if (versioningWritingTask != null) { + versioningWritingTask.join(); + } + if (existanceWritingTask != null) { + existanceWritingTask.join(); + } + } + + @Override + public void batchBegin() { + // un support!!! + } + + @Override + public void batchCommit() { + // un support!!! + } + + private static class ParallelVersioningWritingTask extends RecursiveTask { + + private static final long serialVersionUID = -2603448698013687038L; + + private VersioningWritingSet[] wss; + private int offset; + private int count; + private VersioningKVStorage storage; + + public ParallelVersioningWritingTask(VersioningWritingSet[] wss, int offset, int count, + VersioningKVStorage storage) { + this.wss = wss; + this.offset = offset; + this.count = count; + this.storage = storage; + } + + /** + * 返回错误任务的计数器; + */ + @Override + protected Boolean compute() { + if (count > MAX_PARALLEL_DB_WRITE_SIZE) { + int count1 = count / 2; + int count2 = count - count1; + ParallelVersioningWritingTask task1 = new ParallelVersioningWritingTask(wss, offset, count1, storage); + ParallelVersioningWritingTask task2 = new ParallelVersioningWritingTask(wss, offset + count1, count2, + storage); + ForkJoinTask.invokeAll(task1, task2); + boolean success = task1.join(); + success = task2.join() & success; + return success; + } else { + for (int i = 0; i < count; i++) { + wss[offset + i].flushTo(storage); + } + return true; + } + } + } + + private static class ParallelExistanceWritingTask extends RecursiveTask { + + private static final long serialVersionUID = -7101095718404738821L; + + private ExistanceWritingSet[] wss; + private int offset; + private int count; + private ExPolicyKVStorage storage; + + public ParallelExistanceWritingTask(ExistanceWritingSet[] wss, int offset, int count, + ExPolicyKVStorage storage) { + this.wss = wss; + this.offset = offset; + this.count = count; + this.storage = storage; + } + + /** + * 返回错误任务的计数器; + */ + @Override + protected Boolean compute() { + if (count > MAX_PARALLEL_DB_WRITE_SIZE) { + int count1 = count / 2; + int count2 = count - count1; + ParallelExistanceWritingTask task1 = new ParallelExistanceWritingTask(wss, offset, count1, storage); + ParallelExistanceWritingTask task2 = new ParallelExistanceWritingTask(wss, offset + count1, count2, + storage); + ForkJoinTask.invokeAll(task1, task2); + boolean success = task1.join(); + success = task2.join() & success; + return success; + } else { + for (int i = 0; i < count; i++) { + wss[offset + i].flushTo(storage); + } + return true; + } + } + } + + private void syncFlush() { + // 不必在“版本”和“存在性”这两类存储接口之间保证向下写入的顺序,也不必保证不同 key 向下写入的顺序; + if (versioningCache.isEmpty() && existanceCache.isEmpty()) { + return; + } + origVersioningStorage.batchBegin(); + for (VersioningWritingSet ws : versioningCache.values()) { + ws.flushTo(origVersioningStorage); + } + origVersioningStorage.batchCommit(); + origExistanceStorage.batchBegin(); + for (ExistanceWritingSet ws : existanceCache.values()) { + ws.flushTo(origExistanceStorage); + } + origExistanceStorage.batchCommit(); + } + + private void clear() { + versioningCache.clear(); + existanceCache.clear(); + } + + /** + * 清空缓冲的数据; + */ + public void cancel() { + clear(); + } + + @Override + public boolean isUpdated() { + return versioningCache.size() > 0 || existanceCache.size() > 0; + } + + @Override + public void commit() { + flush(); + } + + /* + * (non-Javadoc) + * + * @see + * com.jd.blockchain.storage.service.ExPolicyKVStorage#get(java.lang.String) + */ + @Override + public byte[] get(Bytes key) { + // 从“存在性KV存储”读取值; + ExistanceWritingSet ws = existanceCache.get(key); + if (ws == null) { + return origExistanceStorage.get(key); + } + return ws.get(); + } + + @Override + public boolean set(Bytes key, byte[] value, ExPolicy ex) { + if (value == null) { + throw new IllegalArgumentException("Value is null!"); + } + switch (ex) { + case EXISTING: + return setEx(key, value); + case NOT_EXISTING: + return setNx(key, value); + default: + throw new IllegalArgumentException("Unsupported ExistancePolicy[" + ex + "]!"); + } + } + + @Override + public boolean exist(Bytes key) { + ExistanceWritingSet ws = existanceCache.get(key); + if (ws == null) { + return origExistanceStorage.exist(key); + } + return true; + } + + /** + * 当 key 不存在时写入新的键值; + * + * @param key + * @param value + * @return + */ + private boolean setNx(Bytes key, byte[] value) { + // 从“存在性KV存储”读取值; + ExistanceWritingSet ws = existanceCache.get(key); + if (ws == null) { + boolean exist = origExistanceStorage.exist(key); + if (exist) { + return false; + } + synchronized (existanceMutex) { + ws = existanceCache.get(key); + if (ws == null) { + ws = new ExistanceWritingSet(key, value, ExPolicy.NOT_EXISTING); + existanceCache.put(key, ws); + return true; + } + // 并发写,已经存在; + return false; + } + } + // 已经存在; + return false; + } + + /** + * 当 key 已经存在时更新值; + * + * @param key + * @param value + * @return + */ + private boolean setEx(Bytes key, byte[] value) { + // 从“存在性KV存储”读取值; + ExistanceWritingSet ws = existanceCache.get(key); + if (ws == null) { + boolean exist = origExistanceStorage.exist(key); + if (!exist) { + // key 不存在; + return false; + } + synchronized (existanceMutex) { + ws = existanceCache.get(key); + if (ws == null) { + // 初始化,缓存首个更新值以及更新条件; + ws = new ExistanceWritingSet(key, value, ExPolicy.EXISTING); + existanceCache.put(key, ws); + return true; + } + // 并发写,已经存在; + } + } + // 更新值; + ws.set(value); + return true; + } + + // ============================================================= + + /** + * 记录在最新版本之上新写入但未保存的最新的数据版本序列; + * + * @author huanghaiquan + * + */ + private static class VersioningWritingSet { + + private Bytes key; + + private long startingVersion; + + private ArrayList values; + + /** + * + * @param key + * 键; + * @param version + * 新的版本; + * @param value + * 值; + */ + private VersioningWritingSet(Bytes key, long startingVersion, byte[] firstValue) { + this.key = key; + this.startingVersion = startingVersion; + this.values = new ArrayList<>(1); + this.values.add(firstValue); + } + + public byte[] get(long version) { + long idx = version - startingVersion - 1; + if (idx < 0 || idx >= values.size()) { + return null; + } + return values.get((int) idx); + } + + public synchronized long set(byte[] value, long version) { + if (getLatestVersion() == version) { + this.values.add(value); + return version + 1; + } + return -1; + } + + /** + * 当前写入序列的最新版本;
+ * + * 此版本是最新的尚未写入底层存储的数据的版本; + * + * @return + */ + public long getLatestVersion() { + return startingVersion + values.size(); + } + + /** + * 当前写入序列的起始版本;
+ * 同时也是底层存储的最新版本; + * + * @return + */ + public long getStartingVersion() { + return startingVersion; + } + + public VersioningKVEntry getEntry(long version) { + byte[] value = get(version); + if (value == null) { + return null; + } + return new VersioningKVData(key, version, value); + } + + public void flushTo(VersioningKVStorage storage) { + long expVersion = startingVersion; + for (byte[] value : values) { + if (storage.set(key, value, expVersion) < 0) { + throw new IllegalStateException(String.format( + "Fail on flushing data to original storage! Expected version doesn't match! --[KEY=%s][EXPECTED_VERSION=%s]", + key, expVersion)); + } + expVersion++; + } + } + } + + private static class VersioningKVData implements VersioningKVEntry { + + private Bytes key; + + private long version; + + private byte[] value; + + public VersioningKVData(Bytes key, long version, byte[] value) { + this.key = key; + this.version = version; + this.value = value; + } + + @Override + public Bytes getKey() { + return key; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public byte[] getValue() { + return value; + } + + } + + private static class ExistanceWritingSet { + + private Bytes key; + + private volatile byte[] value; + + private ExPolicy initPolicy; + + /** + * + * @param key + * 键; + * @param value + * 值; + * @param initPolicy + * 初始的写入策略; + */ + private ExistanceWritingSet(Bytes key, byte[] value, ExPolicy initPolicy) { + this.key = key; + this.value = value; + this.initPolicy = initPolicy; + } + + public void flushTo(ExPolicyKVStorage origExistanceStorage) { + if (!origExistanceStorage.set(key, value, initPolicy)) { + throw new IllegalStateException(String.format( + "Fail on flushing data to original storage! The existance policy doesn't match --[KEY=%s][POLICY=%s]", + key, initPolicy)); + } + } + + public void set(byte[] value) { + this.value = value; + } + + public byte[] get() { + return value; + } + + } + +} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/ExistancePolicyKVStorageMap.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/ExistancePolicyKVStorageMap.java new file mode 100644 index 00000000..ece8c975 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/ExistancePolicyKVStorageMap.java @@ -0,0 +1,97 @@ +package com.jd.blockchain.storage.service.utils; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesMap; + +/** + * 基于内存的 {@link ExPolicyKVStorage} 实现; + * + * @author huanghaiquan + * + */ +public class ExistancePolicyKVStorageMap implements ExPolicyKVStorage, BytesMap { + // 要维持键的写入顺序,并且最终以相同的顺序输出; + private Map storage; + + private Object mutex = new Object(); + + public ExistancePolicyKVStorageMap() { + storage = new ConcurrentHashMap(); + } + + public ExistancePolicyKVStorageMap(Map external) { + storage = external; + } + + @Override + public byte[] get(Bytes key) { + return (byte[]) storage.get(key); + } + + @Override + public boolean exist(Bytes key) { + return storage.containsKey(key); + } + + @Override + public synchronized boolean set(Bytes key, byte[] value, ExPolicy ex) { + switch (ex) { + case EXISTING: + return setEx(key, value); + case NOT_EXISTING: + return setNx(key, value); + default: + throw new IllegalArgumentException("Unsupported ExistancePolicy[" + ex + "]!"); + } + } + + private boolean setNx(Bytes key, byte[] value) { + if (storage.containsKey(key)) { + return false; + } + synchronized (mutex) { + if (storage.containsKey(key)) { + return false; + } + storage.put(key, value); + return true; + } + } + + private boolean setEx(Bytes key, byte[] value) { + if (!storage.containsKey(key)) { + return false; + } + storage.put(key, value); + return true; + } + + @Override + public Set keySet() { + return storage.keySet(); + } + + public int getCount() { + return storage.size(); + } + + @Override + public byte[] getValue(Bytes key) { + return get(key); + } + + @Override + public void batchBegin() { + // un support!!! + } + + @Override + public void batchCommit() { + // un support!!! + } +} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryBasedDb.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryBasedDb.java new file mode 100644 index 00000000..bbe0856c --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryBasedDb.java @@ -0,0 +1,35 @@ +//package com.jd.blockchain.storage.service.utils; +// +// +//import com.jd.blockchain.storage.service.DbConnection; +//import com.jd.blockchain.storage.service.DbConnectionFactory; +//import com.jd.blockchain.storage.service.KVStorageService; +// +//public class MemoryBasedDb implements DbConnectionFactory, DbConnection { +// +// private MemoryKVStorage testStorage = new MemoryKVStorage(); +// +// @Override +// public boolean support(String scheme) { +// return true; +// } +// +// @Override +// public DbConnection connect(String dbConnectionString) { +// return new MemoryBasedDb(); +// } +// +// @Override +// public DbConnection connect(String dbConnectionString, String password) { +// return new MemoryBasedDb(); +// } +// +// @Override +// public void close(){ +// } +// +// @Override +// public KVStorageService getStorageService() { +// return testStorage; +// } +//} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryDBConn.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryDBConn.java new file mode 100644 index 00000000..0e60fea1 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryDBConn.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.storage.service.utils; + + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.storage.service.KVStorageService; + +public class MemoryDBConn implements DbConnection { + + private MemoryKVStorage testStorage = new MemoryKVStorage(); + + @Override + public void close(){ + } + + @Override + public KVStorageService getStorageService() { + return testStorage; + } + + } \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryDBConnFactory.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryDBConnFactory.java new file mode 100644 index 00000000..d5147cb9 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryDBConnFactory.java @@ -0,0 +1,52 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.storage.service.utils.MemoryDBConnFactory + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/25 上午10:52 + * Description: + */ +package com.jd.blockchain.storage.service.utils; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + * @author shaozhuguang + * @create 2018/12/25 + * @since 1.0.0 + */ + +public class MemoryDBConnFactory implements DbConnectionFactory { + + private Map memMap = new ConcurrentHashMap<>(); + + @Override + public boolean support(String scheme) { + return true; + } + + @Override + public synchronized DbConnection connect(String dbConnectionString) { + DbConnection mem = memMap.get(dbConnectionString); + if (mem == null) { + mem = new MemoryDBConn(); + memMap.put(dbConnectionString, mem); + } + return mem; + } + + @Override + public synchronized DbConnection connect(String dbConnectionString, String password) { + return connect(dbConnectionString); + } + + @Override + public void close() { + memMap.clear(); + } +} \ No newline at end of file diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryKVStorage.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryKVStorage.java new file mode 100644 index 00000000..65920967 --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/MemoryKVStorage.java @@ -0,0 +1,119 @@ +package com.jd.blockchain.storage.service.utils; + +import java.util.HashSet; +import java.util.Set; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.KVStorageService; +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesMap; + +public class MemoryKVStorage implements ExPolicyKVStorage, VersioningKVStorage, KVStorageService, BytesMap { + +// private Set keys = new HashSet<>(); + +// private Set keys = Collections.synchronizedSet(new HashSet<>()); + +// private Map storageMap = new ConcurrentHashMap<>(); + private ExistancePolicyKVStorageMap exStorage = new ExistancePolicyKVStorageMap(); + private VersioningKVStorageMap verStorage = new VersioningKVStorageMap(); + + @Override + public long getVersion(Bytes key) { + return verStorage.getVersion(key); + } + + @Override + public VersioningKVEntry getEntry(Bytes key, long version) { + return verStorage.getEntry(key, version); + } + + @Override + public byte[] get(Bytes key, long version) { + return verStorage.get(key, version); + } + + @Override + public long set(Bytes key, byte[] value, long version) { + return verStorage.set(key, value, version); +// if (v > -1) { +// keys.add(key); +// } +// return v; + } + + @Override + public byte[] get(Bytes key) { + return exStorage.get(key); + } + + @Override + public boolean exist(Bytes key) { + return exStorage.exist(key); + } + + @Override + public boolean set(Bytes key, byte[] value, ExPolicy ex) { + return exStorage.set(key, value, ex); +// if (ok) { +// keys.add(key); +// } +// return ok; + } + + @Override + public Set keySet() { + return getStorageKeySet(); + } + + @Override + public byte[] getValue(Bytes key) { + byte[] v = exStorage.getValue(key); + if (v == null) { + v = verStorage.getValue(key); + } + return v; + } + + public Set getStorageKeySet() { + HashSet keySet = new HashSet<>(exStorage.keySet()); + keySet.addAll(verStorage.keySet()); + return keySet; +// return storageMap.keySet(); + } + + public int getStorageCount() { + return exStorage.getCount() + verStorage.getCount(); +// return storageMap.size(); + } + +// public void printStoragedKeys() { +// String[] keys = StringUtils.toStringArray(getStorageKeySet()); +// StringUtils.sortStringArray(keys); +// for (String k : keys) { +// System.out.println(k); +// } +// } + + @Override + public ExPolicyKVStorage getExPolicyKVStorage() { + return this; + } + + @Override + public VersioningKVStorage getVersioningKVStorage() { + return this; + } + + @Override + public void batchBegin() { + // un support!!! + } + + @Override + public void batchCommit() { + // un support!!! + } +} diff --git a/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/VersioningKVStorageMap.java b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/VersioningKVStorageMap.java new file mode 100644 index 00000000..2569007f --- /dev/null +++ b/source/storage/storage-service/src/main/java/com/jd/blockchain/storage/service/utils/VersioningKVStorageMap.java @@ -0,0 +1,292 @@ +package com.jd.blockchain.storage.service.utils; + +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.storage.service.VersioningKVEntry; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesMap; + +public class VersioningKVStorageMap implements VersioningKVStorage, BytesMap { + +// private Map versions = new ConcurrentHashMap<>(); +// +// // 要维持键的写入顺序,并且最终以相同的顺序输出; +// private Map cache; + + /** + * 版本化KV数据的缓冲区; + */ + private Map versioningCache = new ConcurrentHashMap<>(); + private Object versioningMutex = new Object(); + + + + public VersioningKVStorageMap() { + } + +// public VersioningKVStorageMap(Map external) { +// cache = external; +// } + + @Override + public long getVersion(Bytes key) { + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + return -1; + } + return ws.getLatestVersion(); + } + + @Override + public VersioningKVEntry getEntry(Bytes key, long version) { + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + return null; + } + long latestVersion = ws.getLatestVersion(); + if (version > ws.getLatestVersion()) { + return null; + } + if (version < 0) { + version = latestVersion; + } + // 返回缓冲的新数据;注:这些数据尚未提交到依赖的底层存储; + return ws.getEntry(version); + } + + @Override + public byte[] get(Bytes key, long version) { + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + return null; + } + long latestVersion = ws.getLatestVersion(); + if (version > ws.getLatestVersion()) { + return null; + } + if (version < 0) { + version = latestVersion; + } + // 返回缓冲的新数据;注:这些数据尚未提交到依赖的底层存储; + return ws.get(version); + } + + @Override + public long set(Bytes key, byte[] value, long version) { + if (value == null) { + throw new IllegalArgumentException("Value is null!"); + } + if (version < -1) { + version = -1; + } + VersioningWritingSet ws = versioningCache.get(key); + if (ws == null) { + long latestVersion = -1; + synchronized (versioningMutex) { + ws = versioningCache.get(key); + if (ws == null) { + if (version == latestVersion) { + ws = new VersioningWritingSet(key, latestVersion, value); + versioningCache.put(key, ws); + return version+1; + } + // 指定的版本不是最新版本; + return -1; + } + // 存在并发写,退出同步之后由该 key 的 VersioningWritingSet 来控制写入; + } + } + return ws.set(value, version); + } + + @Override + public Set keySet() { + return versioningCache.keySet(); + } + + @Override + public byte[] getValue(Bytes key) { + return get(key, getVersion(key)); + } + + public int getCount() { + return versioningCache.size(); + } + + @Override + public void batchBegin() { + // un support!!! + } + + @Override + public void batchCommit() { + // un support!!! + } + + + /** + * 记录在最新版本之上新写入但未保存的最新的数据版本序列; + * + * @author huanghaiquan + * + */ + private static class VersioningWritingSet { + + private Bytes key; + + private long startingVersion; + + private ArrayList values; + + /** + * + * @param key + * 键; + * @param version + * 新的版本; + * @param value + * 值; + */ + private VersioningWritingSet(Bytes key, long startingVersion, byte[] firstValue) { + this.key = key; + this.startingVersion = startingVersion; + this.values = new ArrayList<>(1); + this.values.add(firstValue); + } + + public byte[] get(long version) { + long idx = version - startingVersion - 1; + if (idx < 0 || idx >= values.size()) { + return null; + } + return values.get((int) idx); + } + + public synchronized long set(byte[] value, long version) { + if (getLatestVersion() == version) { + this.values.add(value); + return version + 1; + } + return -1; + } + + /** + * 当前写入序列的最新版本;
+ * + * 此版本是最新的尚未写入底层存储的数据的版本; + * + * @return + */ + public long getLatestVersion() { + return startingVersion + values.size(); + } + + /** + * 当前写入序列的起始版本;
+ * 同时也是底层存储的最新版本; + * + * @return + */ + public long getStartingVersion() { + return startingVersion; + } + + public VersioningKVEntry getEntry(long version) { + byte[] value = get(version); + if (value == null) { + return null; + } + return new VersioningKVData(key, version, value); + } + + public void flushTo(VersioningKVStorage storage) { + long expVersion = startingVersion; + for (byte[] value : values) { + if (storage.set(key, value, expVersion) < 0) { + throw new IllegalStateException(String.format( + "Fail on flushing data to original storage! Expected version doesn't match! --[KEY=%s][EXPECTED_VERSION=%s]", + key, expVersion)); + } + expVersion++; + } + } + } + + private static class VersioningKVData implements VersioningKVEntry { + + private Bytes key; + + private long version; + + private byte[] value; + + public VersioningKVData(Bytes key, long version, byte[] value) { + this.key = key; + this.version = version; + this.value = value; + } + + @Override + public Bytes getKey() { + return key; + } + + @Override + public long getVersion() { + return version; + } + + @Override + public byte[] getValue() { + return value; + } + + } + + +// private static class CachedSetEntry implements VersioningKVEntry { +// +// private String key; +// +// private long version; +// +// private byte[] value; +// +// /** +// * +// * @param key +// * 键; +// * @param version +// * 新的版本; +// * @param value +// * 值; +// */ +// private CachedSetEntry(String key, long version, byte[] value) { +// this.key = key; +// this.version = version; +// this.value = value; +// } +// +// @Override +// public String getKey() { +// return key; +// } +// +// @Override +// public long getVersion() { +// return version; +// } +// +// @Override +// public byte[] getValue() { +// return value; +// } +// +// } + + +} diff --git a/source/storage/storage-service/src/test/java/test/com/jd/blockchain/storage/service/utils/BufferedKVStorageTest.java b/source/storage/storage-service/src/test/java/test/com/jd/blockchain/storage/service/utils/BufferedKVStorageTest.java new file mode 100644 index 00000000..b14ad85a --- /dev/null +++ b/source/storage/storage-service/src/test/java/test/com/jd/blockchain/storage/service/utils/BufferedKVStorageTest.java @@ -0,0 +1,158 @@ +package test.com.jd.blockchain.storage.service.utils; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyLong; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.io.UnsupportedEncodingException; + +import org.junit.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.mockito.verification.VerificationMode; + +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.utils.BufferedKVStorage; +import com.jd.blockchain.storage.service.utils.ExistancePolicyKVStorageMap; +import com.jd.blockchain.storage.service.utils.VersioningKVStorageMap; +import com.jd.blockchain.utils.Bytes; + +public class BufferedKVStorageTest { + + @Test + public void test() throws UnsupportedEncodingException { + ExPolicyKVStorage exStorage = Mockito.mock(ExPolicyKVStorage.class); + when(exStorage.get(any())).thenReturn(null); + when(exStorage.set(any(), any(), any())).thenReturn(true); + + VersioningKVStorage verStorage = Mockito.mock(VersioningKVStorage.class); + when(verStorage.getVersion(any())).thenReturn(-1L); + when(verStorage.get(any(), anyLong())).thenReturn(null); + when(verStorage.getEntry(any(), anyLong())).thenReturn(null); + when(verStorage.set(any(), any(), anyLong())).thenAnswer(new Answer() { + @Override + public Long answer(InvocationOnMock invocation) throws Throwable { + long ver = (long) invocation.getArguments()[2]; + return ver + 1; + } + }); + + BufferedKVStorage bufStorage = new BufferedKVStorage(exStorage, verStorage, false); + + byte[] data = "ABC".getBytes("UTF-8"); + long v = bufStorage.set(Bytes.fromString("A"), data, -1); + assertEquals(0, v); + v = bufStorage.set(Bytes.fromString("A"), data, v); + assertEquals(1, v); + v = bufStorage.set(Bytes.fromString("A"), data, v); + assertEquals(2, v); + + boolean ok = bufStorage.set(Bytes.fromString("B"), data, ExPolicy.NOT_EXISTING); + assertTrue(ok); + ok = bufStorage.set(Bytes.fromString("C"), data, ExPolicy.NOT_EXISTING); + assertTrue(ok); + ok = bufStorage.set(Bytes.fromString("D"), data, ExPolicy.NOT_EXISTING); + assertTrue(ok); + + verify(verStorage, times(0)).set(any(), any(), anyLong()); + verify(exStorage, times(0)).set(any(), any(), any()); + + bufStorage.flush(); + + verify(verStorage, times(3)).set(any(), any(), anyLong()); + verify(verStorage, times(1)).set(eq(Bytes.fromString("A")), any(), eq(-1L)); + verify(verStorage, times(1)).set(eq(Bytes.fromString("A")), any(), eq(0L)); + verify(verStorage, times(1)).set(eq(Bytes.fromString("A")), any(), eq(1L)); + + verify(exStorage, times(3)).set(any(), any(), any()); + verify(exStorage, times(1)).set(eq(Bytes.fromString("B")), any(), eq(ExPolicy.NOT_EXISTING)); + verify(exStorage, times(1)).set(eq(Bytes.fromString("C")), any(), eq(ExPolicy.NOT_EXISTING)); + verify(exStorage, times(1)).set(eq(Bytes.fromString("D")), any(), eq(ExPolicy.NOT_EXISTING)); + } + + // 改变了存储结构,此测试用例不再适合; + // @Test + // public void testDataSet() { + // + // ExistancePolicyKVStorageMap memoryExStorage = new + // ExistancePolicyKVStorageMap(); + // + // VersioningKVStorageMap memoryVerStorage = new VersioningKVStorageMap(); + // + // long v = memoryVerStorage.set("A", "A1".getBytes(), -1); + // assertEquals(0, v); + // v = memoryVerStorage.set("A", "A2".getBytes(), 0); + // assertEquals(1, v); + // v = memoryVerStorage.set("A", "A3".getBytes(), 1); + // assertEquals(2, v); + // v = memoryVerStorage.set("B", "B1".getBytes(), -1); + // assertEquals(0, v); + // + // BufferedKVStorage bufferedStorage = new BufferedKVStorage(memoryExStorage, + // memoryVerStorage, false); + // + // // test versioning get; + // byte[] value = bufferedStorage.get("A", 0); + // assertEquals("A1", new String(value)); + // + // value = bufferedStorage.get("A", -1); + // assertEquals("A3", new String(value)); + // + // value = bufferedStorage.get("A", 2); + // assertEquals("A3", new String(value)); + // + // value = bufferedStorage.get("B", 0); + // assertEquals("B1", new String(value)); + // + // + // // test versioning buffered set; + // v = bufferedStorage.set("C", "C1".getBytes(), -1); + // assertEquals(v, 0); + // + // v = bufferedStorage.set("C", "C2".getBytes(), 0); + // assertEquals(v, 1); + // + // v = bufferedStorage.set("D", "D1".getBytes(), -1); + // assertEquals(v, 0); + // + // v = bufferedStorage.set("E", "E1".getBytes(), 0); + // assertEquals(v, -1); + // + // + // value = bufferedStorage.get("C", 0); + // assertEquals("C1", new String(value)); + // value = bufferedStorage.get("C", 1); + // assertEquals("C2", new String(value)); + // value = bufferedStorage.get("D", 0); + // assertEquals("D1", new String(value)); + // value = bufferedStorage.get("E", 0); + // assertNull(value); + // + // value = memoryVerStorage.get("C", 0); + // assertNull(value); + // value = memoryVerStorage.get("D", 0); + // assertNull(value); + // value = memoryVerStorage.get("E", 0); + // assertNull(value); + // + // bufferedStorage.flush(); + // + // value = memoryVerStorage.get("C", 0); + // assertEquals("C1", new String(value)); + // value = memoryVerStorage.get("C", 1); + // assertEquals("C2", new String(value)); + // value = memoryVerStorage.get("D", 0); + // assertEquals("D1", new String(value)); + // value = memoryVerStorage.get("E", 0); + // assertNull(value); + // } + +} diff --git a/source/test/pom.xml b/source/test/pom.xml new file mode 100644 index 00000000..f26c27e0 --- /dev/null +++ b/source/test/pom.xml @@ -0,0 +1,32 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + test + pom + + + test-consensus-client + test-consensus-node + test-ledger-core + test-integration + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/test/test-consensus-client/pom.xml b/source/test/test-consensus-client/pom.xml new file mode 100644 index 00000000..c57a5b2c --- /dev/null +++ b/source/test/test-consensus-client/pom.xml @@ -0,0 +1,48 @@ + + 4.0.0 + + com.jd.blockchain + test + 0.8.2.RELEASE + + test-consensus-client + + + + org.springframework.boot + spring-boot-starter-web + + + com.jd.blockchain + consensus-bftsmart + ${project.version} + + + com.jd.blockchain + utils-http + ${project.version} + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/ConsensusSettingService.java b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/ConsensusSettingService.java new file mode 100644 index 00000000..007f233c --- /dev/null +++ b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/ConsensusSettingService.java @@ -0,0 +1,20 @@ +package test.perf.com.jd.blockchain.consensus.client; + +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.HttpService; + +/** + * Created by zhangshuang3 on 2018/9/11. + */ +@HttpService +public interface ConsensusSettingService { + + @HttpAction(path = "/node/settings", method = HttpMethod.GET) + public String getConsensusSettingsHex(); + + @HttpAction(path = "/node/topology", method = HttpMethod.GET) + public String getConsensusTopologyHex(); + +} + diff --git a/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/Settings.java b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/Settings.java new file mode 100644 index 00000000..a296249c --- /dev/null +++ b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/Settings.java @@ -0,0 +1,142 @@ +package test.perf.com.jd.blockchain.consensus.client; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +import com.jd.blockchain.utils.io.ByteArray; + +/** + * @author huanghaiquan + * + */ +@Configuration +@ConfigurationProperties(prefix = "client") +public class Settings { + + private String name; + + private ConsensusSetting consensus; + + public ByteArray getLedgerHash() { + return ledgerHash; + } + + public void setLedgerHash(ByteArray ledgerHash) { + this.ledgerHash = ledgerHash; + } + + private ByteArray ledgerHash; + + public ConsensusSetting getConsensus() { + return consensus; + } + + public void setConsensus(ConsensusSetting consensus) { + this.consensus = consensus; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + // =================================================================================================== + /** + * 共识相关的参数设置; + * + * @author huanghaiquan + * + */ + public static class ConsensusSetting { + + /** + * 本机用于共识的IP地址; + */ + private String ip; + + /** + * 本机用于共识的端口; + */ + private int port; + + public ConsensusSetting() { + } + + public ConsensusSetting(String ip, int port) { + this.ip = ip; + this.port = port; + } + + private BftsmartSetting bftsmartConfig = new BftsmartSetting("config/system.config", "config/hosts.config"); + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } + + public BftsmartSetting getBftsmartConfig() { + return bftsmartConfig; + } + + public void setBftsmartConfig(BftsmartSetting bftsmartConfig) { + this.bftsmartConfig = bftsmartConfig; + } + + } + + public static class BftsmartSetting { + + private String hosts; + + private String system; + + private String home; + + public BftsmartSetting() { + } + + public BftsmartSetting(String systemConfig, String hostsConfig) { + this.system = systemConfig; + this.hosts = hostsConfig; + } + + public String getHosts() { + return hosts; + } + + public void setHosts(String hosts) { + this.hosts = hosts; + } + + public String getSystem() { + return system; + } + + public void setSystem(String system) { + this.system = system; + } + + public String getHome() { + return home; + } + + public void setHome(String home) { + this.home = home; + } + + } +} diff --git a/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebBooter.java b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebBooter.java new file mode 100644 index 00000000..f010417c --- /dev/null +++ b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebBooter.java @@ -0,0 +1,17 @@ +package test.perf.com.jd.blockchain.consensus.client; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +public class WebBooter { + + public static void main(String[] args) { + SpringApplication.run(WebBooter.class, args); + } + +} diff --git a/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebClient.java b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebClient.java new file mode 100644 index 00000000..d3e674a3 --- /dev/null +++ b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebClient.java @@ -0,0 +1,110 @@ +//package test.perf.com.jd.blockchain.consensus.client; +// +//import java.util.Random; +// +//import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +//import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; +//import com.jd.blockchain.consensus.bftsmart.client.BftsmartClientConfig; +//import com.jd.blockchain.consensus.bftsmart.client.BftsmartClientSettings; +//import com.jd.blockchain.consensus.bftsmart.client.BftsmartConsensusClient; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +//import com.jd.blockchain.ledger.BlockchainKeyGenerator; +//import my.utils.http.agent.HttpServiceAgent; +//import my.utils.http.agent.ServiceEndpoint; +//import my.utils.serialize.binary.BinarySerializeUtils; +//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 my.utils.codec.HexUtils; +// +//@RestController +//@RequestMapping(path="bft") +//public class WebClient { +// +// @Autowired +// private Settings settings; +// +// private Random random=new Random(); +// +// private volatile byte[] msgBytes; +// +// private BftsmartConsensusClient client; +// +// private BftsmartConsensusSettings setting; +// +// private BftsmartTopology topology; +// +// public WebClient() { +// random = new Random(); +// updateMessage(32); +// } +// +// private void init() { +// } +// +// @RequestMapping(path="/message", method=RequestMethod.GET) +// public String getMessage() { +// return String.format("[size=%s]--[%s]", msgBytes.length, HexUtils.encode(msgBytes)); +// } +// +// private void updateMessage(int size) { +// msgBytes = new byte[size]; +// random.nextBytes(msgBytes); +// } +// +// @RequestMapping(path="/message/set/{size}", method=RequestMethod.GET) +// public String setMessage(@PathVariable("size") int size) { +// if (size < 1 || size > 1024*1024*200) { +// return "Size cann't be less than 1 byte or great than 200 MB! "; +// } +// updateMessage(size); +// return getMessage(); +// } +// +// @RequestMapping(path="/test/ordered", method=RequestMethod.GET) +// public String testOrdered() { +// if (client == null) { +// throw new IllegalStateException("client not exist"); +// } +// +// //CallerInfo info = Profiler.registerInfo("jd-chain-bftsmart-performence-client-proxy", false, true); +// byte[] retn = client.getMessageService().sendOrdered(msgBytes).get(); +// if(retn.length == 1 && retn[0] == 1){ +// //Profiler.registerInfoEnd(info); +// return "OK"; +// } +// //Profiler.functionError(info); +// return "FAIL"; +// } +// +// +// @RequestMapping(path="/connect/{from}/{port}", method=RequestMethod.GET) +// public String connect(@PathVariable("from") String from, @PathVariable("port") int port){ +// if (client != null) { +// throw new IllegalStateException("Has been connected to nodes!"); +// } +// ServiceEndpoint endpoint = new ServiceEndpoint(from, port, false); +// ConsensusSettingService settingService = HttpServiceAgent.createService(ConsensusSettingService.class, endpoint); +// String hexSetting = settingService.getConsensusSettingsHex(); +// String hexTopology = settingService.getConsensusTopologyHex(); +// setting = BinarySerializeUtils.deserialize(HexUtils.decode(hexSetting)); +// topology = BinarySerializeUtils.deserialize(HexUtils.decode(hexTopology)); +// +// // 0.8.0 version +//// client = new BftsmartConsensusClient(0, setting, topology); +// +// // 0.8.1 version +// PubKey clientPubKey= BlockchainKeyGenerator.getInstance().generate().getPubKey(); +// +// BftsmartClientSettings clientSettings = new BftsmartClientConfig(0, clientPubKey,setting, topology); +// +// BftsmartConsensusClient client = new BftsmartConsensusClient(clientSettings); +// +// return "OK"; +// } +// +//} diff --git a/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebConfiguration.java b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebConfiguration.java new file mode 100644 index 00000000..b43acb34 --- /dev/null +++ b/source/test/test-consensus-client/src/main/java/test/perf/com/jd/blockchain/consensus/client/WebConfiguration.java @@ -0,0 +1,11 @@ +package test.perf.com.jd.blockchain.consensus.client; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +public class WebConfiguration { + +} + diff --git a/source/test/test-consensus-client/src/main/resources/application.properties b/source/test/test-consensus-client/src/main/resources/application.properties new file mode 100644 index 00000000..6fdb220e --- /dev/null +++ b/source/test/test-consensus-client/src/main/resources/application.properties @@ -0,0 +1,28 @@ +server.port=10010 + +#server.ssl.key-store=classpath:mykeys.jks +#server.ssl.key-store-password=abc123 +#server.ssl.key-password=abc123 + +server.tomcat.accesslog.enabled=false + +debug=false + +#logging.file=logs/peer.log +logging.level.com.jd.blockchain.peer=DEBUG +logging.level.org.org.springframework=DEBUG + +spring.mvc.favicon.enabled=false + + + +client.name=peer[0] + +client.consensus.ip=127.0.0.1 +client.consensus.port=10000 + +client.consensus.bftsmart-config.home=config +client.consensus.bftsmart-config.system=config/system.config +client.consensus.bftsmart-config.hosts=config/hosts.config + + diff --git a/source/test/test-consensus-node/config/bft-system.config b/source/test/test-consensus-node/config/bft-system.config new file mode 100644 index 00000000..ddf28b45 --- /dev/null +++ b/source/test/test-consensus-node/config/bft-system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/test/test-consensus-node/config/hosts.config b/source/test/test-consensus-node/config/hosts.config new file mode 100644 index 00000000..d1775d3a --- /dev/null +++ b/source/test/test-consensus-node/config/hosts.config @@ -0,0 +1,36 @@ +# 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. + +# This file defines the replicas ids, IPs and ports. +# It is used by the replicas and clients to find connection info +# to the initial replicas. +# The ports defined here are the ports used by clients to communicate +# with the replicas. Additional connections are opened by replicas to +# communicate with each other. This additional connection is opened in the +# next port defined here. For an example, consider the line "0 127.0.0.1 11000". +# That means that clients will open a communication channel to replica 0 in +# IP 127.0.0.1 and port 11000. On startup, replicas with id different than 0 +# will open a communication channel to replica 0 in port 11001. +# The same holds for replicas 1, 2, 3 ... N. + +#server id, address and port (the ids from 0 to n-1 are the service replicas) +0 127.0.0.1 11000 +1 127.0.0.1 11010 +2 127.0.0.1 11020 +3 127.0.0.1 11030 +#4 192.168.151.33 11040 +#5 192.168.151.38 11050 +#6 127.0.0.1 11060 +#7 127.0.0.1 11070 +7001 127.0.0.1 11100 diff --git a/source/test/test-consensus-node/config/system.config b/source/test/test-consensus-node/config/system.config new file mode 100644 index 00000000..ada42b49 --- /dev/null +++ b/source/test/test-consensus-node/config/system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/test/test-consensus-node/pom.xml b/source/test/test-consensus-node/pom.xml new file mode 100644 index 00000000..dff4dc21 --- /dev/null +++ b/source/test/test-consensus-node/pom.xml @@ -0,0 +1,45 @@ + + 4.0.0 + + com.jd.blockchain + test + 0.8.2.RELEASE + + test-consensus-node + + + org.springframework.boot + spring-boot-starter-web + + + com.jd.blockchain + peer + ${project.version} + + + + + + + + com.jd.blockchain + consensus-bftsmart + 0.8.2.RELEASE + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/ConsensusTester.java b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/ConsensusTester.java new file mode 100644 index 00000000..3eb52366 --- /dev/null +++ b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/ConsensusTester.java @@ -0,0 +1,546 @@ +//package test.perf.com.jd.blockchain.consensus.node; +// +//import java.io.IOException; +//import java.io.InputStream; +//import java.util.Collections; +//import java.util.LinkedList; +//import java.util.List; +//import java.util.Properties; +//import java.util.concurrent.CountDownLatch; +//import java.util.concurrent.CyclicBarrier; +//import java.util.concurrent.atomic.AtomicInteger; +// +//import com.jd.blockchain.consensus.*; +//import com.jd.blockchain.consensus.bftsmart.*; +//import com.jd.blockchain.consensus.bftsmart.client.BftsmartClientSettings; +//import com.jd.blockchain.consensus.bftsmart.service.BftsmartServerSettingConfig; +//import com.jd.blockchain.consensus.service.MessageHandle; +//import com.jd.blockchain.consensus.service.ServerSettings; +//import com.jd.blockchain.consensus.service.StateMachineReplicate; +//import com.jd.blockchain.crypto.asymmetric.PubKey; +//import com.jd.blockchain.ledger.BlockchainKeyGenerator; +//import com.jd.blockchain.peer.consensus.ConsensusMessageDispatcher; +//import com.jd.blockchain.peer.consensus.LedgerStateManager; +//import com.jd.blockchain.tools.keygen.KeyGenCommand; +//import my.utils.PropertiesUtils; +//import my.utils.Property; +//import my.utils.net.NetworkAddress; +//import org.springframework.core.io.ClassPathResource; +// +//import bftsmart.reconfiguration.util.HostsConfig; +//import my.utils.ConsoleUtils; +//import my.utils.concurrent.AsyncFuture; +//import my.utils.concurrent.ThreadInvoker; +//import my.utils.concurrent.ThreadInvoker.AsyncCallback; +//import my.utils.concurrent.ThreadUtils; +//import my.utils.io.BytesUtils; +//import my.utils.io.FileUtils; +// +//public class ConsensusTester { +// +// public static final String PASSWORD = "abc"; +// +// public static final String[] PUB_KEYS = { "endPsK36imXrY66pru6ttZ8dZ3TynWekmdqoM1K7ZRRoRBBiYVzM", +// "endPsK36jQE1uYpdVRSnwQXVYhgAMWTaMJiAqii7URiULoBDLUUN", +// "endPsK36fc7FSecKAJCJdFhTejbPHMLaGcihJVQCv95czCq4tW5n", +// "endPsK36m1grx8mkTMgh8XQHiiaNzajdC5hkuqP6pAuLmMbYkzd4" }; +// +// public static final String[] PRIV_KEYS = { +// "177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X", +// "177gjwQwTdXthkutDKVgKwiq6wWfLWYuxhji1U2N1C5MzqLRWCLZXo3i2g4vpfcEAQUPG8H", +// "177gjvLHUjxvAWsqVcGgV8eHgVNBvJZYDfpP9FLjTouR1gEJNiamYu1qjTNDh18XWyLg8or", +// "177gk2VtYeGbK5TS2xWhbSZA4BsT9Xj5Fb8hqCzxzgbojVVcqaDSFFrFPsLbZBx7rszyCNy" }; +// +// public static volatile boolean debug = false; +// +// public static MessageHandle consensusMessageHandler = new ConsensusMessageDispatcher(); +// public static StateMachineReplicate consensusStateManager = new LedgerStateManager(); +// +// public static void main(String[] args) { +// // DataContractRegistry.register(ActionResponse.class); +// try { +// HostsConfig hosts = new HostsConfig(); +// hosts.add(0, "127.0.0.1", 10000); +// hosts.add(1, "127.0.0.1", 10010); +// hosts.add(2, "127.0.0.1", 10020); +// hosts.add(3, "127.0.0.1", 10030); +// +// TestServceHandle handle0 = new TestServceHandle(0); +// AsyncCallback call0 = startReplica(0, hosts, handle0); +// TestServceHandle handle1 = new TestServceHandle(1); +// AsyncCallback call1 = startReplica(1, hosts, handle1); +// TestServceHandle handle2 = new TestServceHandle(2); +// AsyncCallback call2 = startReplica(2, hosts, handle2); +// TestServceHandle handle3 = new TestServceHandle(3); +// AsyncCallback call3 = startReplica(3, hosts, handle3); +// +// BftsmartConsensusServlet replica0 = call0.waitReturn(); +// BftsmartConsensusServlet replica1 = call1.waitReturn(); +// BftsmartConsensusServlet replica2 = call2.waitReturn(); +// BftsmartConsensusServlet replica3 = call3.waitReturn(); +// +// ConsoleUtils.info("All replicas have started!"); +// +// Topology tp = replica0.getTopology().copyOf(); +// +// { +// // 单步测试; +// // TestServce clientService = createClientService(0, hosts, tp); +// // debug = true; +// // ConsoleUtils.info("First message..."); +// // String resp = clientService.hello("AAAA"); +// //// String resp = clientService.hello("[SLEEP] AAAA"); +// // ConsoleUtils.info("response:[%s]", resp); +// +// // ConsoleUtils.info("Second message..."); +// // resp = clientService.hello("[SLEEP] BBBB"); +// // ConsoleUtils.info("response:[%s]", resp); +// } +// { +// // 异步调用; +// // TestServce clientService = createClientService(0, hosts, tp); +// // clientService = AsyncInvoker.asynchorize(TestServce.class, clientService); +// // debug = true; +// // long startTs = System.currentTimeMillis(); +// // ConsoleUtils.info("[%s] Async send first message...", startTs); +// // AsyncResult ayncResult = +// // AsyncInvoker.call(clientService.hello("AAAA")); +// // ayncResult.addListener(new my.utils.concurrent.AsyncCallback() { +// // @Override +// // public void complete(String replyMessage, Throwable error) { +// // if (error != null) { +// // long endTs = System.currentTimeMillis(); +// // ConsoleUtils.error("[%s][spanTS=%s] Async response error!!! --%s", endTs, +// // endTs - startTs, +// // error.getMessage()); +// // error.printStackTrace(); +// // return; +// // } +// // +// // ConsoleUtils.info("Async response:[%s]", replyMessage); +// // } +// // }); +// +// } +// +// { +// // 单客户端并发消息发送测试; +// // TestServce clientService = createClientService(0, hosts, tp); +// // testConcurrentMessageSending(clientService, 100); +// } +// +// { +// // 多客户端并发消息发送测试; +// int msgCount = 60000; +// +// AtomicInteger receiveCount = new AtomicInteger(0); +// +// // my.utils.concurrent.AsyncCallback callback = new +// // my.utils.concurrent.AsyncCallback() { +// // @Override +// // public void complete(String replyMessage, Throwable error) { +// // if (error != null) { +// // long endTs = System.currentTimeMillis(); +// // ConsoleUtils.error("[%s][spanTS=%s] Async response error!!! --%s", endTs, +// // endTs - startTs, +// // error.getMessage()); +// // error.printStackTrace(); +// // return; +// // } +// // int c = receiveCount.incrementAndGet(); +// // if (c >= msgCount) { +// // long endTs = System.currentTimeMillis(); +// // ConsoleUtils.info("\r\n============== All message has been received response! +// // [耗时:%s millis][TPS=%.2f] =====\r\n", (endTs - startTs), msgCount * 1000.0D +// // /(endTs - startTs)); +// // } +// // } +// // }; +// +// TestServce[] sessions = createSessions(10, hosts, tp); +// +// long startTs = System.currentTimeMillis(); +// ConsoleUtils.info("[%s] Async send first message...", startTs); +// AtomicInteger arrivedCounter = new AtomicInteger(0); +// my.utils.concurrent.AsyncHandle allArrivedCallback = new my.utils.concurrent.AsyncHandle() { +// +// @Override +// public void complete(String returnValue, Throwable error) { +// int c = arrivedCounter.incrementAndGet(); +// if (c >= 3) { +// long endTs = System.currentTimeMillis(); +// ConsoleUtils.info( +// "\r\n============== All message has been received by most nodes! [耗时:%s millis][TPS=%.2f] =====\r\n", +// (endTs - startTs), msgCount * 1000.0D / (endTs - startTs)); +// +// } +// if (c >= 4) { +// ConsoleUtils.info("Verify consistence of all replicas after all message received!"); +// verifyConsistence(msgCount, handle0, handle1, handle2, handle3); +// } +// } +// }; +// +// handle0.setThreshold(msgCount, allArrivedCallback); +// handle1.setThreshold(msgCount, allArrivedCallback); +// handle2.setThreshold(msgCount, allArrivedCallback); +// handle3.setThreshold(msgCount, allArrivedCallback); +// +// testConcurrentClient(sessions, msgCount, null); +// ConsoleUtils.info("Complete client sending."); +// +// boolean consistent; +// int tryTimes = 0; +// do { +// ConsoleUtils.info("Verify consistence of all replicas...[%s]", tryTimes); +// consistent = verifyConsistence(msgCount, handle0, handle1, handle2, handle3); +// if (consistent) { +// break; +// } +// tryTimes++; +// ThreadUtils.sleepUninterrupted(1000); +// } while (tryTimes < 5); +// } +// ConsoleUtils.info("----- Test finish! -----"); +// } catch (Exception e) { +// e.printStackTrace(); +// } +// } +// +// public static boolean verifyConsistence(int expectedMessageCount, TestServceHandle... handles) { +// try { +// for (int i = 0; i < handles.length; i++) { +// assertEquals(expectedMessageCount, handles[i].getMessageCount(), "replica[" + i + "] message count"); +// } +// +// for (int i = 0; i < expectedMessageCount; i++) { +// String msg0 = handles[0].getMessage(i); +// assertNotNULL(msg0, "replica[" + i + "]"); +// for (int j = 1; j < handles.length; j++) { +// String msg1 = handles[j].getMessage(i); +// assertEquals(msg0, msg1, "message comparison between replica[0] and replica[" + j + "]"); +// } +// } +// +// ConsoleUtils.info("========== states of all replicas are consistence! ======="); +// return true; +// } catch (Exception e) { +// e.printStackTrace(); +// return false; +// } +// } +// +// public static void assertNotNULL(Object actual, String note) { +// if (actual == null) { +// throw new IllegalStateException(String.format("%s (expected NULL, but actual[%s])", note, actual)); +// } +// } +// +// public static void assertEquals(Object expected, Object actual, String note) { +// if (expected == null && actual == null) { +// return; +// } +// if (expected == null) { +// throw new IllegalStateException(String.format("%s (expected[%s], but actual[%s])", note, expected, actual)); +// } +// if (!expected.equals(actual)) { +// throw new IllegalStateException(String.format("%s (expected[%s], but actual[%s])", note, expected, actual)); +// } +// } +// +// private static TestServce[] createSessions(int sessionCount, HostsConfig hosts, Topology tp) { +// TestServce[] clients = new TestServce[sessionCount]; +// for (int i = 0; i < clients.length; i++) { +// PubKey clientPubKey= BlockchainKeyGenerator.getInstance().generate().getPubKey(); +// TestServce clientService = createClientService(i, clientPubKey, hosts, tp.copyOf()); +// clients[i] = AsyncInvoker.asynchorize(TestServce.class, clientService); +// } +// return clients; +// } +// +// public static void testConcurrentClient(TestServce[] clients, int count, +// my.utils.concurrent.AsyncHandle callback) { +// // TestServce[] clients = createSessions(id, hosts, tp); +// +// ConsoleUtils.info("All clients has conected to replicas..."); +// +// MessageSendingTask[] tasks = new MessageSendingTask[count]; +// +// int sessionCount = clients.length; +// CyclicBarrier barrier = new CyclicBarrier(sessionCount); +// AtomicInteger counter = new AtomicInteger(0); +// for (int i = 0; i < sessionCount; i++) { +// tasks[i] = new MessageSendingTask(count, counter, clients[i], barrier, null, callback); +// Thread thrd = new Thread(tasks[i]); +// +// thrd.start(); +// } +// +// } +// +// public static void testConcurrentMessageSending(TestServce clientService, int count) { +// +// CyclicBarrier barrier = new CyclicBarrier(count); +// for (int i = 0; i < count; i++) { +// +// Thread thrd = new Thread(new Runnable() { +// +// @Override +// public void run() { +// try { +// barrier.await(); +// clientService.hello("AAAA-" + count); +// } catch (Exception e) { +// ConsoleUtils.error("Error occurred on sending message! --%s", e.getMessage()); +// e.printStackTrace(); +// } +// } +// }); +// +// thrd.start(); +// } +// } +// +// public static TestServce createClientService(int id, PubKey clientPubKey, HostsConfig hosts, Topology tp) { +// Properties systemConfig = loadConsensusSetting(); +// +// BftsmartTopology topology = (BftsmartTopology)tp.copyOf(); +// Property[] bftsmartSystemConfigs = PropertiesUtils.getOrderedValues(systemConfig); +// +// BftsmartNodeSettings[] nodesSettings = new BftsmartNodeSettings[hosts.getNum()]; +// +// for (int i = 0; i < hosts.getNum(); i++) { +// PubKey pubKey = KeyGenCommand.decodePubKey(PUB_KEYS[i]); +// BftsmartNodeConfig nodeConfig = new BftsmartNodeConfig(pubKey, i, +// new NetworkAddress(hosts.getHost(i), hosts.getPort(i), false)); +// nodesSettings[i] = nodeConfig; +// } +// +// ConsensusSettings consensusSettings = new BftsmartConsensusConfig(nodesSettings, null, +// bftsmartSystemConfigs); +// +// BftsmartClientSettings clientSettings = new BftsmartClientSettings(id, clientPubKey, consensusSettings, topology); +// BftsmartConsensusProxyFactory serviceFactory = BftsmartConsensusProxyFactory.connect(clientSettings); +// +// // BftsmartConsensusSetting consensusSetting = new BftsmartConsensusSetting(id, +// // systemConfig, hosts); +// // BftsmartConsensusServiceFactory serviceFactory = +// // BftsmartConsensusServiceFactory.connect(0, consensusSetting, +// // (BftsmartTopology) tp); +// +// return serviceFactory.getService(TestServce.class); +// } +// +// public static AsyncCallback startReplica(int id, HostsConfig hosts, +// TestServce serviceHandle) { +// Properties systemConfig = loadConsensusSetting(); +// +// Property[] bftsmartSystemConfigs = PropertiesUtils.getOrderedValues(systemConfig); +// +// BftsmartNodeSettings currNodeSettings = null; +// +// for (int i = 0; i < hosts.getNum(); i++) { +// PubKey pubKey = KeyGenCommand.decodePubKey(PUB_KEYS[i]); +// BftsmartNodeConfig nodeConfig = new BftsmartNodeConfig(pubKey, i, +// new NetworkAddress(hosts.getHost(i), hosts.getPort(i), false)); +// +// if (i == id) { +// currNodeSettings = nodeConfig; +// } +// } +// +// ServerSettings serverSettings = new BftsmartServerSettingConfig(); +// ((BftsmartServerSettingConfig) serverSettings).setRealmName(null); +// ((BftsmartServerSettingConfig) serverSettings).setReplicaSettings(currNodeSettings); +// +// // BftsmartConsensusServlet servlet = new BftsmartConsensusServlet(id, +// // systemConfig, hosts); +// BftsmartConsensusServlet servlet = new BftsmartConsensusServlet(serverSettings, consensusMessageHandler, +// consensusStateManager); +//// servlet.addServiceHandler(TestServce.class, serviceHandle); +// +// ThreadInvoker invoker = new ThreadInvoker() { +// @Override +// protected BftsmartConsensusServlet invoke() throws Exception { +// try { +// servlet.start(); +// ConsoleUtils.info("Replica[%s] start success.", id); +// return servlet; +// } catch (Exception e) { +// ConsoleUtils.info("Replica[%s] start failed!", id); +// e.printStackTrace(); +// throw e; +// } +// } +// }; +// +// return invoker.start(); +// } +// +// public static Properties loadConsensusSetting() { +// ClassPathResource ledgerInitSettingResource = new ClassPathResource("system.config"); +// try (InputStream in = ledgerInitSettingResource.getInputStream()) { +// return FileUtils.readProperties(in); +// } catch (IOException e) { +// throw new IllegalStateException(e.getMessage(), e); +// } +// } +// +// public static class TextMessageConverter implements BinaryMessageConverter { +// +// @Override +// public byte[] encode(Object message) { +// return BytesUtils.toBytes((String) message); +// } +// +// @Override +// public Object decode(byte[] messageBytes) { +// return BytesUtils.toString(messageBytes); +// } +// +// } +// +// public static class DefaultGroupIndexer implements GroupIndexer { +// +// private byte[] groupId = { (byte) 0 }; +// +// @Override +// public byte[] getGroupId(Object[] messageObjects) { +// return groupId; +// } +// +// } +// +// // =================================================== +// +// public static interface TestServce { +// @OrderedAction(groupIndexer = DefaultGroupIndexer.class, responseConverter = TextMessageConverter.class) +// String hello(@ActionMessage(converter = TextMessageConverter.class) String msg); +// } +// +// private static class TestServceHandle implements TestServce, StateHandle { +// +// private int id; +// +// private List msgs = Collections.synchronizedList(new LinkedList<>()); +// +// private AtomicInteger counter = new AtomicInteger(0); +// private int threshold; +// private my.utils.concurrent.AsyncHandle callback; +// +// public void setThreshold(int threshold, my.utils.concurrent.AsyncHandle callback) { +// this.counter.set(0); +// this.threshold = threshold; +// this.callback = callback; +// } +// +// public int getMessageCount() { +// return msgs.size(); +// } +// +// public String getMessage(int index) { +// return msgs.get(index); +// } +// +// public void clear() { +// msgs.clear(); +// } +// +// public TestServceHandle(int id) { +// this.id = id; +// } +// +// @Override +// public String hello(String msg) { +// if (debug) { +// ConsoleUtils.info("Handle message in replica[%s]. --MSG:%s", id, msg); +// } +// msgs.add(msg); +// int c = counter.incrementAndGet(); +// if (c == threshold && callback != null) { +// callback.complete("OK", null); +// } +// if (msg != null && msg.startsWith("[SLEEP]")) { +// try { +// Thread.sleep(600000); +// } catch (InterruptedException e) { +// } +// } +// return "OK"; +// } +// +// @Override +// public byte[] takeSnapshot() { +// int msgCount = getMessageCount(); +// ConsoleUtils.info("Take snapshot...[replica.id=%s][message.count=%s]", id, msgCount); +// return BytesUtils.toBytes(msgCount); +// } +// +// @Override +// public void installSnapshot(byte[] snapshot) { +// int msgCount = getMessageCount(); +// ConsoleUtils.info("Intall snapshot...[replica.id=%s][message.count=%s][snapshot.size=%s]", id, msgCount, +// snapshot == null ? 0 : snapshot.length); +// } +// +// } +// +// private static class MessageSendingTask implements Runnable { +// +// public static final String MESSAGE = "_ABCDEF"; +// +// private CyclicBarrier barrier; +// +// private CountDownLatch latch; +// +// private TestServce client; +// +// private int totalCount; +// private AtomicInteger counter; +// +// private my.utils.concurrent.AsyncHandle callback; +// +// // public MessageSendingTask(int taskId, TestServce client) { +// // this(taskId, client, null, null, null); +// // } +// +// public MessageSendingTask(int totalCount, AtomicInteger counter, TestServce client, CyclicBarrier barrier, +// CountDownLatch latch, my.utils.concurrent.AsyncHandle callback) { +// this.totalCount = totalCount; +// this.counter = counter; +// this.barrier = barrier; +// this.latch = latch; +// this.client = client; +// this.callback = callback; +// } +// +// @Override +// public void run() { +// try { +// if (barrier != null) { +// barrier.await(); +// } +// while (true) { +// int taskId = counter.incrementAndGet(); +// if (taskId > totalCount) { +// return; +// } +// AsyncFuture result = AsyncInvoker.call(client.hello(taskId + MESSAGE)); +// if (callback != null) { +// result.whenCompleteAsync(callback); +// } +// } +// +// } catch (Exception e) { +// ConsoleUtils.error("Error occurred on sending message! --%s", e.getMessage()); +// e.printStackTrace(); +// } finally { +// if (latch != null) { +// latch.countDown(); +// } +// } +// } +// +// } +// +//} diff --git a/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/Settings.java b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/Settings.java new file mode 100644 index 00000000..c7f75903 --- /dev/null +++ b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/Settings.java @@ -0,0 +1,57 @@ +package test.perf.com.jd.blockchain.consensus.node; + +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Configuration; + +/** + * @author huanghaiquan + * + */ +@Configuration +@ConfigurationProperties(prefix = "bft") +public class Settings { + + private String name; + + private String systemConfig = "config/system.config"; + + private String nodesConfig = "config/hosts.config"; + +// private String runtimeHome = "./runtime"; + + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + // =================================================================================================== + + public String getSystemConfig() { + return systemConfig; + } + + public void setSystemConfig(String systemConfig) { + this.systemConfig = systemConfig; + } + + public String getNodesConfig() { + return nodesConfig; + } + + public void setNodesConfig(String nodesConfig) { + this.nodesConfig = nodesConfig; + } + +// public String getRuntimeHome() { +// return runtimeHome; +// } +// +// public void setRuntimeHome(String runtimeHome) { +// this.runtimeHome = runtimeHome; +// } + +} diff --git a/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/TestReplica.java b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/TestReplica.java new file mode 100644 index 00000000..df8a8e4c --- /dev/null +++ b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/TestReplica.java @@ -0,0 +1,55 @@ +package test.perf.com.jd.blockchain.consensus.node; + +import java.util.Properties; + +import bftsmart.reconfiguration.util.HostsConfig; +import com.jd.blockchain.consensus.AsyncActionResponse; +import com.jd.blockchain.consensus.action.ActionRequest; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartNodeServer; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartServerSettingConfig; +import com.jd.blockchain.consensus.bftsmart.service.BftsmartServerSettings; +import com.jd.blockchain.consensus.service.MessageHandle; +import com.jd.blockchain.peer.consensus.ConsensusMessageDispatcher; +import com.jd.blockchain.utils.ConsoleUtils; + +public class TestReplica extends BftsmartNodeServer { + + private byte[] retnOK = {1}; + + public TestReplica(int id, Properties systemsConfig, HostsConfig hostConfig) { + super(new BftsmartServerSettingConfig(), new ConsensusMessageDispatcher(), null); + } + +// @Override +// protected AsyncActionResponse execute(ActionRequest request) { +// ConsoleUtils.info("Receive request ..."); +// return new SimpleResponse(retnOK); +// } + + private static class SimpleResponse implements AsyncActionResponse{ + + private byte[] data; + + public SimpleResponse(byte[] data) { + this.data = data; + } + + @Override + public byte[] process() { + return data; + } + + } + + @Override + public void installSnapshot(byte[] state) { + // TODO Auto-generated method stub + + } + + @Override + public byte[] getSnapshot() { + // TODO Auto-generated method stub + return null; + } +} diff --git a/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/TestWebController.java b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/TestWebController.java new file mode 100644 index 00000000..420704bf --- /dev/null +++ b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/TestWebController.java @@ -0,0 +1,138 @@ +package test.perf.com.jd.blockchain.consensus.node; + +import java.util.Properties; +import java.util.TreeSet; + +import javax.annotation.PostConstruct; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.Topology; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +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 bftsmart.reconfiguration.util.HostsConfig; + +@RestController +public class TestWebController { + + @Autowired + private Settings settings; + + private Properties systemProperties; + + private HostsConfig nodesConfig; + + private TestReplica replica; + + public TestWebController() { + } + + @PostConstruct + private void init() { + nodesConfig = new HostsConfig(settings.getNodesConfig()); + systemProperties = FileUtils.readProperties(settings.getSystemConfig()); + } + + @RequestMapping(path = "/configs/consensus", method = RequestMethod.GET) + public String getSystemProperties() { + TreeSet names = new TreeSet<>(systemProperties.stringPropertyNames()); + StringBuilder content = new StringBuilder(); + for (String n : names) { + content.append(String.format("%s=%s
\r\n", n, systemProperties.getProperty(n))); + } + return content.toString(); + } + + @RequestMapping(path = "/configs/consensus/set/{key}/{value}", method = RequestMethod.GET) + public String setSystemProperty(@PathVariable("key") String key, @PathVariable("value") String value) { + systemProperties.setProperty(key, value); + return getSystemProperties(); + } + + @RequestMapping(path = "/configs/consensus/set/default", method = RequestMethod.GET) + public String setSystemDefaultProperty() { + systemProperties = FileUtils.readProperties(settings.getSystemConfig()); + return getSystemProperties(); + } + + @RequestMapping(path = "/configs/nodes", method = RequestMethod.GET) + public String getNodes() { + StringBuilder content = new StringBuilder(); + int[] ids = nodesConfig.getHostsIds(); + for (int id : ids) { + content.append(String.format("%s - %s:%s [%s]
\r\n", id, nodesConfig.getHost(id), nodesConfig.getPort(id), + nodesConfig.getServerToServerPort(id))); + } + return content.toString(); + } + + + @RequestMapping(path = "/configs/nodes/set/{id}/{host}/{port}", method = RequestMethod.GET) + public String setNode(@PathVariable("id") int id, @PathVariable("host") String host, + @PathVariable("port") int port) { + nodesConfig.add(id, host, port); + return getNodes(); + } + + @RequestMapping(path = "/node/id", method = RequestMethod.GET) + public String getNodeID() { + return "Node.ID=" + (replica == null ? 0 : replica.getId()); + } + + @RequestMapping(path = "/node/status", method = RequestMethod.GET) + public String getStatus() { + return replica == null ? "STOPPED" : "RUNNING"; + } + + @RequestMapping(path = "/node/start/{id}", method = RequestMethod.GET) + public String start(@PathVariable("id")int id) { + if (replica != null ) { + return "Already started!"; + } + TestReplica replica = new TestReplica(id, systemProperties, nodesConfig); + //after new TestConsensusReplica systemProperties field will be removed + replica.start(); + this.replica = replica; + return "success!"; + } + + @RequestMapping(path = "/node/stop", method = RequestMethod.GET) + public String stop() { + if (replica == null ) { + return "Already stopped!"; + } + TestReplica replica = this.replica; + this.replica = null; + replica.stop(); + return "stopped!"; + } + + + @RequestMapping(path = "/node/topology", method = RequestMethod.GET) + public String getNodesTopology(){ + if (replica == null ) { + throw new IllegalStateException("Replica not start"); + } + Topology tp = replica.getTopology().copyOf(); + byte[] bytesTP = BinarySerializeUtils.serialize(tp); + return HexUtils.encode(bytesTP); + } + + @RequestMapping(path = "/node/settings", method = RequestMethod.GET) + public String getNodesSettings(){ + if (replica == null ) { + throw new IllegalStateException("Replica not start"); + } + ConsensusSettings settings = replica.getConsensusSetting(); + byte[] bytesSettings = BinarySerializeUtils.serialize(settings); + return HexUtils.encode(bytesSettings); + } + +} diff --git a/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/WebBooter.java b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/WebBooter.java new file mode 100644 index 00000000..fa62c90d --- /dev/null +++ b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/WebBooter.java @@ -0,0 +1,17 @@ +package test.perf.com.jd.blockchain.consensus.node; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; + +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +public class WebBooter { + + public static void main(String[] args) { + SpringApplication.run(WebBooter.class, args); + } + +} diff --git a/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/WebConfiguration.java b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/WebConfiguration.java new file mode 100644 index 00000000..34a4843a --- /dev/null +++ b/source/test/test-consensus-node/src/main/java/test/perf/com/jd/blockchain/consensus/node/WebConfiguration.java @@ -0,0 +1,11 @@ +package test.perf.com.jd.blockchain.consensus.node; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; + +@Configuration +@ComponentScan +public class WebConfiguration { + +} + diff --git a/source/test/test-consensus-node/src/main/resources/application.properties b/source/test/test-consensus-node/src/main/resources/application.properties new file mode 100644 index 00000000..bbe35d4d --- /dev/null +++ b/source/test/test-consensus-node/src/main/resources/application.properties @@ -0,0 +1,19 @@ +server.port=9000 +#server.ssl.key-store=classpath:mykeys.jks +#server.ssl.key-store-password=abc123 +#server.ssl.key-password=abc123 + +server.tomcat.accesslog.enabled=false + +debug=false + +#logging.file=logs/peer.log +logging.level.com.jd.blockchain.peer=DEBUG +logging.level.org.org.springframework=DEBUG + +spring.mvc.favicon.enabled=false + +bft.name=node-0 +bft.nodes-config=config/hosts.config +bft.system-config=config/system.config +bft.runtime-home=config \ No newline at end of file diff --git a/source/test/test-consensus-node/src/main/resources/system.config b/source/test/test-consensus-node/src/main/resources/system.config new file mode 100644 index 00000000..bf8beec8 --- /dev/null +++ b/source/test/test-consensus-node/src/main/resources/system.config @@ -0,0 +1,121 @@ +# 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 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 = 800 +system.totalordermulticast.global_checkpoint_period = 5000 + +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 diff --git a/source/test/test-integration/pom.xml b/source/test/test-integration/pom.xml new file mode 100644 index 00000000..9aa3e873 --- /dev/null +++ b/source/test/test-integration/pom.xml @@ -0,0 +1,62 @@ + + 4.0.0 + + com.jd.blockchain + test + 0.8.2.RELEASE + + test-integration + + + + com.jd.blockchain + peer + ${project.version} + + + com.jd.blockchain + storage-rocksdb + ${project.version} + + + com.jd.blockchain + storage-redis + ${project.version} + + + com.jd.blockchain + gateway + ${project.version} + + + com.jd.blockchain + tools-initializer + ${project.version} + + + com.jd.blockchain + sdk-client + ${project.version} + + + com.jd.blockchain + contract-model + ${project.version} + + + io.nats + jnats + 2.2.0 + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + \ No newline at end of file diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/GatewayTestRunner.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/GatewayTestRunner.java new file mode 100644 index 00000000..d152cd02 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/GatewayTestRunner.java @@ -0,0 +1,76 @@ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.gateway.GatewayConfigProperties; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.gateway.GatewayServerBooter; + +import org.springframework.core.io.ClassPathResource; +import org.springframework.util.CollectionUtils; + +import java.util.Map; + +public class GatewayTestRunner { + + private NetworkAddress serviceAddress; + + private GatewayServerBooter gatewayServer; + + public GatewayTestRunner(String host, int port, KeyPairConfig gatewayDefaultKey, NetworkAddress masterPeerAddres) { + this(host, port, gatewayDefaultKey, masterPeerAddres, null,null); + } + + public GatewayTestRunner(String host, int port, KeyPairConfig gatewayDefaultKey, NetworkAddress masterPeerAddres, String[] providers, + Map otherMap) { + this.serviceAddress = new NetworkAddress(host, port); + GatewayConfigProperties config = new GatewayConfigProperties(); + + config.http().setHost(host); + config.http().setPort(port); + + if (providers != null) { + for (String provider : providers) { + config.providerConfig().add(provider); + } + } + + config.setMasterPeerAddress(masterPeerAddres); + + config.keys().getDefault().setPubKeyValue(gatewayDefaultKey.getPubKeyValue()); + config.keys().getDefault().setPrivKeyValue(gatewayDefaultKey.getPrivKeyValue()); + config.keys().getDefault().setPrivKeyPassword(gatewayDefaultKey.getPrivKeyPassword()); + + if(!CollectionUtils.isEmpty(otherMap)){ + config.setDataRetrievalUrl(otherMap.get("DATA_RETRIEVAL_URL").toString()); + } + + + //get the springConfigLocation; + ClassPathResource configResource = new ClassPathResource("application-gw.properties"); + String springConfigLocation = "classPath:"+configResource.getPath(); + + this.gatewayServer = new GatewayServerBooter(config,springConfigLocation); + } + + public AsyncCallback start() { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected Object invoke() throws Exception { + gatewayServer.start(); + return null; + } + }; + + return invoker.start(); + } + + public void stop() { + gatewayServer.close(); + } + + public NetworkAddress getServiceAddress() { + return serviceAddress; + } +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegratedContext.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegratedContext.java new file mode 100644 index 00000000..cdbdf4d1 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegratedContext.java @@ -0,0 +1,101 @@ +package test.com.jd.blockchain.intgr; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; + +public class IntegratedContext { + + private Map nodes = new HashMap<>(); + + public int[] getNodeIds() { + int[] ids = new int[nodes.size()]; + int i = 0; + for (Integer id : nodes.keySet()) { + ids[i] = id.intValue(); + i++; + } + Arrays.sort(ids); + return ids; + } + + public Node getNode(int id) { + return nodes.get(id); + } + + public void addNode(Node node) { + nodes.put(node.getId(), node); + } + + public static class Node { + + private int id; + + private CryptoKeyPair partiKeyPair; + + // private NetworkAddress consensusAddress; + private ConsensusSettings consensusSettings; + + private LedgerManager ledgerManager; + + private CompositeConnectionFactory storageDB; + + private LedgerBindingConfig bindingConfig; + + public Node(int id) { + this.id = id; + } + + public ConsensusSettings getConsensusAddress() { + return consensusSettings; + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + } + + public CompositeConnectionFactory getStorageDB() { + return storageDB; + } + + public CryptoKeyPair getPartiKeyPair() { + return partiKeyPair; + } + + public void setPartiKeyPair(CryptoKeyPair partiKeyPair) { + this.partiKeyPair = partiKeyPair; + } + + public int getId() { + return id; + } + + public void setConsensusSettings(ConsensusSettings consensusSettings) { + this.consensusSettings = consensusSettings; + } + + public void setLedgerManager(LedgerManager ledgerManager) { + this.ledgerManager = ledgerManager; + } + + public void setStorageDB(CompositeConnectionFactory storageDB) { + this.storageDB = storageDB; + } + + public LedgerBindingConfig getBindingConfig() { + return bindingConfig; + } + + public void setBindingConfig(LedgerBindingConfig bindingConfig) { + this.bindingConfig = bindingConfig; + } + + } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java new file mode 100644 index 00000000..49bbd6b5 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/IntegrationTest.java @@ -0,0 +1,694 @@ +package test.com.jd.blockchain.intgr; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Random; +import java.util.concurrent.CountDownLatch; + +import com.jd.blockchain.storage.service.KVStorageService; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.AccountHeader; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInfo; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.UserInfo; +import com.jd.blockchain.ledger.core.DataAccountSet; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.perf.LedgerInitializeWebTest; + +public class IntegrationTest { + // 合约测试使用的初始化数据; + BlockchainKeyPair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainKeyPair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); + private String contractZipName = "AssetContract1.contract"; + private String eventName = "issue-asset"; + HashDigest txContentHash; + String pubKeyVal = "jd.com" + System.currentTimeMillis();; + // String userPubKeyVal = "this is user's pubKey"; + // 保存资产总数的键; + private static final String KEY_TOTAL = "TOTAL"; + // 第二个参数; + private static final String KEY_ABC = "abc"; + + private static final String MQ_SERVER = "nats://127.0.0.1:4222"; + + private static final String MQ_TOPIC = "subject"; + + private static String memDbConnString = LedgerInitConsensusConfig.memConnectionStrings[0]; + + // private static final MQConnectionConfig mqConnConfig = new + // MQConnectionConfig(); + // static { + // mqConnConfig.setServer(MQ_SERVER); + // mqConnConfig.setTopic(MQ_TOPIC); + // } + + public static void main_(String[] args) { + // init ledgers of all nodes ; + IntegratedContext context = initLedgers(); + Node node0 = context.getNode(0); + Node node1 = context.getNode(1); + Node node2 = context.getNode(2); + Node node3 = context.getNode(3); + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 10200); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, node0.getBindingConfig(), node0.getStorageDB()); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 10210); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, node1.getBindingConfig(), node1.getStorageDB()); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 10220); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, node2.getBindingConfig(), node2.getStorageDB()); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 10230); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, node3.getBindingConfig(), node3.getStorageDB()); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWebTest.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(LedgerInitializeWebTest.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(LedgerInitializeWebTest.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + // GatewayTestRunner gateway0 = new GatewayTestRunner("127.0.0.1", 10300, + // gwkey0, peerSrvAddr0); + GatewayTestRunner gateway0 = new GatewayTestRunner("127.0.0.1", 10300, gwkey0, peerSrvAddr0); + + // KeyPairConfig gwkey1 = new KeyPairConfig(); + // gwkey1.setPubKeyValue(LedgerInitializeWebTest.PUB_KEYS[1]); + // gwkey1.setPrivKeyValue(LedgerInitializeWebTest.PRIV_KEYS[1]); + // gwkey1.setPrivKeyPassword(encodedBase58Pwd); + // GatewayTestRunner gateway1 = new GatewayTestRunner("127.0.0.1", 10310, + // gwkey1, peerSrvAddr1); + + AsyncCallback gwStarting0 = gateway0.start(); + // AsyncCallback gwStarting1 = gateway1.start(); + + gwStarting0.waitReturn(); + // gwStarting1.waitReturn(); + + // 执行测试用例之前,校验每个节点的一致性; + // testConsistencyAmongNodes(context); + + testSDK(gateway0, context); + + // 执行测试用例之后,校验每个节点的一致性; + // testConsistencyAmongNodes(context); + } + + /** + * 检查所有节点之间的账本是否一致; + * + * @param context + */ + private void testConsistencyAmongNodes(IntegratedContext context) { + int[] ids = context.getNodeIds(); + Node[] nodes = new Node[ids.length]; + LedgerRepository[] ledgers = new LedgerRepository[ids.length]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = context.getNode(ids[i]); + HashDigest ledgerHash = nodes[i].getLedgerManager().getLedgerHashs()[0]; + ledgers[i] = nodes[i].getLedgerManager().getLedger(ledgerHash); + } + LedgerRepository ledger0 = ledgers[0]; + LedgerBlock latestBlock0 = ledger0.retrieveLatestBlock(); + for (int i = 1; i < ledgers.length; i++) { + LedgerRepository otherLedger = ledgers[i]; + LedgerBlock otherLatestBlock = otherLedger.retrieveLatestBlock(); + } + } + + private static void testSDK(GatewayTestRunner gateway, IntegratedContext context) { + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + BlockchainService bcsrv = gwsrvFact.getBlockchainService(); + + HashDigest[] ledgerHashs = bcsrv.getLedgerHashs(); + + CryptoKeyPair adminKey = context.getNode(0).getPartiKeyPair(); + + BlockchainKeyPair newUserAcount = testSDK_RegisterUser(adminKey, ledgerHashs[0], bcsrv, context); + + // BlockchainKeyPair newDataAccount = testSDK_RegisterDataAccount(adminKey, + // ledgerHashs[0], bcsrv, context); + // + // testSDK_InsertData(adminKey, ledgerHashs[0], bcsrv, + // newDataAccount.getAddress(), context); + // + // LedgerBlock latestBlock = testSDK_Contract(adminKey, ledgerHashs[0], bcsrv, + // context); + + } + + private void testSDK_InsertData(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, + String dataAccountAddress, IntegratedContext context) { + + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = blockchainService.newTransaction(ledgerHash); + + // -------------------------------------- + // 将商品信息写入到指定的账户中; + // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; + String dataAccount = dataAccountAddress; + + String dataKey = "jingdong" + new Random().nextInt(100000); + byte[] dataVal = "www.jd.com".getBytes(); + + txTemp.dataAccount(dataAccount).set(dataKey, dataVal, -1); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + prepTx.sign(adminKey); + + // 提交交易; + TransactionResponse txResp = prepTx.commit(); + + Node node0 = context.getNode(0); + LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); + ledgerOfNode0.retrieveLatestBlock(); // 更新内存 + + // 先验证应答 + KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, dataAccountAddress, dataKey); + for (KVDataEntry kvDataEntry : kvDataEntries) { + String valHexText = (String) kvDataEntry.getValue(); + byte[] valBytes = HexUtils.decode(valHexText); + String valText = new String(valBytes); + System.out.println(valText); + } + } + + private static BlockchainKeyPair testSDK_RegisterUser(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, IntegratedContext context) { + // 注册用户,并验证最终写入; + BlockchainKeyPair user = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(user.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + + HashDigest transactionHash = ptx.getHash(); + + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + Node node0 = context.getNode(0); + LedgerManage ledgerManager = new LedgerManager(); + + KVStorageService storageService = node0.getStorageDB().connect(memDbConnString).getStorageService(); + + LedgerRepository ledgerOfNode0 = ledgerManager.register(ledgerHash, storageService); + + return user; + } + + private BlockchainKeyPair testSDK_RegisterDataAccount(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, IntegratedContext context) { + // 注册数据账户,并验证最终写入; + BlockchainKeyPair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.dataAccounts().register(dataAccount.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + + HashDigest transactionHash = ptx.getHash(); + + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + Node node0 = context.getNode(0); + // LedgerRepository ledgerOfNode0 = + // node0.getLedgerManager().getLedger(ledgerHash); + LedgerManage ledgerManager = new LedgerManager(); + + KVStorageService storageService = node0.getStorageDB().connect(memDbConnString).getStorageService(); + + LedgerRepository ledgerOfNode0 = ledgerManager.register(ledgerHash, storageService); + long latestBlockHeight = ledgerOfNode0.retrieveLatestBlockHeight(); + + return dataAccount; + } + + private void testSDK_Query(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, + IntegratedContext context, BlockchainKeyPair newUserAcount, BlockchainKeyPair newDataAcount) { + + Bytes userAddress = newUserAcount.getAddress(); + Bytes dataAddress = newDataAcount.getAddress(); + + Node node0 = context.getNode(0); + // LedgerRepository ledgerOfNode0 = + // node0.getLedgerManager().getLedger(ledgerHash); + LedgerManage ledgerManager = new LedgerManager(); + + KVStorageService storageService = node0.getStorageDB().connect(memDbConnString).getStorageService(); + + LedgerRepository ledgerOfNode0 = ledgerManager.register(ledgerHash, storageService); + + // getLedgerHashs + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + for (HashDigest hashDigest : ledgerHashs) { + if (hashDigest.equals(ledgerHash)) { + break; + } + System.out.println("Query getLedgerHashs error! ledgerHash not exist!"); + } + + // getLedger + LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); + long ledgerHeight = ledgerInfo.getLatestBlockHeight(); + + // getConsensusParticipants + ParticipantNode[] consensusParticipants = blockchainService.getConsensusParticipants(ledgerHash); + for (int i = 0; i < 4; i++) { + } + + // getBlock + for (int i = 0; i < ledgerHeight + 1; i++) { + LedgerBlock expectBlock = ledgerOfNode0.getBlock(i); + } + + // getTransactionCount according to blockhash + for (int i = 0; i < ledgerHeight + 1; i++) { + LedgerBlock expectBlock = ledgerOfNode0.getBlock(i); + long expectTransactionCount = ledgerOfNode0.getTransactionSet(expectBlock).getTotalCount(); + long actualTransactionCount = blockchainService.getTransactionCount(ledgerHash, expectBlock.getHash()); + } + + // getDataAccountCount according to blockhash + for (int i = 0; i < ledgerHeight + 1; i++) { + LedgerBlock expectBlock = ledgerOfNode0.getBlock(i); + long expectDataCount = ledgerOfNode0.getDataAccountSet(expectBlock).getTotalCount(); + long actualDataCount = blockchainService.getDataAccountCount(ledgerHash, expectBlock.getHash()); + } + + // getUserCount according to blockhash + for (int i = 0; i < ledgerHeight + 1; i++) { + LedgerBlock expectBlock = ledgerOfNode0.getBlock(i); + long expectUserCount = ledgerOfNode0.getUserAccountSet(expectBlock).getTotalCount(); + long actualUserCount = blockchainService.getUserCount(ledgerHash, expectBlock.getHash()); + } + + // getContractCount according to blockhash + for (int i = 0; i < ledgerHeight + 1; i++) { + LedgerBlock expectBlock = ledgerOfNode0.getBlock(i); + long expectContractCount = ledgerOfNode0.getContractAccountSet(expectBlock).getTotalCount(); + long actualContractCount = blockchainService.getContractCount(ledgerHash, expectBlock.getHash()); + } + + // getTransactionCount according to height + // getDataAccountCount according to height + // getUserCount according to height + // getContractCount according to height + long expectTransactionTotal = 0; + long expectUserTotal = 0; + long expectDataTotal = 0; + long expectContractTotal = 0; + for (int i = 0; i < ledgerHeight + 1; i++) { + // actual block acount total + long transactionCount = blockchainService.getTransactionCount(ledgerHash, i); + long userCount = blockchainService.getUserCount(ledgerHash, i); + long dataCount = blockchainService.getDataAccountCount(ledgerHash, i); + long contractCount = blockchainService.getContractCount(ledgerHash, i); + + // expect block acount total + LedgerBlock ledgerBlock = ledgerOfNode0.getBlock(i); + expectTransactionTotal = ledgerOfNode0.getTransactionSet(ledgerBlock).getTotalCount(); + expectUserTotal = ledgerOfNode0.getUserAccountSet(ledgerBlock).getTotalCount(); + expectDataTotal = ledgerOfNode0.getDataAccountSet(ledgerBlock).getTotalCount(); + expectContractTotal = ledgerOfNode0.getContractAccountSet(ledgerBlock).getTotalCount(); + } + + // getTransactionTotalCount + long actualTransactionTotal = blockchainService.getTransactionTotalCount(ledgerHash); + + // getUserTotalCount + long actualUserTotal = blockchainService.getUserTotalCount(ledgerHash); + + // getDataAccountTotalCount + long actualDataAccountTotal = blockchainService.getDataAccountTotalCount(ledgerHash); + + // getContractTotalCount + long actualContractAccountTotal = blockchainService.getContractTotalCount(ledgerHash); + + // getTransactions + // getTransactionByContentHash + // getTransactionStateByContentHash + // ledger-core not implement + // for (int i = 0; i < ledgerHeight + 1; i++) { + // LedgerBlock ledgerBlock = ledgerOfNode0.getBlock(i); + // HashDigest blockHash = ledgerBlock.getHash(); + // long expectCount = + // ledgerOfNode0.getTransactionSet(ledgerBlock).getTotalCount(); + // long actualCount = blockchainService.getTransactionCount(ledgerHash, i); + // LedgerTransaction[] ledgerTransactions1 = + // blockchainService.getTransactions(ledgerHash, i, 0, (int)(actualCount - 1)); + // LedgerTransaction[] ledgerTransactions2 = + // blockchainService.getTransactions(ledgerHash, blockHash, 0, (int)(expectCount + // - 1)); + // assertEquals(ledgerTransactions1.length, ledgerTransactions2.length); + // assertArrayEquals(ledgerTransactions1, ledgerTransactions2); + // + // for (LedgerTransaction ledgerTransaction : ledgerTransactions1) { + // assertEquals(ledgerTransaction, + // blockchainService.getTransactionByContentHash(ledgerHash, + // ledgerTransaction.getTransactionContent().getHash())); + // assertEquals(TransactionState.SUCCESS, + // blockchainService.getTransactionStateByContentHash(ledgerHash, + // ledgerTransaction.getTransactionContent().getHash())); + // } + // } + // getUser + UserInfo userInfo = blockchainService.getUser(ledgerHash, userAddress.toString()); + + // getDataAccount + AccountHeader accountHeader = blockchainService.getDataAccount(ledgerHash, dataAddress.toString()); + + // getDataEntries + + return; + } + + public static ConsensusProvider getConsensusProvider() { + return ConsensusProviders.getProvider("com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + } + + private static IntegratedContext initLedgers() { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_integration(); + Properties props = LedgerInitializeWebTest.loadConsensusSetting(); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + LedgerInitializeWebTest.NodeWebContext nodeCtx0 = new LedgerInitializeWebTest.NodeWebContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + LedgerInitializeWebTest.NodeWebContext nodeCtx1 = new LedgerInitializeWebTest.NodeWebContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + LedgerInitializeWebTest.NodeWebContext nodeCtx2 = new LedgerInitializeWebTest.NodeWebContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + LedgerInitializeWebTest.NodeWebContext nodeCtx3 = new LedgerInitializeWebTest.NodeWebContext(3, initAddr3); + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWebTest.PRIV_KEYS[0], + LedgerInitializeWebTest.PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWebTest.PRIV_KEYS[1], + LedgerInitializeWebTest.PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWebTest.PRIV_KEYS[2], + LedgerInitializeWebTest.PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWebTest.PRIV_KEYS[3], + LedgerInitializeWebTest.PASSWORD); + + String encodedPassword = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWebTest.PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri("memory://local/0"); + LedgerBindingConfig bindingConfig0 = new LedgerBindingConfig(); + AsyncCallback callback0 = nodeCtx0.startInitCommand(privkey0, encodedPassword, initSetting, csProps, + csProvider, testDb0, consolePrompter, bindingConfig0, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri("memory://local/1"); + LedgerBindingConfig bindingConfig1 = new LedgerBindingConfig(); + AsyncCallback callback1 = nodeCtx1.startInitCommand(privkey1, encodedPassword, initSetting, csProps, + csProvider, testDb1, consolePrompter, bindingConfig1, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri("memory://local/2"); + LedgerBindingConfig bindingConfig2 = new LedgerBindingConfig(); + AsyncCallback callback2 = nodeCtx2.startInitCommand(privkey2, encodedPassword, initSetting, csProps, + csProvider, testDb2, consolePrompter, bindingConfig2, quitLatch); + + DBConnectionConfig testDb3 = new DBConnectionConfig(); + testDb3.setConnectionUri("memory://local/3"); + LedgerBindingConfig bindingConfig3 = new LedgerBindingConfig(); + AsyncCallback callback3 = nodeCtx3.startInitCommand(privkey3, encodedPassword, initSetting, csProps, + csProvider, testDb3, consolePrompter, bindingConfig3, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + LedgerRepository ledger0 = nodeCtx0.registLedger(ledgerHash0); + LedgerRepository ledger1 = nodeCtx1.registLedger(ledgerHash1); + LedgerRepository ledger2 = nodeCtx2.registLedger(ledgerHash2); + LedgerRepository ledger3 = nodeCtx3.registLedger(ledgerHash3); + + IntegratedContext context = new IntegratedContext(); + + Node node0 = new Node(0); + node0.setConsensusSettings(csProps); + node0.setLedgerManager(nodeCtx0.getLedgerManager()); + node0.setStorageDB(nodeCtx0.getStorageDB()); + node0.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(0).getPubKey(), privkey0)); + node0.setBindingConfig(bindingConfig0); + context.addNode(node0); + + Node node1 = new Node(1); + node1.setConsensusSettings(csProps); + node1.setLedgerManager(nodeCtx1.getLedgerManager()); + node1.setStorageDB(nodeCtx1.getStorageDB()); + node1.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(1).getPubKey(), privkey1)); + node1.setBindingConfig(bindingConfig1); + context.addNode(node1); + + Node node2 = new Node(2); + node2.setConsensusSettings(csProps); + node2.setLedgerManager(nodeCtx2.getLedgerManager()); + node2.setStorageDB(nodeCtx2.getStorageDB()); + node2.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(2).getPubKey(), privkey2)); + node2.setBindingConfig(bindingConfig2); + context.addNode(node2); + + Node node3 = new Node(3); + node3.setConsensusSettings(csProps); + node3.setLedgerManager(nodeCtx3.getLedgerManager()); + node3.setStorageDB(nodeCtx3.getStorageDB()); + node3.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(3).getPubKey(), privkey3)); + node3.setBindingConfig(bindingConfig3); + context.addNode(node3); + + nodeCtx0.closeServer(); + nodeCtx1.closeServer(); + nodeCtx2.closeServer(); + nodeCtx3.closeServer(); + + return context; + } + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private LedgerBlock testSDK_Contract(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, IntegratedContext context) { + // valid the basic data in contract; + prepareContractData(adminKey, ledgerHash, blockchainService, context); + + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + byte[] contractCode = getChainCodeBytes(); + + txTpl.users().register(userKey.getIdentity()); + + txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + txResp.getContentHash(); + + Node node0 = context.getNode(0); + LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); + LedgerBlock block = ledgerOfNode0.getBlock(txResp.getBlockHeight()); + byte[] contractCodeInDb = ledgerOfNode0.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) + .getChainCode(); + txContentHash = ptx.getHash(); + + // execute the contract; + testContractExe(adminKey, ledgerHash, userKey, blockchainService, context); + + return block; + } + + private void testContractExe(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainKeyPair userKey, + BlockchainService blockchainService, IntegratedContext context) { + LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); + LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + + txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, + ("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" + + userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + txContentHash.toBase58() + + "##" + pubKeyVal).getBytes()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + txResp.getContentHash(); + + LedgerInfo latestLedgerInfo = blockchainService.getLedger(ledgerHash); + + Node node0 = context.getNode(0); + LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); + LedgerBlock backgroundLedgerBlock = ledgerOfNode0.retrieveLatestBlock(); + + // 验证合约中的赋值,外部可以获得; + DataAccountSet dataAccountSet = ledgerOfNode0.getDataAccountSet(backgroundLedgerBlock); + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); + Bytes dataAddress = AddressEncoding.generateAddress(pubKey); + + // 验证userAccount,从合约内部赋值,然后外部验证;由于目前不允许输入重复的key,所以在内部合约中构建的key,不便于在外展示,屏蔽之; + // UserAccountSet userAccountSet = + // ledgerOfNode0.getUserAccountSet(backgroundLedgerBlock); + // PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, + // userPubKeyVal.getBytes()); + // String userAddress = AddressEncoding.generateAddress(userPubKey); + // assertEquals(userAddress, userAccountSet.getUser(userAddress).getAddress()); + } + + private void prepareContractData(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, + IntegratedContext context) { + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + // 注册数据账户,并验证最终写入; + txTpl.dataAccounts().register(contractDataKey.getIdentity()); + DataAccountKVSetOperation kvsetOP = txTpl.dataAccount(contractDataKey.getAddress()) + .set("A", "Value_A_0".getBytes(), -1).set("B", "Value_B_0".getBytes(), -1) + .set(KEY_TOTAL, "total value,dataAccount".getBytes(), -1) + .set(KEY_ABC, "abc value,dataAccount".getBytes(), -1) + // 所有的模拟数据都在这个dataAccount中填充; + .set("ledgerHash", ledgerHash.getRawDigest(), -1).getOperation(); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + Node node0 = context.getNode(0); + + LedgerRepository ledgerOfNode0 = node0.getLedgerManager().getLedger(ledgerHash); + LedgerBlock block = ledgerOfNode0.getBlock(txResp.getBlockHeight()); + byte[] val1InDb = ledgerOfNode0.getDataAccountSet(block).getDataAccount(contractDataKey.getAddress()) + .getBytes("A"); + byte[] val2InDb = ledgerOfNode0.getDataAccountSet(block).getDataAccount(contractDataKey.getAddress()) + .getBytes(KEY_TOTAL); + } + + /** + * 根据合约构建字节数组; + * + * @return + */ + private byte[] getChainCodeBytes() { + // 构建合约的字节数组; + byte[] contractCode = null; + File file = null; + InputStream input = null; + try { + ClassPathResource contractPath = new ClassPathResource(contractZipName); + file = new File(contractPath.getURI()); + input = new FileInputStream(file); + // 这种暴力的读取压缩包,在class解析时有问题,所有需要改进; + contractCode = new byte[input.available()]; + input.read(contractCode); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return contractCode; + } +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/LedgerInitConsensusConfig.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/LedgerInitConsensusConfig.java new file mode 100644 index 00000000..cceb97e7 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/LedgerInitConsensusConfig.java @@ -0,0 +1,97 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.intgr.LedgerInitConsensusConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/21 下午5:52 + * Description: + */ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.utils.io.FileUtils; + +import test.com.jd.blockchain.intgr.perf.LedgerPerformanceTest; + +import java.io.File; + +/** + * + * @author shaozhuguang + * @create 2018/12/21 + * @since 1.0.0 + */ + +public class LedgerInitConsensusConfig { + + public static ConsensusConfig mqConfig = new ConsensusConfig(); + + public static ConsensusConfig bftsmartConfig = new ConsensusConfig(); + + public static String[] redisConnectionStrings = new String[4]; + + public static String[] memConnectionStrings = new String[4]; + + public static String[] rocksdbConnectionStrings = new String[4]; + + public static String[] rocksdbDirStrings = new String[4]; + + public static String[] mqProvider = new String[1]; + + public static String[] bftsmartProvider = new String[1]; + + public static String[] mqAndbftsmartProvider = new String[2]; + + static { + mqConfig.provider = "com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider"; + mqConfig.configPath = "mq.config"; + + bftsmartConfig.provider = "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"; + bftsmartConfig.configPath = "bftsmart.config"; + +// for (int i = 0; i < redisConnectionStrings.length; i++) { +// redisConnectionStrings[i] = "redis://127.0.0.1:6379/" + i; +// } + redisConnectionStrings[0] = "redis://192.168.54.112:6379"; + redisConnectionStrings[1] = "redis://192.168.54.112:6379/1"; + redisConnectionStrings[2] = "redis://192.168.54.112:6379/2"; + redisConnectionStrings[3] = "redis://192.168.54.112:6379/3"; + + for (int i = 0; i < memConnectionStrings.length; i++) { + memConnectionStrings[i] = "memory://local/" + i; + } + + mqProvider[0] = mqConfig.provider; + bftsmartProvider[0] = bftsmartConfig.provider; + + mqAndbftsmartProvider[0] = mqConfig.provider; + mqAndbftsmartProvider[1] = bftsmartConfig.provider; + + for (int i = 0; i < rocksdbConnectionStrings.length; i++) { + String currDir = FileUtils.getCurrentDir() + File.separator + "rocks.db"; + String dbDir = new File(currDir, "rocksdb" + i + ".db").getAbsolutePath(); + rocksdbDirStrings[i] = dbDir; + rocksdbConnectionStrings[i] = "rocksdb://" + dbDir; + } + } + + public static ConsensusProvider getConsensusProvider(String providerName) { + return ConsensusProviders.getProvider(providerName); + } + + public static class ConsensusConfig { + + String provider; + + String configPath; + + public String getProvider() { + return provider; + } + + public String getConfigPath() { + return configPath; + } + } +} \ No newline at end of file diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/PeerTestRunner.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/PeerTestRunner.java new file mode 100644 index 00000000..c2c552ed --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/PeerTestRunner.java @@ -0,0 +1,62 @@ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.peer.PeerServerBooter; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class PeerTestRunner { + + private NetworkAddress serviceAddress; + + private volatile PeerServerBooter peerServer; + + private LedgerBindingConfig ledgerBindingConfig; + + public DbConnectionFactory getDBConnectionFactory() { + return peerServer.getDBConnectionFactory(); + } + + public NetworkAddress getServiceAddress() { + return serviceAddress; + } + + public LedgerBindingConfig getLedgerBindingConfig() { + return ledgerBindingConfig; + } + + public PeerTestRunner(NetworkAddress serviceAddress, LedgerBindingConfig ledgerBindingConfig) { + this(serviceAddress, ledgerBindingConfig, null); + } + + public PeerTestRunner(NetworkAddress serviceAddress, LedgerBindingConfig ledgerBindingConfig, + DbConnectionFactory dbConnectionFactory) { + this.serviceAddress = serviceAddress; + this.ledgerBindingConfig = ledgerBindingConfig; + if (dbConnectionFactory == null) { + this.peerServer = new PeerServerBooter(ledgerBindingConfig, serviceAddress.getHost(), serviceAddress.getPort(),null); + }else { + this.peerServer = new PeerServerBooter(ledgerBindingConfig, serviceAddress.getHost(), serviceAddress.getPort(),null, + dbConnectionFactory); + } + } + + public AsyncCallback start() { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected Object invoke() throws Exception { + peerServer.start(); + + return null; + } + }; + + return invoker.start(); + } + + public void stop() { + peerServer.close(); + } +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/PresetAnswerPrompter.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/PresetAnswerPrompter.java new file mode 100644 index 00000000..152ee356 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/PresetAnswerPrompter.java @@ -0,0 +1,35 @@ +package test.com.jd.blockchain.intgr; + +import java.util.Properties; + +import com.jd.blockchain.tools.initializer.ConsolePrompter; + +public class PresetAnswerPrompter extends ConsolePrompter { + + private Properties answers = new Properties(); + + private String defaultAnswer; + + public PresetAnswerPrompter(String defaultAnswer) { + this.defaultAnswer = defaultAnswer; + } + + public void setAnswer(String tag, String answer) { + answers.setProperty(tag, answer); + } + + public void setDefaultAnswer(String defaultAnswer) { + this.defaultAnswer = defaultAnswer; + } + + @Override + public String confirm(String tag, String format, Object... args) { + System.out.print(String.format(format, args)); + String answer = answers.getProperty(tag, defaultAnswer); + System.out.println(String.format("\r\n [Mocked answer:%s]", answer)); + return answer; + } + + + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/consensus/ConsensusTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/consensus/ConsensusTest.java new file mode 100644 index 00000000..f625fb08 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/consensus/ConsensusTest.java @@ -0,0 +1,463 @@ +package test.com.jd.blockchain.intgr.consensus; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.storage.service.DbConnection; +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitCommand; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.GatewayTestRunner; +import test.com.jd.blockchain.intgr.IntegratedContext; +import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.PeerTestRunner; +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; +import test.com.jd.blockchain.intgr.perf.LedgerInitTestConfiguration; +import test.com.jd.blockchain.intgr.perf.TransactionCommitter; +import test.com.jd.blockchain.intgr.perf.Utils; + +public class ConsensusTest { + + public static final int CONCURRENT_USER_COUNT = 2; + public static final int USER_TX_COUNT = 1; + + public static void main_(String[] args) { + // init ledgers of all nodes ; + IntegratedContext context = initLedgers(); + Node node0 = context.getNode(0); + Node node1 = context.getNode(1); + Node node2 = context.getNode(2); + Node node3 = context.getNode(3); + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 10200); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, node0.getBindingConfig(), node0.getStorageDB()); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 10210); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, node1.getBindingConfig(), node1.getStorageDB()); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 10220); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, node2.getBindingConfig(), node2.getStorageDB()); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 10230); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, node3.getBindingConfig(), node3.getStorageDB()); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(Utils.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(Utils.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(Utils.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway0 = new GatewayTestRunner("127.0.0.1", 10300, gwkey0, peerSrvAddr0); + + KeyPairConfig gwkey1 = new KeyPairConfig(); + gwkey1.setPubKeyValue(Utils.PUB_KEYS[1]); + gwkey1.setPrivKeyValue(Utils.PRIV_KEYS[1]); + gwkey1.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway1 = new GatewayTestRunner("127.0.0.1", 10310, gwkey1, peerSrvAddr1); + + AsyncCallback gwStarting0 = gateway0.start(); + AsyncCallback gwStarting1 = gateway1.start(); + + gwStarting0.waitReturn(); + gwStarting1.waitReturn(); + + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway0.getServiceAddress()); + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + + int batchSize = CONCURRENT_USER_COUNT * USER_TX_COUNT; + BlockchainKeyPair[] keys = generateKeys(batchSize); + + HashDigest ledgerHash = node0.getLedgerManager().getLedgerHashs()[0]; + LedgerRepository ledger = node0.getLedgerManager().getLedger(ledgerHash); + + PreparedTransaction[] ptxs = prepareTransactions_RegisterDataAcount(keys, node0.getPartiKeyPair(), ledgerHash, + blockchainService); + + LedgerBlock latestBlock = ledger.getLatestBlock(); + + ConsoleUtils.info("\r\n-----------------------------------------------"); + ConsoleUtils.info("------ 开始执行交易 [当前区块高度=%s] ------", latestBlock.getHeight()); + ConsoleUtils.info("-----------------------------------------------\r\n"); + long startTs = System.currentTimeMillis(); + + // for (PreparedTransaction ptx : ptxs) { + // ptx.commit(); + // } + + CyclicBarrier barrier = new CyclicBarrier(CONCURRENT_USER_COUNT); + CountDownLatch latch = new CountDownLatch(CONCURRENT_USER_COUNT); + for (int i = 0; i < CONCURRENT_USER_COUNT; i++) { + TransactionCommitter committer = new TransactionCommitter(ptxs, i * USER_TX_COUNT, USER_TX_COUNT); + committer.start(barrier, latch); + } + try { + latch.await(); + } catch (InterruptedException e) { + ConsoleUtils.error("Error occurred on waiting accomplishing all tx committing. --%s", e.getMessage()); + } + + long elapsedTs = System.currentTimeMillis() - startTs; + + latestBlock = ledger.retrieveLatestBlock(); + ConsoleUtils.info("全部交易执行完成! -- 当前区块高度=%s; 交易数=%s; 总耗时= %s ms; TPS=%.2f", latestBlock.getHeight(), batchSize, + elapsedTs, (batchSize * 1000.00D / elapsedTs)); + } + + private static BlockchainKeyPair[] generateKeys(int count) { + BlockchainKeyPair[] keys = new BlockchainKeyPair[count]; + for (int i = 0; i < count; i++) { + keys[i] = BlockchainKeyGenerator.getInstance().generate(); + } + return keys; + } + + private static PreparedTransaction[] prepareTransactions_RegisterDataAcount(BlockchainKeyPair[] userKeys, + CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService) { + PreparedTransaction[] ptxs = new PreparedTransaction[userKeys.length]; + for (int i = 0; i < ptxs.length; i++) { + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(userKeys[i].getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + ptxs[i] = ptx; + } + + return ptxs; + } + + public static ConsensusProvider getConsensusProvider() { + return ConsensusProviders.getProvider("com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + } + + private static IntegratedContext initLedgers() { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_integration(); + Properties props = Utils.loadConsensusSetting(); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeInitContext nodeCtx0 = new NodeInitContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeInitContext nodeCtx1 = new NodeInitContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeInitContext nodeCtx2 = new NodeInitContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeInitContext nodeCtx3 = new NodeInitContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[0], Utils.PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[1], Utils.PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[2], Utils.PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[3], Utils.PASSWORD); + + String encodedPassword = KeyGenCommand.encodePasswordAsBase58(Utils.PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri("memory://local/0"); + LedgerBindingConfig bindingConfig0 = new LedgerBindingConfig(); + AsyncCallback callback0 = nodeCtx0.startInitCommand(privkey0, encodedPassword, initSetting, csProps,csProvider, + testDb0, consolePrompter, bindingConfig0, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri("memory://local/1"); + LedgerBindingConfig bindingConfig1 = new LedgerBindingConfig(); + AsyncCallback callback1 = nodeCtx1.startInitCommand(privkey1, encodedPassword, initSetting, csProps,csProvider, + testDb1, consolePrompter, bindingConfig1, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri("memory://local/2"); + LedgerBindingConfig bindingConfig2 = new LedgerBindingConfig(); + AsyncCallback callback2 = nodeCtx2.startInitCommand(privkey2, encodedPassword, initSetting, csProps,csProvider, + testDb2, consolePrompter, bindingConfig2, quitLatch); + + DBConnectionConfig testDb3 = new DBConnectionConfig(); + testDb3.setConnectionUri("memory://local/3"); + LedgerBindingConfig bindingConfig3 = new LedgerBindingConfig(); + AsyncCallback callback3 = nodeCtx3.startInitCommand(privkey3, encodedPassword, initSetting, csProps,csProvider, + testDb3, consolePrompter, bindingConfig3, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + nodeCtx0.registLedger(ledgerHash0); + nodeCtx1.registLedger(ledgerHash1); + nodeCtx2.registLedger(ledgerHash2); + nodeCtx3.registLedger(ledgerHash3); + + IntegratedContext context = new IntegratedContext(); + + Node node0 = new Node(0); + node0.setConsensusSettings(csProps); + node0.setLedgerManager(nodeCtx0.getLedgerManager()); + node0.setStorageDB(nodeCtx0.getStorageDB()); + node0.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(0).getPubKey(), privkey0)); + node0.setBindingConfig(bindingConfig0); + context.addNode(node0); + + Node node1 = new Node(1); + node1.setConsensusSettings(csProps); + node1.setLedgerManager(nodeCtx1.getLedgerManager()); + node1.setStorageDB(nodeCtx1.getStorageDB()); + node1.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(1).getPubKey(), privkey1)); + node1.setBindingConfig(bindingConfig1); + context.addNode(node1); + + Node node2 = new Node(2); + node2.setConsensusSettings(csProps); + node2.setLedgerManager(nodeCtx2.getLedgerManager()); + node2.setStorageDB(nodeCtx2.getStorageDB()); + node2.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(2).getPubKey(), privkey2)); + node2.setBindingConfig(bindingConfig2); + context.addNode(node2); + + Node node3 = new Node(3); + node3.setConsensusSettings(csProps); + node3.setLedgerManager(nodeCtx3.getLedgerManager()); + node3.setStorageDB(nodeCtx3.getStorageDB()); + node3.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(3).getPubKey(), privkey3)); + node3.setBindingConfig(bindingConfig3); + context.addNode(node3); + + nodeCtx0.closeServer(); + nodeCtx1.closeServer(); + nodeCtx2.closeServer(); + nodeCtx3.closeServer(); + + return context; + } + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private static class NodeInitContext { + + private NetworkAddress serverAddress; + + private DBConnectionConfig dbConnConfig; + + private volatile ConfigurableApplicationContext ctx; + + private volatile LedgerInitProcess initProcess; + + private volatile LedgerInitializeWebController controller; + + private volatile LedgerManager ledgerManager; + + private volatile CompositeConnectionFactory db; + + private int id; + + public int getId() { + return controller.getId(); + } + + public TransactionContent getInitTxContent() { + return controller.getInitTxContent(); + } + + public LedgerInitPermission getLocalPermission() { + return controller.getLocalPermission(); + } + + public LedgerInitDecision getLocalDecision() { + return controller.getLocalDecision(); + } + + public NodeInitContext(int id, NetworkAddress serverAddress) { + this.id = id; + this.serverAddress = serverAddress; + } + + public LedgerRepository registLedger(HashDigest ledgerHash) { + // LedgerManage ledgerManager = ctx.getBean(LedgerManage.class); + // + // DbConnectionFactory dbConnFactory = ctx.getBean(DbConnectionFactory.class); + // DbConnection conn = dbConnFactory.connect(dbConnConfig.getUri(), + // dbConnConfig.getPassword()); + + DbConnection conn = db.connect(dbConnConfig.getUri(), dbConnConfig.getPassword()); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); + return ledgerRepo; + } + + public SignatureDigest createPermissionRequestSignature(int requesterId, PrivKey privKey) { + return controller.signPermissionRequest(requesterId, privKey); + } + + public AsyncCallback startInit(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, Prompter prompter, + CountDownLatch quitLatch) { + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + doStartServer(); + + // NodeWebContext.this.initProcess = ctx.getBean(LedgerInitProcess.class); + NodeInitContext.this.dbConnConfig = dbConnConfig; + HashDigest ledgerHash = NodeInitContext.this.initProcess.initialize(id, privKey, setting, csProps, csProvider, + dbConnConfig, prompter); + + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public AsyncCallback startInitCommand(PrivKey privKey, String base58Pwd, + LedgerInitProperties ledgerSetting, ConsensusSettings csProps, ConsensusProvider csProvider, + DBConnectionConfig dbConnConfig, Prompter prompter, LedgerBindingConfig conf, + CountDownLatch quitLatch) { + this.db = new CompositeConnectionFactory(); + this.dbConnConfig = dbConnConfig; + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + LedgerInitCommand initCmd = new LedgerInitCommand(); + HashDigest ledgerHash = initCmd.startInit(id, privKey, base58Pwd, ledgerSetting, csProps, + csProvider, dbConnConfig, prompter, conf, db); + NodeInitContext.this.ledgerManager = initCmd.getLedgerManager(); + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public LedgerInitPermission preparePermision(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps) { + return controller.prepareLocalPermission(id, privKey, setting, csProps); + } + + public boolean consensusPermission(PrivKey privKey) { + return controller.consensusPermisions(privKey); + } + + public LedgerInitDecision prepareLedger(DBConnectionConfig dbConnConfig, PrivKey privKey) { + controller.connectDb(dbConnConfig); + return controller.makeLocalDecision(privKey); + } + + public void startServer() { + ThreadInvoker invoker = new ThreadInvoker() { + + @Override + protected Object invoke() throws Exception { + doStartServer(); + return null; + } + }; + + invoker.startAndWait(); + } + + public void setPrompter(Prompter prompter) { + controller.setPrompter(prompter); + } + + public void doStartServer() { + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String nodebug = "--debug=false"; + String[] innerArgs = { argServerAddress, argServerPort, nodebug }; + + ctx = SpringApplication.run(LedgerInitTestConfiguration.class, innerArgs); + + ctx.setId("Node-" + id); + controller = ctx.getBean(LedgerInitializeWebController.class); + ledgerManager = ctx.getBean(LedgerManager.class); + db = ctx.getBean(CompositeConnectionFactory.class); + initProcess = ctx.getBean(LedgerInitProcess.class); + } + + public void closeServer() { + if (this.ctx != null) { + this.ctx.close(); + this.ctx = null; + } + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + // return ctx.getBean(LedgerManager.class); + } + + public CompositeConnectionFactory getStorageDB() { + return db; + // return ctx.getBean(MemoryBasedDb.class); + } + } +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/DBType.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/DBType.java new file mode 100644 index 00000000..e15078e8 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/DBType.java @@ -0,0 +1,11 @@ +package test.com.jd.blockchain.intgr.perf; + +public enum DBType { + + DEFAULT, + + REDIS, + + ROCKSDB + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/GlobalPerformanceTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/GlobalPerformanceTest.java new file mode 100644 index 00000000..a58acae7 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/GlobalPerformanceTest.java @@ -0,0 +1,475 @@ +package test.com.jd.blockchain.intgr.perf; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitCommand; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.GatewayTestRunner; +import test.com.jd.blockchain.intgr.IntegratedContext; +import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.PeerTestRunner; +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; + +public class GlobalPerformanceTest { + + public static final int CONCURRENT_USER_COUNT = 1; + public static final int USER_TX_COUNT = 1; + + public static void test(String[] args) { + // init ledgers of all nodes ; + IntegratedContext context = initLedgers(); + Node node0 = context.getNode(0); + Node node1 = context.getNode(1); + Node node2 = context.getNode(2); + Node node3 = context.getNode(3); + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 10200); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, node0.getBindingConfig(), node0.getStorageDB()); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 10210); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, node1.getBindingConfig(), node1.getStorageDB()); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 10220); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, node2.getBindingConfig(), node2.getStorageDB()); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 10230); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, node3.getBindingConfig(), node3.getStorageDB()); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(Utils.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(Utils.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(Utils.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway0 = new GatewayTestRunner("127.0.0.1", 10300, gwkey0, peerSrvAddr0); + + KeyPairConfig gwkey1 = new KeyPairConfig(); + gwkey1.setPubKeyValue(Utils.PUB_KEYS[1]); + gwkey1.setPrivKeyValue(Utils.PRIV_KEYS[1]); + gwkey1.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway1 = new GatewayTestRunner("127.0.0.1", 10310, gwkey1, peerSrvAddr1); + + AsyncCallback gwStarting0 = gateway0.start(); + AsyncCallback gwStarting1 = gateway1.start(); + + gwStarting0.waitReturn(); + gwStarting1.waitReturn(); + + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway0.getServiceAddress()); + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + + int batchSize = CONCURRENT_USER_COUNT * USER_TX_COUNT; + BlockchainKeyPair[] keys = generateKeys(batchSize); + + HashDigest ledgerHash = node0.getLedgerManager().getLedgerHashs()[0]; + LedgerRepository ledger = node0.getLedgerManager().getLedger(ledgerHash); + + PreparedTransaction[] ptxs = prepareTransactions_RegisterDataAcount(keys, node0.getPartiKeyPair(), ledgerHash, + blockchainService); + + LedgerBlock latestBlock = ledger.getLatestBlock(); + + ConsoleUtils.info("\r\n-----------------------------------------------"); + ConsoleUtils.info("------ 开始执行交易 [当前区块高度=%s] ------", latestBlock.getHeight()); + ConsoleUtils.info("-----------------------------------------------\r\n"); + long startTs = System.currentTimeMillis(); + + // for (PreparedTransaction ptx : ptxs) { + // ptx.commit(); + // } + + CyclicBarrier barrier = new CyclicBarrier(CONCURRENT_USER_COUNT); + CountDownLatch latch = new CountDownLatch(CONCURRENT_USER_COUNT); + for (int i = 0; i < CONCURRENT_USER_COUNT; i++) { + TransactionCommitter committer = new TransactionCommitter(ptxs, i * USER_TX_COUNT, USER_TX_COUNT); + committer.start(barrier, latch); + } + try { + latch.await(); + } catch (InterruptedException e) { + ConsoleUtils.error("Error occurred on waiting accomplishing all tx committing. --%s", e.getMessage()); + } + + long elapsedTs = System.currentTimeMillis() - startTs; + + latestBlock = ledger.retrieveLatestBlock(); + ConsoleUtils.info("全部交易执行完成! -- 当前区块高度=%s; 交易数=%s; 总耗时= %s ms; TPS=%.2f", latestBlock.getHeight(), batchSize, + elapsedTs, (batchSize * 1000.00D / elapsedTs)); + } + + private static BlockchainKeyPair[] generateKeys(int count) { + BlockchainKeyPair[] keys = new BlockchainKeyPair[count]; + for (int i = 0; i < count; i++) { + keys[i] = BlockchainKeyGenerator.getInstance().generate(); + } + return keys; + } + + private static PreparedTransaction[] prepareTransactions_RegisterDataAcount(BlockchainKeyPair[] userKeys, + CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService) { + PreparedTransaction[] ptxs = new PreparedTransaction[userKeys.length]; + for (int i = 0; i < ptxs.length; i++) { + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(userKeys[i].getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + ptxs[i] = ptx; + } + + return ptxs; + } + + public static ConsensusProvider getConsensusProvider() { + return ConsensusProviders.getProvider("com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + } + + private static IntegratedContext initLedgers() { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_integration(); + Properties props = Utils.loadConsensusSetting(); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeInitContext nodeCtx0 = new NodeInitContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeInitContext nodeCtx1 = new NodeInitContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeInitContext nodeCtx2 = new NodeInitContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeInitContext nodeCtx3 = new NodeInitContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[0], Utils.PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[1], Utils.PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[2], Utils.PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[3], Utils.PASSWORD); + + String encodedPassword = KeyGenCommand.encodePasswordAsBase58(Utils.PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri("memory://local/0"); + LedgerBindingConfig bindingConfig0 = new LedgerBindingConfig(); + AsyncCallback callback0 = nodeCtx0.startInitCommand(privkey0, encodedPassword, initSetting, csProps, + csProvider, testDb0, consolePrompter, bindingConfig0, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri("memory://local/1"); + LedgerBindingConfig bindingConfig1 = new LedgerBindingConfig(); + AsyncCallback callback1 = nodeCtx1.startInitCommand(privkey1, encodedPassword, initSetting, csProps, + csProvider, testDb1, consolePrompter, bindingConfig1, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri("memory://local/2"); + LedgerBindingConfig bindingConfig2 = new LedgerBindingConfig(); + AsyncCallback callback2 = nodeCtx2.startInitCommand(privkey2, encodedPassword, initSetting, csProps, + csProvider, testDb2, consolePrompter, bindingConfig2, quitLatch); + + DBConnectionConfig testDb3 = new DBConnectionConfig(); + testDb3.setConnectionUri("memory://local/3"); + LedgerBindingConfig bindingConfig3 = new LedgerBindingConfig(); + AsyncCallback callback3 = nodeCtx3.startInitCommand(privkey3, encodedPassword, initSetting, csProps, + csProvider, testDb3, consolePrompter, bindingConfig3, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + nodeCtx0.registLedger(ledgerHash0); + nodeCtx1.registLedger(ledgerHash1); + nodeCtx2.registLedger(ledgerHash2); + nodeCtx3.registLedger(ledgerHash3); + + IntegratedContext context = new IntegratedContext(); + + Node node0 = new Node(0); + node0.setConsensusSettings(csProps); + node0.setLedgerManager(nodeCtx0.getLedgerManager()); + node0.setStorageDB(nodeCtx0.getStorageDB()); + node0.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(0).getPubKey(), privkey0)); + node0.setBindingConfig(bindingConfig0); + context.addNode(node0); + + Node node1 = new Node(1); + node1.setConsensusSettings(csProps); + node1.setLedgerManager(nodeCtx1.getLedgerManager()); + node1.setStorageDB(nodeCtx1.getStorageDB()); + node1.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(1).getPubKey(), privkey1)); + node1.setBindingConfig(bindingConfig1); + context.addNode(node1); + + Node node2 = new Node(2); + node2.setConsensusSettings(csProps); + node2.setLedgerManager(nodeCtx2.getLedgerManager()); + node2.setStorageDB(nodeCtx2.getStorageDB()); + node2.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(2).getPubKey(), privkey2)); + node2.setBindingConfig(bindingConfig2); + context.addNode(node2); + + Node node3 = new Node(3); + node3.setConsensusSettings(csProps); + node3.setLedgerManager(nodeCtx3.getLedgerManager()); + node3.setStorageDB(nodeCtx3.getStorageDB()); + node3.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(3).getPubKey(), privkey3)); + node3.setBindingConfig(bindingConfig3); + context.addNode(node3); + + nodeCtx0.closeServer(); + nodeCtx1.closeServer(); + nodeCtx2.closeServer(); + nodeCtx3.closeServer(); + + return context; + } + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private static class NodeInitContext { + + private NetworkAddress serverAddress; + + private DBConnectionConfig dbConnConfig; + + private volatile ConfigurableApplicationContext ctx; + + private volatile LedgerInitProcess initProcess; + + private volatile LedgerInitializeWebController controller; + + private volatile LedgerManager ledgerManager; + + private volatile CompositeConnectionFactory db; + + private List conns = new ArrayList<>(); + + private int id; + + public int getId() { + return controller.getId(); + } + + public TransactionContent getInitTxContent() { + return controller.getInitTxContent(); + } + + public LedgerInitPermission getLocalPermission() { + return controller.getLocalPermission(); + } + + public LedgerInitDecision getLocalDecision() { + return controller.getLocalDecision(); + } + + public NodeInitContext(int id, NetworkAddress serverAddress) { + this.id = id; + this.serverAddress = serverAddress; + } + + public LedgerRepository registLedger(HashDigest ledgerHash) { + // LedgerManage ledgerManager = ctx.getBean(LedgerManage.class); + // + // DbConnectionFactory dbConnFactory = ctx.getBean(DbConnectionFactory.class); + // DbConnection conn = dbConnFactory.connect(dbConnConfig.getUri(), + // dbConnConfig.getPassword()); + + DbConnection conn = db.connect(dbConnConfig.getUri(), dbConnConfig.getPassword()); + conns.add(conn); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); + return ledgerRepo; + } + + public SignatureDigest createPermissionRequestSignature(int requesterId, PrivKey privKey) { + return controller.signPermissionRequest(requesterId, privKey); + } + + public AsyncCallback startInit(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, CountDownLatch quitLatch) { + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + doStartServer(); + + // NodeWebContext.this.initProcess = ctx.getBean(LedgerInitProcess.class); + NodeInitContext.this.dbConnConfig = dbConnConfig; + HashDigest ledgerHash = NodeInitContext.this.initProcess.initialize(id, privKey, setting, csProps, + csProvider, dbConnConfig, prompter); + + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public AsyncCallback startInitCommand(PrivKey privKey, String base58Pwd, + LedgerInitProperties ledgerSetting, ConsensusSettings csProps, ConsensusProvider csProvider, + DBConnectionConfig dbConnConfig, Prompter prompter, LedgerBindingConfig conf, + CountDownLatch quitLatch) { + this.db = new CompositeConnectionFactory(); + this.dbConnConfig = dbConnConfig; + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + LedgerInitCommand initCmd = new LedgerInitCommand(); + HashDigest ledgerHash = initCmd.startInit(id, privKey, base58Pwd, ledgerSetting, csProps, + csProvider, dbConnConfig, prompter, conf, db); + NodeInitContext.this.ledgerManager = initCmd.getLedgerManager(); + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public LedgerInitPermission preparePermision(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps) { + return controller.prepareLocalPermission(id, privKey, setting, csProps); + } + + public boolean consensusPermission(PrivKey privKey) { + return controller.consensusPermisions(privKey); + } + + public LedgerInitDecision prepareLedger(DBConnectionConfig dbConnConfig, PrivKey privKey) { + controller.connectDb(dbConnConfig); + return controller.makeLocalDecision(privKey); + } + + public void startServer() { + ThreadInvoker invoker = new ThreadInvoker() { + + @Override + protected Object invoke() throws Exception { + doStartServer(); + return null; + } + }; + + invoker.startAndWait(); + } + + public void setPrompter(Prompter prompter) { + controller.setPrompter(prompter); + } + + public void doStartServer() { + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String nodebug = "--debug=false"; + String[] innerArgs = { argServerAddress, argServerPort, nodebug }; + + ctx = SpringApplication.run(LedgerInitTestConfiguration.class, innerArgs); + + ctx.setId("Node-" + id); + controller = ctx.getBean(LedgerInitializeWebController.class); + ledgerManager = ctx.getBean(LedgerManager.class); + db = ctx.getBean(CompositeConnectionFactory.class); + initProcess = ctx.getBean(LedgerInitProcess.class); + } + + public void closeServer() { + try { + if (this.ctx != null) { + this.ctx.close(); + this.ctx = null; + } + } catch (Exception e1) { + // Ignore; + } + for (DbConnection conn : conns) { + try { + conn.close(); + } catch (Exception e) { + // Ignore; + } + } + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + // return ctx.getBean(LedgerManager.class); + } + + public CompositeConnectionFactory getStorageDB() { + return db; + // return ctx.getBean(MemoryBasedDb.class); + } + } +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitTestConfiguration.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitTestConfiguration.java new file mode 100644 index 00000000..235e4d4b --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitTestConfiguration.java @@ -0,0 +1,30 @@ +package test.com.jd.blockchain.intgr.perf; + +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; +import com.jd.blockchain.tools.initializer.web.InitWebSecurityConfiguration; +import com.jd.blockchain.tools.initializer.web.InitWebServerConfiguration; + +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +@Import(value = { InitWebServerConfiguration.class, InitWebSecurityConfiguration.class }) +public class LedgerInitTestConfiguration { + + @Bean + public MemoryDBConnFactory getStorageDB() { + return new MemoryDBConnFactory(); + } + +// @Bean +// public LedgerManager getLedgerManager() { +// return new LedgerManager(); +// } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitWebTestConfiguration.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitWebTestConfiguration.java new file mode 100644 index 00000000..4b513b74 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitWebTestConfiguration.java @@ -0,0 +1,29 @@ +package test.com.jd.blockchain.intgr.perf; + +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; +import com.jd.blockchain.tools.initializer.web.InitWebSecurityConfiguration; +import com.jd.blockchain.tools.initializer.web.InitWebServerConfiguration; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +@Import(value = { InitWebServerConfiguration.class, InitWebSecurityConfiguration.class }) +public class LedgerInitWebTestConfiguration { + + @Bean + public MemoryDBConnFactory getStorageDB() { + return new MemoryDBConnFactory(); + } + +// @Bean +// public LedgerManager getLedgerManager() { +// return new LedgerManager(); +// } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitializeTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitializeTest.java new file mode 100644 index 00000000..8c8018b0 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitializeTest.java @@ -0,0 +1,287 @@ +package test.com.jd.blockchain.intgr.perf; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.UserAccount; +import com.jd.blockchain.ledger.core.UserAccountSet; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.InitConsensusServiceFactory; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; + +public class LedgerInitializeTest { + + public static final String PASSWORD = "abc"; + + public static final String[] PUB_KEYS = { "endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna", + "endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ", + "endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R", + "endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR" }; + + public static final String[] PRIV_KEYS = { + "177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY", + "177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2", + "177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN", + "177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J" }; + + private Map serviceRegisterMap = new ConcurrentHashMap<>(); + + public static ConsensusProvider getConsensusProvider() { + return ConsensusProviders.getProvider("com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider"); + } + + public void testInitWith4Nodes() { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting(); + Properties props = loadConsensusSetting(); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + NodeContext node0 = new NodeContext(initSetting.getConsensusParticipant(0).getInitializerAddress(), + serviceRegisterMap); + NodeContext node1 = new NodeContext(initSetting.getConsensusParticipant(1).getInitializerAddress(), + serviceRegisterMap); + NodeContext node2 = new NodeContext(initSetting.getConsensusParticipant(2).getInitializerAddress(), + serviceRegisterMap); + NodeContext node3 = new NodeContext(initSetting.getConsensusParticipant(3).getInitializerAddress(), + serviceRegisterMap); + + String[] memoryConnString = new String[]{"memory://local/0", "memory://local/1", "memory://local/2", "memory://local/3"}; + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(memoryConnString[0]); + AsyncCallback callback0 = node0.startInit(0, privkey0, initSetting, csProps, csProvider, testDb0, + consolePrompter); + + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(memoryConnString[1]); + AsyncCallback callback1 = node1.startInit(1, privkey1, initSetting, csProps, csProvider, testDb1, + consolePrompter); + + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(memoryConnString[2]); + AsyncCallback callback2 = node2.startInit(2, privkey2, initSetting, csProps, csProvider, testDb2, + consolePrompter); + + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + DBConnectionConfig testDb03 = new DBConnectionConfig(); + testDb03.setConnectionUri(memoryConnString[3]); + AsyncCallback callback3 = node3.startInit(3, privkey3, initSetting, csProps, csProvider, testDb03, + consolePrompter); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + LedgerRepository ledger0 = node0.registLedger(ledgerHash0, memoryConnString[0]); + LedgerRepository ledger1 = node1.registLedger(ledgerHash1, memoryConnString[1]); + LedgerRepository ledger2 = node2.registLedger(ledgerHash2, memoryConnString[2]); + LedgerRepository ledger3 = node3.registLedger(ledgerHash3, memoryConnString[3]); + + LedgerBlock genesisBlock = ledger0.getLatestBlock(); + + UserAccountSet userset0 = ledger0.getUserAccountSet(genesisBlock); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + Bytes address0 = AddressEncoding.generateAddress(pubKey0); + UserAccount user0_0 = userset0.getUser(address0); + + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + Bytes address1 = AddressEncoding.generateAddress(pubKey1); + UserAccount user1_0 = userset0.getUser(address1); + + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + Bytes address2 = AddressEncoding.generateAddress(pubKey2); + UserAccount user2_0 = userset0.getUser(address2); + + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + Bytes address3 = AddressEncoding.generateAddress(pubKey3); + UserAccount user3_0 = userset0.getUser(address3); + } + + public static LedgerInitProperties loadInitSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web2.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("bftsmart.config"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static class NodeContext { + + private LedgerManager ledgerManager = new LedgerManager(); + + private MemoryDBConnFactory memoryDBConnFactory = new MemoryDBConnFactory(); +// private MemoryBasedDb storageDb = new MemoryBasedDb(); + + private InitConsensusServiceFactory initCsServiceFactory; + + private LedgerInitProcess initProcess; + + private CryptoKeyPair partiKey; + + public CryptoKeyPair getPartiKey() { + return partiKey; + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + } + + public MemoryDBConnFactory getMemoryDBConnFactory() { + return memoryDBConnFactory; + } + +// public MemoryBasedDb () { +// return storageDb; +// } + + public NodeContext(NetworkAddress address, Map serviceRegisterMap) { + this.initCsServiceFactory = new MultiThreadInterInvokerFactory(serviceRegisterMap); + LedgerInitializeWebController initController = new LedgerInitializeWebController(ledgerManager, memoryDBConnFactory, + initCsServiceFactory); + serviceRegisterMap.put(address, initController); + this.initProcess = initController; + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter) { + + partiKey = new CryptoKeyPair(setting.getConsensusParticipant(0).getPubKey(), privKey); + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + return initProcess.initialize(currentId, privKey, setting, csProps, csProvider, dbConnConfig, + prompter); + } + }; + + return invoker.start(); + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, boolean autoVerifyHash) { + + CryptoConfig cryptoSetting = new CryptoConfig(); + cryptoSetting.setAutoVerifyHash(autoVerifyHash); + cryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + + partiKey = new CryptoKeyPair(setting.getConsensusParticipant(0).getPubKey(), privKey); + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + return initProcess.initialize(currentId, privKey, setting, csProps, csProvider, dbConnConfig, + prompter, cryptoSetting); + } + }; + + return invoker.start(); + } + + public LedgerRepository registLedger(HashDigest ledgerHash, String connString) { + return ledgerManager.register(ledgerHash, memoryDBConnFactory.connect(connString).getStorageService()); + } + } + + private static class MultiThreadInterInvokerFactory implements InitConsensusServiceFactory { + + private Map nodeConsesusServices; + + public MultiThreadInterInvokerFactory(Map nodeConsesusServices) { + this.nodeConsesusServices = nodeConsesusServices; + } + + @Override + public LedgerInitConsensusService connect(NetworkAddress endpointAddress) { + return new InitConsensusServiceProxy(nodeConsesusServices.get(endpointAddress)); + } + + } + + private static class InitConsensusServiceProxy implements LedgerInitConsensusService { + + private LedgerInitConsensusService initCsService; + + public InitConsensusServiceProxy(LedgerInitConsensusService initCsService) { + this.initCsService = initCsService; + } + + @Override + public LedgerInitPermission requestPermission(int requesterId, SignatureDigest signature) { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected LedgerInitPermission invoke() { + return initCsService.requestPermission(requesterId, signature); + } + }; + return invoker.startAndWait(); + } + + @Override + public LedgerInitDecision synchronizeDecision(LedgerInitDecision initDecision) { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected LedgerInitDecision invoke() { + return initCsService.synchronizeDecision(initDecision); + } + }; + return invoker.startAndWait(); + } + + } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitializeWebTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitializeWebTest.java new file mode 100644 index 00000000..647f9024 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerInitializeWebTest.java @@ -0,0 +1,519 @@ +package test.com.jd.blockchain.intgr.perf; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.UserAccount; +import com.jd.blockchain.ledger.core.UserAccountSet; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.storage.service.DbConnection; +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitCommand; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.HttpInitConsensServiceFactory; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; + +public class LedgerInitializeWebTest { + + public static final String PASSWORD = LedgerInitializeTest.PASSWORD; + + public static final String[] PUB_KEYS = LedgerInitializeTest.PUB_KEYS; + + public static final String[] PRIV_KEYS = LedgerInitializeTest.PRIV_KEYS; + + /** + * 测试一个节点向多个节点请求新建许可的过程; + */ + public void testWithSingleSteps() { + Prompter prompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + + HttpInitConsensServiceFactory httpCsSrvFactory = new HttpInitConsensServiceFactory(); + + // 加载初始化配置; + LedgerInitProperties initSetting = loadInitSetting_1(); + // 加载共识配置; + Properties props = loadConsensusSetting(); +// ConsensusProperties csProps = new ConsensusProperties(props); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext node0 = new NodeWebContext(0, initAddr0); + node0.startServer(); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext node1 = new NodeWebContext(1, initAddr1); + node1.startServer(); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext node2 = new NodeWebContext(2, initAddr2); + node2.startServer(); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext node3 = new NodeWebContext(3, initAddr3); + node3.startServer(); + + node0.setPrompter(prompter); + node1.setPrompter(prompter); + node2.setPrompter(prompter); + node3.setPrompter(prompter); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + + // 测试生成“账本初始化许可”; + LedgerInitPermission permission0 = testPreparePermisssion(node0, privkey0, initSetting, csProps); + LedgerInitPermission permission1 = testPreparePermisssion(node1, privkey1, initSetting, csProps); + LedgerInitPermission permission2 = testPreparePermisssion(node2, privkey2, initSetting, csProps); + LedgerInitPermission permission3 = testPreparePermisssion(node3, privkey3, initSetting, csProps); + + TransactionContent initTxContent0 = node0.getInitTxContent(); + TransactionContent initTxContent1 = node1.getInitTxContent(); + TransactionContent initTxContent2 = node2.getInitTxContent(); + TransactionContent initTxContent3 = node3.getInitTxContent(); + + if (!initTxContent0.getHash().equals(initTxContent1.getHash())) { + Operation[] oplist0 = initTxContent0.getOperations(); + Operation[] oplist1 = initTxContent1.getOperations(); + + LedgerInitOperation initOp0 = (LedgerInitOperation) oplist0[0]; + LedgerInitOperation initOp1 = (LedgerInitOperation) oplist1[0]; + + byte[] initOpBytes0 = BinaryEncodingUtils.encode(initOp0, LedgerInitOperation.class); + byte[] initOpBytes1 = BinaryEncodingUtils.encode(initOp1, LedgerInitOperation.class); + + UserRegisterOperation regOp00 = (UserRegisterOperation) oplist0[1]; + UserRegisterOperation regOp10 = (UserRegisterOperation) oplist1[1]; + byte[] regOpBytes00 = BinaryEncodingUtils.encode(regOp00, UserRegisterOperation.class); + byte[] regOpBytes10 = BinaryEncodingUtils.encode(regOp10, UserRegisterOperation.class); + + UserRegisterOperation regOp01 = (UserRegisterOperation) oplist0[2]; + UserRegisterOperation regOp11 = (UserRegisterOperation) oplist1[2]; + byte[] regOpBytes01 = BinaryEncodingUtils.encode(regOp01, UserRegisterOperation.class); + byte[] regOpBytes11 = BinaryEncodingUtils.encode(regOp11, UserRegisterOperation.class); + + UserRegisterOperation regOp02 = (UserRegisterOperation) oplist0[3]; + UserRegisterOperation regOp12 = (UserRegisterOperation) oplist1[3]; + byte[] regOpBytes02 = BinaryEncodingUtils.encode(regOp02, UserRegisterOperation.class); + byte[] regOpBytes12 = BinaryEncodingUtils.encode(regOp12, UserRegisterOperation.class); + + UserRegisterOperation regOp03 = (UserRegisterOperation) oplist0[4]; + UserRegisterOperation regOp13 = (UserRegisterOperation) oplist1[4]; + byte[] regOpBytes03 = BinaryEncodingUtils.encode(regOp03, UserRegisterOperation.class); + byte[] regOpBytes13 = BinaryEncodingUtils.encode(regOp13, UserRegisterOperation.class); + + } + + // 测试请求“账本初始化许可”; + // test request permission, and verify the response; + LedgerInitConsensusService initCsService0 = httpCsSrvFactory.connect(initAddr0); + LedgerInitConsensusService initCsService1 = httpCsSrvFactory.connect(initAddr1); + LedgerInitConsensusService initCsService2 = httpCsSrvFactory.connect(initAddr2); + LedgerInitConsensusService initCsService3 = httpCsSrvFactory.connect(initAddr3); + + testRequestPermission(node0, privkey0, node1, initCsService1); + testRequestPermission(node0, privkey0, node2, initCsService2); + testRequestPermission(node0, privkey0, node3, initCsService3); + testRequestPermission(node1, privkey1, node0, initCsService0); + testRequestPermission(node1, privkey1, node2, initCsService2); + testRequestPermission(node1, privkey1, node3, initCsService3); + testRequestPermission(node2, privkey2, node0, initCsService0); + testRequestPermission(node2, privkey2, node1, initCsService1); + testRequestPermission(node2, privkey2, node3, initCsService3); + testRequestPermission(node3, privkey3, node0, initCsService0); + testRequestPermission(node3, privkey3, node1, initCsService1); + testRequestPermission(node3, privkey3, node2, initCsService2); + + // 测试在节点之间共识彼此的“账本初始化许可” + boolean allPermitted0 = node0.consensusPermission(privkey0); + boolean allPermitted1 = node1.consensusPermission(privkey1); + boolean allPermitted2 = node2.consensusPermission(privkey2); + boolean allPermitted3 = node3.consensusPermission(privkey3); + + // 测试生成账本,并创建“账本初始化决议”; + DBConnectionConfig testDb0 = new DBConnectionConfig("memory://local/0"); + DBConnectionConfig testDb1 = new DBConnectionConfig("memory://local/1"); + DBConnectionConfig testDb2 = new DBConnectionConfig("memory://local/2"); + DBConnectionConfig testDb3 = new DBConnectionConfig("memory://local/3"); + + LedgerInitDecision dec0 = node0.prepareLedger(testDb0, privkey0); + LedgerInitDecision dec1 = node1.prepareLedger(testDb1, privkey1); + LedgerInitDecision dec2 = node2.prepareLedger(testDb2, privkey2); + LedgerInitDecision dec3 = node3.prepareLedger(testDb3, privkey3); + + testRequestDecision(node0, node1, initCsService1); + testRequestDecision(node0, node2, initCsService2); + testRequestDecision(node0, node3, initCsService3); + testRequestDecision(node1, node0, initCsService0); + testRequestDecision(node1, node2, initCsService2); + testRequestDecision(node1, node3, initCsService3); + testRequestDecision(node2, node0, initCsService0); + testRequestDecision(node2, node1, initCsService1); + testRequestDecision(node2, node3, initCsService3); + testRequestDecision(node3, node0, initCsService0); + testRequestDecision(node3, node1, initCsService1); + testRequestDecision(node3, node2, initCsService2); + } + + private LedgerInitPermission testPreparePermisssion(NodeWebContext node, PrivKey privKey, + LedgerInitProperties setting, ConsensusSettings csProps) { + LedgerInitPermission permission = node.preparePermision(privKey, setting, csProps); + + return permission; + } + + private void testRequestPermission(NodeWebContext fromNode, PrivKey fromPrivkey, NodeWebContext targetNode, + LedgerInitConsensusService targetNodeService) { + SignatureDigest reqSignature = fromNode.createPermissionRequestSignature(fromNode.getId(), fromPrivkey); + LedgerInitPermission targetPermission = targetNodeService.requestPermission(fromNode.getId(), reqSignature); + } + + private void testRequestDecision(NodeWebContext fromNode, NodeWebContext targetNode, + LedgerInitConsensusService targetNodeService) { + LedgerInitDecision targetDecision = targetNodeService.synchronizeDecision(fromNode.getLocalDecision()); + } + + public SignatureDigest signPermissionRequest(int requesterId, PrivKey privKey, LedgerInitProperties initSetting) { + byte[] reqAuthBytes = BytesUtils.concat(BytesUtils.toBytes(requesterId), initSetting.getLedgerSeed()); + SignatureDigest reqAuthSign = CryptoUtils.sign(CryptoAlgorithm.ED25519).sign(privKey, reqAuthBytes); + return reqAuthSign; + } + + private static ConsensusProvider getConsensusProvider() { + return ConsensusProviders.getProvider("com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + } + + public void testInitWith4Nodes() { + System.out.println("----------- is daemon=" + Thread.currentThread().isDaemon()); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_2(); + Properties props = loadConsensusSetting(); +// ConsensusProperties csProps = new ConsensusProperties(props); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext node0 = new NodeWebContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext node1 = new NodeWebContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext node2 = new NodeWebContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext node3 = new NodeWebContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri("memory://local/0"); + AsyncCallback callback0 = node0.startInit(privkey0, initSetting, csProps, csProvider, testDb0, consolePrompter, + quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri("memory://local/1"); + AsyncCallback callback1 = node1.startInit(privkey1, initSetting, csProps, csProvider, testDb1, consolePrompter, + quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri("memory://local/2"); + AsyncCallback callback2 = node2.startInit(privkey2, initSetting, csProps,csProvider, testDb2, consolePrompter, + quitLatch); + + DBConnectionConfig testDb03 = new DBConnectionConfig(); + testDb03.setConnectionUri("memory://local/3"); + AsyncCallback callback3 = node3.startInit(privkey3, initSetting, csProps, csProvider, testDb03, consolePrompter, + quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + LedgerRepository ledger0 = node0.registLedger(ledgerHash0); + LedgerRepository ledger1 = node1.registLedger(ledgerHash1); + LedgerRepository ledger2 = node2.registLedger(ledgerHash2); + LedgerRepository ledger3 = node3.registLedger(ledgerHash3); + + LedgerBlock genesisBlock = ledger0.getLatestBlock(); + + UserAccountSet userset0 = ledger0.getUserAccountSet(genesisBlock); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + Bytes address0 = AddressEncoding.generateAddress(pubKey0); + UserAccount user0_0 = userset0.getUser(address0); + + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + Bytes address1 = AddressEncoding.generateAddress(pubKey1); + UserAccount user1_0 = userset0.getUser(address1); + + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + Bytes address2 = AddressEncoding.generateAddress(pubKey2); + UserAccount user2_0 = userset0.getUser(address2); + + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + Bytes address3 = AddressEncoding.generateAddress(pubKey3); + UserAccount user3_0 = userset0.getUser(address3); + } + + public static LedgerInitProperties loadInitSetting_1() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web1.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static LedgerInitProperties loadInitSetting_2() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web2.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("bftsmart.config"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static class NodeWebContext { + + private NetworkAddress serverAddress; + + private DBConnectionConfig dbConnConfig; + +// private MQConnectionConfig mqConnConfig; + + private volatile ConfigurableApplicationContext ctx; + + private volatile LedgerInitProcess initProcess; + + private volatile LedgerInitializeWebController controller; + + private volatile LedgerManager ledgerManager; + + private volatile CompositeConnectionFactory db; + + private int id; + + public int getId() { + return controller.getId(); + } + + public TransactionContent getInitTxContent() { + return controller.getInitTxContent(); + } + + public LedgerInitPermission getLocalPermission() { + return controller.getLocalPermission(); + } + + public LedgerInitDecision getLocalDecision() { + return controller.getLocalDecision(); + } + + public NodeWebContext(int id, NetworkAddress serverAddress) { + this.id = id; + this.serverAddress = serverAddress; + } + + public LedgerRepository registLedger(HashDigest ledgerHash) { + // LedgerManage ledgerManager = ctx.getBean(LedgerManage.class); + // + // DbConnectionFactory dbConnFactory = ctx.getBean(DbConnectionFactory.class); + // DbConnection conn = dbConnFactory.connect(dbConnConfig.getUri(), + // dbConnConfig.getPassword()); + + // DbConnection conn = db.connect(dbConnConfig.getUri(), + // dbConnConfig.getPassword()); + DbConnection conn = db.connect(dbConnConfig.getUri()); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); + return ledgerRepo; + } + + public SignatureDigest createPermissionRequestSignature(int requesterId, PrivKey privKey) { + return controller.signPermissionRequest(requesterId, privKey); + } + + public AsyncCallback startInit(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, CountDownLatch quitLatch) { + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + doStartServer(); + + // NodeWebContext.this.initProcess = ctx.getBean(LedgerInitProcess.class); + NodeWebContext.this.dbConnConfig = dbConnConfig; + HashDigest ledgerHash = NodeWebContext.this.initProcess.initialize(id, privKey, setting, csProps, + csProvider, dbConnConfig, prompter); + + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + // public AsyncCallback startInitCommand(PrivKey privKey, String + // base58Pwd, LedgerInitProperties ledgerSetting, ConsensusSettings csProps, + // DBConnectionConfig dbConnConfig, + // Prompter prompter, LedgerBindingConfig conf, CountDownLatch quitLatch) { + // return startInitCommand(privKey, base58Pwd, ledgerSetting, csProps, + // dbConnConfig, prompter, conf, quitLatch); + // } + + public AsyncCallback startInitCommand(PrivKey privKey, String base58Pwd, + LedgerInitProperties ledgerSetting, ConsensusSettings csProps, ConsensusProvider csProvider, + DBConnectionConfig dbConnConfig, Prompter prompter, LedgerBindingConfig conf, + CountDownLatch quitLatch) { + this.db = new CompositeConnectionFactory(); + this.dbConnConfig = dbConnConfig; + // this.mqConnConfig = mqConnConfig; + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + LedgerInitCommand initCmd = new LedgerInitCommand(); + HashDigest ledgerHash = initCmd.startInit(id, privKey, base58Pwd, ledgerSetting, csProps, + csProvider, dbConnConfig, prompter, conf, db); + NodeWebContext.this.ledgerManager = initCmd.getLedgerManager(); + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public LedgerInitPermission preparePermision(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps) { + return controller.prepareLocalPermission(id, privKey, setting, csProps); + } + + public boolean consensusPermission(PrivKey privKey) { + return controller.consensusPermisions(privKey); + } + + public LedgerInitDecision prepareLedger(DBConnectionConfig dbConnConfig, PrivKey privKey) { + controller.connectDb(dbConnConfig); + return controller.makeLocalDecision(privKey); + } + + public void startServer() { + ThreadInvoker invoker = new ThreadInvoker() { + + @Override + protected Object invoke() throws Exception { + doStartServer(); + return null; + } + }; + + invoker.startAndWait(); + } + + public void setPrompter(Prompter prompter) { + controller.setPrompter(prompter); + } + + public void doStartServer() { + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String nodebug = "--debug=false"; + String[] innerArgs = { argServerAddress, argServerPort, nodebug }; + + ctx = SpringApplication.run(LedgerInitWebTestConfiguration.class, innerArgs); + + ctx.setId("Node-" + id); + controller = ctx.getBean(LedgerInitializeWebController.class); + ledgerManager = ctx.getBean(LedgerManager.class); + db = ctx.getBean(CompositeConnectionFactory.class); + initProcess = ctx.getBean(LedgerInitProcess.class); + } + + public void closeServer() { + if (this.ctx != null) { + this.ctx.close(); + this.ctx = null; + } + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + // return ctx.getBean(LedgerManager.class); + } + + public CompositeConnectionFactory getStorageDB() { + return db; + } + } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerPerformanceTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerPerformanceTest.java new file mode 100644 index 00000000..81bfdf43 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/LedgerPerformanceTest.java @@ -0,0 +1,617 @@ +package test.com.jd.blockchain.intgr.perf; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; +import com.jd.blockchain.ledger.data.TxBuilder; +import com.jd.blockchain.ledger.service.TransactionBatchResultHandle; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.storage.service.impl.redis.JedisConnection; +import com.jd.blockchain.storage.service.impl.redis.RedisConnectionFactory; +import com.jd.blockchain.storage.service.impl.redis.RedisStorageService; +import com.jd.blockchain.storage.service.impl.rocksdb.RocksDBConnectionFactory; +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.ArgumentSet; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.springframework.core.io.ClassPathResource; +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; +import test.com.jd.blockchain.intgr.perf.Utils.NodeContext; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.DoubleStream; + +//import com.jd.blockchain.storage.service.utils.MemoryBasedDb; + +/** + * 账本性能测试;
+ * + * 可用的参数:
+ * -o: 优化模式执行;此时merkle树在加载时不做检查; + *

+ * + * -redis: 基于 redis 数据库进行测试;需要预先启动redis数据库,4个节点分别连接到本机 redis://127.0.0.1 的端口为 + * 6079、6179、6279、6379 的 4 个数据库实例; + *

+ * + * -rocksdb: 基于 rocksDB 进行测试;不需要预先配置数据库,4个节点连接到当前路径下的4个RocksDB数据库目录: + * rocksdb0.db、rocksdb1.db、rocksdb2.db、rocksdb3.db; + *

+ * + * -silent: 采用静默模式启动测试用例;否则会在准备就绪后等待输入任意键才正式开始测试,并在完成测试后等待输入任意键才退出; + * + * + * @author huanghaiquan + * + */ +public class LedgerPerformanceTest { + static { + DataContractRegistry.register(LedgerInitOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + } + + public static void test(String[] args) { + NodeContext[] nodes = null; + try { + boolean usertest = ArgumentSet.hasOption(args, "-usertest"); + boolean optimized = ArgumentSet.hasOption(args, "-o"); + boolean useRedis = ArgumentSet.hasOption(args, "-redis"); + boolean useRocksDB = ArgumentSet.hasOption(args, "-rocksdb"); + boolean silent = ArgumentSet.hasOption(args, "-silent"); + boolean contract = ArgumentSet.hasOption(args, "-contract"); + boolean mqConsensus = ArgumentSet.hasOption(args, "-mq"); + DBType dbType = DBType.DEFAULT; + if (useRedis) { + dbType = DBType.REDIS; + } + if (useRocksDB) { + dbType = DBType.ROCKSDB; + } + + CryptoAlgorithm hashAlg = ArgumentSet.hasOption(args, "-160") ? CryptoAlgorithm.RIPEMD160 + : CryptoAlgorithm.SHA256; + System.out.println( + String.format("----- LedgerPerformanceTest [HashAlgorithm=%s][DBType=%s] ----", hashAlg, dbType)); + // 初始化,并获取其中一个节点的账本,单独进行性能测试; + String provider = "com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"; + String config = "bftsmart.config"; + if (mqConsensus) { + provider = "com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider"; + config = "mq.config"; + } + nodes = initLedgers(optimized, hashAlg, dbType, provider, config); + + NodeContext testNode = nodes[0]; + LedgerManager ledgerManager = testNode.getLedgerManager(); + HashDigest ledgerHash = ledgerManager.getLedgerHashs()[0]; + + DefaultOperationHandleRegisteration opHandler = new DefaultOperationHandleRegisteration(); + + System.out.println("Ledger is ready!"); + + int batchSize = 1000; + int batchCount = 30; + + if (args.length > 0) { + if (!args[0].startsWith("-")) { + batchSize = Integer.parseInt(args[0]); + } + } + if (args.length > 1) { + if (!args[1].startsWith("-")) { + batchCount = Integer.parseInt(args[1]); + } + } + if (contract){ + testContract(ledgerHash, testNode.getPartiKey(), ledgerManager, opHandler, batchSize, batchCount, silent); + } + + if (usertest) { + testUserRegistering(ledgerHash, testNode.getPartiKey(), ledgerManager, opHandler, batchSize, batchCount, + silent); + } else { + testKVWrite(ledgerHash, testNode.getPartiKey(), ledgerManager, opHandler, batchSize, batchCount, + silent); + } + } catch (Exception e) { + System.out.println("----------- error [" + e.getMessage() + "]-----------"); + e.printStackTrace(); + } finally { + if (nodes != null) { + for (NodeContext node : nodes) { + node.getStorageDb().close(); + } + } + } + } + + /** + * 执行针对“注册用户”的性能测试; + * + * @param ledgerHash + * @param adminKey + * @param ledgerManager + * @param opHandler + * @param batchSize + * @param batchCount + * @param silent + */ + private static void testUserRegistering(HashDigest ledgerHash, CryptoKeyPair adminKey, LedgerManager ledgerManager, + DefaultOperationHandleRegisteration opHandler, int batchSize, int batchCount, boolean silent) { + LedgerRepository ledger = ledgerManager.getLedger(ledgerHash); + ConsoleUtils.info("\r\n\r\n================= 准备测试交易 [注册用户] ================="); + + int totalCount = batchSize * batchCount; + List txList = prepareUserRegisterRequests(ledgerHash, totalCount, adminKey); + + // 预热; + ConsoleUtils.info("preheat......"); + int preheatTxBatch = 10; + int preheatTxBatchSize = 10; + int preheatTotalTx = preheatTxBatch * preheatTxBatchSize; + List preheatTxList = prepareUserRegisterRequests(ledgerHash, preheatTotalTx, adminKey); + execPerformanceTest(preheatTxBatch, preheatTxBatchSize, preheatTxList, ledger, ledgerManager, opHandler, false); + preheatTxList.clear(); + preheatTxList = null; + + if (!silent) { + ConsoleUtils.confirm("\r\nTest is ready! Any key to continue..."); + } + + execPerformanceTest(batchCount, batchSize, txList, ledger, ledgerManager, opHandler, true); + + if (!silent) { + ConsoleUtils.confirm("\r\nTest completed! Any key to quit..."); + } + + } + + /** + * 执行针对“写入数据”的性能测试; + * + * @param ledgerHash + * @param adminKey + * @param ledgerManager + * @param opHandler + * @param batchSize + * @param batchCount + * @param silent + */ + private static void testKVWrite(HashDigest ledgerHash, CryptoKeyPair adminKey, LedgerManager ledgerManager, + DefaultOperationHandleRegisteration opHandler, int batchSize, int batchCount, boolean silent) { + LedgerRepository ledger = ledgerManager.getLedger(ledgerHash); + ConsoleUtils.info("\r\n\r\n================= 准备测试交易 [写入数据] ================="); + + // 创建数据账户; + BlockchainIdentity[] dataAccounts = new BlockchainIdentity[10]; + List dataAccountRegTxList = prepareDataAccountRegisterRequests(ledgerHash, dataAccounts, + adminKey, false); + execPerformanceTest(1, dataAccounts.length, dataAccountRegTxList, ledger, ledgerManager, opHandler, false); + + // 预热; + ConsoleUtils.info("preheat......"); + int preheatTxBatch = 10; + int preheatTxBatchSize = 10; + int preheatTotalTx = preheatTxBatch * preheatTxBatchSize; + List preheatTxList = prepareDataWriteRequests(ledgerHash, dataAccounts, preheatTotalTx, + adminKey, false); + execPerformanceTest(preheatTxBatch, preheatTxBatchSize, preheatTxList, ledger, ledgerManager, opHandler, false); + preheatTxList.clear(); + preheatTxList = null; + + // 准备正式数据; + int totalCount = batchSize * batchCount; + List txList = prepareDataWriteRequests(ledgerHash, dataAccounts, totalCount, adminKey, + false); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); + + if (!silent) { +// ConsoleUtils.confirm("\r\nTest is ready! Any key to continue..."); + consolePrompter.confirm("testKVWrite", "Test is ready! Any key to continue..."); + } + + execPerformanceTest(batchCount, batchSize, txList, ledger, ledgerManager, opHandler, true); + + if (!silent) { +// ConsoleUtils.confirm("\r\nTest completed! Any key to quit..."); + consolePrompter.confirm("testKVWrite", "Test completed! Any key to quit..."); + } + + } + + /** + * 执行针对“执行合约”的性能测试; + * + * @param ledgerHash + * @param adminKey + * @param ledgerManager + * @param opHandler + * @param batchSize + * @param batchCount + * @param silent + */ + private static void testContract(HashDigest ledgerHash, CryptoKeyPair adminKey, LedgerManager ledgerManager, + DefaultOperationHandleRegisteration opHandler, int batchSize, int batchCount, boolean silent) { + LedgerRepository ledger = ledgerManager.getLedger(ledgerHash); + ConsoleUtils.info("\r\n\r\n================= 准备测试交易 [执行合约] ================="); + + LedgerBlock latestBlock = ledger.getLatestBlock(); + LedgerDataSet previousDataSet = ledger.getDataSet(latestBlock); + LedgerEditor newEditor = ledger.createNextBlock(); + TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, + ledgerManager); + + // 准备请求 + int totalCount = batchSize * batchCount; + List contractTxList = prepareContractRequests(ledgerHash, + adminKey, totalCount, false, txProc); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); + + if (!silent) { +// ConsoleUtils.confirm("\r\nTest is ready! Any key to continue..."); + consolePrompter.confirm("testContract", "Test is ready! Any key to continue..."); + } + + execPerformanceTest(batchCount, batchSize, contractTxList, ledger, ledgerManager, opHandler, true); + + if (!silent) { +// ConsoleUtils.confirm("\r\nTest completed! Any key to quit..."); + consolePrompter.confirm("testContract", "Test completed! Any key to quit..."); + } + + } + private static void execPerformanceTest(int batchCount, int batchSize, List txList, + LedgerRepository ledger, LedgerManager ledgerManager, DefaultOperationHandleRegisteration opHandler, + boolean statistic) { + double[] tpss = new double[batchCount]; + long batchStartTs = System.currentTimeMillis(); + for (int i = 0; i < batchCount; i++) { + LedgerBlock latestBlock = ledger.getLatestBlock(); + LedgerDataSet previousDataSet = ledger.getDataSet(latestBlock); + if (statistic) { + ConsoleUtils.info("------ 开始执行交易, 即将生成区块[高度:%s] ------", (latestBlock.getHeight() + 1)); + } + long startTs = System.currentTimeMillis(); + + LedgerEditor newEditor = ledger.createNextBlock(); + TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, + ledgerManager); + + testTxExec(txList, i * batchSize, batchSize, txProc); + + if (statistic) { + long elapsedTs = System.currentTimeMillis() - startTs; + + tpss[i] = batchSize * 1000.00D / elapsedTs; + ConsoleUtils.info("新区块已生成! 交易数=%s; 总耗时= %s ms; TPS=%.2f", batchSize, elapsedTs, tpss[i]); + } + } + if (!statistic) { + return; + } + long batchElapsedTs = System.currentTimeMillis() - batchStartTs; + double globalTPS = batchSize * batchCount * 1000.00D / batchElapsedTs; + double avgTPS = DoubleStream.of(tpss).average().getAsDouble(); + double maxTPS = DoubleStream.of(tpss).max().getAsDouble(); + double variance = DoubleStream.of(tpss).reduce(0, (r, i) -> r + Math.pow(i - avgTPS, 2)) / tpss.length; + double stdDeviation = Math.sqrt(variance); + + ConsoleUtils.info("\r\n**********************************************"); + ConsoleUtils.info("区块数:%s; 交易总数:%s; 总体TPS:%.2f;\r\n单区块TPS均值:%.2f;单区块TPS峰值:%.2f;单区块TPS波动(标准差):%.2f", batchCount, + batchSize * batchCount, globalTPS, avgTPS, maxTPS, stdDeviation); + ConsoleUtils.info("**********************************************\r\n"); + } + + private static void testTxExec(List txList, int from, int count, + TransactionBatchProcessor txProc) { + for (int i = 0; i < count; i++) { + txProc.schedule(txList.get(from + i)); + } + TransactionBatchResultHandle handle = txProc.prepare(); + handle.commit(); + } + + public static List prepareUserRegisterRequests(HashDigest ledgerHash, int count, + CryptoKeyPair adminKey) { + long startTs = System.currentTimeMillis(); + List txList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + txbuilder.users().register(userKey.getIdentity()); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + txList.add(reqBuilder.buildRequest()); + } + long elapsedTs = System.currentTimeMillis() - startTs; + ConsoleUtils.info( + "============ Performance of Preparing user registering tx requests... TOTAL=%s; TPS=%.2f; TIME=%s millis ============", + count, (count * 1000.0 / elapsedTs), elapsedTs); + ConsoleUtils.info("====================================================="); + return txList; + } + + public static List prepareDataAccountRegisterRequests(HashDigest ledgerHash, + BlockchainIdentity[] dataAccounts, CryptoKeyPair adminKey, boolean statistic) { + int count = dataAccounts.length; + long startTs = System.currentTimeMillis(); + List txList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair dataAccountKey = BlockchainKeyGenerator.getInstance().generate(); + dataAccounts[i] = dataAccountKey.getIdentity(); + txbuilder.dataAccounts().register(dataAccounts[i]); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + txList.add(reqBuilder.buildRequest()); + } + if (statistic) { + long elapsedTs = System.currentTimeMillis() - startTs; + ConsoleUtils.info( + "============ Performance of Preparing data account registering tx requests... TOTAL=%s; TPS=%.2f; TIME=%s millis ============", + count, (count * 1000.0 / elapsedTs), elapsedTs); + ConsoleUtils.info("====================================================="); + } + return txList; + } + + public static List prepareDataWriteRequests(HashDigest ledgerHash, + BlockchainIdentity[] dataAccounts, int count, CryptoKeyPair adminKey, boolean statistic) { + long startTs = System.currentTimeMillis(); + List txList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + // BlockchainKeyPair dataAccountKey = + // BlockchainKeyGenerator.getInstance().generate(); + BlockchainIdentity targetAccount = dataAccounts[count % dataAccounts.length]; + txbuilder.dataAccount(targetAccount.getAddress()).set("key-" + startTs + "-" + i, + BytesUtils.toBytes("value-" + i), -1L); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + txList.add(reqBuilder.buildRequest()); + } + if (statistic) { + long elapsedTs = System.currentTimeMillis() - startTs; + ConsoleUtils.info( + "============ Performance of Preparing data account registering tx requests... TOTAL=%s; TPS=%.2f; TIME=%s millis ============", + count, (count * 1000.0 / elapsedTs), elapsedTs); + ConsoleUtils.info("====================================================="); + } + return txList; + } + + public static ConsensusProvider getConsensusProvider(String provider) { + return ConsensusProviders.getProvider(provider); + } + public static List prepareContractRequests(HashDigest ledgerHash, + CryptoKeyPair adminKey, int count, boolean statistic, TransactionBatchProcessor txProc) { + + // deploy contract + byte[] chainCode; + try { +// InputStream input = LedgerPerformanceTest.class.getClassLoader().getResourceAsStream("Setkv.contract"); + InputStream input = LedgerPerformanceTest.class.getClassLoader().getResourceAsStream("example1.jar"); + chainCode = new byte[input.available()]; + input.read(chainCode); + }catch (IOException e){ + e.printStackTrace(); + return null; + } + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair contractAccountKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainIdentity contractIdentity = contractAccountKey.getIdentity(); + txbuilder.contracts().deploy(contractIdentity, chainCode); + + // create data account + BlockchainKeyPair dataAccountKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainIdentity dataIdentity = dataAccountKey.getIdentity(); + + txbuilder.dataAccounts().register(dataIdentity); + + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + TransactionResponse resp = txProc.schedule(reqBuilder.buildRequest()); + System.out.println(resp.isSuccess()); + TransactionBatchResultHandle handle = txProc.prepare(); + handle.commit(); + try{ + + Thread.sleep(1000); + } catch (Exception e){ + e.printStackTrace(); + } + + long startTs = System.currentTimeMillis(); + List txList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + txbuilder = new TxBuilder(ledgerHash); + String args = dataIdentity.getAddress().toString() + "##"+Integer.toString(i)+ "##"+Integer.toString(i); + txbuilder.contractEvents().send(contractIdentity.getAddress(), "hello", args.getBytes()); +// txbuilder.contractEvents().send(contractIdentity.getAddress(), "print", args.getBytes()); + reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + txList.add(reqBuilder.buildRequest()); + } + if (statistic) { + long elapsedTs = System.currentTimeMillis() - startTs; + ConsoleUtils.info( + "============ Performance of Preparing contract execute tx requests... TOTAL=%s; TPS=%.2f; TIME=%s millis ============", + count, (count * 1000.0 / elapsedTs), elapsedTs); + ConsoleUtils.info("====================================================="); + } + return txList; + } + + public static NodeContext[] initLedgers(boolean optimized, CryptoAlgorithm hashAlg, DBType dbType, String provider, String config) { + Map serviceRegisterMap = new ConcurrentHashMap<>(); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting(); + Properties props = loadConsensusSetting(config); + ConsensusProvider csProvider = getConsensusProvider(provider); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + DBSetting dbsetting0; + DBSetting dbsetting1; + DBSetting dbsetting2; + DBSetting dbsetting3; + if (dbType == DBType.REDIS) { + dbsetting0 = DBSetting.createRedisDBSetting("redis://127.0.0.1:6079"); + dbsetting1 = DBSetting.createRedisDBSetting("redis://127.0.0.1:6179"); + dbsetting2 = DBSetting.createRedisDBSetting("redis://127.0.0.1:6279"); + dbsetting3 = DBSetting.createRedisDBSetting("redis://127.0.0.1:6379"); + + cleanRedisDB(dbsetting0); + cleanRedisDB(dbsetting1); + cleanRedisDB(dbsetting2); + cleanRedisDB(dbsetting3); + } else if (dbType == DBType.ROCKSDB) { + String currDir = FileUtils.getCurrentDir() + File.separator + "rocks.db"; + + String dbDir0 = new File(currDir, "rocksdb0.db").getAbsolutePath(); + String dbDir1 = new File(currDir, "rocksdb1.db").getAbsolutePath(); + String dbDir2 = new File(currDir, "rocksdb2.db").getAbsolutePath(); + String dbDir3 = new File(currDir, "rocksdb3.db").getAbsolutePath(); + + // clean db first; + FileUtils.deleteFile(dbDir0); + FileUtils.deleteFile(dbDir1); + FileUtils.deleteFile(dbDir2); + FileUtils.deleteFile(dbDir3); + + dbsetting0 = DBSetting.createRocksDBSetting("rocksdb://" + dbDir0); + dbsetting1 = DBSetting.createRocksDBSetting("rocksdb://" + dbDir1); + dbsetting2 = DBSetting.createRocksDBSetting("rocksdb://" + dbDir2); + dbsetting3 = DBSetting.createRocksDBSetting("rocksdb://" + dbDir3); + } else { + dbsetting0 = DBSetting.createMemoryDBSetting("memory://local/0"); + dbsetting1 = DBSetting.createMemoryDBSetting("memory://local/1"); + dbsetting2 = DBSetting.createMemoryDBSetting("memory://local/2"); + dbsetting3 = DBSetting.createMemoryDBSetting("memory://local/3"); + } + + NodeContext node0 = new NodeContext(initSetting.getConsensusParticipant(0).getInitializerAddress(), + serviceRegisterMap, dbsetting0.connectionFactory); + NodeContext node1 = new NodeContext(initSetting.getConsensusParticipant(1).getInitializerAddress(), + serviceRegisterMap, dbsetting1.connectionFactory); + NodeContext node2 = new NodeContext(initSetting.getConsensusParticipant(2).getInitializerAddress(), + serviceRegisterMap, dbsetting2.connectionFactory); + NodeContext node3 = new NodeContext(initSetting.getConsensusParticipant(3).getInitializerAddress(), + serviceRegisterMap, dbsetting3.connectionFactory); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[0], Utils.PASSWORD); + AsyncCallback callback0 = node0.startInit(0, privkey0, initSetting, csProps, csProvider, + dbsetting0.connectionConfig, consolePrompter, !optimized, hashAlg); + + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[1], Utils.PASSWORD); + AsyncCallback callback1 = node1.startInit(1, privkey1, initSetting, csProps, csProvider, + dbsetting1.connectionConfig, consolePrompter, !optimized, hashAlg); + + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[2], Utils.PASSWORD); + AsyncCallback callback2 = node2.startInit(2, privkey2, initSetting, csProps, csProvider, + dbsetting2.connectionConfig, consolePrompter, !optimized, hashAlg); + + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(Utils.PRIV_KEYS[3], Utils.PASSWORD); + AsyncCallback callback3 = node3.startInit(3, privkey3, initSetting, csProps, csProvider, + dbsetting3.connectionConfig, consolePrompter, !optimized, hashAlg); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + node0.registLedger(ledgerHash0, dbsetting0.connectionConfig); + node1.registLedger(ledgerHash1, dbsetting1.connectionConfig); + node2.registLedger(ledgerHash2, dbsetting2.connectionConfig); + node3.registLedger(ledgerHash3, dbsetting3.connectionConfig); + + return new NodeContext[] { node0, node1, node2, node3 }; + } + + private static void cleanRedisDB(DBSetting dbsetting) { + RedisConnectionFactory redisConnFactory = (RedisConnectionFactory) dbsetting.connectionFactory; + JedisConnection dbConn = (JedisConnection) redisConnFactory.connect(dbsetting.connectionConfig.getUri()); + RedisStorageService redisStorage = (RedisStorageService) dbConn.getStorageService(); + redisStorage.clearDB(); + dbConn.close(); + } + + public static LedgerInitProperties loadInitSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web2.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting(String config) { + ClassPathResource ledgerInitSettingResource = new ClassPathResource(config); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static class DBSetting { + + private DBConnectionConfig connectionConfig; + + private DbConnectionFactory connectionFactory; + + public static DBSetting createMemoryDBSetting(String uri) { + DBSetting setting = new DBSetting(); + setting.connectionConfig = new DBConnectionConfig(uri); + setting.connectionFactory = new MemoryDBConnFactory(); + return setting; + } + + public static DBSetting createRedisDBSetting(String uri) { + DBSetting setting = new DBSetting(); + setting.connectionConfig = new DBConnectionConfig(uri); + setting.connectionFactory = new RedisConnectionFactory(); + return setting; + } + + public static DBSetting createRocksDBSetting(String uri) { + DBSetting setting = new DBSetting(); + setting.connectionConfig = new DBConnectionConfig(uri); + setting.connectionFactory = new RocksDBConnectionFactory(); + return setting; + } + + } +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/PerformanceTest.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/PerformanceTest.java new file mode 100644 index 00000000..38eba982 --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/PerformanceTest.java @@ -0,0 +1,134 @@ +package test.com.jd.blockchain.intgr.perf; + +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.KVStorageService; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.impl.redis.RedisConnectionFactory; +import com.jd.blockchain.utils.ArgumentSet; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.security.ShaUtils; + +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.ForkJoinPool; +import java.util.concurrent.ForkJoinTask; +import java.util.concurrent.RecursiveAction; + +public class PerformanceTest { + + public static void main(String[] args) { + try { + boolean testLedger = !ArgumentSet.hasOption(args, "-test=storage"); + if (testLedger) { + LedgerPerformanceTest.test(new String[]{"-silent", "-contract", "-o"}); + return; + } + + // GlobalPerformanceTest.test(args); + +// testRedisWriting(args); + } catch (Exception e) { + e.printStackTrace(); + }finally { + ForkJoinPool.commonPool().shutdown(); + } + } + + private static void testRedisWriting(String[] args) { + ConsoleUtils.info("-------------------- start redis test --------------------"); + RedisConnectionFactory redisConnFactory = new RedisConnectionFactory(); + DbConnection conn = redisConnFactory.connect("redis://127.0.0.1:6079"); + KVStorageService storage = conn.getStorageService(); + VersioningKVStorage vs = storage.getVersioningKVStorage(); + + byte[] data = BytesUtils.toBytes("TestDATA"); + + int count = 100000; + if (args.length > 0) { + count = Integer.parseInt(args[0]); + } + if (args.length > 0) { + for (String arg : args) { + if (arg.startsWith("-threshold=")) { + int threshold = Integer.parseInt(arg.substring("-threshold=".length())); + if (threshold > 0) { + RedisWriteTestTask.THRESHOLD = threshold; + } + } + } + } + + Random rand = new Random(); + byte[] nameBytes = new byte[16]; + rand.nextBytes(nameBytes); + String name = Base58Utils.encode(ShaUtils.hash_256(nameBytes)); + rand.nextBytes(nameBytes); + name = name + "/" + Base58Utils.encode(ShaUtils.hash_256(nameBytes)); + rand.nextBytes(nameBytes); + name = name + "/" + Base58Utils.encode(ShaUtils.hash_256(nameBytes)); + + long startTS = System.currentTimeMillis(); + + RedisWriteTestTask task = new RedisWriteTestTask(name, 0, count, data, vs); + ForkJoinPool.commonPool().invoke(task); + + long elapsedTS = System.currentTimeMillis() - startTS; + + double globalTPS = count * 1000.00D / elapsedTS; + + ConsoleUtils.info("\r\n**********************************************"); + ConsoleUtils.info("写入KEY总数:%s; 总体TPS:%.2f;", count, globalTPS); + ConsoleUtils.info("**********************************************\r\n"); + + try { + conn.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public static class RedisWriteTestTask extends RecursiveAction { + + private static final long serialVersionUID = -8415596564326385834L; + + public static int THRESHOLD = 800; + + private int offset; + + private int count; + + private byte[] data; + + private String name; + + private VersioningKVStorage vs; + + public RedisWriteTestTask(String name, int offset, int count, byte[] data, VersioningKVStorage vs) { + this.name = name; + this.offset = offset; + this.count = count; + this.data = data; + this.vs = vs; + } + + @Override + protected void compute() { + if (count > THRESHOLD) { + int count1 = count / 2; + RedisWriteTestTask task1 = new RedisWriteTestTask(name, offset, count1, data, vs); + RedisWriteTestTask task2 = new RedisWriteTestTask(name, offset + count1, count - count1, data, vs); + ForkJoinTask.invokeAll(task1, task2); + } else { + for (int i = 0; i < count; i++) { + String key = String.format("[%s][%s]-TEST-KEY-%s", name, offset, i); + vs.set(Bytes.fromString(key), data, -1); + } + } + } + + } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/TransactionCommitter.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/TransactionCommitter.java new file mode 100644 index 00000000..5f66263d --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/TransactionCommitter.java @@ -0,0 +1,46 @@ +package test.com.jd.blockchain.intgr.perf; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.CyclicBarrier; + +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.utils.ConsoleUtils; + +public class TransactionCommitter { + + private PreparedTransaction[] ptxs; + private int startIndex; + private int count; + + public TransactionCommitter(PreparedTransaction[] ptxs, int startIndex, int count) { + this.ptxs = ptxs; + this.startIndex = startIndex; + this.count = count; + } + + public void start(CyclicBarrier barrier, CountDownLatch latch) { + Thread thrd = new Thread(new Runnable() { + @Override + public void run() { + try { + barrier.await(); + } catch (Exception e) { + System.out.println(" Barrier await error! --" + e.getMessage()); + e.printStackTrace(); + } + ConsoleUtils.info("Start committing... [%s]", startIndex); + try { + for (int i = 0; i < count; i++) { + ptxs[startIndex + i].commit(); + } + } catch (Exception e) { + System.out.println("Error occured on committing! --" + e.getMessage()); + e.printStackTrace(); + } + latch.countDown(); + } + }); + thrd.start(); + } + +} diff --git a/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/Utils.java b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/Utils.java new file mode 100644 index 00000000..c54680df --- /dev/null +++ b/source/test/test-integration/src/main/java/test/com/jd/blockchain/intgr/perf/Utils.java @@ -0,0 +1,211 @@ +package test.com.jd.blockchain.intgr.perf; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.InitConsensusServiceFactory; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class Utils { + + public static final String PASSWORD = "abc"; + + public static final String[] PUB_KEYS = { "endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna", + "endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ", + "endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R", + "endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR" }; + + public static final String[] PRIV_KEYS = { + "177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY", + "177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2", + "177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN", + "177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J" }; + + private Map serviceRegisterMap = new ConcurrentHashMap<>(); + + public static LedgerInitProperties loadInitSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web2.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("bftsmart.config"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static class NodeContext { + + private LedgerManager ledgerManager = new LedgerManager(); + + private DbConnectionFactory dbConnFactory; + + private InitConsensusServiceFactory initCsServiceFactory; + + private LedgerInitProcess initProcess; + + private CryptoKeyPair partiKey; + + public CryptoKeyPair getPartiKey() { + return partiKey; + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + } + + public DbConnectionFactory getStorageDb() { + return dbConnFactory; + } + + public NodeContext(NetworkAddress address, Map serviceRegisterMap, + DbConnectionFactory dbConnFactory) { + this.dbConnFactory = dbConnFactory; + this.initCsServiceFactory = new MultiThreadInterInvokerFactory(serviceRegisterMap); + LedgerInitializeWebController initController = new LedgerInitializeWebController(ledgerManager, + dbConnFactory, initCsServiceFactory); + serviceRegisterMap.put(address, initController); + this.initProcess = initController; + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider consensusProvider, DBConnectionConfig dbConnConfig, + Prompter prompter) { + + partiKey = new CryptoKeyPair(setting.getConsensusParticipant(0).getPubKey(), privKey); + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + return initProcess.initialize(currentId, privKey, setting, csProps, consensusProvider, dbConnConfig, + prompter); + } + }; + + return invoker.start(); + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider consensusProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, boolean autoVerifyHash) { + return startInit(currentId, privKey, setting, csProps, consensusProvider, dbConnConfig, prompter, + autoVerifyHash, CryptoAlgorithm.SHA256); + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider consensusProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, boolean autoVerifyHash, CryptoAlgorithm hashAlg) { + CryptoConfig cryptoSetting = new CryptoConfig(); + cryptoSetting.setAutoVerifyHash(autoVerifyHash); + cryptoSetting.setHashAlgorithm(hashAlg); + + return startInit(currentId, privKey, setting, csProps, consensusProvider, dbConnConfig, prompter, + cryptoSetting); + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider consensusProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, CryptoSetting cryptoSetting) { + + partiKey = new CryptoKeyPair(setting.getConsensusParticipant(0).getPubKey(), privKey); + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + return initProcess.initialize(currentId, privKey, setting, csProps, consensusProvider, dbConnConfig, + prompter, cryptoSetting); + } + }; + + return invoker.start(); + } + + public LedgerRepository registLedger(HashDigest ledgerHash, DBConnectionConfig dbConnConf) { + return ledgerManager.register(ledgerHash, dbConnFactory.connect(dbConnConf.getUri()).getStorageService()); + } + } + + private static class MultiThreadInterInvokerFactory implements InitConsensusServiceFactory { + + private Map nodeConsesusServices; + + public MultiThreadInterInvokerFactory(Map nodeConsesusServices) { + this.nodeConsesusServices = nodeConsesusServices; + } + + @Override + public LedgerInitConsensusService connect(NetworkAddress endpointAddress) { + return new InitConsensusServiceProxy(nodeConsesusServices.get(endpointAddress)); + } + + } + + private static class InitConsensusServiceProxy implements LedgerInitConsensusService { + + private LedgerInitConsensusService initCsService; + + public InitConsensusServiceProxy(LedgerInitConsensusService initCsService) { + this.initCsService = initCsService; + } + + @Override + public LedgerInitPermission requestPermission(int requesterId, SignatureDigest signature) { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected LedgerInitPermission invoke() { + return initCsService.requestPermission(requesterId, signature); + } + }; + return invoker.startAndWait(); + } + + @Override + public LedgerInitDecision synchronizeDecision(LedgerInitDecision initDecision) { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected LedgerInitDecision invoke() { + return initCsService.synchronizeDecision(initDecision); + } + }; + return invoker.startAndWait(); + } + + } + +} diff --git a/source/test/test-integration/src/main/resources/Performance.contract b/source/test/test-integration/src/main/resources/Performance.contract new file mode 100644 index 00000000..ff4bd63d Binary files /dev/null and b/source/test/test-integration/src/main/resources/Performance.contract differ diff --git a/source/test/test-integration/src/main/resources/example1.jar b/source/test/test-integration/src/main/resources/example1.jar new file mode 100644 index 00000000..7b76b919 Binary files /dev/null and b/source/test/test-integration/src/main/resources/example1.jar differ diff --git a/source/test/test-integration/src/main/resources/ledger_init_test.init b/source/test/test-integration/src/main/resources/ledger_init_test.init new file mode 100644 index 00000000..b936de8c --- /dev/null +++ b/source/test/test-integration/src/main/resources/ledger_init_test.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=false +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=8800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=8810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=8820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=8830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/main/resources/ledger_init_test_integration.init b/source/test/test-integration/src/main/resources/ledger_init_test_integration.init new file mode 100644 index 00000000..7744270f --- /dev/null +++ b/source/test/test-integration/src/main/resources/ledger_init_test_integration.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=false +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=10100 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=10110 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=10120 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=10130 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/main/resources/mq.config b/source/test/test-integration/src/main/resources/mq.config new file mode 100644 index 00000000..730b7acc --- /dev/null +++ b/source/test/test-integration/src/main/resources/mq.config @@ -0,0 +1,12 @@ +system.msg.queue.server=nats://127.0.0.1:4222 +system.msg.queue.topic.tx=tx-topic +system.msg.queue.topic.bl=bl-topic +system.msg.queue.topic.msg=msg-topic +system.msg.queue.block.txsize=1000 +system.msg.queue.block.maxdelay=2000 + +system.servers.num=4 +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR \ No newline at end of file diff --git a/source/test/test-integration/src/main/resources/system.config b/source/test/test-integration/src/main/resources/system.config new file mode 100644 index 00000000..407d70ca --- /dev/null +++ b/source/test/test-integration/src/main/resources/system.config @@ -0,0 +1,121 @@ +# 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 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 = 4000 + + +#Maximum batch size (in number of messages) +system.totalordermulticast.maxbatchsize = 10000 + +#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 = 1 + +#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 = 4000 +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 = false + +#Custom View Storage; +#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java new file mode 100644 index 00000000..6b8476f3 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBase.java @@ -0,0 +1,553 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.intgr.perf.IntegrationBase + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/25 下午3:40 + * Description: + */ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.apache.commons.io.FileUtils; +import org.junit.Assert; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicLong; + +import static org.junit.Assert.*; + + +/** + * + * @author shaozhuguang + * @create 2018/12/25 + * @since 1.0.0 + */ + +public class IntegrationBase { + + static { + DataContractRegistry.register(LedgerInitOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + } + + public static final String PASSWORD = "abc"; + + public static final String[] PUB_KEYS = { "endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna", + "endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ", + "endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R", + "endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR" }; + + public static final String[] PRIV_KEYS = { + "177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY", + "177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2", + "177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN", + "177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J" }; + + public static final AtomicLong validLong = new AtomicLong(); + + public static KeyPairResponse testSDK_RegisterUser(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService) { + // 注册用户,并验证最终写入; + BlockchainKeyPair user = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(user.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + + HashDigest transactionHash = ptx.getHash(); + + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + KeyPairResponse keyPairResponse = new KeyPairResponse(); + keyPairResponse.keyPair = user; + keyPairResponse.txResp = txResp; + keyPairResponse.txHash = transactionHash; + return keyPairResponse; + } + + public static KeyPairResponse testSDK_RegisterDataAccount(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService) { + // 注册数据账户,并验证最终写入; + BlockchainKeyPair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.dataAccounts().register(dataAccount.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + + HashDigest transactionHash = ptx.getHash(); + + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + KeyPairResponse keyPairResponse = new KeyPairResponse(); + keyPairResponse.keyPair = dataAccount; + keyPairResponse.txResp = txResp; + keyPairResponse.txHash = transactionHash; + return keyPairResponse; + } + + public static KvResponse testSDK_InsertData(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, + Bytes dataAccount) { + + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = blockchainService.newTransaction(ledgerHash); + + // -------------------------------------- + // 将商品信息写入到指定的账户中; + // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; + String dataKey = "jingdong" + System.currentTimeMillis() + new Random().nextInt(100000); + byte[] dataVal = "www.jd.com".getBytes(); + + txTemp.dataAccount(dataAccount).set(dataKey, dataVal, -1); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + HashDigest transactionHash = prepTx.getHash(); + + // 使用私钥进行签名; + prepTx.sign(adminKey); + + // 提交交易; + TransactionResponse txResp = prepTx.commit(); + + KvResponse kvResponse = new KvResponse(); + kvResponse.ledgerHash = ledgerHash; + kvResponse.dataAccount = dataAccount; + kvResponse.txResp = txResp; + kvResponse.txHash = transactionHash; + kvResponse.key = dataKey; + kvResponse.value = dataVal; + return kvResponse; + } + + public static void testSDK_InsertData_morePage(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, + Bytes dataAccount) { + + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = blockchainService.newTransaction(ledgerHash); + + // -------------------------------------- + // 将商品信息写入到指定的账户中; + // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; + for(int i=0;i<12;i++){ + String dataKey = "jingdong" + System.currentTimeMillis() + new Random().nextInt(100000); + byte[] dataVal = "www.jd.com".getBytes(); + txTemp.dataAccount(dataAccount).set(dataKey, dataVal, -1); + } + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + // 使用私钥进行签名; + prepTx.sign(adminKey); + // 提交交易; + prepTx.commit(); + } + + public static void validKeyPair(IntegrationBase.KeyPairResponse keyPairResponse, LedgerRepository ledgerRepository, KeyPairType keyPairType) { + TransactionResponse txResp = keyPairResponse.txResp; + HashDigest transactionHash = keyPairResponse.txHash; + BlockchainKeyPair keyPair = keyPairResponse.keyPair; + long index = validLong.incrementAndGet(); + System.out.printf("validKeyPair start %s \r\n", index); + ledgerRepository.retrieveLatestBlock(); + + assertEquals(txResp.getExecutionState(), TransactionState.SUCCESS); + assertEquals(txResp.getBlockHeight(), ledgerRepository.getLatestBlockHeight()); + assertEquals(txResp.getContentHash(), transactionHash); + assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); + if (keyPairType == KeyPairType.USER) { + assertTrue(ledgerRepository.getUserAccountSet(ledgerRepository.getLatestBlock()).contains(keyPair.getAddress())); + } + + if (keyPairType == KeyPairType.DATAACCOUNT) { + assertNotNull(ledgerRepository.getDataAccountSet(ledgerRepository.getLatestBlock()) + .getDataAccount(keyPair.getAddress())); + } + System.out.printf("validKeyPair end %s \r\n", index); + } + + public static void validKeyPair(IntegrationBase.KeyPairResponse keyPairResponse, LedgerRepository ledgerRepository, KeyPairType keyPairType, CountDownLatch countDownLatch) { + + TransactionResponse txResp = keyPairResponse.txResp; + HashDigest transactionHash = keyPairResponse.txHash; + BlockchainKeyPair keyPair = keyPairResponse.keyPair; + ledgerRepository.retrieveLatestBlock(); + + assertEquals(txResp.getExecutionState(), TransactionState.SUCCESS); + assertEquals(txResp.getBlockHeight(), ledgerRepository.getLatestBlockHeight()); + assertEquals(txResp.getContentHash(), transactionHash); + assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); + if (keyPairType == KeyPairType.USER) { + assertTrue(ledgerRepository.getUserAccountSet(ledgerRepository.getLatestBlock()).contains(keyPair.getAddress())); + } + + if (keyPairType == KeyPairType.DATAACCOUNT) { + assertNotNull(ledgerRepository.getDataAccountSet(ledgerRepository.getLatestBlock()) + .getDataAccount(keyPair.getAddress())); + } + countDownLatch.countDown(); + } + + public static void validKvWrite(IntegrationBase.KvResponse kvResponse, LedgerRepository ledgerRepository, BlockchainService blockchainService) { + // 先验证应答 + TransactionResponse txResp = kvResponse.getTxResp(); + HashDigest transactionHash = kvResponse.getTxHash(); + HashDigest ledgerHash = kvResponse.getLedgerHash(); + String daAddress = kvResponse.getDataAccount().toBase58(); + String dataKey = kvResponse.getKey(); + byte[] dataVal = kvResponse.getValue(); + + ledgerRepository.retrieveLatestBlock(); + + assertEquals(TransactionState.SUCCESS, txResp.getExecutionState()); + assertEquals(txResp.getBlockHeight(), ledgerRepository.getLatestBlockHeight()); + assertEquals(txResp.getContentHash(), transactionHash); + assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); + + KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, daAddress, dataKey); + for (KVDataEntry kvDataEntry : kvDataEntries) { + assertEquals(dataKey, kvDataEntry.getKey()); + String valHexText = (String) kvDataEntry.getValue(); + byte[] valBytes = HexUtils.decode(valHexText); + boolean isEqual = Arrays.equals(dataVal, valBytes); + assertTrue(isEqual); + } + } + + + public static LedgerRepository[] buildLedgers(LedgerBindingConfig[] bindingConfigs, DbConnectionFactory[] dbConnectionFactories){ + int[] ids = {0, 1, 2, 3}; + LedgerRepository[] ledgers = new LedgerRepository[ids.length]; + LedgerManager[] ledgerManagers = new LedgerManager[ids.length]; + for (int i = 0; i < ids.length; i++) { + ledgerManagers[i] = new LedgerManager(); + HashDigest ledgerHash = bindingConfigs[0].getLedgerHashs()[0]; + DbConnection conn = dbConnectionFactories[i].connect(bindingConfigs[i].getLedger(ledgerHash).getDbConnection().getUri()); + ledgers[i] = ledgerManagers[i].register(ledgerHash, conn.getStorageService()); + } + return ledgers; + } + + public static void testConsistencyAmongNodes(LedgerRepository[] ledgers) { + LedgerRepository ledger0 = ledgers[0]; + LedgerBlock latestBlock0 = ledger0.retrieveLatestBlock(); + for (int i = 1; i < ledgers.length; i++) { + LedgerRepository otherLedger = ledgers[i]; + LedgerBlock otherLatestBlock = otherLedger.retrieveLatestBlock(); + assertEquals(ledger0.getHash(), otherLedger.getHash()); + assertEquals(ledger0.getLatestBlockHeight(), otherLedger.getLatestBlockHeight()); + assertEquals(ledger0.getLatestBlockHash(), otherLedger.getLatestBlockHash()); + + assertEquals(latestBlock0.getHeight(), otherLatestBlock.getHeight()); + assertEquals(latestBlock0.getHash(), otherLatestBlock.getHash()); + assertEquals(latestBlock0.getAdminAccountHash(), otherLatestBlock.getAdminAccountHash()); + assertEquals(latestBlock0.getTransactionSetHash(), otherLatestBlock.getTransactionSetHash()); + assertEquals(latestBlock0.getUserAccountSetHash(), otherLatestBlock.getUserAccountSetHash()); + assertEquals(latestBlock0.getDataAccountSetHash(), otherLatestBlock.getDataAccountSetHash()); + assertEquals(latestBlock0.getContractAccountSetHash(), otherLatestBlock.getContractAccountSetHash()); + assertEquals(latestBlock0.getPreviousHash(), otherLatestBlock.getPreviousHash()); + } + } + + public static PeerTestRunner[] peerNodeStart(HashDigest ledgerHash, String dbType) { + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 12000); + LedgerBindingConfig bindingConfig0 = loadBindingConfig(0, ledgerHash, dbType); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, bindingConfig0); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 12010); + LedgerBindingConfig bindingConfig1 = loadBindingConfig(1, ledgerHash, dbType); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, bindingConfig1); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 12020); + LedgerBindingConfig bindingConfig2 = loadBindingConfig(2, ledgerHash, dbType); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, bindingConfig2); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 12030); + LedgerBindingConfig bindingConfig3 = loadBindingConfig(3, ledgerHash, dbType); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, bindingConfig3); + + ThreadInvoker.AsyncCallback peerStarting0 = peer0.start(); + ThreadInvoker.AsyncCallback peerStarting1 = peer1.start(); + ThreadInvoker.AsyncCallback peerStarting2 = peer2.start(); + ThreadInvoker.AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + return new PeerTestRunner[]{peer0, peer1, peer2, peer3}; + } + + public static LedgerBindingConfig loadBindingConfig(int id, HashDigest ledgerHash, String dbType) { + LedgerBindingConfig ledgerBindingConfig; + String newLedger = ledgerHash.toBase58(); + String resourceClassPath = "ledger-binding-" + dbType + "-" + id + ".conf"; + String ledgerBindingUrl = IntegrationBase.class.getResource("/") + resourceClassPath; + + try { + URL url = new URL(ledgerBindingUrl); + File ledgerBindingConf = new File(url.getPath()); + System.out.printf("URL-ledgerBindingConf = %s \r\n", url.getPath()); + if (ledgerBindingConf.exists()) { + List readLines = FileUtils.readLines(ledgerBindingConf); + + List writeLines = new ArrayList<>(); + + if (readLines != null && !readLines.isEmpty()) { + String oldLedgerLine = null; + for (String readLine : readLines) { + if (readLine.startsWith("ledger")) { + oldLedgerLine = readLine; + break; + } + } + String[] oldLedgerArray = oldLedgerLine.split("="); + + String oldLedger = oldLedgerArray[1]; + if (!oldLedger.equalsIgnoreCase(newLedger)) { + for (String readLine : readLines) { + String newLine = readLine.replace(oldLedger, newLedger); + if (dbType.equalsIgnoreCase("rocksdb")) { + if (newLine.contains("db.uri")) { + String[] propArray = newLine.split("="); + String dbKey = propArray[0]; + String dbValue = LedgerInitConsensusConfig.rocksdbConnectionStrings[id]; + newLine = dbKey + "=" + dbValue; + } + } + writeLines.add(newLine); + } + } else if(dbType.equalsIgnoreCase("rocksdb")) { + for (String readLine : readLines) { + String newLine = readLine; + if (readLine.contains("db.uri")) { + String[] propArray = readLine.split("="); + String dbKey = propArray[0]; + String dbValue = LedgerInitConsensusConfig.rocksdbConnectionStrings[id]; + newLine = dbKey + "=" + dbValue; + } + writeLines.add(newLine); + } + } + if (!writeLines.isEmpty()) { + FileUtils.writeLines(ledgerBindingConf, writeLines); + } + } + } + } catch (Exception e) { + + } + + ClassPathResource res = new ClassPathResource(resourceClassPath); + try(InputStream in = res.getInputStream()){ + ledgerBindingConfig = LedgerBindingConfig.resolve(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + return ledgerBindingConfig; + } + + public static class KeyPairResponse { + HashDigest txHash; + + BlockchainKeyPair keyPair; + + TransactionResponse txResp; + + public BlockchainKeyPair getKeyPair() { + return keyPair; + } + + public TransactionResponse getTxResp() { + return txResp; + } + + public HashDigest getTxHash() { + return txHash; + } + } + + public static class KvResponse { + + Bytes dataAccount; + + HashDigest ledgerHash; + + HashDigest txHash; + + TransactionResponse txResp; + + String key; + + byte[] value; + + public HashDigest getTxHash() { + return txHash; + } + + public TransactionResponse getTxResp() { + return txResp; + } + + public String getKey() { + return key; + } + + public byte[] getValue() { + return value; + } + + public HashDigest getLedgerHash() { + return ledgerHash; + } + + public Bytes getDataAccount() { + return dataAccount; + } + } + + public enum KeyPairType { + USER, + DATAACCOUNT + } + + // 合约测试使用的初始化数据; + BlockchainKeyPair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainKeyPair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); + // 保存资产总数的键; + private static final String KEY_TOTAL = "TOTAL"; + // 第二个参数; + private static final String KEY_ABC = "abc"; + private String contractZipName = "Example1.jar"; + HashDigest txContentHash; + String pubKeyVal = "jd.com"+System.currentTimeMillis(); + private String eventName = "issue-asset"; + public LedgerBlock testSDK_Contract(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService,LedgerRepository ledgerRepository) { + System.out.println("adminKey="+ AddressEncoding.generateAddress(adminKey.getPubKey())); + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + System.out.println("userKey="+userKey.getAddress()); + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(userKey.getIdentity()); + + // 定义交易; + byte[] contractCode = getChainCodeBytes(); + txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + assertTrue(txResp.isSuccess()); + + // 验证结果; + txResp.getContentHash(); + + LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); + byte[] contractCodeInDb = ledgerRepository.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) + .getChainCode(); + assertArrayEquals(contractCode, contractCodeInDb); + txContentHash = ptx.getHash(); + + // execute the contract; + testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository); + + return block; + } + + private void testContractExe(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainKeyPair userKey, + BlockchainService blockchainService,LedgerRepository ledgerRepository) { + LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); + LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + + txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, + ("888##123##" + contractDataKey.getAddress()).getBytes()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + txResp.getContentHash(); + Assert.assertTrue(txResp.isSuccess()); + } + + /** + * 根据合约构建字节数组; + * + * @return + */ + private byte[] getChainCodeBytes() { + // 构建合约的字节数组; + byte[] contractCode = null; + File file = null; + InputStream input = null; + try { + ClassPathResource contractPath = new ClassPathResource(contractZipName); + file = new File(contractPath.getURI()); + assertTrue("contract zip file is not exist.", file.exists() == true); + input = new FileInputStream(file); + // 这种暴力的读取压缩包,在class解析时有问题,所有需要改进; + contractCode = new byte[input.available()]; + input.read(contractCode); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return contractCode; + } +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBaseTest.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBaseTest.java new file mode 100644 index 00000000..3161c06e --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationBaseTest.java @@ -0,0 +1,245 @@ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.tools.initializer.*; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.springframework.core.io.ClassPathResource; +import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class IntegrationBaseTest { + + LedgerInitConsensusConfig.ConsensusConfig bftsmartConfig = LedgerInitConsensusConfig.bftsmartConfig; + + public IntegratedContext context = initLedgers(bftsmartConfig.getConfigPath(), bftsmartConfig.getProvider()); + public GatewayTestRunner gateway0; + public GatewayTestRunner gateway1; + + public void startPeer() { + // init ledgers of all nodes ; + Node node0 = context.getNode(0); + Node node1 = context.getNode(1); + Node node2 = context.getNode(2); + Node node3 = context.getNode(3); + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 13200); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, node0.getBindingConfig(), node0.getStorageDB()); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 13210); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, node1.getBindingConfig(), node1.getStorageDB()); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 13220); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, node2.getBindingConfig(), node2.getStorageDB()); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 13230); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, node3.getBindingConfig(), node3.getStorageDB()); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + gateway0 = new GatewayTestRunner("127.0.0.1", 13300, gwkey0, peerSrvAddr0); + + KeyPairConfig gwkey1 = new KeyPairConfig(); + gwkey1.setPubKeyValue(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[1]); + gwkey1.setPrivKeyValue(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[1]); + gwkey1.setPrivKeyPassword(encodedBase58Pwd); + gateway1 = new GatewayTestRunner("127.0.0.1", 13310, gwkey1, peerSrvAddr1); + + AsyncCallback gwStarting0 = gateway0.start(); + AsyncCallback gwStarting1 = gateway1.start(); + + gwStarting0.waitReturn(); + gwStarting1.waitReturn(); + } + + public void testConsistencyAmongNodes(IntegratedContext context) { + int[] ids = context.getNodeIds(); + Node[] nodes = new Node[ids.length]; + LedgerRepository[] ledgers = new LedgerRepository[ids.length]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = context.getNode(ids[i]); + HashDigest ledgerHash = nodes[i].getLedgerManager().getLedgerHashs()[0]; + ledgers[i] = nodes[i].getLedgerManager().getLedger(ledgerHash); + } + LedgerRepository ledger0 = ledgers[0]; + LedgerBlock latestBlock0 = ledger0.retrieveLatestBlock(); + for (int i = 1; i < ledgers.length; i++) { + LedgerRepository otherLedger = ledgers[i]; + LedgerBlock otherLatestBlock = otherLedger.retrieveLatestBlock(); + assertEquals(ledger0.getHash(), otherLedger.getHash()); + assertEquals(ledger0.getLatestBlockHeight(), otherLedger.getLatestBlockHeight()); + assertEquals(ledger0.getLatestBlockHash(), otherLedger.getLatestBlockHash()); + + assertEquals(latestBlock0.getHeight(), otherLatestBlock.getHeight()); + assertEquals(latestBlock0.getHash(), otherLatestBlock.getHash()); + assertEquals(latestBlock0.getAdminAccountHash(), otherLatestBlock.getAdminAccountHash()); + assertEquals(latestBlock0.getTransactionSetHash(), otherLatestBlock.getTransactionSetHash()); + assertEquals(latestBlock0.getUserAccountSetHash(), otherLatestBlock.getUserAccountSetHash()); + assertEquals(latestBlock0.getDataAccountSetHash(), otherLatestBlock.getDataAccountSetHash()); + assertEquals(latestBlock0.getContractAccountSetHash(), otherLatestBlock.getContractAccountSetHash()); + assertEquals(latestBlock0.getPreviousHash(), otherLatestBlock.getPreviousHash()); + } + } + + private IntegratedContext initLedgers(String configPath, String providerName) { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_integration(); + Properties props = LedgerInitializeWeb4SingleStepsTest.loadConsensusSetting(configPath); + ConsensusProvider csProvider = LedgerInitConsensusConfig.getConsensusProvider(providerName); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext nodeCtx0 = new NodeWebContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext nodeCtx1 = new NodeWebContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext nodeCtx2 = new NodeWebContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext nodeCtx3 = new NodeWebContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[1], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[2], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[3], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + String encodedPassword = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(LedgerInitConsensusConfig.memConnectionStrings[0]); + LedgerBindingConfig bindingConfig0 = new LedgerBindingConfig(); + AsyncCallback callback0 = nodeCtx0.startInitCommand(privkey0, encodedPassword, initSetting, csProps, + csProvider, testDb0, consolePrompter, bindingConfig0, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(LedgerInitConsensusConfig.memConnectionStrings[1]); + LedgerBindingConfig bindingConfig1 = new LedgerBindingConfig(); + AsyncCallback callback1 = nodeCtx1.startInitCommand(privkey1, encodedPassword, initSetting, csProps, + csProvider, testDb1, consolePrompter, bindingConfig1, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(LedgerInitConsensusConfig.memConnectionStrings[2]); + LedgerBindingConfig bindingConfig2 = new LedgerBindingConfig(); + AsyncCallback callback2 = nodeCtx2.startInitCommand(privkey2, encodedPassword, initSetting, csProps, + csProvider, testDb2, consolePrompter, bindingConfig2, quitLatch); + + DBConnectionConfig testDb3 = new DBConnectionConfig(); + testDb3.setConnectionUri(LedgerInitConsensusConfig.memConnectionStrings[3]); + LedgerBindingConfig bindingConfig3 = new LedgerBindingConfig(); + AsyncCallback callback3 = nodeCtx3.startInitCommand(privkey3, encodedPassword, initSetting, csProps, + csProvider, testDb3, consolePrompter, bindingConfig3, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + assertNotNull(ledgerHash0); + assertEquals(ledgerHash0, ledgerHash1); + assertEquals(ledgerHash0, ledgerHash2); + assertEquals(ledgerHash0, ledgerHash3); + + LedgerRepository ledger0 = nodeCtx0.registLedger(ledgerHash0); + LedgerRepository ledger1 = nodeCtx1.registLedger(ledgerHash1); + LedgerRepository ledger2 = nodeCtx2.registLedger(ledgerHash2); + LedgerRepository ledger3 = nodeCtx3.registLedger(ledgerHash3); + + assertNotNull(ledger0); + assertNotNull(ledger1); + assertNotNull(ledger2); + assertNotNull(ledger3); + + IntegratedContext context = new IntegratedContext(); + + Node node0 = new Node(0); + node0.setConsensusSettings(csProps); + node0.setLedgerManager(nodeCtx0.getLedgerManager()); + node0.setStorageDB(nodeCtx0.getStorageDB()); + node0.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(0).getPubKey(), privkey0)); + node0.setBindingConfig(bindingConfig0); + context.addNode(node0); + + Node node1 = new Node(1); + node1.setConsensusSettings(csProps); + node1.setLedgerManager(nodeCtx1.getLedgerManager()); + node1.setStorageDB(nodeCtx1.getStorageDB()); + node1.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(1).getPubKey(), privkey1)); + node1.setBindingConfig(bindingConfig1); + context.addNode(node1); + + Node node2 = new Node(2); + node2.setConsensusSettings(csProps); + node2.setLedgerManager(nodeCtx2.getLedgerManager()); + node2.setStorageDB(nodeCtx2.getStorageDB()); + node2.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(2).getPubKey(), privkey2)); + node2.setBindingConfig(bindingConfig2); + context.addNode(node2); + + Node node3 = new Node(3); + node3.setConsensusSettings(csProps); + node3.setLedgerManager(nodeCtx3.getLedgerManager()); + node3.setStorageDB(nodeCtx3.getStorageDB()); + node3.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(3).getPubKey(), privkey3)); + node3.setBindingConfig(bindingConfig3); + context.addNode(node3); + + nodeCtx0.closeServer(); + nodeCtx1.closeServer(); + nodeCtx2.closeServer(); + nodeCtx3.closeServer(); + + return context; + } + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java new file mode 100644 index 00000000..c097cfa6 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest2.java @@ -0,0 +1,362 @@ +package test.com.jd.blockchain.intgr; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInfo; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; + +/** + * 测试合约,提交后不立即进行验证,因为此时可能还没有完成正式结块; + */ +public class IntegrationTest2 { + // 合约测试使用的初始化数据; + BlockchainKeyPair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); + private String contractZipName = "AssetContract3.contract"; + private String eventName = "issue-asset"; + + @Test + public void test() { + // init ledgers of all nodes ; + IntegratedContext context = initLedgers(LedgerInitConsensusConfig.mqConfig, + LedgerInitConsensusConfig.memConnectionStrings); + Node node0 = context.getNode(0); + Node node1 = context.getNode(1); + Node node2 = context.getNode(2); + Node node3 = context.getNode(3); + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 13200); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, node0.getBindingConfig(), node0.getStorageDB()); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 13210); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, node1.getBindingConfig(), node1.getStorageDB()); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 13220); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2,node2.getBindingConfig(), node2.getStorageDB()); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 13230); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3,node3.getBindingConfig(), node3.getStorageDB()); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway0 = new GatewayTestRunner("127.0.0.1", 13300, gwkey0, peerSrvAddr0); + + KeyPairConfig gwkey1 = new KeyPairConfig(); + gwkey1.setPubKeyValue(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[1]); + gwkey1.setPrivKeyValue(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[1]); + gwkey1.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway1 = new GatewayTestRunner("127.0.0.1", 13310, gwkey1, peerSrvAddr1); + + AsyncCallback gwStarting0 = gateway0.start(); + AsyncCallback gwStarting1 = gateway1.start(); + + gwStarting0.waitReturn(); + gwStarting1.waitReturn(); + + // 执行测试用例之前,校验每个节点的一致性; + testConsistencyAmongNodes(context); + + testSDK(gateway0, context); + + // 执行测试用例之后,校验每个节点的一致性; + testConsistencyAmongNodes(context); + } + + private void testConsistencyAmongNodes(IntegratedContext context) { + int[] ids = context.getNodeIds(); + Node[] nodes = new Node[ids.length]; + LedgerRepository[] ledgers = new LedgerRepository[ids.length]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = context.getNode(ids[i]); + HashDigest ledgerHash = nodes[i].getLedgerManager().getLedgerHashs()[0]; + ledgers[i] = nodes[i].getLedgerManager().getLedger(ledgerHash); + } + LedgerRepository ledger0 = ledgers[0]; + LedgerBlock latestBlock0 = ledger0.retrieveLatestBlock(); + for (int i = 1; i < ledgers.length; i++) { + LedgerRepository otherLedger = ledgers[i]; + LedgerBlock otherLatestBlock = otherLedger.retrieveLatestBlock(); + assertEquals(ledger0.getHash(), otherLedger.getHash()); + assertEquals(ledger0.getLatestBlockHeight(), otherLedger.getLatestBlockHeight()); + assertEquals(ledger0.getLatestBlockHash(), otherLedger.getLatestBlockHash()); + + assertEquals(latestBlock0.getHeight(), otherLatestBlock.getHeight()); + assertEquals(latestBlock0.getHash(), otherLatestBlock.getHash()); + assertEquals(latestBlock0.getAdminAccountHash(), otherLatestBlock.getAdminAccountHash()); + assertEquals(latestBlock0.getTransactionSetHash(), otherLatestBlock.getTransactionSetHash()); + assertEquals(latestBlock0.getUserAccountSetHash(), otherLatestBlock.getUserAccountSetHash()); + assertEquals(latestBlock0.getDataAccountSetHash(), otherLatestBlock.getDataAccountSetHash()); + assertEquals(latestBlock0.getContractAccountSetHash(), otherLatestBlock.getContractAccountSetHash()); + assertEquals(latestBlock0.getPreviousHash(), otherLatestBlock.getPreviousHash()); + } + } + + private void testSDK(GatewayTestRunner gateway, IntegratedContext context) { + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + BlockchainService bcsrv = gwsrvFact.getBlockchainService(); + + HashDigest[] ledgerHashs = bcsrv.getLedgerHashs(); + + CryptoKeyPair adminKey = context.getNode(0).getPartiKeyPair(); + + testSDK_Contract(adminKey, ledgerHashs[0], bcsrv, context); + + } + + private IntegratedContext initLedgers(LedgerInitConsensusConfig.ConsensusConfig config, String[] dbConns) { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_integration(); + Properties props = LedgerInitializeWeb4SingleStepsTest.loadConsensusSetting(config.getConfigPath()); + ConsensusProvider csProvider = LedgerInitConsensusConfig.getConsensusProvider(config.getProvider()); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext nodeCtx0 = new NodeWebContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext nodeCtx1 = new NodeWebContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext nodeCtx2 = new NodeWebContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext nodeCtx3 = new NodeWebContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[1], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[2], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[3], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + String encodedPassword = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(dbConns[0]); + LedgerBindingConfig bindingConfig0 = new LedgerBindingConfig(); + AsyncCallback callback0 = nodeCtx0.startInitCommand(privkey0, encodedPassword, initSetting, csProps, csProvider, + testDb0, consolePrompter, bindingConfig0, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(dbConns[1]); + LedgerBindingConfig bindingConfig1 = new LedgerBindingConfig(); + AsyncCallback callback1 = nodeCtx1.startInitCommand(privkey1, encodedPassword, initSetting, csProps,csProvider, + testDb1, consolePrompter, bindingConfig1, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(dbConns[2]); + LedgerBindingConfig bindingConfig2 = new LedgerBindingConfig(); + AsyncCallback callback2 = nodeCtx2.startInitCommand(privkey2, encodedPassword, initSetting, csProps,csProvider, + testDb2, consolePrompter, bindingConfig2, quitLatch); + + DBConnectionConfig testDb3 = new DBConnectionConfig(); + testDb3.setConnectionUri(dbConns[3]); + LedgerBindingConfig bindingConfig3 = new LedgerBindingConfig(); + AsyncCallback callback3 = nodeCtx3.startInitCommand(privkey3, encodedPassword, initSetting, csProps,csProvider, + testDb3, consolePrompter, bindingConfig3, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + assertNotNull(ledgerHash0); + assertEquals(ledgerHash0, ledgerHash1); + assertEquals(ledgerHash0, ledgerHash2); + assertEquals(ledgerHash0, ledgerHash3); + + LedgerRepository ledger0 = nodeCtx0.registLedger(ledgerHash0); + LedgerRepository ledger1 = nodeCtx1.registLedger(ledgerHash1); + LedgerRepository ledger2 = nodeCtx2.registLedger(ledgerHash2); + LedgerRepository ledger3 = nodeCtx3.registLedger(ledgerHash3); + + assertNotNull(ledger0); + assertNotNull(ledger1); + assertNotNull(ledger2); + assertNotNull(ledger3); + + IntegratedContext context = new IntegratedContext(); + + Node node0 = new Node(0); + node0.setConsensusSettings(csProps); + node0.setLedgerManager(nodeCtx0.getLedgerManager()); + node0.setStorageDB(nodeCtx0.getStorageDB()); + node0.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(0).getPubKey(), privkey0)); + node0.setBindingConfig(bindingConfig0); + context.addNode(node0); + + Node node1 = new Node(1); + node1.setConsensusSettings(csProps); + node1.setLedgerManager(nodeCtx1.getLedgerManager()); + node1.setStorageDB(nodeCtx1.getStorageDB()); + node1.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(1).getPubKey(), privkey1)); + node1.setBindingConfig(bindingConfig1); + context.addNode(node1); + + Node node2 = new Node(2); + node2.setConsensusSettings(csProps); + node2.setLedgerManager(nodeCtx2.getLedgerManager()); + node2.setStorageDB(nodeCtx2.getStorageDB()); + node2.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(2).getPubKey(), privkey2)); + node2.setBindingConfig(bindingConfig2); + context.addNode(node2); + + Node node3 = new Node(3); + node3.setConsensusSettings(csProps); + node3.setLedgerManager(nodeCtx3.getLedgerManager()); + node3.setStorageDB(nodeCtx3.getStorageDB()); + node3.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(3).getPubKey(), privkey3)); + node3.setBindingConfig(bindingConfig3); + context.addNode(node3); + + nodeCtx0.closeServer(); + nodeCtx1.closeServer(); + nodeCtx2.closeServer(); + nodeCtx3.closeServer(); + + return context; + } + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private void testSDK_Contract(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, IntegratedContext context) { + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + byte[] contractCode = getChainCodeBytes(); + + txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + assertTrue(txResp.isSuccess()); + + // execute the contract; + testContractExe(adminKey, ledgerHash, userKey, blockchainService, context); + } + + private void testContractExe(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainKeyPair userKey, + BlockchainService blockchainService, IntegratedContext context) { + LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); + LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight()-1); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + + txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, + ("888##999##abc").getBytes()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + assertTrue(txResp.isSuccess()); + } + + + /** + * 根据合约构建字节数组; + * + * @return + */ + private byte[] getChainCodeBytes() { + // 构建合约的字节数组; + byte[] contractCode = null; + File file = null; + InputStream input = null; + try { + ClassPathResource contractPath = new ClassPathResource(contractZipName); + file = new File(contractPath.getURI()); + assertTrue("contract zip file is not exist.", file.exists() == true); + input = new FileInputStream(file); + // 这种暴力的读取压缩包,在class解析时有问题,所有需要改进; + contractCode = new byte[input.available()]; + input.read(contractCode); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return contractCode; + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Bftsmart.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Bftsmart.java new file mode 100644 index 00000000..79b9e189 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4Bftsmart.java @@ -0,0 +1,155 @@ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; + +import org.junit.Test; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import static test.com.jd.blockchain.intgr.IntegrationBase.*; + +public class IntegrationTest4Bftsmart { + + private static final boolean isRegisterUser = true; + + private static final boolean isRegisterDataAccount = true; + + private static final boolean isWriteKv = true; + + private static final String DB_TYPE_MEM = "mem"; + + private static final String DB_TYPE_REDIS = "redis"; + + private static final String DB_TYPE_ROCKSDB = "rocksdb"; + + @Test + public void test4Memory() { + test(LedgerInitConsensusConfig.bftsmartProvider, DB_TYPE_MEM, LedgerInitConsensusConfig.memConnectionStrings); + } + + @Test + public void test4Redis() { +// test(LedgerInitConsensusConfig.bftsmartProvider, DB_TYPE_REDIS, LedgerInitConsensusConfig.redisConnectionStrings); + } + + public void test(String[] providers, String dbType, String[] dbConnections) { + + + final ExecutorService sendReqExecutors = Executors.newFixedThreadPool(20); + + + // 内存账本初始化 + HashDigest ledgerHash = initLedger(dbConnections); + + // 启动Peer节点 + PeerTestRunner[] peerNodes = peerNodeStart(ledgerHash, dbType); + + DbConnectionFactory dbConnectionFactory0 = peerNodes[0].getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory1 = peerNodes[1].getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory2 = peerNodes[2].getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory3 = peerNodes[3].getDBConnectionFactory(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeTest.PASSWORD); + + GatewayConfigProperties.KeyPairConfig gwkey0 = new GatewayConfigProperties.KeyPairConfig(); + gwkey0.setPubKeyValue(IntegrationBase.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(IntegrationBase.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway = new GatewayTestRunner("127.0.0.1", 11000, gwkey0, + peerNodes[0].getServiceAddress(), providers,null); + + ThreadInvoker.AsyncCallback gwStarting = gateway.start(); + + gwStarting.waitReturn(); + + // 执行测试用例之前,校验每个节点的一致性; + LedgerRepository[] ledgers = buildLedgers(new LedgerBindingConfig[]{ + peerNodes[0].getLedgerBindingConfig(), + peerNodes[1].getLedgerBindingConfig(), + peerNodes[2].getLedgerBindingConfig(), + peerNodes[3].getLedgerBindingConfig(), + }, + new DbConnectionFactory[]{ + dbConnectionFactory0, + dbConnectionFactory1, + dbConnectionFactory2, + dbConnectionFactory3}); + + IntegrationBase.testConsistencyAmongNodes(ledgers); + + LedgerRepository ledgerRepository = ledgers[0]; + + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(IntegrationBase.PRIV_KEYS[0], IntegrationBase.PASSWORD); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(IntegrationBase.PUB_KEYS[0]); + + CryptoKeyPair adminKey = new CryptoKeyPair(pubKey0, privkey0); + + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + + int size = 15; + CountDownLatch countDownLatch = new CountDownLatch(size); + if (isRegisterUser) { + for (int i = 0; i < size; i++) { + sendReqExecutors.execute(() -> { + + System.out.printf(" sdk execute time = %s threadId = %s \r\n", System.currentTimeMillis(), Thread.currentThread().getId()); + IntegrationBase.KeyPairResponse userResponse = IntegrationBase.testSDK_RegisterUser(adminKey, ledgerHash, blockchainService); + + validKeyPair(userResponse, ledgerRepository, IntegrationBase.KeyPairType.USER); + countDownLatch.countDown(); + }); + + } + } + try { + countDownLatch.await(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + if (isRegisterDataAccount) { + IntegrationBase.KeyPairResponse dataAccountResponse = IntegrationBase.testSDK_RegisterDataAccount(adminKey, ledgerHash, blockchainService); + + validKeyPair(dataAccountResponse, ledgerRepository, IntegrationBase.KeyPairType.DATAACCOUNT); + + if (isWriteKv) { + BlockchainKeyPair da = dataAccountResponse.keyPair; + IntegrationBase.KvResponse kvResponse = IntegrationBase.testSDK_InsertData(adminKey, ledgerHash, blockchainService, da.getAddress()); + validKvWrite(kvResponse, ledgerRepository, blockchainService); + } + } + + try { + Thread.sleep(60000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + IntegrationBase.testConsistencyAmongNodes(ledgers); + } + private HashDigest initLedger(String[] dbConnections) { + LedgerInitializeWeb4Nodes ledgerInit = new LedgerInitializeWeb4Nodes(); + HashDigest ledgerHash = ledgerInit.testInitWith4Nodes(LedgerInitConsensusConfig.bftsmartConfig, dbConnections); + System.out.printf("LedgerHash = %s \r\n", ledgerHash.toBase58()); + return ledgerHash; + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4MQ.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4MQ.java new file mode 100644 index 00000000..aae9912d --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTest4MQ.java @@ -0,0 +1,169 @@ +package test.com.jd.blockchain.intgr; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.FileUtils; + +import org.junit.Test; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4Nodes; + +import java.util.HashMap; +import java.util.Map; + +import static test.com.jd.blockchain.intgr.IntegrationBase.*; + +public class IntegrationTest4MQ { + + private static final boolean isRegisterUser = true; + + private static final boolean isRegisterDataAccount = true; + + private static final boolean isWriteKv = true; + private static final boolean isContract = false; + + private static final boolean isOnline = true; + private static final int online_time = 60000*60; + + private static final String DB_TYPE_MEM = "mem"; + + private static final String DB_TYPE_REDIS = "redis"; + + private static final String DB_TYPE_ROCKSDB = "rocksdb"; + + private static final String DATA_RETRIEVAL_URL= "http://192.168.151.39:10001"; + + @Test + public void test4Memory() { + test(LedgerInitConsensusConfig.mqProvider, DB_TYPE_MEM, LedgerInitConsensusConfig.memConnectionStrings); + } + + @Test + public void test4Redis() { +// test(LedgerInitConsensusConfig.mqProvider, DB_TYPE_REDIS, LedgerInitConsensusConfig.redisConnectionStrings); + } + + @Test + public void test4Rocksdb() { + test(LedgerInitConsensusConfig.mqProvider, DB_TYPE_ROCKSDB, LedgerInitConsensusConfig.rocksdbConnectionStrings); + } + + public void test(String[] providers, String dbType, String[] dbConnections) { + + // 内存账本初始化 + HashDigest ledgerHash = initLedger(dbType, dbConnections); + + // 启动Peer节点 + PeerTestRunner[] peerNodes = peerNodeStart(ledgerHash, dbType); + + DbConnectionFactory dbConnectionFactory0 = peerNodes[0].getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory1 = peerNodes[1].getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory2 = peerNodes[2].getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory3 = peerNodes[3].getDBConnectionFactory(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeTest.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(IntegrationBase.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(IntegrationBase.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + + Map otherMap = new HashMap(); + otherMap.put("DATA_RETRIEVAL_URL",DATA_RETRIEVAL_URL); + GatewayTestRunner gateway = new GatewayTestRunner("127.0.0.1", 11000, gwkey0, + peerNodes[0].getServiceAddress(), providers, otherMap); + + AsyncCallback gwStarting = gateway.start(); + + gwStarting.waitReturn(); + + // 执行测试用例之前,校验每个节点的一致性; + LedgerRepository[] ledgers = buildLedgers(new LedgerBindingConfig[]{ + peerNodes[0].getLedgerBindingConfig(), + peerNodes[1].getLedgerBindingConfig(), + peerNodes[2].getLedgerBindingConfig(), + peerNodes[3].getLedgerBindingConfig(), + }, + new DbConnectionFactory[]{ + dbConnectionFactory0, + dbConnectionFactory1, + dbConnectionFactory2, + dbConnectionFactory3}); + + IntegrationBase.testConsistencyAmongNodes(ledgers); + + LedgerRepository ledgerRepository = ledgers[0]; + + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(IntegrationBase.PRIV_KEYS[0], IntegrationBase.PASSWORD); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(IntegrationBase.PUB_KEYS[0]); + + CryptoKeyPair adminKey = new CryptoKeyPair(pubKey0, privkey0); + + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + + if (isRegisterUser) { + IntegrationBase.KeyPairResponse userResponse = IntegrationBase.testSDK_RegisterUser(adminKey, ledgerHash, blockchainService); + + validKeyPair(userResponse, ledgerRepository, IntegrationBase.KeyPairType.USER); + } + + if (isRegisterDataAccount) { + IntegrationBase.KeyPairResponse dataAccountResponse = IntegrationBase.testSDK_RegisterDataAccount(adminKey, ledgerHash, blockchainService); + + validKeyPair(dataAccountResponse, ledgerRepository, IntegrationBase.KeyPairType.DATAACCOUNT); + + if (isWriteKv) { + BlockchainKeyPair da = dataAccountResponse.keyPair; + IntegrationBase.KvResponse kvResponse = IntegrationBase.testSDK_InsertData(adminKey, ledgerHash, blockchainService, da.getAddress()); + validKvWrite(kvResponse, ledgerRepository, blockchainService); + //more page + testSDK_InsertData_morePage(adminKey, ledgerHash, blockchainService, da.getAddress()); + } + } + + if(isContract){ + IntegrationBase integrationBase = new IntegrationBase(); + integrationBase.testSDK_Contract(adminKey, ledgerHash, blockchainService,ledgerRepository); + } + + IntegrationBase.testConsistencyAmongNodes(ledgers); + + if(isOnline){ + try { + Thread.sleep(online_time); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + private HashDigest initLedger(String dbType, String[] dbConnections) { + if (dbType.equalsIgnoreCase(DB_TYPE_ROCKSDB)) { + // rocksdb 需要先删除文件 + for (String dbDir : LedgerInitConsensusConfig.rocksdbDirStrings) { + try { + FileUtils.deleteFile(dbDir); + } catch (Exception e) { + e.printStackTrace(); + } + } + } + LedgerInitializeWeb4Nodes ledgerInit = new LedgerInitializeWeb4Nodes(); + HashDigest ledgerHash = ledgerInit.testInitWith4Nodes(LedgerInitConsensusConfig.mqConfig, dbConnections); + System.out.printf("LedgerHash = %s \r\n", ledgerHash.toBase58()); + return ledgerHash; + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java new file mode 100644 index 00000000..35d9ea09 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestAll4Redis.java @@ -0,0 +1,531 @@ +package test.com.jd.blockchain.intgr; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInfo; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.core.DataAccount; +import com.jd.blockchain.ledger.core.DataAccountSet; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; + +public class IntegrationTestAll4Redis { + + + public static final String PASSWORD = "abc"; + + public static final String[] PUB_KEYS = { "endPsK36imXrY66pru6ttZ8dZ3TynWekmdqoM1K7ZRRoRBBiYVzM", + "endPsK36jQE1uYpdVRSnwQXVYhgAMWTaMJiAqii7URiULoBDLUUN", + "endPsK36fc7FSecKAJCJdFhTejbPHMLaGcihJVQCv95czCq4tW5n", + "endPsK36m1grx8mkTMgh8XQHiiaNzajdC5hkuqP6pAuLmMbYkzd4" }; + + public static final String[] PRIV_KEYS = { + "177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X", + "177gjwQwTdXthkutDKVgKwiq6wWfLWYuxhji1U2N1C5MzqLRWCLZXo3i2g4vpfcEAQUPG8H", + "177gjvLHUjxvAWsqVcGgV8eHgVNBvJZYDfpP9FLjTouR1gEJNiamYu1qjTNDh18XWyLg8or", + "177gk2VtYeGbK5TS2xWhbSZA4BsT9Xj5Fb8hqCzxzgbojVVcqaDSFFrFPsLbZBx7rszyCNy" }; + + // batch transactions keys + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainKeyPair dataKey = BlockchainKeyGenerator.getInstance().generate(); + + // 合约测试使用的初始化数据; + BlockchainKeyPair contractDataKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainKeyPair contractDeployKey = BlockchainKeyGenerator.getInstance().generate(); + private String contractZipName = "AssetContract1.contract"; + private String eventName = "issue-asset"; + HashDigest txContentHash; + String pubKeyVal = "jd.com"+System.currentTimeMillis(); +// String userPubKeyVal = "this is user's pubKey"; + // 保存资产总数的键; + private static final String KEY_TOTAL = "TOTAL"; + // 第二个参数; + private static final String KEY_ABC = "abc"; + + @Test + public void test() { + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 10200); + LedgerBindingConfig bindingConfig0 = loadBindingConfig(0); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, bindingConfig0); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 10210); + LedgerBindingConfig bindingConfig1 = loadBindingConfig(1); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, bindingConfig1); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 10220); + LedgerBindingConfig bindingConfig2 = loadBindingConfig(2); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, bindingConfig2); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 10230); + LedgerBindingConfig bindingConfig3 = loadBindingConfig(3); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, bindingConfig3); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + DbConnectionFactory dbConnectionFactory0 = peer0.getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory1 = peer1.getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory2 = peer2.getDBConnectionFactory(); + DbConnectionFactory dbConnectionFactory3 = peer3.getDBConnectionFactory(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(PUB_KEYS[0]); + gwkey0.setPrivKeyValue(PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway0 = new GatewayTestRunner("127.0.0.1", 11000, gwkey0, peerSrvAddr0); + + AsyncCallback gwStarting0 = gateway0.start(); + + gwStarting0.waitReturn(); + + // 执行测试用例之前,校验每个节点的一致性; + LedgerRepository[] ledgers = buildLedgers(new LedgerBindingConfig[]{bindingConfig0, bindingConfig1, bindingConfig2, bindingConfig3}, + new DbConnectionFactory[]{dbConnectionFactory0,dbConnectionFactory1,dbConnectionFactory2,dbConnectionFactory3}); + testConsistencyAmongNodes(ledgers); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + + CryptoKeyPair adminKey = new CryptoKeyPair(pubKey0,privkey0); + + testWriteBatchTransactions(gateway0, adminKey, ledgers[0]); + + + testSDK(gateway0, adminKey, ledgers[0]); + + // 执行测试用例之后,校验每个节点的一致性; + testConsistencyAmongNodes(ledgers); + } + + private LedgerBindingConfig loadBindingConfig(int id){ + ClassPathResource res = new ClassPathResource("ledger-binding-redis-" + id + ".conf"); + try(InputStream in = res.getInputStream()){ + return LedgerBindingConfig.resolve(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private LedgerRepository[] buildLedgers(LedgerBindingConfig[] bindingConfigs, DbConnectionFactory[] dbConnectionFactories){ + int[] ids = {0, 1, 2, 3}; + LedgerRepository[] ledgers = new LedgerRepository[ids.length]; + LedgerManager[] ledgerManagers = new LedgerManager[ids.length]; + for (int i = 0; i < ids.length; i++) { + ledgerManagers[i] = new LedgerManager(); + HashDigest ledgerHash = bindingConfigs[0].getLedgerHashs()[0]; + DbConnection conn = dbConnectionFactories[i].connect(bindingConfigs[i].getLedger(ledgerHash).getDbConnection().getUri(), + bindingConfigs[i].getLedger(ledgerHash).getDbConnection().getPassword()); + ledgers[i] = ledgerManagers[i].register(ledgerHash, conn.getStorageService()); + } + return ledgers; + } + + private void testConsistencyAmongNodes(LedgerRepository[] ledgers) { + LedgerRepository ledger0 = ledgers[0]; + LedgerBlock latestBlock0 = ledger0.retrieveLatestBlock(); + for (int i = 1; i < ledgers.length; i++) { + LedgerRepository otherLedger = ledgers[i]; + LedgerBlock otherLatestBlock = otherLedger.retrieveLatestBlock(); + assertEquals(ledger0.getHash(), otherLedger.getHash()); + assertEquals(ledger0.getLatestBlockHeight(), otherLedger.getLatestBlockHeight()); + assertEquals(latestBlock0.getHeight(), otherLatestBlock.getHeight()); + assertEquals(latestBlock0.getAdminAccountHash(), otherLatestBlock.getAdminAccountHash()); + assertEquals(latestBlock0.getUserAccountSetHash(), otherLatestBlock.getUserAccountSetHash()); + assertEquals(latestBlock0.getDataAccountSetHash(), otherLatestBlock.getDataAccountSetHash()); + assertEquals(latestBlock0.getContractAccountSetHash(), otherLatestBlock.getContractAccountSetHash()); + assertEquals(latestBlock0.getPreviousHash(), otherLatestBlock.getPreviousHash()); + + assertEquals(latestBlock0.getTransactionSetHash(), otherLatestBlock.getTransactionSetHash()); + assertEquals(ledger0.getLatestBlockHash(), otherLedger.getLatestBlockHash()); + assertEquals(latestBlock0.getHash(), otherLatestBlock.getHash()); + } + } + + //测试一个区块包含多个交易的写入情况,并验证写入结果; + private void testWriteBatchTransactions(GatewayTestRunner gateway, CryptoKeyPair adminKey,LedgerRepository ledgerRepository) { + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHashs[0]); + + //regist user account + txTpl.users().register(userKey.getIdentity()); + + //regist data account + txTpl.dataAccounts().register(dataKey.getIdentity()); + + //add kv ops for data account + DataAccountKVSetOperation dataKvsetOP = txTpl.dataAccount(dataKey.getAddress()) + .set("A", "Value_A_0".getBytes(), -1) + .set("B", "Value_B_0".getBytes(), -1) + .set("C", "Value_C_0".getBytes(), -1) + .set("D", "Value_D_0".getBytes(), -1).getOperation(); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + assertTrue(txResp.isSuccess()); + assertEquals(ledgerRepository.retrieveLatestBlockHeight(), txResp.getBlockHeight()); + + assertArrayEquals("Value_A_0".getBytes(), ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getBytes("A")); + assertArrayEquals("Value_B_0".getBytes(), ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getBytes("B")); + assertArrayEquals("Value_C_0".getBytes(), ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getBytes("C")); + assertArrayEquals("Value_D_0".getBytes(), ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getBytes("D")); + assertEquals(0, ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getDataVersion("A")); + assertEquals(0, ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getDataVersion("B")); + assertEquals(0, ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getDataVersion("C")); + assertEquals(0, ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()).getDataAccount(dataKey.getAddress()).getDataVersion("D")); + + return; + } + + + private void testSDK(GatewayTestRunner gateway, CryptoKeyPair adminKey,LedgerRepository ledgerRepository) { + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + BlockchainService bcsrv = gwsrvFact.getBlockchainService(); + + HashDigest[] ledgerHashs = bcsrv.getLedgerHashs(); + BlockchainKeyPair newUserAcount = testSDK_RegisterUser(adminKey, ledgerHashs[0], bcsrv, ledgerRepository); + BlockchainKeyPair newDataAccount = testSDK_RegisterDataAccount(adminKey, ledgerHashs[0], bcsrv, ledgerRepository); + testSDK_InsertData(adminKey, ledgerHashs[0], bcsrv, newDataAccount.getAddress(), ledgerRepository); + LedgerBlock latestBlock = testSDK_Contract(adminKey, ledgerHashs[0], bcsrv, ledgerRepository); + + } + + private void testSDK_InsertData(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainService blockchainService, + Bytes dataAccountAddress, LedgerRepository ledgerRepository) { + + // 在本地定义注册账号的 TX; + TransactionTemplate txTemp = blockchainService.newTransaction(ledgerHash); + + // -------------------------------------- + // 将商品信息写入到指定的账户中; + // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; + Bytes dataAccount = dataAccountAddress; + + String dataKey = "jingdong" + new Random().nextInt(100000); + byte[] dataVal = "www.jd.com".getBytes(); + + txTemp.dataAccount(dataAccount).set(dataKey, dataVal, -1); + + // TX 准备就绪; + PreparedTransaction prepTx = txTemp.prepare(); + + // 使用私钥进行签名; + prepTx.sign(adminKey); + + // 提交交易; + TransactionResponse txResp = prepTx.commit(); + + ledgerRepository.retrieveLatestBlock(); // 更新内存 + + // 先验证应答 + assertEquals(TransactionState.SUCCESS, txResp.getExecutionState()); + assertEquals(txResp.getBlockHeight(), ledgerRepository.getLatestBlockHeight()); + assertEquals(txResp.getContentHash(), prepTx.getHash()); + assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); + + KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHash, dataAccountAddress.toString(), dataKey); + for (KVDataEntry kvDataEntry : kvDataEntries) { + assertEquals(dataKey, kvDataEntry.getKey()); + String valHexText = (String) kvDataEntry.getValue(); + byte[] valBytes = HexUtils.decode(valHexText); + String valText = new String(valBytes); + System.out.println(valText); + } + } + + private BlockchainKeyPair testSDK_RegisterDataAccount(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, LedgerRepository ledgerRepository) { + // 注册数据账户,并验证最终写入; + BlockchainKeyPair dataAccount = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.dataAccounts().register(dataAccount.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + + HashDigest transactionHash = ptx.getHash(); + + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + // LedgerRepository ledgerOfNode0 = + // node0.getLedgerManager().getLedger(ledgerHash); + LedgerManage ledgerManager = new LedgerManager(); + long latestBlockHeight = ledgerRepository.retrieveLatestBlockHeight(); + + assertEquals(txResp.getExecutionState(), TransactionState.SUCCESS); + assertEquals(txResp.getBlockHeight(), latestBlockHeight); + assertEquals(txResp.getContentHash(), transactionHash); + assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); + assertNotNull(ledgerRepository.getDataAccountSet(ledgerRepository.getLatestBlock()) + .getDataAccount(dataAccount.getAddress())); + + return dataAccount; + } + + private BlockchainKeyPair testSDK_RegisterUser(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, LedgerRepository ledgerRepository) { + // 注册用户,并验证最终写入; + BlockchainKeyPair user = BlockchainKeyGenerator.getInstance().generate(); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(user.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + + HashDigest transactionHash = ptx.getHash(); + + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + LedgerManage ledgerManager = new LedgerManager(); + assertEquals(txResp.getExecutionState(), TransactionState.SUCCESS); + assertEquals(txResp.getBlockHeight(), ledgerRepository.getLatestBlockHeight()); + assertEquals(txResp.getContentHash(), transactionHash); + assertEquals(txResp.getBlockHash(), ledgerRepository.getLatestBlockHash()); + assertTrue(ledgerRepository.getUserAccountSet(ledgerRepository.getLatestBlock()).contains(user.getAddress())); + + return user; + } + + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private LedgerBlock testSDK_Contract(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService,LedgerRepository ledgerRepository) { + System.out.println("adminKey="+AddressEncoding.generateAddress(adminKey.getPubKey())); + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + System.out.println("userKey="+userKey.getAddress()); + // valid the basic data in contract; +// prepareContractData(adminKey, ledgerHash, blockchainService,ledgerRepository); + + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + txTpl.users().register(userKey.getIdentity()); + + // 定义交易; + // 注册数据账户,并验证最终写入; + txTpl.dataAccounts().register(contractDataKey.getIdentity()); +// dataAccountSet.getDataAccount(dataAddress) + DataAccount dataAccount = ledgerRepository.getDataAccountSet(ledgerRepository.getLatestBlock()).getDataAccount(contractDataKey.getAddress()); + + DataAccountKVSetOperation kvsetOP = txTpl.dataAccount(contractDataKey.getAddress()) + .set("A", "Value_A_0".getBytes(), -1) + .set("B", "Value_B_0".getBytes(), -1) + .set(KEY_TOTAL, "total value,dataAccount".getBytes(), -1) + .set(KEY_ABC, "abc value,dataAccount".getBytes(), -1) + // 所有的模拟数据都在这个dataAccount中填充; + .set("ledgerHash", ledgerHash.getRawDigest(), -1).getOperation(); + + byte[] contractCode = getChainCodeBytes(); + txTpl.contracts().deploy(contractDeployKey.getIdentity(), contractCode); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + assertTrue(txResp.isSuccess()); + + // 验证结果; + txResp.getContentHash(); + + LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); + byte[] contractCodeInDb = ledgerRepository.getContractAccountSet(block).getContract(contractDeployKey.getAddress()) + .getChainCode(); + assertArrayEquals(contractCode, contractCodeInDb); + txContentHash = ptx.getHash(); + + // execute the contract; + testContractExe(adminKey, ledgerHash, userKey, blockchainService, ledgerRepository); + + return block; + } + + private void testContractExe(CryptoKeyPair adminKey, HashDigest ledgerHash, BlockchainKeyPair userKey, + BlockchainService blockchainService,LedgerRepository ledgerRepository) { + LedgerInfo ledgerInfo = blockchainService.getLedger(ledgerHash); + LedgerBlock previousBlock = blockchainService.getBlock(ledgerHash, ledgerInfo.getLatestBlockHeight() - 1); + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + + txTpl.contractEvents().send(contractDeployKey.getAddress(), eventName, + ("888##abc##" + contractDataKey.getAddress() + "##" + previousBlock.getHash().toBase58() + "##" + + userKey.getAddress() + "##" + contractDeployKey.getAddress() + "##" + + txContentHash.toBase58()+"##"+pubKeyVal).getBytes()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + // 验证结果; + txResp.getContentHash(); + + LedgerInfo latestLedgerInfo = blockchainService.getLedger(ledgerHash); + assertEquals(ledgerInfo.getLatestBlockHeight() + 1, latestLedgerInfo.getLatestBlockHeight()); + assertEquals(txResp.getBlockHeight(), latestLedgerInfo.getLatestBlockHeight()); + + LedgerBlock backgroundLedgerBlock = ledgerRepository.retrieveLatestBlock(); + assertEquals(txResp.getBlockHeight(), backgroundLedgerBlock.getHeight()); + + // 验证合约中的赋值,外部可以获得; + DataAccountSet dataAccountSet = ledgerRepository.getDataAccountSet(backgroundLedgerBlock); + PubKey pubKey = new PubKey(CryptoAlgorithm.ED25519, pubKeyVal.getBytes()); + Bytes dataAddress = AddressEncoding.generateAddress(pubKey); + assertEquals(dataAddress, dataAccountSet.getDataAccount(dataAddress).getAddress()); + assertEquals("hello", new String(dataAccountSet.getDataAccount(dataAddress).getBytes(KEY_TOTAL, -1))); + + // 验证userAccount,从合约内部赋值,然后外部验证;内部定义动态key,外部不便于得到,临时屏蔽; +// UserAccountSet userAccountSet = ledgerRepository.getUserAccountSet(backgroundLedgerBlock); +// PubKey userPubKey = new PubKey(CryptoAlgorithm.ED25519, userPubKeyVal.getBytes()); +// String userAddress = AddressEncoding.generateAddress(userPubKey); +// assertEquals(userAddress, userAccountSet.getUser(userAddress).getAddress()); + } + + private void prepareContractData(CryptoKeyPair adminKey, HashDigest ledgerHash, + BlockchainService blockchainService, LedgerRepository ledgerRepository) { + + // 定义交易; + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); + + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + assertTrue(txResp.isSuccess()); + + // 验证结果; + LedgerBlock block = ledgerRepository.getBlock(txResp.getBlockHeight()); + byte[] val1InDb = ledgerRepository.getDataAccountSet(block).getDataAccount(contractDataKey.getAddress()) + .getBytes("A"); + byte[] val2InDb = ledgerRepository.getDataAccountSet(block).getDataAccount(contractDataKey.getAddress()) + .getBytes(KEY_TOTAL); + assertArrayEquals("Value_A_0".getBytes(), val1InDb); + assertArrayEquals("total value,dataAccount".getBytes(), val2InDb); + } + + /** + * 根据合约构建字节数组; + * + * @return + */ + private byte[] getChainCodeBytes() { + // 构建合约的字节数组; + byte[] contractCode = null; + File file = null; + InputStream input = null; + try { + ClassPathResource contractPath = new ClassPathResource(contractZipName); + file = new File(contractPath.getURI()); + assertTrue("contract zip file is not exist.", file.exists() == true); + input = new FileInputStream(file); + // 这种暴力的读取压缩包,在class解析时有问题,所有需要改进; + contractCode = new byte[input.available()]; + input.read(contractCode); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if (input != null) { + input.close(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + return contractCode; + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestDataAccount.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestDataAccount.java new file mode 100644 index 00000000..24d58608 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/IntegrationTestDataAccount.java @@ -0,0 +1,383 @@ +package test.com.jd.blockchain.intgr; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import com.jd.blockchain.storage.service.DbConnection; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.alibaba.fastjson.JSON; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties.KeyPairConfig; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.KVDataEntry; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.PreparedTransaction; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.TransactionTemplate; +import com.jd.blockchain.ledger.core.DataAccountSet; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.IntegratedContext.Node; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeWeb4SingleStepsTest.NodeWebContext; + +public class IntegrationTestDataAccount { + + LedgerInitConsensusConfig.ConsensusConfig config = LedgerInitConsensusConfig.mqConfig; + + public IntegratedContext context = initLedgers(config, LedgerInitConsensusConfig.memConnectionStrings); + public GatewayTestRunner gateway0; + public GatewayTestRunner gateway1; + + private class JsonTest { + String name; + + public JsonTest(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + @Test + public void startPeer() { + + // init ledgers of all nodes ; + Node node0 = context.getNode(0); + Node node1 = context.getNode(1); + Node node2 = context.getNode(2); + Node node3 = context.getNode(3); + + NetworkAddress peerSrvAddr0 = new NetworkAddress("127.0.0.1", 14200); + PeerTestRunner peer0 = new PeerTestRunner(peerSrvAddr0, node0.getBindingConfig(), node0.getStorageDB()); + + NetworkAddress peerSrvAddr1 = new NetworkAddress("127.0.0.1", 14210); + PeerTestRunner peer1 = new PeerTestRunner(peerSrvAddr1, node1.getBindingConfig(), node1.getStorageDB()); + + NetworkAddress peerSrvAddr2 = new NetworkAddress("127.0.0.1", 14220); + PeerTestRunner peer2 = new PeerTestRunner(peerSrvAddr2, node2.getBindingConfig(), node2.getStorageDB()); + + NetworkAddress peerSrvAddr3 = new NetworkAddress("127.0.0.1", 14230); + PeerTestRunner peer3 = new PeerTestRunner(peerSrvAddr3, node3.getBindingConfig(), node3.getStorageDB()); + + AsyncCallback peerStarting0 = peer0.start(); + AsyncCallback peerStarting1 = peer1.start(); + AsyncCallback peerStarting2 = peer2.start(); + AsyncCallback peerStarting3 = peer3.start(); + + peerStarting0.waitReturn(); + peerStarting1.waitReturn(); + peerStarting2.waitReturn(); + peerStarting3.waitReturn(); + + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + KeyPairConfig gwkey0 = new KeyPairConfig(); + gwkey0.setPubKeyValue(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[0]); + gwkey0.setPrivKeyValue(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + gateway0 = new GatewayTestRunner("127.0.0.1", 13300, gwkey0, peerSrvAddr0); + + KeyPairConfig gwkey1 = new KeyPairConfig(); + gwkey1.setPubKeyValue(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[1]); + gwkey1.setPrivKeyValue(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[1]); + gwkey1.setPrivKeyPassword(encodedBase58Pwd); + gateway1 = new GatewayTestRunner("127.0.0.1", 13310, gwkey1, peerSrvAddr1); + + AsyncCallback gwStarting0 = gateway0.start(); + AsyncCallback gwStarting1 = gateway1.start(); + + gwStarting0.waitReturn(); + gwStarting1.waitReturn(); + + // 执行测试用例之前,校验每个节点的一致性; + testConsistencyAmongNodes(context); + + // temp test add + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PubKey pubKey0 = KeyGenCommand.decodePubKey(LedgerInitializeWeb4SingleStepsTest.PUB_KEYS[0]); + CryptoKeyPair adminKey = new CryptoKeyPair(pubKey0, privkey0); + + // regist data account + Bytes dataAddr = registDataAccount(gateway0, adminKey, context); + + // add kv ops to data account + testAddKvOpToDataAccount(gateway0, adminKey, context, dataAddr); + + // 执行测试用例之后,校验每个节点的一致性; + testConsistencyAmongNodes(context); + } + + private Bytes registDataAccount(GatewayTestRunner gateway, CryptoKeyPair adminKey, IntegratedContext context) { + // 连接网关; + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHashs[0]); + + // BlockchainKeyPair user = BlockchainKeyGenerator.getInstance().generate(); + BlockchainKeyPair data = BlockchainKeyGenerator.getInstance().generate(); + + // regist user account + // txTpl.users().register(user.getIdentity()); + + // //regist data account + txTpl.dataAccounts().register(data.getIdentity()); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + try { + Thread.sleep(6000); + } catch (Exception e) { + e.printStackTrace(); + } + + return data.getAddress(); + + } + + // 通过调用SDK->GATEWAY,测试一个区块包含多个交易时的写入情况,并验证写入结果; + private void testAddKvOpToDataAccount(GatewayTestRunner gateway, CryptoKeyPair adminKey, IntegratedContext context, + Bytes dataAddr) { + + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + HashDigest[] ledgerHashs = blockchainService.getLedgerHashs(); + + LedgerManager ledgerManager = context.getNode(0).getLedgerManager(); + + DbConnection memoryBasedDb = context.getNode(0).getStorageDB().connect(LedgerInitConsensusConfig.memConnectionStrings[0]); + + LedgerRepository ledgerRepository = ledgerManager.register(ledgerHashs[0], memoryBasedDb.getStorageService()); + + DataAccountSet dataAccountSet = ledgerRepository.getDataAccountSet(ledgerRepository.retrieveLatestBlock()); + + TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHashs[0]); + + long currentTime = System.currentTimeMillis(); + byte[] time = BytesUtils.toBytes(currentTime); + + JsonTest jsonTest = new JsonTest("Jack"); + // + // //add kv ops for data account: Bytes, string, long, json string + DataAccountKVSetOperation dataKvsetOP = txTpl.dataAccount(dataAddr).set("A", "Value_A_0".getBytes(), -1) + .set("B", "Value_B_0", -1).set("C", currentTime, -1).set("D", JSON.toJSONString(jsonTest), -1) + .getOperation(); + + // 签名; + PreparedTransaction ptx = txTpl.prepare(); + ptx.sign(adminKey); + + // 提交并等待共识返回; + TransactionResponse txResp = ptx.commit(); + + try { + Thread.sleep(6000); + } catch (Exception e) { + e.printStackTrace(); + } + + KVDataEntry[] kvDataEntries = blockchainService.getDataEntries(ledgerHashs[0], dataAddr.toBase58(), "A", "B", + "C", "D"); + for (int i = 0; i < kvDataEntries.length; i++) { + Object result = kvDataEntries[i].getValue(); + System.out.println("result = " + result); + } + + return; + } + + public void testConsistencyAmongNodes(IntegratedContext context) { + int[] ids = context.getNodeIds(); + Node[] nodes = new Node[ids.length]; + LedgerRepository[] ledgers = new LedgerRepository[ids.length]; + for (int i = 0; i < nodes.length; i++) { + nodes[i] = context.getNode(ids[i]); + HashDigest ledgerHash = nodes[i].getLedgerManager().getLedgerHashs()[0]; + ledgers[i] = nodes[i].getLedgerManager().getLedger(ledgerHash); + } + LedgerRepository ledger0 = ledgers[0]; + LedgerBlock latestBlock0 = ledger0.retrieveLatestBlock(); + for (int i = 1; i < ledgers.length; i++) { + LedgerRepository otherLedger = ledgers[i]; + LedgerBlock otherLatestBlock = otherLedger.retrieveLatestBlock(); + assertEquals(ledger0.getHash(), otherLedger.getHash()); + assertEquals(ledger0.getLatestBlockHeight(), otherLedger.getLatestBlockHeight()); + assertEquals(ledger0.getLatestBlockHash(), otherLedger.getLatestBlockHash()); + + assertEquals(latestBlock0.getHeight(), otherLatestBlock.getHeight()); + assertEquals(latestBlock0.getHash(), otherLatestBlock.getHash()); + assertEquals(latestBlock0.getAdminAccountHash(), otherLatestBlock.getAdminAccountHash()); + assertEquals(latestBlock0.getTransactionSetHash(), otherLatestBlock.getTransactionSetHash()); + assertEquals(latestBlock0.getUserAccountSetHash(), otherLatestBlock.getUserAccountSetHash()); + assertEquals(latestBlock0.getDataAccountSetHash(), otherLatestBlock.getDataAccountSetHash()); + assertEquals(latestBlock0.getContractAccountSetHash(), otherLatestBlock.getContractAccountSetHash()); + assertEquals(latestBlock0.getPreviousHash(), otherLatestBlock.getPreviousHash()); + } + } + + private IntegratedContext initLedgers(LedgerInitConsensusConfig.ConsensusConfig config, String[] dbConns) { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_integration(); + Properties props = LedgerInitializeWeb4SingleStepsTest.loadConsensusSetting(config.getConfigPath()); + ConsensusProvider csProvider = LedgerInitConsensusConfig.getConsensusProvider(config.getProvider()); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext nodeCtx0 = new NodeWebContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext nodeCtx1 = new NodeWebContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext nodeCtx2 = new NodeWebContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext nodeCtx3 = new NodeWebContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[0], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[1], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[2], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeWeb4SingleStepsTest.PRIV_KEYS[3], + LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + String encodedPassword = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeWeb4SingleStepsTest.PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(dbConns[0]); + LedgerBindingConfig bindingConfig0 = new LedgerBindingConfig(); + AsyncCallback callback0 = nodeCtx0.startInitCommand(privkey0, encodedPassword, initSetting, csProps, + csProvider, testDb0, consolePrompter, bindingConfig0, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(dbConns[1]); + LedgerBindingConfig bindingConfig1 = new LedgerBindingConfig(); + AsyncCallback callback1 = nodeCtx1.startInitCommand(privkey1, encodedPassword, initSetting, csProps, + csProvider, testDb1, consolePrompter, bindingConfig1, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(dbConns[2]); + LedgerBindingConfig bindingConfig2 = new LedgerBindingConfig(); + AsyncCallback callback2 = nodeCtx2.startInitCommand(privkey2, encodedPassword, initSetting, csProps, + csProvider, testDb2, consolePrompter, bindingConfig2, quitLatch); + + DBConnectionConfig testDb3 = new DBConnectionConfig(); + testDb3.setConnectionUri(dbConns[3]); + LedgerBindingConfig bindingConfig3 = new LedgerBindingConfig(); + AsyncCallback callback3 = nodeCtx3.startInitCommand(privkey3, encodedPassword, initSetting, csProps, + csProvider, testDb3, consolePrompter, bindingConfig3, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + assertNotNull(ledgerHash0); + assertEquals(ledgerHash0, ledgerHash1); + assertEquals(ledgerHash0, ledgerHash2); + assertEquals(ledgerHash0, ledgerHash3); + + LedgerRepository ledger0 = nodeCtx0.registLedger(ledgerHash0); + LedgerRepository ledger1 = nodeCtx1.registLedger(ledgerHash1); + LedgerRepository ledger2 = nodeCtx2.registLedger(ledgerHash2); + LedgerRepository ledger3 = nodeCtx3.registLedger(ledgerHash3); + + assertNotNull(ledger0); + assertNotNull(ledger1); + assertNotNull(ledger2); + assertNotNull(ledger3); + + IntegratedContext context = new IntegratedContext(); + + Node node0 = new Node(0); + node0.setConsensusSettings(csProps); + node0.setLedgerManager(nodeCtx0.getLedgerManager()); + node0.setStorageDB(nodeCtx0.getStorageDB()); + node0.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(0).getPubKey(), privkey0)); + node0.setBindingConfig(bindingConfig0); + context.addNode(node0); + + Node node1 = new Node(1); + node1.setConsensusSettings(csProps); + node1.setLedgerManager(nodeCtx1.getLedgerManager()); + node1.setStorageDB(nodeCtx1.getStorageDB()); + node1.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(1).getPubKey(), privkey1)); + node1.setBindingConfig(bindingConfig1); + context.addNode(node1); + + Node node2 = new Node(2); + node2.setConsensusSettings(csProps); + node2.setLedgerManager(nodeCtx2.getLedgerManager()); + node2.setStorageDB(nodeCtx2.getStorageDB()); + node2.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(2).getPubKey(), privkey2)); + node2.setBindingConfig(bindingConfig2); + context.addNode(node2); + + Node node3 = new Node(3); + node3.setConsensusSettings(csProps); + node3.setLedgerManager(nodeCtx3.getLedgerManager()); + node3.setStorageDB(nodeCtx3.getStorageDB()); + node3.setPartiKeyPair(new CryptoKeyPair(initSetting.getConsensusParticipant(3).getPubKey(), privkey3)); + node3.setBindingConfig(bindingConfig3); + context.addNode(node3); + + nodeCtx0.closeServer(); + nodeCtx1.closeServer(); + nodeCtx2.closeServer(); + nodeCtx3.closeServer(); + + return context; + } + + public static LedgerInitProperties loadInitSetting_integration() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_integration.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartConfig.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartConfig.java new file mode 100644 index 00000000..e65229d1 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartConfig.java @@ -0,0 +1,225 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: BftsmartConfig + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/10 下午3:43 + * Description: + */ +package test.com.jd.blockchain.intgr.batch.bftsmart; + +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.security.ShaUtils; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +/** + * + * @author shaozhuguang + * @create 2019/1/10 + * @since 1.0.0 + */ + +public class BftsmartConfig { + + public static final String BFTSMART_DIR = "bftsmart" + File.separator; + + public static final String[] PUB_KEY = new String[64]; + + public static final String[] PRIV_KEY = new String[64]; + + public static final String PWD = Base58Utils.encode(ShaUtils.hash_256("abc".getBytes()));; + + public static final int startBftsmartPort = 26000; + + public static final int startInitPort = 15000; + + static { + load(); + } + + @Test + public void test4ConfigLoad() { + bftsmartConfigInit(4); + bftsmartLedgerInit(4); + } + + @Test + public void test8ConfigLoad() { + bftsmartConfigInit(8); + bftsmartLedgerInit(8); + } + + @Test + public void test16ConfigLoad() { + bftsmartConfigInit(16); + bftsmartLedgerInit(16); + } + + @Test + public void test32ConfigLoad() { + bftsmartConfigInit(32); + bftsmartLedgerInit(32); + } + + @Test + public void test64ConfigLoad() { + bftsmartConfigInit(64); + bftsmartLedgerInit(64); + } + + public void bftsmartLedgerInit(int size) { + String file = BFTSMART_DIR + "ledger_init_bftsmart-" + size + ".init"; + ClassPathResource res = new ClassPathResource(file); + try { + File resFile = res.getFile(); + List fileContent = org.apache.commons.io.FileUtils.readLines(resFile); + // 处理新内容 + List newFileContent = handleInitContent(fileContent, size); + org.apache.commons.io.FileUtils.writeLines(resFile, newFileContent); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private List handleInitContent(List fileContent, int size) { + List newFileContent = new ArrayList<>(); + for (String srcLine : fileContent) { + String dstLine = srcLine; + if (srcLine.startsWith("cons_parti.count")) { + dstLine = "cons_parti.count=" + size; + } else if (srcLine.startsWith("###############cons_parti_configs###############")) { + List parts = initParts(size); + newFileContent.addAll(parts); + dstLine = null; + } + if (dstLine != null) { + newFileContent.add(dstLine); + } + } + return newFileContent; + } + + private List initParts(int size) { + List parts = new ArrayList<>(); + for (int i = 0; i < size; i++) { + parts.add(""); + parts.add("#第" + i + "个参与方的名称"); + parts.add("cons_parti." + i + ".name=xx-" + i + ".com"); + parts.add("#第" + i + "个参与方的公钥文件路径"); + parts.add("cons_parti." + i + ".pubkey-path="); + parts.add("#第" + i + "个参与方的公钥内容(由keygen工具生成),此参数优先于 pubkey-path 参数"); + parts.add("cons_parti." + i + ".pubkey=" + PUB_KEY[i]); + parts.add("#第" + i + "个参与方的账本初始服务的主机"); + parts.add("cons_parti." + i + ".initializer.host=127.0.0.1"); + parts.add("#第" + i + "个参与方的账本初始服务的端口"); + int portVal = startInitPort + i * 15; + parts.add("cons_parti." + i + ".initializer.port=" + portVal); + parts.add("#第" + i + "个参与方的账本初始服务是否开启安全连接"); + parts.add("cons_parti." + i + ".initializer.secure=false"); + parts.add(""); + } + return parts; + } + + public void bftsmartConfigInit(int size) { + String file = BFTSMART_DIR + "bftsmart-" + size + ".config"; + ClassPathResource res = new ClassPathResource(file); + try { + File resFile = res.getFile(); + List fileContent = org.apache.commons.io.FileUtils.readLines(resFile); + // 处理新内容 + List newFileContent = handleContent(fileContent, size); + org.apache.commons.io.FileUtils.writeLines(resFile, newFileContent); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public List handleContent(List fileContent, int size) { + List newFileContent = new ArrayList<>(); + for (String srcLine : fileContent) { + String dstLine = srcLine; + if (srcLine.startsWith("system.initial.view")) { + dstLine = "system.initial.view = " + initViews(size); + } else if (srcLine.startsWith("system.servers.num")) { + dstLine = "system.servers.num = " + size; + } else if (srcLine.startsWith("system.servers.f")) { + dstLine = "system.servers.f = " + initFsize(size); + } else if (srcLine.startsWith("###############system.server###############")) { + List servers = initServers(size); + newFileContent.addAll(servers); + dstLine = null; + } + if (dstLine != null) { + newFileContent.add(dstLine); + } + } + return newFileContent; + } + + private List initServers(int size) { + List servers = new ArrayList<>(); + for (int i = 0; i < size; i++) { + String pubKey = "system.server." + i + ".pubkey=" + PUB_KEY[i]; + String host = "system.server." + i + ".network.host=127.0.0.1"; + int portVal = startBftsmartPort + i * 10; + String port = "system.server." + i + ".network.port=" + portVal; + String secure = "system.server." + i+ ".network.secure=false"; + servers.add("############################################"); + servers.add("###### #Consensus Participant" + i + " ######"); + servers.add("############################################"); + servers.add(""); + servers.add(pubKey); + servers.add(host); + servers.add(port); + servers.add(secure); + servers.add(""); + } + return servers; + } + + private int initFsize(int size) { + return (size - 1) / 3; + } + + private String initViews(int size) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < size; i++) { + if (stringBuilder.length() > 0) { + stringBuilder.append(","); + } + stringBuilder.append(i); + } + return stringBuilder.toString(); + } + + public static void load() { + ClassPathResource res = new ClassPathResource(BFTSMART_DIR + "bftsmart-users.conf"); + try(InputStream in = res.getInputStream()){ + loadUsers(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static void loadUsers(InputStream in) { + Properties props = FileUtils.readProperties(in, "UTF-8"); + for (int i = 0; i < PUB_KEY.length; i++) { + String currUserPubKey = "user[" + i + "]pubKeyBase58"; + String currUserPrivKey = "user[" + i + "]privKeyBase58"; + PUB_KEY[i] = props.getProperty(currUserPubKey).trim(); + PRIV_KEY[i] = props.getProperty(currUserPrivKey).trim(); + System.out.printf("user[%s] pubKey=%s \r\n", i, PUB_KEY[i]); + System.out.printf("user[%s] privKey=%s \r\n", i, PRIV_KEY[i]); + } + } +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartLedgerInit.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartLedgerInit.java new file mode 100644 index 00000000..bc1506c4 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartLedgerInit.java @@ -0,0 +1,393 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: BftsmartLedgerInit + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/10 下午5:03 + * Description: + */ +package test.com.jd.blockchain.intgr.batch.bftsmart; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.gateway.GatewayConfigProperties; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.peer.PeerServerBooter; +import com.jd.blockchain.sdk.BlockchainService; +import com.jd.blockchain.sdk.client.GatewayServiceFactory; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitCommand; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; +import org.junit.Before; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; +import test.com.jd.blockchain.intgr.GatewayTestRunner; +import test.com.jd.blockchain.intgr.IntegrationBase; +import test.com.jd.blockchain.intgr.LedgerInitConsensusConfig; +import test.com.jd.blockchain.intgr.PeerTestRunner; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; + +import static test.com.jd.blockchain.intgr.IntegrationBase.buildLedgers; +import static test.com.jd.blockchain.intgr.IntegrationBase.validKeyPair; +import static test.com.jd.blockchain.intgr.IntegrationBase.validKvWrite; + +/** + * + * @author shaozhuguang + * @create 2019/1/10 + * @since 1.0.0 + */ + +public class BftsmartLedgerInit { + +// private final ExecutorService ledgerInitPools = Executors.newFixedThreadPool(64); + + private final ExecutorService nodeStartPools = Executors.newCachedThreadPool(); + + private final ExecutorService txSendPools = Executors.newFixedThreadPool(20); + + private static String localPath = FileUtils.getCurrentDir() + File.separator + "bftsmart-rocks.db"; + + private BftsmartConfig bftsmartConfig = new BftsmartConfig(); + + private static final int peerStartPort = 20000; + + private static final int registerUserSize = 500; + + private static final int kvStorageSize = 1000; + + private static final boolean isBrowser = true; + + @Before + public void before() { + File localDir = new File(localPath); + if (!localDir.exists()) { + localDir.mkdir(); + } + } + + @Test + public void localConf4NodesLoad() { + bftsmartConfig.test4ConfigLoad(); + localConfLoad(4); + ledgerInitNodes(4); + } + + @Test + public void start4Nodes() { + localConf4NodesLoad(); + PeerTestRunner[] peerNodes = startNodes(4); + // 检查账本一致性 + LedgerRepository[] ledgers = checkNodes(peerNodes); + + txRequestTest(peerNodes, ledgers); + } + + @Test + public void localConf8NodesLoad() { + bftsmartConfig.test8ConfigLoad(); + localConfLoad(8); + ledgerInitNodes(8); + } + + @Test + public void start8Nodes() { + localConf8NodesLoad(); + PeerTestRunner[] peerNodes = startNodes(8); + // 检查账本一致性 + LedgerRepository[] ledgers = checkNodes(peerNodes); + + txRequestTest(peerNodes, ledgers); + } + + @Test + public void localConf16NodesLoad() { + bftsmartConfig.test16ConfigLoad(); + localConfLoad(16); + ledgerInitNodes(16); + } + + @Test + public void start16Nodes() { + localConf16NodesLoad(); + PeerTestRunner[] peerNodes = startNodes(16); + // 检查账本一致性 + LedgerRepository[] ledgers = checkNodes(peerNodes); + + txRequestTest(peerNodes, ledgers); + } + + @Test + public void localConf32NodesLoad() { + bftsmartConfig.test32ConfigLoad(); + localConfLoad(32); + ledgerInitNodes(32); + } + + @Test + public void start32Nodes() { + localConf32NodesLoad(); +// ledgerInitPools.shutdown(); + PeerTestRunner[] peerNodes = startNodes(32); + // 检查账本一致性 + LedgerRepository[] ledgers = checkNodes(peerNodes); + + txRequestTest(peerNodes, ledgers); + } + + @Test + public void localConf64NodesLoad() { + bftsmartConfig.test64ConfigLoad(); + localConfLoad(64); + ledgerInitNodes(64); + } + + @Test + public void start64Nodes() { + localConf64NodesLoad(); + PeerTestRunner[] peerNodes = startNodes(64); + // 检查账本一致性 + LedgerRepository[] ledgers = checkNodes(peerNodes); + + txRequestTest(peerNodes, ledgers); + } + + public void txRequestTest(PeerTestRunner[] peerNodes, LedgerRepository[] ledgers) { + // 测试K-V + GatewayTestRunner gateway = initGateWay(peerNodes[0]); + + LedgerRepository ledgerRepository = ledgers[0]; + + HashDigest ledgerHash = ledgerRepository.getHash(); + + GatewayServiceFactory gwsrvFact = GatewayServiceFactory.connect(gateway.getServiceAddress()); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(BftsmartConfig.PRIV_KEY[0], IntegrationBase.PASSWORD); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(BftsmartConfig.PUB_KEY[0]); + + CryptoKeyPair adminKey = new CryptoKeyPair(pubKey0, privkey0); + + BlockchainService blockchainService = gwsrvFact.getBlockchainService(); + + CountDownLatch cdlUser = new CountDownLatch(registerUserSize); + System.out.println("--------- Register Users Start ---------"); + + for (int i = 0; i < registerUserSize; i++) { + txSendPools.execute(() -> { + IntegrationBase.KeyPairResponse userResponse = IntegrationBase.testSDK_RegisterUser(adminKey, ledgerHash, blockchainService); +// validKeyPair(userResponse, ledgerRepository, IntegrationBase.KeyPairType.USER); + cdlUser.countDown(); + }); + } + IntegrationBase.KeyPairResponse dataAccountResponse = null; + try { + System.out.println("--------- Register Users Waiting ---------"); + cdlUser.await(); + IntegrationBase.testConsistencyAmongNodes(ledgers); + System.out.println("--------- Register Users Success ---------"); + System.out.println("--------- Register DataAccount Start ---------"); + dataAccountResponse = IntegrationBase.testSDK_RegisterDataAccount(adminKey, ledgerHash, blockchainService); + validKeyPair(dataAccountResponse, ledgerRepository, IntegrationBase.KeyPairType.DATAACCOUNT); + System.out.println("--------- Register DataAccount Success ---------"); + Thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + +// CountDownLatch cdlKv = new CountDownLatch(kvStorageSize); +// BlockchainKeyPair da = dataAccountResponse.getKeyPair(); +// for (int i = 0; i < kvStorageSize; i++) { +// txSendPools.execute(() -> { +// IntegrationBase.testSDK_InsertData(adminKey, ledgerHash, blockchainService, da.getAddress()); +// cdlKv.countDown(); +// }); +// } + + try { +// cdlKv.await(); + Thread.sleep(2000); + IntegrationBase.testConsistencyAmongNodes(ledgers); + System.out.println("--------- TestConsistencyAmongNodes Success ---------"); + if (isBrowser) { + Thread.sleep(Integer.MAX_VALUE); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + public GatewayTestRunner initGateWay(PeerTestRunner peerNode) { + String encodedBase58Pwd = KeyGenCommand.encodePasswordAsBase58(LedgerInitializeTest.PASSWORD); + + GatewayConfigProperties.KeyPairConfig gwkey0 = new GatewayConfigProperties.KeyPairConfig(); + gwkey0.setPubKeyValue(BftsmartConfig.PUB_KEY[0]); + gwkey0.setPrivKeyValue(BftsmartConfig.PRIV_KEY[0]); + gwkey0.setPrivKeyPassword(encodedBase58Pwd); + GatewayTestRunner gateway = new GatewayTestRunner("127.0.0.1", 11000, gwkey0, + peerNode.getServiceAddress(), LedgerInitConsensusConfig.bftsmartProvider,null); + + ThreadInvoker.AsyncCallback gwStarting = gateway.start(); + + gwStarting.waitReturn(); + + return gateway; + } + + public LedgerRepository[] checkNodes(PeerTestRunner[] peerNodes) { + int size = peerNodes.length; + LedgerBindingConfig[] ledgerBindingConfigs = new LedgerBindingConfig[size]; + DbConnectionFactory[] connectionFactories = new DbConnectionFactory[size]; + for (int i = 0; i < size; i++) { + ledgerBindingConfigs[i] = peerNodes[i].getLedgerBindingConfig(); + connectionFactories[i] = peerNodes[i].getDBConnectionFactory(); + } + + // 执行测试用例之前,校验每个节点的一致性; + LedgerRepository[] ledgers = buildLedgers(ledgerBindingConfigs, connectionFactories); + IntegrationBase.testConsistencyAmongNodes(ledgers); + return ledgers; + } + + public PeerTestRunner[] startNodes(int size) { + PeerTestRunner[] peerNodes = new PeerTestRunner[size]; + CountDownLatch countDownLatch = new CountDownLatch(size); + try { + for (int i = 0; i < size; i++) { + final int index = i; + nodeStartPools.execute(() -> { + try { + NetworkAddress peerSrvAddr = new NetworkAddress("127.0.0.1", peerStartPort + index * 10); + String ledgerBindingConf = BftsmartConfig.BFTSMART_DIR + "conf" + File.separator + index + File.separator + "ledger-binding.conf"; + ClassPathResource ledgerBindingConfRes = new ClassPathResource(ledgerBindingConf); + LedgerBindingConfig bindingConfig = LedgerBindingConfig.resolve(ledgerBindingConfRes.getInputStream()); + PeerTestRunner peer = new PeerTestRunner(peerSrvAddr, bindingConfig); + ThreadInvoker.AsyncCallback peerStarting = peer.start(); + peerStarting.waitReturn(); + peerNodes[index] = peer; + countDownLatch.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } + countDownLatch.await(); + } catch (Exception e) { + e.printStackTrace(); + } + return peerNodes; + } + + public void ledgerInitNodes(int size) { + try { + CountDownLatch countDownLatch = new CountDownLatch(size); + for (int i = 0; i < size; i++) { + final int index = i; + nodeStartPools.execute((() -> { + try { + // 启动tool-booter + String[] args = new String[4]; + args[0] = "-l"; + String localConf = BftsmartConfig.BFTSMART_DIR + "conf" + File.separator + index + File.separator + "local-bftsmart-" + index + ".conf"; + ClassPathResource localConfRes = new ClassPathResource(localConf); + args[1] = localConfRes.getFile().getAbsolutePath(); + args[2] = "-i"; + String ledgerInit = BftsmartConfig.BFTSMART_DIR + "ledger_init_bftsmart-" + size + ".init"; + ClassPathResource ledgerInitRes = new ClassPathResource(ledgerInit); + args[3] = ledgerInitRes.getFile().getAbsolutePath(); + LedgerInitCommand.main(args); + countDownLatch.countDown(); + } catch (Exception e) { + e.printStackTrace(); + } + })); + } + countDownLatch.await(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public void localConfLoad(int size) { + for (int i = 0; i < size; i++) { + localConfAllLoad(i, size); + } + } + + public void localConfAllLoad(int index, int size) { + String file = BftsmartConfig.BFTSMART_DIR + "conf" + File.separator + index + File.separator + "local-bftsmart-" + index + ".conf"; + ClassPathResource res = new ClassPathResource(file); + try { + File resFile = res.getFile(); + // 处理新内容 + List newFileContent = handleLocalConfContent(index, size); + org.apache.commons.io.FileUtils.writeLines(resFile, newFileContent); + } catch (Exception e) { + e.printStackTrace(); + } + } + + public List handleLocalConfContent(int index, int size) throws Exception { + List newFileContent = new ArrayList<>(); + newFileContent.add("#当前参与方的 id"); + newFileContent.add("local.parti.id=" + index); + newFileContent.add(""); + + newFileContent.add("#当前参与方的公钥"); + newFileContent.add("local.parti.pubkey=" + BftsmartConfig.PUB_KEY[index]); + newFileContent.add(""); + + newFileContent.add("#当前参与方的私钥(密文编码)"); + newFileContent.add("local.parti.privkey=" + BftsmartConfig.PRIV_KEY[index]); + newFileContent.add(""); + + newFileContent.add("#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入"); + newFileContent.add("local.parti.pwd=" + BftsmartConfig.PWD); + newFileContent.add(""); + + String outDir = BftsmartConfig.BFTSMART_DIR + "conf" + File.separator + index + File.separator; + ClassPathResource outDirRes = new ClassPathResource(outDir); + + newFileContent.add("#账本初始化完成后生成的\"账本绑定配置文件\"的输出目录"); + newFileContent.add("ledger.binding.out=" + outDirRes.getFile().getAbsolutePath()); + newFileContent.add(""); + + String dbDir = new File(localPath, "rocksdb" + index + ".db").getAbsolutePath(); + FileUtils.deleteFile(dbDir); + + newFileContent.add("#账本数据库的连接字符"); + newFileContent.add("ledger.db.uri=rocksdb://" + dbDir); + newFileContent.add(""); + + newFileContent.add("#账本数据库的连接口令"); + newFileContent.add("ledger.db.pwd="); + newFileContent.add(""); + + + String consensusDir = BftsmartConfig.BFTSMART_DIR + "bftsmart-" + size + ".config"; + ClassPathResource consensusDirRes = new ClassPathResource(consensusDir); + + newFileContent.add("#共识配置文件路径"); + newFileContent.add("consensus.conf=" + consensusDirRes.getFile().getAbsolutePath()); + newFileContent.add(""); + + newFileContent.add("#共识Providers配置"); + newFileContent.add("consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + return newFileContent; + } +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartTestBase.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartTestBase.java new file mode 100644 index 00000000..237cc4b5 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/batch/bftsmart/BftsmartTestBase.java @@ -0,0 +1,51 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: BftsmartTestBase + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/10 下午3:32 + * Description: + */ +package test.com.jd.blockchain.intgr.batch.bftsmart; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.security.ShaUtils; +import org.junit.Test; + +import static com.jd.blockchain.tools.keygen.KeyGenCommand.encodePrivKey; +import static com.jd.blockchain.tools.keygen.KeyGenCommand.encodePubKey; + +/** + * + * @author shaozhuguang + * @create 2019/1/10 + * @since 1.0.0 + */ + +public class BftsmartTestBase { + + private final int userSize = 64; + + private final byte[] pwdBytes= ShaUtils.hash_256("abc".getBytes()); + + private final String base58Pwd = Base58Utils.encode(pwdBytes); + + @Test + public void newUsers() { + for (int i = 0; i < userSize; i++) { + CryptoKeyPair kp = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + + String base58PubKey = encodePubKey(kp.getPubKey()); + + String base58PrivKey = encodePrivKey(kp.getPrivKey(), pwdBytes); + + System.out.printf("user[%s] privKeyBase58 = %s \r\n", i, base58PrivKey); + System.out.printf("user[%s] pubKeyBase58 = %s \r\n", i, base58PubKey); + System.out.println("------------------------------------------------------"); + } + System.out.printf("pwdBase58 = %s \r\n", base58Pwd); + } +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/capability/LedgerPerfCapabilityTest.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/capability/LedgerPerfCapabilityTest.java new file mode 100644 index 00000000..6c6e4642 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/capability/LedgerPerfCapabilityTest.java @@ -0,0 +1,65 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: test.com.jd.blockchain.intgr.perf.LedgerPerfCapabilityTest + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/6 上午11:15 + * Description: + */ +package test.com.jd.blockchain.intgr.capability; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.UserRegisterOperation; +import org.junit.Test; +import test.com.jd.blockchain.intgr.perf.LedgerPerformanceTest; + +/** + * + * @author shaozhuguang + * @create 2018/12/6 + * @since 1.0.0 + */ + +public class LedgerPerfCapabilityTest { + + private final String CONSENSUS = "-mq"; + + private final String SILENT = "-silent"; + + private final String ROCKSDB = "-rocksdb"; + + private final String USERTEST = "-usertest"; + + static { + DataContractRegistry.register(LedgerInitOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + } + + @Test + public void testKvStorage4Memory() { + LedgerPerformanceTest.test(new String[]{SILENT, CONSENSUS}); + } + + @Test + public void testUserRegister4Memory() { + LedgerPerformanceTest.test(new String[]{USERTEST, SILENT, CONSENSUS}); + } + + @Test + public void testKvStorage4Rocksdb() { + LedgerPerformanceTest.test(new String[]{ROCKSDB, SILENT, CONSENSUS}); + } + + @Test + public void testUserRegister4Rocksdb() { + LedgerPerformanceTest.test(new String[]{USERTEST, ROCKSDB, SILENT, CONSENSUS}); + } + + public void testUserRegister4Redis() { + //test example not verify redis + LedgerPerformanceTest.test(new String[]{"-redis", "-usertest"}); + } + + public void testContract(){LedgerPerformanceTest.test(new String[]{"-contract", "-silent"});} +} \ No newline at end of file diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitSettingTest.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitSettingTest.java new file mode 100644 index 00000000..b2cc5198 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitSettingTest.java @@ -0,0 +1,55 @@ +package test.com.jd.blockchain.intgr.initializer; + +import static org.junit.Assert.*; + +import java.io.IOException; +import java.io.InputStream; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.LedgerInitProperties.ConsensusParticipantConfig; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.codec.HexUtils; + +public class LedgerInitSettingTest { + + @Test + public void test() throws IOException { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger.init"); + InputStream in = ledgerInitSettingResource.getInputStream(); + try { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + assertEquals(4, setting.getConsensusParticipantCount()); + String expectedLedgerSeed = "932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe".replace("-", ""); + String actualLedgerSeed = HexUtils.encode(setting.getLedgerSeed()); + assertEquals(expectedLedgerSeed, actualLedgerSeed); + + ConsensusParticipantConfig part0 = setting.getConsensusParticipant(0); + assertEquals("jd.com", part0.getName()); + assertEquals("keys/jd-com.pub", part0.getPubKeyPath()); + PubKey pubKey0 = KeyGenCommand.decodePubKey("endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna"); + assertEquals(pubKey0, part0.getPubKey()); +// assertEquals("127.0.0.1", part0.getConsensusAddress().getHost()); +// assertEquals(8900, part0.getConsensusAddress().getPort()); +// assertEquals(true, part0.getConsensusAddress().isSecure()); + assertEquals("127.0.0.1", part0.getInitializerAddress().getHost()); + assertEquals(8800, part0.getInitializerAddress().getPort()); + assertEquals(true, part0.getInitializerAddress().isSecure()); + + ConsensusParticipantConfig part1 = setting.getConsensusParticipant(1); + assertEquals(false, part1.getInitializerAddress().isSecure()); + PubKey pubKey1 = KeyGenCommand.decodePubKey("endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ"); + assertEquals(pubKey1, part1.getPubKey()); + + ConsensusParticipantConfig part2 = setting.getConsensusParticipant(2); + assertEquals(null, part2.getPubKey()); + + } finally { + in.close(); + } + } + +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitWebTestConfiguration.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitWebTestConfiguration.java new file mode 100644 index 00000000..c95a2c06 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitWebTestConfiguration.java @@ -0,0 +1,29 @@ +package test.com.jd.blockchain.intgr.initializer; + +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +import com.jd.blockchain.tools.initializer.web.InitWebSecurityConfiguration; +import com.jd.blockchain.tools.initializer.web.InitWebServerConfiguration; + +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +@Import(value = { InitWebServerConfiguration.class, InitWebSecurityConfiguration.class }) +public class LedgerInitWebTestConfiguration { + + @Bean + public CompositeConnectionFactory getCompositeConnectionFactory() { + return new CompositeConnectionFactory(); + } + +// @Bean +// public LedgerManager getLedgerManager() { +// return new LedgerManager(); +// } + +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeTest.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeTest.java new file mode 100644 index 00000000..9bbb5c9e --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeTest.java @@ -0,0 +1,325 @@ +package test.com.jd.blockchain.intgr.initializer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import com.jd.blockchain.storage.service.utils.MemoryDBConnFactory; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.UserAccount; +import com.jd.blockchain.ledger.core.UserAccountSet; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.InitConsensusServiceFactory; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.IntegrationBase; +import test.com.jd.blockchain.intgr.LedgerInitConsensusConfig; +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; + +public class LedgerInitializeTest { + + static { + DataContractRegistry.register(LedgerInitOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + } + + public static final String PASSWORD = IntegrationBase.PASSWORD; + + public static final String[] PUB_KEYS = IntegrationBase.PUB_KEYS; + + public static final String[] PRIV_KEYS = IntegrationBase.PRIV_KEYS; + + private Map serviceRegisterMap = new ConcurrentHashMap<>(); + + public void testMQInitByMemWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.mqConfig, LedgerInitConsensusConfig.memConnectionStrings); + } + + public void testMQInitByRedisWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.mqConfig, LedgerInitConsensusConfig.redisConnectionStrings); + } + + @Test + public void testBftsmartInitWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.bftsmartConfig, LedgerInitConsensusConfig.memConnectionStrings); + } + + public void testInitWith4Nodes(LedgerInitConsensusConfig.ConsensusConfig config, String[] dbConnections) { + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting(); + Properties props = loadConsensusSetting(config.getConfigPath()); + ConsensusProvider csProvider = LedgerInitConsensusConfig.getConsensusProvider(config.getProvider()); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + NodeContext node0 = new NodeContext(initSetting.getConsensusParticipant(0).getInitializerAddress(), + serviceRegisterMap); + NodeContext node1 = new NodeContext(initSetting.getConsensusParticipant(1).getInitializerAddress(), + serviceRegisterMap); + NodeContext node2 = new NodeContext(initSetting.getConsensusParticipant(2).getInitializerAddress(), + serviceRegisterMap); + NodeContext node3 = new NodeContext(initSetting.getConsensusParticipant(3).getInitializerAddress(), + serviceRegisterMap); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(dbConnections[0]); + AsyncCallback callback0 = node0.startInit(0, privkey0, initSetting, csProps, csProvider, testDb0, + consolePrompter); + + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(dbConnections[1]); + AsyncCallback callback1 = node1.startInit(1, privkey1, initSetting, csProps, csProvider, testDb1, + consolePrompter); + + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(dbConnections[2]); + AsyncCallback callback2 = node2.startInit(2, privkey2, initSetting, csProps, csProvider, testDb2, + consolePrompter); + + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + DBConnectionConfig testDb03 = new DBConnectionConfig(); + testDb03.setConnectionUri(dbConnections[3]); + AsyncCallback callback3 = node3.startInit(3, privkey3, initSetting, csProps, csProvider, testDb03, + consolePrompter); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + assertNotNull(ledgerHash0); + assertEquals(ledgerHash0, ledgerHash1); + assertEquals(ledgerHash0, ledgerHash2); + assertEquals(ledgerHash0, ledgerHash3); + + LedgerRepository ledger0 = node0.registLedger(ledgerHash0, dbConnections[0]); + LedgerRepository ledger1 = node1.registLedger(ledgerHash1, dbConnections[1]); + LedgerRepository ledger2 = node2.registLedger(ledgerHash2, dbConnections[2]); + LedgerRepository ledger3 = node3.registLedger(ledgerHash3, dbConnections[3]); + + assertNotNull(ledger0); + assertNotNull(ledger1); + assertNotNull(ledger2); + assertNotNull(ledger3); + + LedgerBlock genesisBlock = ledger0.getLatestBlock(); + assertEquals(0, genesisBlock.getHeight()); + assertEquals(ledgerHash0, genesisBlock.getHash()); + + UserAccountSet userset0 = ledger0.getUserAccountSet(genesisBlock); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + Bytes address0 = AddressEncoding.generateAddress(pubKey0); + UserAccount user0_0 = userset0.getUser(address0); + assertNotNull(user0_0); + + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + Bytes address1 = AddressEncoding.generateAddress(pubKey1); + UserAccount user1_0 = userset0.getUser(address1); + assertNotNull(user1_0); + + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + Bytes address2 = AddressEncoding.generateAddress(pubKey2); + UserAccount user2_0 = userset0.getUser(address2); + assertNotNull(user2_0); + + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + Bytes address3 = AddressEncoding.generateAddress(pubKey3); + UserAccount user3_0 = userset0.getUser(address3); + assertNotNull(user3_0); + } + + public static LedgerInitProperties loadInitSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting(String configPath) { + ClassPathResource ledgerInitSettingResource = new ClassPathResource(configPath); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + private static class ConsensusConfig { + String provider; + + String configPath; + + public String getProvider() { + return provider; + } + + public String getConfigPath() { + return configPath; + } + } + + public static class NodeContext { + + private LedgerManager ledgerManager = new LedgerManager(); + + private MemoryDBConnFactory storageDb = new MemoryDBConnFactory(); + + private InitConsensusServiceFactory initCsServiceFactory; + + private LedgerInitProcess initProcess; + + private CryptoKeyPair partiKey; + + public CryptoKeyPair getPartiKey() { + return partiKey; + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + } + + public MemoryDBConnFactory getStorageDb() { + return storageDb; + } + + public NodeContext(NetworkAddress address, Map serviceRegisterMap) { + this.initCsServiceFactory = new MultiThreadInterInvokerFactory(serviceRegisterMap); + LedgerInitializeWebController initController = new LedgerInitializeWebController(ledgerManager, storageDb, + initCsServiceFactory); + serviceRegisterMap.put(address, initController); + this.initProcess = initController; + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter) { + + partiKey = new CryptoKeyPair(setting.getConsensusParticipant(0).getPubKey(), privKey); + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + return initProcess.initialize(currentId, privKey, setting, csProps, csProvider, dbConnConfig, + prompter); + } + }; + + return invoker.start(); + } + + public AsyncCallback startInit(int currentId, PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, boolean autoVerifyHash) { + + CryptoConfig cryptoSetting = new CryptoConfig(); + cryptoSetting.setAutoVerifyHash(autoVerifyHash); + cryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + + partiKey = new CryptoKeyPair(setting.getConsensusParticipant(0).getPubKey(), privKey); + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + return initProcess.initialize(currentId, privKey, setting, csProps, csProvider, dbConnConfig, + prompter, cryptoSetting); + } + }; + + return invoker.start(); + } + + public LedgerRepository registLedger(HashDigest ledgerHash, String memConn) { + return ledgerManager.register(ledgerHash, storageDb.connect(memConn).getStorageService()); + } + } + + private static class MultiThreadInterInvokerFactory implements InitConsensusServiceFactory { + + private Map nodeConsesusServices; + + public MultiThreadInterInvokerFactory(Map nodeConsesusServices) { + this.nodeConsesusServices = nodeConsesusServices; + } + + @Override + public LedgerInitConsensusService connect(NetworkAddress endpointAddress) { + return new InitConsensusServiceProxy(nodeConsesusServices.get(endpointAddress)); + } + + } + + private static class InitConsensusServiceProxy implements LedgerInitConsensusService { + + private LedgerInitConsensusService initCsService; + + public InitConsensusServiceProxy(LedgerInitConsensusService initCsService) { + this.initCsService = initCsService; + } + + @Override + public LedgerInitPermission requestPermission(int requesterId, SignatureDigest signature) { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected LedgerInitPermission invoke() { + return initCsService.requestPermission(requesterId, signature); + } + }; + return invoker.startAndWait(); + } + + @Override + public LedgerInitDecision synchronizeDecision(LedgerInitDecision initDecision) { + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected LedgerInitDecision invoke() { + return initCsService.synchronizeDecision(initDecision); + } + }; + return invoker.startAndWait(); + } + + } + +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeWeb4Nodes.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeWeb4Nodes.java new file mode 100644 index 00000000..7c6ca543 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeWeb4Nodes.java @@ -0,0 +1,271 @@ +package test.com.jd.blockchain.intgr.initializer; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.*; +import com.jd.blockchain.ledger.core.*; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import com.jd.blockchain.tools.initializer.*; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; +import test.com.jd.blockchain.intgr.IntegrationBase; +import test.com.jd.blockchain.intgr.LedgerInitConsensusConfig; +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import static org.junit.Assert.*; + +public class LedgerInitializeWeb4Nodes { + + public static final String PASSWORD = IntegrationBase.PASSWORD; + + public static final String[] PUB_KEYS = IntegrationBase.PUB_KEYS; + + public static final String[] PRIV_KEYS = IntegrationBase.PRIV_KEYS; + + @Test + public void testMQInitByMemWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.mqConfig, LedgerInitConsensusConfig.memConnectionStrings); + } + + @Test + public void testMQInitByRedisWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.mqConfig, LedgerInitConsensusConfig.redisConnectionStrings); + } + + @Test + public void testBftsmartLedgerInitByMemWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.bftsmartConfig, LedgerInitConsensusConfig.memConnectionStrings); + } + + @Test + public void testBftsmartLedgerInitByRedisWith4Nodes() { + testInitWith4Nodes(LedgerInitConsensusConfig.bftsmartConfig, LedgerInitConsensusConfig.redisConnectionStrings); + } + + public HashDigest testInitWith4Nodes(LedgerInitConsensusConfig.ConsensusConfig config, String[] dbConns) { + System.out.println("----------- is daemon=" + Thread.currentThread().isDaemon()); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting_2(); + Properties props = loadConsensusSetting(config.getConfigPath()); + ConsensusProvider csProvider = LedgerInitConsensusConfig.getConsensusProvider(config.getProvider()); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext node0 = new NodeWebContext(0, initAddr0); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext node1 = new NodeWebContext(1, initAddr1); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext node2 = new NodeWebContext(2, initAddr2); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext node3 = new NodeWebContext(3, initAddr3); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + CountDownLatch quitLatch = new CountDownLatch(4); + + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(dbConns[0]); + AsyncCallback callback0 = node0.startInit(privkey0, initSetting, csProps, csProvider, testDb0, + consolePrompter, quitLatch); + + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(dbConns[1]); + AsyncCallback callback1 = node1.startInit(privkey1, initSetting, csProps, csProvider, testDb1, + consolePrompter, quitLatch); + + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(dbConns[2]); + AsyncCallback callback2 = node2.startInit(privkey2, initSetting, csProps, csProvider, testDb2, + consolePrompter, quitLatch); + + DBConnectionConfig testDb03 = new DBConnectionConfig(); + testDb03.setConnectionUri(dbConns[3]); + AsyncCallback callback3 = node3.startInit(privkey3, initSetting, csProps, csProvider, testDb03, + consolePrompter, quitLatch); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + assertNotNull(ledgerHash0); + assertEquals(ledgerHash0, ledgerHash1); + assertEquals(ledgerHash0, ledgerHash2); + assertEquals(ledgerHash0, ledgerHash3); + + LedgerRepository ledger0 = node0.registLedger(ledgerHash0); + LedgerRepository ledger1 = node1.registLedger(ledgerHash1); + LedgerRepository ledger2 = node2.registLedger(ledgerHash2); + LedgerRepository ledger3 = node3.registLedger(ledgerHash3); + + assertNotNull(ledger0); + assertNotNull(ledger1); + assertNotNull(ledger2); + assertNotNull(ledger3); + + LedgerBlock genesisBlock = ledger0.getLatestBlock(); + assertEquals(0, genesisBlock.getHeight()); + assertEquals(ledgerHash0, genesisBlock.getHash()); + + UserAccountSet userset0 = ledger0.getUserAccountSet(genesisBlock); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + Bytes address0 = AddressEncoding.generateAddress(pubKey0); + System.out.printf("localNodeAddress0 = %s \r\n", address0.toBase58()); + UserAccount user0_0 = userset0.getUser(address0); + assertNotNull(user0_0); + + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + Bytes address1 = AddressEncoding.generateAddress(pubKey1); + UserAccount user1_0 = userset0.getUser(address1); + assertNotNull(user1_0); + System.out.printf("localNodeAddress1 = %s \r\n", address1.toBase58()); + + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + Bytes address2 = AddressEncoding.generateAddress(pubKey2); + UserAccount user2_0 = userset0.getUser(address2); + assertNotNull(user2_0); + System.out.printf("localNodeAddress2 = %s \r\n", address2.toBase58()); + + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + Bytes address3 = AddressEncoding.generateAddress(pubKey3); + UserAccount user3_0 = userset0.getUser(address3); + assertNotNull(user3_0); + System.out.printf("localNodeAddress3 = %s \r\n", address3.toBase58()); + + return ledgerHash0; + } + + public static LedgerInitProperties loadInitSetting_2() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web2.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting(String configPath) { + ClassPathResource ledgerInitSettingResource = new ClassPathResource(configPath); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static class NodeWebContext { + + private NetworkAddress serverAddress; + + private DBConnectionConfig dbConnConfig; + + private volatile ConfigurableApplicationContext ctx; + + private volatile LedgerInitProcess initProcess; + + private volatile LedgerInitializeWebController controller; + + private volatile LedgerManager ledgerManager; + + private volatile CompositeConnectionFactory db; + + private int id; + + public int getId() { + return controller.getId(); + } + + public TransactionContent getInitTxContent() { + return controller.getInitTxContent(); + } + + public LedgerInitPermission getLocalPermission() { + return controller.getLocalPermission(); + } + + public LedgerInitDecision getLocalDecision() { + return controller.getLocalDecision(); + } + + public NodeWebContext(int id, NetworkAddress serverAddress) { + this.id = id; + this.serverAddress = serverAddress; + } + + public LedgerRepository registLedger(HashDigest ledgerHash) { + DbConnection conn = db.connect(dbConnConfig.getUri()); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); + return ledgerRepo; + } + + public AsyncCallback startInit(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, CountDownLatch quitLatch) { + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + doStartServer(); + + NodeWebContext.this.dbConnConfig = dbConnConfig; + HashDigest ledgerHash = NodeWebContext.this.initProcess.initialize(id, privKey, setting, csProps, + csProvider, dbConnConfig, prompter); + + System.out.printf("ledgerHash = %s \r\n", ledgerHash.toBase58()); + + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + + public void doStartServer() { + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String nodebug = "--debug=false"; + String[] innerArgs = { argServerAddress, argServerPort, nodebug }; + + ctx = SpringApplication.run(LedgerInitWebTestConfiguration.class, innerArgs); + + ctx.setId("Node-" + id); + controller = ctx.getBean(LedgerInitializeWebController.class); + ledgerManager = ctx.getBean(LedgerManager.class); + db = ctx.getBean(CompositeConnectionFactory.class); + initProcess = ctx.getBean(LedgerInitProcess.class); + } + } +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeWeb4SingleStepsTest.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeWeb4SingleStepsTest.java new file mode 100644 index 00000000..64e419b2 --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/initializer/LedgerInitializeWeb4SingleStepsTest.java @@ -0,0 +1,473 @@ +package test.com.jd.blockchain.intgr.initializer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.concurrent.CountDownLatch; + +import com.jd.blockchain.storage.service.impl.composite.CompositeConnectionFactory; +import org.junit.Test; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInitOperation; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.UserAccount; +import com.jd.blockchain.ledger.core.UserAccountSet; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.ledger.data.TxRequestBuilder; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitCommand; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.HttpInitConsensServiceFactory; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.initializer.web.LedgerInitializeWebController; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.ThreadInvoker; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.LedgerInitConsensusConfig; +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; + +public class LedgerInitializeWeb4SingleStepsTest { + + public static final String PASSWORD = LedgerInitializeTest.PASSWORD; + + public static final String[] PUB_KEYS = LedgerInitializeTest.PUB_KEYS; + + public static final String[] PRIV_KEYS = LedgerInitializeTest.PRIV_KEYS; + + /** + * 测试一个节点向多个节点请求新建许可的过程; + */ + + public void testWithSingleSteps() { + + } + + public void testWithSingleSteps(LedgerInitConsensusConfig.ConsensusConfig consensusConfig, String[] dbConns) { + Prompter prompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + + HttpInitConsensServiceFactory httpCsSrvFactory = new HttpInitConsensServiceFactory(); + + // 加载初始化配置; + LedgerInitProperties initSetting = loadInitSetting_1(); + // 加载共识配置; + Properties props = loadConsensusSetting(consensusConfig.getConfigPath()); + ConsensusProvider csProvider = LedgerInitConsensusConfig.getConsensusProvider(consensusConfig.getProvider()); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + // 启动服务器; + NetworkAddress initAddr0 = initSetting.getConsensusParticipant(0).getInitializerAddress(); + NodeWebContext node0 = new NodeWebContext(0, initAddr0); + node0.startServer(); + + NetworkAddress initAddr1 = initSetting.getConsensusParticipant(1).getInitializerAddress(); + NodeWebContext node1 = new NodeWebContext(1, initAddr1); + node1.startServer(); + + NetworkAddress initAddr2 = initSetting.getConsensusParticipant(2).getInitializerAddress(); + NodeWebContext node2 = new NodeWebContext(2, initAddr2); + node2.startServer(); + + NetworkAddress initAddr3 = initSetting.getConsensusParticipant(3).getInitializerAddress(); + NodeWebContext node3 = new NodeWebContext(3, initAddr3); + node3.startServer(); + + node0.setPrompter(prompter); + node1.setPrompter(prompter); + node2.setPrompter(prompter); + node3.setPrompter(prompter); + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[0], PASSWORD); + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[1], PASSWORD); + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[2], PASSWORD); + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(PRIV_KEYS[3], PASSWORD); + + PubKey pubKey0 = KeyGenCommand.decodePubKey(PUB_KEYS[0]); + PubKey pubKey1 = KeyGenCommand.decodePubKey(PUB_KEYS[1]); + PubKey pubKey2 = KeyGenCommand.decodePubKey(PUB_KEYS[2]); + PubKey pubKey3 = KeyGenCommand.decodePubKey(PUB_KEYS[3]); + + // 测试生成“账本初始化许可”; + LedgerInitPermission permission0 = testPreparePermisssion(node0, privkey0, initSetting, csProps); + LedgerInitPermission permission1 = testPreparePermisssion(node1, privkey1, initSetting, csProps); + LedgerInitPermission permission2 = testPreparePermisssion(node2, privkey2, initSetting, csProps); + LedgerInitPermission permission3 = testPreparePermisssion(node3, privkey3, initSetting, csProps); + + TransactionContent initTxContent0 = node0.getInitTxContent(); + TransactionContent initTxContent1 = node1.getInitTxContent(); + TransactionContent initTxContent2 = node2.getInitTxContent(); + TransactionContent initTxContent3 = node3.getInitTxContent(); + + assertTrue(TxRequestBuilder.verifySignature(initTxContent0, permission0.getTransactionSignature(), pubKey0)); + assertTrue(TxRequestBuilder.verifySignature(initTxContent1, permission1.getTransactionSignature(), pubKey1)); + assertTrue(TxRequestBuilder.verifySignature(initTxContent2, permission2.getTransactionSignature(), pubKey2)); + assertTrue(TxRequestBuilder.verifySignature(initTxContent3, permission3.getTransactionSignature(), pubKey3)); + + assertNotNull(initTxContent0.getHash()); + if (!initTxContent0.getHash().equals(initTxContent1.getHash())) { + assertNull(initTxContent0.getLedgerHash()); + assertNull(initTxContent1.getLedgerHash()); + Operation[] oplist0 = initTxContent0.getOperations(); + Operation[] oplist1 = initTxContent1.getOperations(); + assertEquals(oplist0.length, oplist1.length); + + LedgerInitOperation initOp0 = (LedgerInitOperation) oplist0[0]; + LedgerInitOperation initOp1 = (LedgerInitOperation) oplist1[0]; + + byte[] initOpBytes0 = BinaryEncodingUtils.encode(initOp0, LedgerInitOperation.class); + byte[] initOpBytes1 = BinaryEncodingUtils.encode(initOp1, LedgerInitOperation.class); + assertTrue(BytesUtils.equals(initOpBytes0, initOpBytes1)); + + UserRegisterOperation regOp00 = (UserRegisterOperation) oplist0[1]; + UserRegisterOperation regOp10 = (UserRegisterOperation) oplist1[1]; + byte[] regOpBytes00 = BinaryEncodingUtils.encode(regOp00, UserRegisterOperation.class); + byte[] regOpBytes10 = BinaryEncodingUtils.encode(regOp10, UserRegisterOperation.class); + assertTrue(BytesUtils.equals(regOpBytes00, regOpBytes10)); + + UserRegisterOperation regOp01 = (UserRegisterOperation) oplist0[2]; + UserRegisterOperation regOp11 = (UserRegisterOperation) oplist1[2]; + byte[] regOpBytes01 = BinaryEncodingUtils.encode(regOp01, UserRegisterOperation.class); + byte[] regOpBytes11 = BinaryEncodingUtils.encode(regOp11, UserRegisterOperation.class); + assertTrue(BytesUtils.equals(regOpBytes01, regOpBytes11)); + + UserRegisterOperation regOp02 = (UserRegisterOperation) oplist0[3]; + UserRegisterOperation regOp12 = (UserRegisterOperation) oplist1[3]; + byte[] regOpBytes02 = BinaryEncodingUtils.encode(regOp02, UserRegisterOperation.class); + byte[] regOpBytes12 = BinaryEncodingUtils.encode(regOp12, UserRegisterOperation.class); + assertTrue(BytesUtils.equals(regOpBytes02, regOpBytes12)); + + UserRegisterOperation regOp03 = (UserRegisterOperation) oplist0[4]; + UserRegisterOperation regOp13 = (UserRegisterOperation) oplist1[4]; + byte[] regOpBytes03 = BinaryEncodingUtils.encode(regOp03, UserRegisterOperation.class); + byte[] regOpBytes13 = BinaryEncodingUtils.encode(regOp13, UserRegisterOperation.class); + assertTrue(BytesUtils.equals(regOpBytes03, regOpBytes13)); + + } + assertEquals(initTxContent0.getHash(), initTxContent1.getHash()); + assertEquals(initTxContent0.getHash(), initTxContent2.getHash()); + assertEquals(initTxContent0.getHash(), initTxContent3.getHash()); + + assertNull(initTxContent0.getLedgerHash()); + assertNull(initTxContent1.getLedgerHash()); + assertNull(initTxContent2.getLedgerHash()); + assertNull(initTxContent3.getLedgerHash()); + + // 测试请求“账本初始化许可”; + // test request permission, and verify the response; + LedgerInitConsensusService initCsService0 = httpCsSrvFactory.connect(initAddr0); + LedgerInitConsensusService initCsService1 = httpCsSrvFactory.connect(initAddr1); + LedgerInitConsensusService initCsService2 = httpCsSrvFactory.connect(initAddr2); + LedgerInitConsensusService initCsService3 = httpCsSrvFactory.connect(initAddr3); + + testRequestPermission(node0, privkey0, node1, initCsService1); + testRequestPermission(node0, privkey0, node2, initCsService2); + testRequestPermission(node0, privkey0, node3, initCsService3); + testRequestPermission(node1, privkey1, node0, initCsService0); + testRequestPermission(node1, privkey1, node2, initCsService2); + testRequestPermission(node1, privkey1, node3, initCsService3); + testRequestPermission(node2, privkey2, node0, initCsService0); + testRequestPermission(node2, privkey2, node1, initCsService1); + testRequestPermission(node2, privkey2, node3, initCsService3); + testRequestPermission(node3, privkey3, node0, initCsService0); + testRequestPermission(node3, privkey3, node1, initCsService1); + testRequestPermission(node3, privkey3, node2, initCsService2); + + // 测试在节点之间共识彼此的“账本初始化许可” + boolean allPermitted0 = node0.consensusPermission(privkey0); + boolean allPermitted1 = node1.consensusPermission(privkey1); + boolean allPermitted2 = node2.consensusPermission(privkey2); + boolean allPermitted3 = node3.consensusPermission(privkey3); + + assertTrue(allPermitted0); + assertTrue(allPermitted1); + assertTrue(allPermitted2); + assertTrue(allPermitted3); + + // 测试生成账本,并创建“账本初始化决议”; + DBConnectionConfig testDb0 = new DBConnectionConfig(dbConns[0]); + DBConnectionConfig testDb1 = new DBConnectionConfig(dbConns[1]); + DBConnectionConfig testDb2 = new DBConnectionConfig(dbConns[2]); + DBConnectionConfig testDb3 = new DBConnectionConfig(dbConns[3]); + + LedgerInitDecision dec0 = node0.prepareLedger(testDb0, privkey0); + LedgerInitDecision dec1 = node1.prepareLedger(testDb1, privkey1); + LedgerInitDecision dec2 = node2.prepareLedger(testDb2, privkey2); + LedgerInitDecision dec3 = node3.prepareLedger(testDb3, privkey3); + + assertEquals(dec0.getLedgerHash(), dec1.getLedgerHash()); + assertEquals(dec0.getLedgerHash(), dec2.getLedgerHash()); + assertEquals(dec0.getLedgerHash(), dec3.getLedgerHash()); + + testRequestDecision(node0, node1, initCsService1); + testRequestDecision(node0, node2, initCsService2); + testRequestDecision(node0, node3, initCsService3); + testRequestDecision(node1, node0, initCsService0); + testRequestDecision(node1, node2, initCsService2); + testRequestDecision(node1, node3, initCsService3); + testRequestDecision(node2, node0, initCsService0); + testRequestDecision(node2, node1, initCsService1); + testRequestDecision(node2, node3, initCsService3); + testRequestDecision(node3, node0, initCsService0); + testRequestDecision(node3, node1, initCsService1); + testRequestDecision(node3, node2, initCsService2); + } + + private LedgerInitPermission testPreparePermisssion(NodeWebContext node, PrivKey privKey, + LedgerInitProperties setting, ConsensusSettings csProps) { + LedgerInitPermission permission = node.preparePermision(privKey, setting, csProps); + + assertEquals(node.getId(), permission.getParticipantId()); + assertNotNull(permission.getTransactionSignature()); + + return permission; + } + + private void testRequestPermission(NodeWebContext fromNode, PrivKey fromPrivkey, NodeWebContext targetNode, + LedgerInitConsensusService targetNodeService) { + SignatureDigest reqSignature = fromNode.createPermissionRequestSignature(fromNode.getId(), fromPrivkey); + LedgerInitPermission targetPermission = targetNodeService.requestPermission(fromNode.getId(), reqSignature); + assertEquals(targetNode.getId(), targetPermission.getParticipantId()); + assertEquals(targetNode.getLocalPermission().getTransactionSignature(), + targetPermission.getTransactionSignature()); + } + + private void testRequestDecision(NodeWebContext fromNode, NodeWebContext targetNode, + LedgerInitConsensusService targetNodeService) { + LedgerInitDecision targetDecision = targetNodeService.synchronizeDecision(fromNode.getLocalDecision()); + assertEquals(targetNode.getId(), targetDecision.getParticipantId()); + assertEquals(targetNode.getLocalDecision().getLedgerHash(), targetDecision.getLedgerHash()); + assertEquals(targetNode.getLocalDecision().getSignature(), targetDecision.getSignature()); + } + + public static LedgerInitProperties loadInitSetting_1() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web1.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting(String configPath) { + ClassPathResource ledgerInitSettingResource = new ClassPathResource(configPath); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static class NodeWebContext { + + private NetworkAddress serverAddress; + + private DBConnectionConfig dbConnConfig; + + private volatile ConfigurableApplicationContext ctx; + + private volatile LedgerInitProcess initProcess; + + private volatile LedgerInitializeWebController controller; + + private volatile LedgerManager ledgerManager; + + private volatile CompositeConnectionFactory db; + + private int id; + + public int getId() { + return controller.getId(); + } + + public TransactionContent getInitTxContent() { + return controller.getInitTxContent(); + } + + public LedgerInitPermission getLocalPermission() { + return controller.getLocalPermission(); + } + + public LedgerInitDecision getLocalDecision() { + return controller.getLocalDecision(); + } + + public NodeWebContext(int id, NetworkAddress serverAddress) { + this.id = id; + this.serverAddress = serverAddress; + } + + public LedgerRepository registLedger(HashDigest ledgerHash) { + // LedgerManage ledgerManager = ctx.getBean(LedgerManage.class); + // + // DbConnectionFactory dbConnFactory = ctx.getBean(DbConnectionFactory.class); + // DbConnection conn = dbConnFactory.connect(dbConnConfig.getUri(), + // dbConnConfig.getPassword()); + + // DbConnection conn = db.connect(dbConnConfig.getUri(), + // dbConnConfig.getPassword()); + DbConnection conn = db.connect(dbConnConfig.getUri()); + LedgerRepository ledgerRepo = ledgerManager.register(ledgerHash, conn.getStorageService()); + return ledgerRepo; + } + + public SignatureDigest createPermissionRequestSignature(int requesterId, PrivKey privKey) { + return controller.signPermissionRequest(requesterId, privKey); + } + + public AsyncCallback startInit(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, CountDownLatch quitLatch) { + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + doStartServer(); + + // NodeWebContext.this.initProcess = ctx.getBean(LedgerInitProcess.class); + NodeWebContext.this.dbConnConfig = dbConnConfig; + HashDigest ledgerHash = NodeWebContext.this.initProcess.initialize(id, privKey, setting, csProps, + csProvider, dbConnConfig, prompter); + + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public AsyncCallback startInitCommand(PrivKey privKey, String base58Pwd, + LedgerInitProperties ledgerSetting, ConsensusSettings csProps, ConsensusProvider csProvider, + DBConnectionConfig dbConnConfig, Prompter prompter, LedgerBindingConfig conf, + CountDownLatch quitLatch) { + this.db = new CompositeConnectionFactory(); + this.dbConnConfig = dbConnConfig; + + ThreadInvoker invoker = new ThreadInvoker() { + @Override + protected HashDigest invoke() throws Exception { + LedgerInitCommand initCmd = new LedgerInitCommand(); + HashDigest ledgerHash = initCmd.startInit(id, privKey, base58Pwd, ledgerSetting, csProps, + csProvider, dbConnConfig, prompter, conf, db); + NodeWebContext.this.ledgerManager = initCmd.getLedgerManager(); + quitLatch.countDown(); + return ledgerHash; + } + }; + + return invoker.start(); + } + + public LedgerInitPermission preparePermision(PrivKey privKey, LedgerInitProperties setting, + ConsensusSettings csProps) { + return controller.prepareLocalPermission(id, privKey, setting, csProps); + } + + public boolean consensusPermission(PrivKey privKey) { + return controller.consensusPermisions(privKey); + } + + public LedgerInitDecision prepareLedger(DBConnectionConfig dbConnConfig, PrivKey privKey) { + controller.connectDb(dbConnConfig); + return controller.makeLocalDecision(privKey); + } + + public void startServer() { + ThreadInvoker invoker = new ThreadInvoker() { + + @Override + protected Object invoke() throws Exception { + doStartServer(); + return null; + } + }; + + invoker.startAndWait(); + } + + public void setPrompter(Prompter prompter) { + controller.setPrompter(prompter); + } + + public void doStartServer() { + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String nodebug = "--debug=false"; + String[] innerArgs = { argServerAddress, argServerPort, nodebug }; + + ctx = SpringApplication.run(LedgerInitWebTestConfiguration.class, innerArgs); + + ctx.setId("Node-" + id); + controller = ctx.getBean(LedgerInitializeWebController.class); + ledgerManager = ctx.getBean(LedgerManager.class); + db = ctx.getBean(CompositeConnectionFactory.class); + initProcess = ctx.getBean(LedgerInitProcess.class); + } + + public void closeServer() { + if (this.ctx != null) { + this.ctx.close(); + this.ctx = null; + } + } + + public LedgerManager getLedgerManager() { + return ledgerManager; + // return ctx.getBean(LedgerManager.class); + } + + public CompositeConnectionFactory getStorageDB() { + return db; + // return ctx.getBean(MemoryBasedDb.class); + } + } + + private static class ConsensusConfig { + String provider; + + String configPath; + + public String getProvider() { + return provider; + } + + public String getConfigPath() { + return configPath; + } + } + +} diff --git a/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/ledger/LedgerBlockGeneratingTest.java b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/ledger/LedgerBlockGeneratingTest.java new file mode 100644 index 00000000..bd20870a --- /dev/null +++ b/source/test/test-integration/src/test/java/test/com/jd/blockchain/intgr/ledger/LedgerBlockGeneratingTest.java @@ -0,0 +1,211 @@ +package test.com.jd.blockchain.intgr.ledger; + +import static org.junit.Assert.assertEquals; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionRequestBuilder; +import com.jd.blockchain.ledger.core.LedgerDataSet; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerRepository; +import com.jd.blockchain.ledger.core.impl.DefaultOperationHandleRegisteration; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.ledger.core.impl.TransactionBatchProcessor; +import com.jd.blockchain.ledger.data.TxBuilder; +import com.jd.blockchain.ledger.service.TransactionBatchResultHandle; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.Prompter; +import com.jd.blockchain.tools.initializer.web.LedgerInitConsensusService; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.concurrent.ThreadInvoker.AsyncCallback; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import test.com.jd.blockchain.intgr.PresetAnswerPrompter; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest; +import test.com.jd.blockchain.intgr.initializer.LedgerInitializeTest.NodeContext; + +public class LedgerBlockGeneratingTest { + + @Test + public void testBlocksGenerating() { + // 初始化,并获取其中一个节点的账本,单独进行性能测试; + NodeContext node = initLedgers(false)[0]; + + LedgerManager ledgerManager = node.getLedgerManager(); + HashDigest ledgerHash = ledgerManager.getLedgerHashs()[0]; + + DefaultOperationHandleRegisteration opHandler = new DefaultOperationHandleRegisteration(); + + System.gc(); + + test(ledgerHash, node.getPartiKey(), ledgerManager, opHandler, 1000, 5); + } + + private static void test(HashDigest ledgerHash, CryptoKeyPair adminKey, LedgerManager ledgerManager, + DefaultOperationHandleRegisteration opHandler, int batchSize, int batchCount) { + LedgerRepository ledger = ledgerManager.getLedger(ledgerHash); + long height = ledger.getLatestBlockHeight(); + assertEquals(0L, height); + + ConsoleUtils.info("\r\n\r\n================= 准备测试交易 [注册用户] ================="); + int totalCount = batchSize * batchCount; + List txList = prepareUserRegisterRequests(ledgerHash, totalCount, adminKey); + + for (int i = 0; i < batchCount; i++) { + LedgerBlock latestBlock = ledger.getLatestBlock(); + assertEquals(height + i, latestBlock.getHeight()); + + LedgerDataSet previousDataSet = ledger.getDataSet(latestBlock); + ConsoleUtils.info("------ 开始执行交易, 即将生成区块[%s] ------", (latestBlock.getHeight() + 1)); + long startTs = System.currentTimeMillis(); + + LedgerEditor newEditor = ledger.createNextBlock(); + TransactionBatchProcessor txProc = new TransactionBatchProcessor(newEditor, previousDataSet, opHandler, + ledgerManager); + + testTxExec(txList, i * batchSize, batchSize, txProc); + + long elapsedTs = System.currentTimeMillis() - startTs; + + ConsoleUtils.info("新区块已生成! 交易数=%s; 总耗时= %s ms; TPS=%.2f", batchSize, elapsedTs, + (batchSize * 1000.00D / elapsedTs)); + + } + + } + + private static void testTxExec(List txList, int from, int count, + TransactionBatchProcessor txProc) { + for (int i = 0; i < count; i++) { + txProc.schedule(txList.get(from + i)); + } + TransactionBatchResultHandle handle = txProc.prepare(); + handle.commit(); + } + + private static List prepareUserRegisterRequests(HashDigest ledgerHash, int count, + CryptoKeyPair adminKey) { + List txList = new ArrayList<>(); + for (int i = 0; i < count; i++) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + txbuilder.users().register(userKey.getIdentity()); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + txList.add(reqBuilder.buildRequest()); + } + return txList; + } + + public static ConsensusProvider getConsensusProvider() { + return ConsensusProviders.getProvider("com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider"); + } + + public static NodeContext[] initLedgers(boolean optimized) { + Map serviceRegisterMap = new ConcurrentHashMap<>(); + + Prompter consolePrompter = new PresetAnswerPrompter("N"); // new ConsolePrompter(); + LedgerInitProperties initSetting = loadInitSetting(); + Properties props = loadConsensusSetting(); + ConsensusProvider csProvider = getConsensusProvider(); + ConsensusSettings csProps = csProvider.getSettingsFactory().getConsensusSettingsBuilder().createSettings(props); + + NodeContext node0 = new NodeContext(initSetting.getConsensusParticipant(0).getInitializerAddress(), + serviceRegisterMap); + NodeContext node1 = new NodeContext(initSetting.getConsensusParticipant(1).getInitializerAddress(), + serviceRegisterMap); + NodeContext node2 = new NodeContext(initSetting.getConsensusParticipant(2).getInitializerAddress(), + serviceRegisterMap); + NodeContext node3 = new NodeContext(initSetting.getConsensusParticipant(3).getInitializerAddress(), + serviceRegisterMap); + + String[] memConns = new String[]{ + "memory://local/0", + "memory://local/1", + "memory://local/2", + "memory://local/3" + }; + + + PrivKey privkey0 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeTest.PRIV_KEYS[0], + LedgerInitializeTest.PASSWORD); + DBConnectionConfig testDb0 = new DBConnectionConfig(); + testDb0.setConnectionUri(memConns[0]); + AsyncCallback callback0 = node0.startInit(0, privkey0, initSetting, csProps, csProvider, testDb0, + consolePrompter, !optimized); + + PrivKey privkey1 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeTest.PRIV_KEYS[1], + LedgerInitializeTest.PASSWORD); + DBConnectionConfig testDb1 = new DBConnectionConfig(); + testDb1.setConnectionUri(memConns[1]); + AsyncCallback callback1 = node1.startInit(1, privkey1, initSetting, csProps, csProvider, testDb1, + consolePrompter, !optimized); + + PrivKey privkey2 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeTest.PRIV_KEYS[2], + LedgerInitializeTest.PASSWORD); + DBConnectionConfig testDb2 = new DBConnectionConfig(); + testDb2.setConnectionUri(memConns[2]); + AsyncCallback callback2 = node2.startInit(2, privkey2, initSetting, csProps, csProvider, testDb2, + consolePrompter, !optimized); + + PrivKey privkey3 = KeyGenCommand.decodePrivKeyWithRawPassword(LedgerInitializeTest.PRIV_KEYS[3], + LedgerInitializeTest.PASSWORD); + DBConnectionConfig testDb03 = new DBConnectionConfig(); + testDb03.setConnectionUri(memConns[3]); + AsyncCallback callback3 = node3.startInit(3, privkey3, initSetting, csProps, csProvider, testDb03, + consolePrompter, !optimized); + + HashDigest ledgerHash0 = callback0.waitReturn(); + HashDigest ledgerHash1 = callback1.waitReturn(); + HashDigest ledgerHash2 = callback2.waitReturn(); + HashDigest ledgerHash3 = callback3.waitReturn(); + + node0.registLedger(ledgerHash0, memConns[0]); + node1.registLedger(ledgerHash1, memConns[1]); + node2.registLedger(ledgerHash2, memConns[2]); + node3.registLedger(ledgerHash3, memConns[3]); + + return new NodeContext[] { node0, node1, node2, node3 }; + } + + public static LedgerInitProperties loadInitSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("ledger_init_test_web2.init"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + LedgerInitProperties setting = LedgerInitProperties.resolve(in); + return setting; + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static Properties loadConsensusSetting() { + ClassPathResource ledgerInitSettingResource = new ClassPathResource("bftsmart.config"); + try (InputStream in = ledgerInitSettingResource.getInputStream()) { + return FileUtils.readProperties(in); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } +} diff --git a/source/test/test-integration/src/test/resources/Example1.jar b/source/test/test-integration/src/test/resources/Example1.jar new file mode 100644 index 00000000..a22feef9 Binary files /dev/null and b/source/test/test-integration/src/test/resources/Example1.jar differ diff --git a/source/test/test-integration/src/test/resources/bftsmart.config b/source/test/test-integration/src/test/resources/bftsmart.config new file mode 100644 index 00000000..970ddf4b --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart.config @@ -0,0 +1,178 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +############################################ +###### Consensus Participant0 ###### +############################################ +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna + +system.server.0.network.host=127.0.0.1 + +system.server.0.network.port=8910 + +system.server.0.network.secure=false + +############################################ +###### #Consensus Participant1 ###### +############################################ +system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ + +system.server.1.network.host=127.0.0.1 + +system.server.1.network.port=8920 + +system.server.1.network.secure=false + +############################################ +###### #Consensus Participant2 ###### +############################################ +system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R + +system.server.2.network.host=127.0.0.1 + +system.server.2.network.port=8930 + +system.server.2.network.secure=false + +############################################ +###### Consensus Participant3 ###### +############################################ +system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR + +system.server.3.network.host=127.0.0.1 + +system.server.3.network.port=8940 + +system.server.3.network.secure=false + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/test/test-integration/src/test/resources/bftsmart/bftsmart-16.config b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-16.config new file mode 100644 index 00000000..f598f118 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-16.config @@ -0,0 +1,136 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +###############system.server############### + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/test/test-integration/src/test/resources/bftsmart/bftsmart-32.config b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-32.config new file mode 100644 index 00000000..f598f118 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-32.config @@ -0,0 +1,136 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +###############system.server############### + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/test/test-integration/src/test/resources/bftsmart/bftsmart-4.config b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-4.config new file mode 100644 index 00000000..f598f118 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-4.config @@ -0,0 +1,136 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +###############system.server############### + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/test/test-integration/src/test/resources/bftsmart/bftsmart-64.config b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-64.config new file mode 100644 index 00000000..f598f118 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-64.config @@ -0,0 +1,136 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +###############system.server############### + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/test/test-integration/src/test/resources/bftsmart/bftsmart-8.config b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-8.config new file mode 100644 index 00000000..f598f118 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-8.config @@ -0,0 +1,136 @@ + +# 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. + + +############################################ +###### Consensus Commit Block Parameters: transaction count ###### +############################################ +system.block.txsize=15 + +############################################ +###### Consensus Commit Block Parameters: delay time ###### +############################################ +system.block.maxdelay=500 + +###############system.server############### + +############################################ +####### Communication Configurations ####### +############################################ + +#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) +#This parameter is not currently being used 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 + diff --git a/source/test/test-integration/src/test/resources/bftsmart/bftsmart-users.conf b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-users.conf new file mode 100644 index 00000000..3ee2c79f --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/bftsmart-users.conf @@ -0,0 +1,128 @@ +user[0]privKeyBase58=177gk1jTpc8qhaNtNn3Gku36zHL21WhQ7UjuCzVNBULuRgDeMrje9Av2SGgUmwgYbKNeguE +user[0]pubKeyBase58=endPsK36hceJEHbT876oATzGzkM2Wj1fpCzUgcbXWZ5iu1M3XTCf +user[1]privKeyBase58=177gjzU7GVC2kbhQ9fYTv1Zuz2bSBb4mcWdge6qEyvr6cTUuaqtXd9faYMeFYgW9YaBvkrk +user[1]pubKeyBase58=endPsK36cRmi1LhvTWNHjEJq3E5UQ2dBLvhpsABUPisE3zgqx2Z1 +user[2]privKeyBase58=177gjvto4zeC2MhYtK3hpvfxrhXhsRSt7q9WaKtZBLxXhWKLdH3YRYRfY8EazdWfgUhMdM8 +user[2]pubKeyBase58=endPsK36dDC65ACSFPmYtgHSQYqaTHWh5A6KWvNPYZwYDfKKsSYV +user[3]privKeyBase58=177gjweFgu9gzWJWXBgj18b8csnYQ77Ky7Gq1Adj92v9K6KywxbiizCejqkYm2oppYotznr +user[3]pubKeyBase58=endPsK36odY2z3kYTAbWnk6uySiyP3FTGY8f4TnNzD9VvDinZ47n +user[4]privKeyBase58=177gjxr5bPmfLZwaZ69i1ZexvpoLThdkahj8tVKNHtPtxqbCPkvhS9w2CEUcKE2YDmPWBt2 +user[4]pubKeyBase58=endPsK36g2yE2YJCZFEWfA2oDSyUZBPBvkRo3ANqgguVcyZrBBXK +user[5]privKeyBase58=177gjy6uro6c6QqcMFqu6cWg37r6vMqLwxvpotUK6vrfhSVLhsRDu5Mx7c4QyP9fMXa1BME +user[5]pubKeyBase58=endPsK36fMpdDaKWXiDo6jFjzGSsiEvC9URB8rUmNZo8sa1FHqNJ +user[6]privKeyBase58=177gjyinU15F85ERGxU9gaZBtrqy5enmwPJUZgxwu8gg6irVNZT6pEpw3W4hXwCyGVo1SPN +user[6]pubKeyBase58=endPsK36fDBPcSozjcZQyzUYL78cFQKZva3z3kknsCVcdoSg2p9i +user[7]privKeyBase58=177gk1VvWwysYDWKG1ndnzTuB2cpsztHdWHWurL72hSTGD1jcuU7KvV2WWdfnX2B2cTebRk +user[7]pubKeyBase58=endPsK36jbtQAgnEPEFofsjZV4LXRqcvoUs9zfzQ6j2php8aiX8q +user[8]privKeyBase58=177gk2KA4Yj2cTZqYwHfnB1CPAktzzPALdRiSUTezUZjFqVedmnQrZHYKcyK1crSNAevDh8 +user[8]pubKeyBase58=endPsK36ox5HmNDXBrHznL4BU7KVLBbAzDhRG2cbKc25m8wpnvKa +user[9]privKeyBase58=177gjyty49KxKiKGsz4pZF2ULrcVvKBErwfKwkk9b1ox8Tne9iVvtR45Sj7QsoRmQnJHPFf +user[9]pubKeyBase58=endPsK36n9w1ErHGnSqwRUQnJnyuBPQ5QmSCxGzB1k75X4NT4qBC +user[10]privKeyBase58=177gjv53KYZMqJVbfAwYtyPcajGmLjrau8Awf6unkbcNjxmHq7qRovMPrZc6ga21ue1Wa7d +user[10]pubKeyBase58=endPsK36q8B6kenwFiZvhKUThdJM4YFmBh1SoCz1tnF1j3Mcjau4 +user[11]privKeyBase58=177gjzMmTmPLTfeiXwv2k2jMn53svpEnKfUANwJXPmt3gcXnfWRdYVg61bpg98XZ66DW1S1 +user[11]pubKeyBase58=endPsK36kaUmnTESBpakavXpviV76hfGj78BJwSAfDhCaxs59QoZ +user[12]privKeyBase58=177gjwJjhm4eYRP9f2QHPewrM5dzoTojXFf1J8UhcNXT3VseVBknK9x6YickwYQkXKL6AGp +user[12]pubKeyBase58=endPsK36crfPSgzCjs6anNJ47AWdPYKS3bME3kkvAawaN1vqs8N1 +user[13]privKeyBase58=177gjzWtyyLdVtHK7GAQk54xUA9Y456KVCMD2SzWCxbd3d21Jzyxniq6xmYeff9oSVctHnB +user[13]pubKeyBase58=endPsK36j5KZYRhDhsgTsC5ZdKp1rcJX5K7nnmxeYwuRkDe7Rysp +user[14]privKeyBase58=177gjw7PK4xTAHgHdhuPWs6FUkTeeXax7U1Ccxp3XmogPsbAiEqo4wak7h2oXsJwJSRLLyE +user[14]pubKeyBase58=endPsK36gd4NXmvPkUWqJuMLg3a7ySGot26n4e53kSrxXsfXBjKG +user[15]privKeyBase58=177gjwBZXWsnyPKwaEF19h21W2ubcdyzuyUANHZcXqY8hfM7x1RYN38J2mWyZCyoNh7Pp1F +user[15]pubKeyBase58=endPsK36jNqThhuRtjiBBgrTRyGMzvQJh2CHRptd8SPzLxYwXcMR +user[16]privKeyBase58=177gjukkSbRFXy5x3NkBKopranzRE15Gmpny1M752dSqSjxi8FgdodVcydYTXyoFpcaJKT8 +user[16]pubKeyBase58=endPsK36ptiZM2QjMrNUwNgYj8xk1Rgj5Zf5sjSL7XAC7yn62puk +user[17]privKeyBase58=177gk1PxxA8hE4pBHJDo7ZsWUFNCQSKZBd57SwnfjgaWUD2pHBTADjxt6S2zSisoDL7gc9i +user[17]pubKeyBase58=endPsK36efCKq91s34hpf8Y2SBkrBr6xSJ14BNh58T5fzL2PFpND +user[18]privKeyBase58=177gjzzvgUt8Gpfw2fHhSe4Fcz7XWa7vzde43HW8RaECq1RrdiVkmJU9tBBYikmJMGjE2X1 +user[18]pubKeyBase58=endPsK36qwTfPUGx5o43K697bFGrex27z9hKyxs3rrfdu6S5LzLe +user[19]privKeyBase58=177gjsZV6iHWFPPRERKxWp7be2iKoNTH5uYjsEKvULTTRamKyhzM2FUJU6952skGx5D3WNs +user[19]pubKeyBase58=endPsK36drkcb7vVK9Gt8kYJQZpdxSmcRTBDZvShehy4Hcy3KCcK +user[20]privKeyBase58=177gk2Y7YjjwYcJ58BTRSpQvDcQcrNy4Kr7MC9vavgBbN3D789NaW5kF7kKTno32hoUQKcy +user[20]pubKeyBase58=endPsK36q9x98p2gKCd3yG9VEPtSU7bDMkYkiQYvBrsLWqUTmJMh +user[21]privKeyBase58=177gk1xuWkrnRogZVR6zZm88jfwY77DYLWqsw38ySAqB7P5gTMwVbacqBQ1WfRDE88ApfuH +user[21]pubKeyBase58=endPsK36kC2xs3j4iYFvroXqGEmRKwwncTQD79DX3NXzaUXeKaYZ +user[22]privKeyBase58=177gjtGutJvehWKacyPQxm1vFVWm8W23YNNydhMh6pkjzN3s894ga9fUe5LuoQCz6iBcHv6 +user[22]pubKeyBase58=endPsK36njitppxk4mSTFQZ8paMrFkCcxyiJmf4qtCFuurmuTcdB +user[23]privKeyBase58=177gjy13tQybqBwqqeer1L5MZ9rZQJ7zdhyJfHNmnx6t1ALqAAcWbmYpjswMCrwqiWXKc1w +user[23]pubKeyBase58=endPsK36n8niT9ZA8CFDfQgNSDekYQfVzqCSMu9uaVf2SSMz8hhJ +user[24]privKeyBase58=177gk2Xog1oV3udGppyb7pRbioBfXtr9CbYXdhn2h13tLAHfRnXdF2t3psjUHBQ9cR9DHvE +user[24]pubKeyBase58=endPsK36edG59E2cBMHZdGPeUaBMaT6jb9NAiR89DP7LUgT7rKCA +user[25]privKeyBase58=177gjuQRNBMaCsFvPF8vnNdjbvfcchVNgGvoJfoQdwJdypn8jYVm8ZXQy89J6Mi5cKFcJQ1 +user[25]pubKeyBase58=endPsK36g8mv52w79ABiDJ8FYoNEi2rFr9QqsHk2gUFCKJSeBnHp +user[26]privKeyBase58=177gjxFrM9exKoEC6LmFVRwW42V44Fbc3FX1PmVgwXB89MXdHz3ivbkHjK7sDxU8wbnWFeK +user[26]pubKeyBase58=endPsK36cLtDH1mKrfVfYhq8hgc5i2U5GjvsiGKJy4CjDuFsbgdw +user[27]privKeyBase58=177gjzjuMCRtGkWMcFTKR3ohYy6s5oGmHtpUwtX4joHJeBCMHhbRj2n3ZPVy5rPzeHr8YvS +user[27]pubKeyBase58=endPsK36qSTJh43PcmUZ3yJNJZo26KGff6gUPWG7CBjw2SDT6dNi +user[28]privKeyBase58=177gjxFUGbrTHdjA65sFWbAQnrsxXMuUA2UuXXnJ32wVVxeLuUpCswEpa2Z6LzeiR1DXvmn +user[28]pubKeyBase58=endPsK36guc4hCb7aiGC3RTrPd8s9czjnQmsXhvHSTp2CXUWnkhK +user[29]privKeyBase58=177gk1c7jZPrPx3r16oaUw15e94qKD6UYz72qDxyDCNHYHhKHmMp1dsWCora8knyRy3EYvC +user[29]pubKeyBase58=endPsK36icvZ2cvfXPoCyADDAgvFw2Hey9GstaFdBXYwfvU31viQ +user[30]privKeyBase58=177gju3UWrfHE1KHZkqLDB7t4bfFHgP92TGBc3C7rFSMTX9NBZX9ZhXk2EQnVyBvkRjHki1 +user[30]pubKeyBase58=endPsK36dtpxCZmyqoYqVcERoRLo4hC5VWo5LXg3dQx2uhPg5w4g +user[31]privKeyBase58=177gjsWzFrHvu5x6Pk353j2UdnTBffcq7TWWwZMX2VifrGDJT135ccuLWQL9PMD114LVzGZ +user[31]pubKeyBase58=endPsK36rf95mB5Vg5tsPxsoaYCBQEoAPEeFiRxZGcxmDUdyNhaf +user[32]privKeyBase58=177gjxVGMzaeT3Gc5iUu8YXgcsCFtX8zHGrxj4tSNduMnfKun6kC4RW56tnBJSBCnNnAdSD +user[32]pubKeyBase58=endPsK36pk5Gs4axsXRPecJAidcF9HeR3FM5tT1KyxRL91y7RAVa +user[33]privKeyBase58=177gk2FXVprLzfkZZUnNkuwr3TAPrEHm6Xj8dsc1uSq4pD5qfoRLFXbT861hDchYFU9XY8A +user[33]pubKeyBase58=endPsK36i76UE9Vg7uJRTtc7adLz5dxJ8VEiqirdRPhQY7QPGQZq +user[34]privKeyBase58=177gjzRCPGL9iSmSKLzUCrYwNtCS2mAzTyBdpRLLJbE25uPBsu68CCGvZp4ppkuora4c8Fe +user[34]pubKeyBase58=endPsK36oBLXYvPMBzm3itx6HnV6BKx1pd8PMXiK1kdhEh3FPs4M +user[35]privKeyBase58=177gjvNmrWKsh6nidSXbKMpPDUjA6azArLgwZ8DbPMeP2doXxgXyZSXuJcgAEEacF5fd32Q +user[35]pubKeyBase58=endPsK36k4npwVVZzeyWB3MRiKRprXQSDL4BMko6RGQfBiSbgGR7 +user[36]privKeyBase58=177gjzYa8Rp9HVE8cwPxTBRCvenWqkV5YWzPGcmRVJpUyd41PPstL2vMHj3G31HMCFngGf9 +user[36]pubKeyBase58=endPsK36jnVe7WNb5FJKuZ74RrQSyuf258Rb72LGWqfgs4EF7C61 +user[37]privKeyBase58=177gjz1YcRRb7KvA5vtJHis59tk5DH6R7k1gugxkVrwaUYsTsXhkjnqEXa2wSsUPyMGXsEX +user[37]pubKeyBase58=endPsK36pY59gVHWf2pHg5v69DPrbYGu4TUbjDHuUcivbEFfG2Bi +user[38]privKeyBase58=177gjvLDQxsVk4uNsZCSLX9k55Kmw2VgDPDv7cMjhJdorWdx8GkyfqtrnGw5SaPfLnRq76N +user[38]pubKeyBase58=endPsK36brD47DbWcpfbzVZ7mqcuLSBhqe6S4tWB5VHNeU3HrCGS +user[39]privKeyBase58=177gjy1mp1DBCDiSqEHRCVtZuP7dTrFSqvbpm4xUYHAVpqaLaxki9ajCJwHr47UMEgd6E7U +user[39]pubKeyBase58=endPsK36fZrfkLA16yAh3YfKn7sfjoCgNB6JML5DNCiWaj2Qjb5t +user[40]privKeyBase58=177gjtUDrLtvxCCKq7M8EMj7LkmbrfpA1gCc4YWzMF3BhnCuGnGzrJBByhfFnHScNWPg9k7 +user[40]pubKeyBase58=endPsK36pRBC9S2G9rh3BPs7SHgQtDLmQxSgFcE6JuLr6nMDJEKc +user[41]privKeyBase58=177gjxP1PK2XcBVZedJ1KfUQs9EViFVu3AgvqS1YHKYiKz2AtRAS4oLrhSFcL7cmfQTxepV +user[41]pubKeyBase58=endPsK36dez73AKqFPs4YsDNnXsCEV4ybGBdBe6wH1a2Xozc55F2 +user[42]privKeyBase58=177gjt2b87GPSS8eFctAkPELqWg6PhXkeBN1eeyDHtacC8YN9pQMB7X53PsnkQr6ZKdKBRR +user[42]pubKeyBase58=endPsK36qQjdrLEjkd3ukEBTJN6DAZQFxWhKH5iZuwAhAUgUK6bC +user[43]privKeyBase58=177gjwtLzpD6nPUzwr8V1L28sxPyBBScLoDdWhKSWeyfsyVqyuR31eroxjyjVcZpFFWMefa +user[43]pubKeyBase58=endPsK36cKze8GEMCQdMaHueetv3VMr6WRh3mTTeKN3TSkUitqTd +user[44]privKeyBase58=177gju6CW6A6WbTD9SDEXDgTAkdJQEh65qiULbvwJSoCNrcvaRqRebZ3AjdmaRqHWFQqHMN +user[44]pubKeyBase58=endPsK36r3jc7212WrVUAxkoz3DMVZSnhGSovT84EJESSo7sAGo5 +user[45]privKeyBase58=177gjuK9Dn1AYL9GzATY26xdKVcdNSiLNcb4h2He5QWebh3czW8Z2ZBPZDh6aJ1CvDHokPH +user[45]pubKeyBase58=endPsK36ihMZ4WDY3g7LN1N4MuAQYBiCZfiDYBVcsQNk3LQtyS3s +user[46]privKeyBase58=177gjzuuC7XSuFySNnuG4Vdxq9KiXKnEze6r3gS6142AtVdcLBcFGEiEKSoxN79LZioXxGP +user[46]pubKeyBase58=endPsK36jwD38VaqUEJX3BScdhTWz8t4JeRPAJvHmk8ohjNCY6a3 +user[47]privKeyBase58=177gjwK2m3Xut94h7apKB9wJJTL4VMogUhDM4L9A6iBpGhvQsNK2g2b2zakeuUhNRVbvVSN +user[47]pubKeyBase58=endPsK36mzgNV7LrB6uH3dwqsBp5V65YjimrFQNPNqHTWUbRuca9 +user[48]privKeyBase58=177gjxucknnafMMd7xQFGgTtVvgKLmRV5zCKj9uPPsiHhXVnb9WDemjmedf13rTsrwcXi5J +user[48]pubKeyBase58=endPsK36qEgVVZYjMzFRxKEaqmdfHDVrEfC6EaLQWDXJgpNjdRhr +user[49]privKeyBase58=177gjtE4XqdWT4duP34L8p1PpLfSumuPwWQnbYdzv8qwW2JJXYt9FpvedYgK7C8zvofM9Q8 +user[49]pubKeyBase58=endPsK36o9VZGMzJmgK8hFR3dFjE94QQNDRPVbsnugB46mhZ4nJQ +user[50]privKeyBase58=177gk2BLKbHnM6xjikbBa2i9uibkJP65Wd2hZQB69fChaxBNMo2GiZjqJbtaxCGwQsBEXUw +user[50]pubKeyBase58=endPsK36eV7Cm6Py8zRg8EfowKDf52mjCbAZdjgezim2Ubj6QVvE +user[51]privKeyBase58=177gjyiWv6wZ1GwMzHw94LJXZdMb7HHRV6XseQM4Be44ffaBVv6GuKpFQLRpQU35hBB4WcT +user[51]pubKeyBase58=endPsK36bkC8rzNy1kBgvv1x7yTHxARegtddrcBm4MRezJnTbzYf +user[52]privKeyBase58=177gjxhwn23LwwjN2Nu3pnN7R1skSukrGqyquDMf5LBxHJQ6RqJzscpXZNu2hTa8AHBaX29 +user[52]pubKeyBase58=endPsK36hAw55q6e3Fn1bkqQwYbW3goYUDs5HAXVM3PtNzxHoh1y +user[53]privKeyBase58=177gjwSd4r9pyJEazwM2ur1sBm3tkCEGifMFPjmPZqZcoTxyy711a2kvgrAiFFu9fMuphit +user[53]pubKeyBase58=endPsK36qkt9RiY2E6RCrwhDuGh5V6K3ZnvcspFuu7xLYvd9b3BS +user[54]privKeyBase58=177gjsD52ce6yn2oZWt21Q7Z6TGovc6eLSD415E99kk6gghAU2VwKH2L7E3gcQh31JHS2Ct +user[54]pubKeyBase58=endPsK36t48ddNpAjpez8b5TFocL4Qm16EyNyv3DqJAABcgJSuys +user[55]privKeyBase58=177gjwzeLxFNECpTsZgaHj7asXDw2tfA6ARDemXLuQJcEyPd24trrvbw59gWNqkRCZ9toqM +user[55]pubKeyBase58=endPsK36qfjKsFh1jsSfT2MKCMwrabYCpKmT3YBd7WbCkRdeLpcW +user[56]privKeyBase58=177gjsefMKGDuAGp29VFQk71yZNhR6zVDejV13R2tfNkhM7b3ftT8htfPdk8axdWBN96p5u +user[56]pubKeyBase58=endPsK36fbEiN5bZfdbiSYt56NvCwhhVHEBuzCHrdn3bUe3HBQp6 +user[57]privKeyBase58=177gjwQGxVjAdzb21Ktm6wNeyKskLppREjtwVxJpwgUYB5FQWJb96qyV9opBjdrs4DFhbHb +user[57]pubKeyBase58=endPsK36ePXN8pAmW7ZnXqu5eCuMa2Xnh9byXHGofpFVsSj1guCM +user[58]privKeyBase58=177gjxRcVAPULLEc6DNZzaimCKz1y6PcfotrKeH5TKpVK1trekX9xjbG3MG6aSgvgrmTGuc +user[58]pubKeyBase58=endPsK36oUSQMP6VTEJjv3bVgtF3mKstRiQFr9dtFx4RHfXaZUkQ +user[59]privKeyBase58=177gjtSeghrFfEeeicuoNFW4ciTJaBWRgdQ4Unp18uUA1uMwjptvjEWnyio2da5u611rRGH +user[59]pubKeyBase58=endPsK36ez3ihRJYW8m5rN4pi4f74cvJbwe8bZ4acNKZLvWX6UsN +user[60]privKeyBase58=177gjzuueMnccbKkoj6EnZxXZhtySEUqWe7Y9qdDPjEKTHEzJvZLWCmgDCADCbBSG7595qm +user[60]pubKeyBase58=endPsK36jmL17it2cqcjjqEuev4bKGBtKhqaRHgTiQHakytGDU3H +user[61]privKeyBase58=177gjuDD2rqj5kyXTJ4effaABK3FUDUNeRJXH4RFv7yQoQ7yqvVkFPUCiWoDAu1F3Rde4hq +user[61]pubKeyBase58=endPsK36rcaHXedAuHARU3Zy5E1UTKvE9Yr5yrp36DPP8Ge7VsCz +user[62]privKeyBase58=177gjuYzszWekfBatM3yg23fVDYHKkoTKTVP6PCQfFDQTfDZN7bTd7DKMr3fQvXrVC3d8Je +user[62]pubKeyBase58=endPsK36tQXnk4u9Tw9Gm4CDUTDPuag2qv6Yyn3RoBqPgD9zxjax +user[63]privKeyBase58=177gjzS5W8CZdhduphojGqeQgp3ZtwGkfRVwWQpTN6K5cUWRM2F4hwUrfURu4nrixf5V418 +user[63]pubKeyBase58=endPsK36q36veT5yUTKkBkhyDhXzWRRLNDjR9fEbWTiD5Eoy8ygN \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/0/local-bftsmart-0.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/0/local-bftsmart-0.conf new file mode 100644 index 00000000..751c76f3 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/0/local-bftsmart-0.conf @@ -0,0 +1,26 @@ +#当前参与方的 id +local.parti.id=0 + +#当前参与方的公钥 +local.parti.pubkey=endPsK36hceJEHbT876oATzGzkM2Wj1fpCzUgcbXWZ5iu1M3XTCf + +#当前参与方的私钥(密文编码) +local.parti.privkey=177gk1jTpc8qhaNtNn3Gku36zHL21WhQ7UjuCzVNBULuRgDeMrje9Av2SGgUmwgYbKNeguE + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入 +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录 +ledger.binding.out=/Users/shaozhuguang/Documents/ideaProjects/jdchain-release/source/test/test-integration/target/test-classes/bftsmart/conf/0 + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///Users/shaozhuguang/Documents/ideaProjects/jdchain-release/source/test/test-integration/bftsmart-rocks.db/rocksdb0.db + +#账本数据库的连接口令 +ledger.db.pwd= + +#共识配置文件路径 +consensus.conf=/Users/shaozhuguang/Documents/ideaProjects/jdchain-release/source/test/test-integration/target/test-classes/bftsmart/bftsmart-4.config + +#共识Providers配置 +consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/1/local-bftsmart-1.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/1/local-bftsmart-1.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/1/local-bftsmart-1.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/10/local-bftsmart-10.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/10/local-bftsmart-10.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/10/local-bftsmart-10.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/11/local-bftsmart-11.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/11/local-bftsmart-11.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/11/local-bftsmart-11.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/12/local-bftsmart-12.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/12/local-bftsmart-12.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/12/local-bftsmart-12.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/13/local-bftsmart-13.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/13/local-bftsmart-13.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/13/local-bftsmart-13.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/14/local-bftsmart-14.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/14/local-bftsmart-14.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/14/local-bftsmart-14.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/15/local-bftsmart-15.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/15/local-bftsmart-15.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/15/local-bftsmart-15.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/16/local-bftsmart-16.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/16/local-bftsmart-16.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/16/local-bftsmart-16.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/17/local-bftsmart-17.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/17/local-bftsmart-17.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/17/local-bftsmart-17.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/18/local-bftsmart-18.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/18/local-bftsmart-18.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/18/local-bftsmart-18.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/19/local-bftsmart-19.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/19/local-bftsmart-19.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/19/local-bftsmart-19.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/2/local-bftsmart-2.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/2/local-bftsmart-2.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/2/local-bftsmart-2.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/20/local-bftsmart-20.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/20/local-bftsmart-20.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/20/local-bftsmart-20.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/21/local-bftsmart-21.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/21/local-bftsmart-21.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/21/local-bftsmart-21.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/22/local-bftsmart-22.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/22/local-bftsmart-22.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/22/local-bftsmart-22.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/23/local-bftsmart-23.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/23/local-bftsmart-23.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/23/local-bftsmart-23.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/24/local-bftsmart-24.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/24/local-bftsmart-24.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/24/local-bftsmart-24.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/25/local-bftsmart-25.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/25/local-bftsmart-25.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/25/local-bftsmart-25.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/26/local-bftsmart-26.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/26/local-bftsmart-26.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/26/local-bftsmart-26.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/27/local-bftsmart-27.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/27/local-bftsmart-27.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/27/local-bftsmart-27.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/28/local-bftsmart-28.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/28/local-bftsmart-28.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/28/local-bftsmart-28.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/29/local-bftsmart-29.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/29/local-bftsmart-29.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/29/local-bftsmart-29.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/3/local-bftsmart-3.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/3/local-bftsmart-3.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/3/local-bftsmart-3.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/30/local-bftsmart-30.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/30/local-bftsmart-30.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/30/local-bftsmart-30.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/31/local-bftsmart-31.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/31/local-bftsmart-31.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/31/local-bftsmart-31.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/32/local-bftsmart-32.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/32/local-bftsmart-32.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/32/local-bftsmart-32.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/33/local-bftsmart-33.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/33/local-bftsmart-33.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/33/local-bftsmart-33.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/34/local-bftsmart-34.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/34/local-bftsmart-34.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/34/local-bftsmart-34.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/35/local-bftsmart-35.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/35/local-bftsmart-35.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/35/local-bftsmart-35.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/36/local-bftsmart-36.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/36/local-bftsmart-36.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/36/local-bftsmart-36.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/37/local-bftsmart-37.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/37/local-bftsmart-37.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/37/local-bftsmart-37.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/38/local-bftsmart-38.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/38/local-bftsmart-38.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/38/local-bftsmart-38.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/39/local-bftsmart-39.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/39/local-bftsmart-39.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/39/local-bftsmart-39.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/4/local-bftsmart-4.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/4/local-bftsmart-4.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/4/local-bftsmart-4.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/40/local-bftsmart-40.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/40/local-bftsmart-40.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/40/local-bftsmart-40.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/41/local-bftsmart-41.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/41/local-bftsmart-41.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/41/local-bftsmart-41.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/42/local-bftsmart-42.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/42/local-bftsmart-42.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/42/local-bftsmart-42.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/43/local-bftsmart-43.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/43/local-bftsmart-43.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/43/local-bftsmart-43.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/44/local-bftsmart-44.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/44/local-bftsmart-44.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/44/local-bftsmart-44.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/45/local-bftsmart-45.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/45/local-bftsmart-45.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/45/local-bftsmart-45.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/46/local-bftsmart-46.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/46/local-bftsmart-46.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/46/local-bftsmart-46.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/47/local-bftsmart-47.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/47/local-bftsmart-47.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/47/local-bftsmart-47.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/48/local-bftsmart-48.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/48/local-bftsmart-48.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/48/local-bftsmart-48.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/49/local-bftsmart-49.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/49/local-bftsmart-49.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/49/local-bftsmart-49.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/5/local-bftsmart-5.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/5/local-bftsmart-5.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/5/local-bftsmart-5.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/50/local-bftsmart-50.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/50/local-bftsmart-50.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/50/local-bftsmart-50.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/51/local-bftsmart-51.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/51/local-bftsmart-51.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/51/local-bftsmart-51.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/52/local-bftsmart-52.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/52/local-bftsmart-52.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/52/local-bftsmart-52.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/53/local-bftsmart-53.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/53/local-bftsmart-53.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/53/local-bftsmart-53.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/54/local-bftsmart-54.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/54/local-bftsmart-54.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/54/local-bftsmart-54.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/55/local-bftsmart-55.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/55/local-bftsmart-55.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/55/local-bftsmart-55.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/56/local-bftsmart-56.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/56/local-bftsmart-56.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/56/local-bftsmart-56.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/57/local-bftsmart-57.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/57/local-bftsmart-57.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/57/local-bftsmart-57.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/58/local-bftsmart-58.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/58/local-bftsmart-58.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/58/local-bftsmart-58.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/59/local-bftsmart-59.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/59/local-bftsmart-59.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/59/local-bftsmart-59.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/6/local-bftsmart-6.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/6/local-bftsmart-6.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/6/local-bftsmart-6.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/60/local-bftsmart-60.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/60/local-bftsmart-60.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/60/local-bftsmart-60.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/61/local-bftsmart-61.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/61/local-bftsmart-61.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/61/local-bftsmart-61.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/62/local-bftsmart-62.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/62/local-bftsmart-62.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/62/local-bftsmart-62.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/63/local-bftsmart-63.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/63/local-bftsmart-63.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/63/local-bftsmart-63.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/7/local-bftsmart-7.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/7/local-bftsmart-7.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/7/local-bftsmart-7.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/8/local-bftsmart-8.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/8/local-bftsmart-8.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/8/local-bftsmart-8.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/conf/9/local-bftsmart-9.conf b/source/test/test-integration/src/test/resources/bftsmart/conf/9/local-bftsmart-9.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/conf/9/local-bftsmart-9.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-16.init b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-16.init new file mode 100644 index 00000000..e65e8595 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-16.init @@ -0,0 +1,10 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=16 + +###############cons_parti_configs############### \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-32.init b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-32.init new file mode 100644 index 00000000..3820cf5e --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-32.init @@ -0,0 +1,10 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=32 + +###############cons_parti_configs############### \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-4.init b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-4.init new file mode 100644 index 00000000..41a48616 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-4.init @@ -0,0 +1,10 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +###############cons_parti_configs############### \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-64.init b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-64.init new file mode 100644 index 00000000..405ea0fd --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-64.init @@ -0,0 +1,10 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=64 + +###############cons_parti_configs############### \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-8.init b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-8.init new file mode 100644 index 00000000..861d9fba --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/ledger_init_bftsmart-8.init @@ -0,0 +1,10 @@ +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=8 + +###############cons_parti_configs############### \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-0.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-0.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-0.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-1.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-1.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-1.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-10.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-10.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-10.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-11.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-11.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-11.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-12.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-12.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-12.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-13.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-13.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-13.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-14.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-14.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-14.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-15.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-15.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-15.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-16.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-16.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-16.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-17.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-17.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-17.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-18.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-18.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-18.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-19.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-19.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-19.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-2.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-2.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-2.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-20.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-20.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-20.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-21.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-21.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-21.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-22.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-22.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-22.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-23.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-23.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-23.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-24.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-24.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-24.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-25.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-25.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-25.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-26.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-26.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-26.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-27.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-27.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-27.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-28.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-28.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-28.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-29.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-29.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-29.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-3.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-3.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-3.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-30.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-30.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-30.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-31.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-31.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-31.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-32.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-32.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-32.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-33.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-33.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-33.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-34.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-34.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-34.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-35.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-35.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-35.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-36.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-36.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-36.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-37.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-37.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-37.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-38.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-38.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-38.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-39.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-39.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-39.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-4.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-4.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-4.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-40.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-40.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-40.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-41.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-41.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-41.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-42.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-42.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-42.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-43.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-43.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-43.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-44.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-44.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-44.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-45.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-45.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-45.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-46.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-46.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-46.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-47.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-47.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-47.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-48.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-48.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-48.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-49.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-49.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-49.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-5.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-5.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-5.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-50.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-50.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-50.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-51.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-51.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-51.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-52.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-52.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-52.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-53.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-53.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-53.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-54.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-54.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-54.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-55.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-55.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-55.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-56.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-56.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-56.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-57.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-57.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-57.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-58.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-58.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-58.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-59.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-59.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-59.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-6.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-6.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-6.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-60.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-60.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-60.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-61.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-61.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-61.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-62.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-62.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-62.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-63.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-63.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-63.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-7.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-7.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-7.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-8.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-8.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-8.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-9.conf b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-9.conf new file mode 100644 index 00000000..6f399577 --- /dev/null +++ b/source/test/test-integration/src/test/resources/bftsmart/local-bftsmart-9.conf @@ -0,0 +1,38 @@ +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsuHdbf3PU68Sm1ZU2aMcyB7sLWj94xwBUoUKvTgHq7qGUfg6ynDB62hocYYXSRXD4X + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=../conf/ + +#账本数据库的连接字符 +ledger.db.uri=rocksdb:///export/App08/peer/rocks.db/rocksdb0.db + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf=../conf/init/system.config + +//ledger.base.server=nats://192.168.151.39:4222 +ledger.base.server=rabbit://192.168.151.39:5672 + +#MQ订阅交易主题; +ledger.base.topic.tx=tx-topic + +#MQ订阅交易结块主题; +ledger.base.topic.bl=bl-topic + +#当前开启的共识算法;msg-queue/bft-smart +ledger.consensus.type=msg-queue + +#发起结块的交易数间隔; +ledger.commit.interv.tx=10 + +#发起结块的时间间隔,单位ms; +ledger.commit.interv.ts=5000 diff --git a/source/test/test-integration/src/test/resources/jdchain.policy b/source/test/test-integration/src/test/resources/jdchain.policy new file mode 100644 index 00000000..a76a7d6f --- /dev/null +++ b/source/test/test-integration/src/test/resources/jdchain.policy @@ -0,0 +1,13 @@ +grant codeBase "file:${java.ext.dirs}/*" { + permission java.security.AllPermission; +}; +grant codeBase "file:/E:/gitCode\\block\\prototype\\source\\*" { + permission java.security.AllPermission; +}; +grant { + permission java.io.FilePermission "f:\\tmp\\1.txt", "read"; + permission java.util.PropertyPermission "file.encoding", "read"; + permission java.lang.RuntimePermission "setSecurityManager"; + permission java.util.PropertyPermission "*", " read,write"; + permission java.lang.reflect.ReflectPermission "suppressAccessChecks"; +}; diff --git a/source/test/test-integration/src/test/resources/ledger-binding-mem-0.conf b/source/test/test-integration/src/test/resources/ledger-binding-mem-0.conf new file mode 100644 index 00000000..650c0a45 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-mem-0.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=0 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=memory://local/0 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-mem-1.conf b/source/test/test-integration/src/test/resources/ledger-binding-mem-1.conf new file mode 100644 index 00000000..7ae542c5 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-mem-1.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=1 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2 +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=memory://local/1 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-mem-2.conf b/source/test/test-integration/src/test/resources/ledger-binding-mem-2.conf new file mode 100644 index 00000000..4fdc0283 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-mem-2.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=2 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=memory://local/2 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-mem-3.conf b/source/test/test-integration/src/test/resources/ledger-binding-mem-3.conf new file mode 100644 index 00000000..72690f5d --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-mem-3.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=3 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=memory://local/3 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-redis-0.conf b/source/test/test-integration/src/test/resources/ledger-binding-redis-0.conf new file mode 100644 index 00000000..9abd158f --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-redis-0.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=0 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=redis://192.168.54.112:6379 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-redis-1.conf b/source/test/test-integration/src/test/resources/ledger-binding-redis-1.conf new file mode 100644 index 00000000..756b6a4d --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-redis-1.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=1 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2 +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=redis://192.168.54.112:6379/1 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-redis-2.conf b/source/test/test-integration/src/test/resources/ledger-binding-redis-2.conf new file mode 100644 index 00000000..5e273bb4 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-redis-2.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=2 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=redis://192.168.54.112:6379/2 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-redis-3.conf b/source/test/test-integration/src/test/resources/ledger-binding-redis-3.conf new file mode 100644 index 00000000..b49929c5 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-redis-3.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=3 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=redis://192.168.54.112:6379/3 +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-0.conf b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-0.conf new file mode 100644 index 00000000..7fd14dcd --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-0.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=0 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmFzgFtHtpbJwMCsmWTwjNGTk6SeMKU1522 +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=rocksdb +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-1.conf b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-1.conf new file mode 100644 index 00000000..5e28739a --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-1.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=1 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjw9u84WtuCsK8u2WeH4nWqzgEoJWY7jJF9AU6XwLHSosrcNX3H6SSBsfvR53HgX7KR2 +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmA98VknTbZ1Z7fmbNPHBuN2pbD89ogy8Ha +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=rocksdb +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-2.conf b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-2.conf new file mode 100644 index 00000000..b03edbe5 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-2.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=2 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gk2FpjufgEon92mf2oRRFXDBZkRy8SkFci7Jxc5pApZEJz3oeCoxieWatDD3Xg7i1QEN +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5SmMWsqV2kbgrRMjyQFtSq1wvYuPzeRVepHG +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=rocksdb +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-3.conf b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-3.conf new file mode 100644 index 00000000..7293b33a --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger-binding-rocksdb-3.conf @@ -0,0 +1,19 @@ +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ + + +#第 1 个账本[6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ]的配置; +#账本的当前共识参与方的ID; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.id=3 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk-path= +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pk=177gjvv7qvfCAXroFezSn23UFXLVLFofKS3y6DXkJ2DwVWS4LcRNtxRgiqWmQEeWNz4KQ3J +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY +#账本的当前共识参与方地址 +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.parti.address=5Sm5QFyvN1dVB4GHFxWhDCp8vsJbNkdx31Ds +#账本的存储数据库的连接字符串; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.uri=rocksdb +#账本的存储数据库的连接口令; +binding.6BCg5vgU57ykY6g2CpyUnt5ZMgdxfD1b3qXxQrRyfiXTQ.db.pwd= \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/ledger.init b/source/test/test-integration/src/test/resources/ledger.init new file mode 100644 index 00000000..5bf66ced --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=true +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=8800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=true + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=8810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey= +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=8820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=true + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=8830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/test/resources/ledger_init_test.init b/source/test/test-integration/src/test/resources/ledger_init_test.init new file mode 100644 index 00000000..b936de8c --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger_init_test.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=false +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=8800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=8810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=8820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=8830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/test/resources/ledger_init_test_integration.init b/source/test/test-integration/src/test/resources/ledger_init_test_integration.init new file mode 100644 index 00000000..7744270f --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger_init_test_integration.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=false +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=10100 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=10110 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=10120 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=10130 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/test/resources/ledger_init_test_web1.init b/source/test/test-integration/src/test/resources/ledger_init_test_web1.init new file mode 100644 index 00000000..9811875f --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger_init_test_web1.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=false +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=7800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=7810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=7820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=7830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/test/resources/ledger_init_test_web2.init b/source/test/test-integration/src/test/resources/ledger_init_test_web2.init new file mode 100644 index 00000000..1511dff8 --- /dev/null +++ b/source/test/test-integration/src/test/resources/ledger_init_test_web2.init @@ -0,0 +1,61 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +#ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=9800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=false + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=9810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=9820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=false + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=9830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/test/test-integration/src/test/resources/mq.config b/source/test/test-integration/src/test/resources/mq.config new file mode 100644 index 00000000..33eefafe --- /dev/null +++ b/source/test/test-integration/src/test/resources/mq.config @@ -0,0 +1,12 @@ +system.msg.queue.server=rabbit://192.168.151.40:5672 +system.msg.queue.topic.tx=tx-topic-3 +system.msg.queue.topic.bl=bl-topic-3 +system.msg.queue.topic.msg=msg-topic-3 +system.msg.queue.block.txsize=1000 +system.msg.queue.block.maxdelay=2000 + +system.servers.num=4 +system.server.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +system.server.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +system.server.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +system.server.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR \ No newline at end of file diff --git a/source/test/test-integration/src/test/resources/system.config b/source/test/test-integration/src/test/resources/system.config new file mode 100644 index 00000000..ada42b49 --- /dev/null +++ b/source/test/test-integration/src/test/resources/system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/test/test-ledger-core/pom.xml b/source/test/test-ledger-core/pom.xml new file mode 100644 index 00000000..bd8b83c1 --- /dev/null +++ b/source/test/test-ledger-core/pom.xml @@ -0,0 +1,24 @@ + + 4.0.0 + + com.jd.blockchain + test + 0.8.2.RELEASE + + test-ledger-core + + + + com.jd.blockchain + ledger-core + ${project.version} + + + com.jd.blockchain + storage-redis + ${project.version} + + + \ No newline at end of file diff --git a/source/test/test-ledger-core/src/main/java/test/perf/com/jd/blockchain/ledger/core/MerkleDatasetPerformanceTester.java b/source/test/test-ledger-core/src/main/java/test/perf/com/jd/blockchain/ledger/core/MerkleDatasetPerformanceTester.java new file mode 100644 index 00000000..e0ca88e2 --- /dev/null +++ b/source/test/test-ledger-core/src/main/java/test/perf/com/jd/blockchain/ledger/core/MerkleDatasetPerformanceTester.java @@ -0,0 +1,174 @@ +package test.perf.com.jd.blockchain.ledger.core; + +import java.io.IOException; +import java.util.Random; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.MerkleDataSet; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.ExPolicyKVStorage; +import com.jd.blockchain.storage.service.VersioningKVStorage; +import com.jd.blockchain.storage.service.impl.redis.RedisConnectionFactory; +import com.jd.blockchain.storage.service.utils.MemoryKVStorage; + +public class MerkleDatasetPerformanceTester { + + private static final String MKL_KEY_PREFIX = ""; + + public static void main(String[] args) { + testPerformanceInMemory(); + + // testPerformanceWithRedis(); + } + + public static void testPerformanceInMemory() { + testInsertPerformanceInMermory(1000, 1); + testInsertPerformanceInMermory(10000, 1); + testInsertPerformanceInMermory(20000, 1); + testInsertPerformanceInMermory(40000, 1); + // testInsertPerformance(80000, 1); + // testInsertPerformance(100000, 1); + // testInsertPerformance(1000000, 1); + + System.out.println("============================================================"); + + testInsertPerformanceInMermory(500, 4); + testInsertPerformanceInMermory(1000, 4); + testInsertPerformanceInMermory(10000, 4); + testInsertPerformanceInMermory(20000, 4); + // testInsertPerformance(40000, 4); + // testInsertPerformance(80000, 4); + System.out.println("============================================================"); + testInsertPerformanceInMermory(100, 10); + testInsertPerformanceInMermory(1000, 10); + testInsertPerformanceInMermory(2000, 10); + testInsertPerformanceInMermory(4000, 10); + testInsertPerformanceInMermory(2000, 20); + testInsertPerformanceInMermory(200, 20); + testInsertPerformanceInMermory(4000, 20); + testInsertPerformanceInMermory(400, 20); + // testInsertPerformance(8000, 10); + System.out.println("============================================================"); + testInsertPerformanceInMermory(100, 100); + testInsertPerformanceInMermory(100, 1000); + testInsertPerformanceInMermory(100, 10000); + System.out.println("============================================================"); + testInsertPerformanceInMermory(20, 4); + testInsertPerformanceInMermory(20, 8); + testInsertPerformanceInMermory(20, 10); + testInsertPerformanceInMermory(20, 20); + testInsertPerformanceInMermory(20, 40); + testInsertPerformanceInMermory(20, 100); + testInsertPerformanceInMermory(20, 400); + testInsertPerformanceInMermory(20, 1000); + } + + public static void testPerformanceWithRedis() { + String redisUri = "redis://127.0.0.1:6379/0"; + RedisConnectionFactory connFact = new RedisConnectionFactory(); + try (DbConnection conn = connFact.connect(redisUri)) { + ExPolicyKVStorage exStorage = conn.getStorageService().getExPolicyKVStorage(); + VersioningKVStorage verStorage = conn.getStorageService().getVersioningKVStorage(); + + testInsertPerformance(1000, 1, exStorage, verStorage); + testInsertPerformance(10000, 1, exStorage, verStorage); + testInsertPerformance(20000, 1, exStorage, verStorage); + testInsertPerformance(40000, 1, exStorage, verStorage); + // testInsertPerformance(80000, 1,exStorage, verStorage); + // testInsertPerformance(100000, 1,exStorage, verStorage); + // testInsertPerformance(1000000, 1,exStorage, verStorage); + + System.out.println("============================================================"); + + testInsertPerformance(500, 4, exStorage, verStorage); + testInsertPerformance(1000, 4, exStorage, verStorage); + testInsertPerformance(10000, 4, exStorage, verStorage); + testInsertPerformance(20000, 4, exStorage, verStorage); + // testInsertPerformance(40000, 4,exStorage, verStorage); + // testInsertPerformance(80000, 4,exStorage, verStorage); + System.out.println("============================================================"); + testInsertPerformance(100, 10, exStorage, verStorage); + testInsertPerformance(1000, 10, exStorage, verStorage); + testInsertPerformance(2000, 10, exStorage, verStorage); + testInsertPerformance(4000, 10, exStorage, verStorage); + testInsertPerformance(2000, 20, exStorage, verStorage); + testInsertPerformance(200, 20, exStorage, verStorage); + testInsertPerformance(4000, 20, exStorage, verStorage); + testInsertPerformance(400, 20, exStorage, verStorage); + // testInsertPerformance(8000, 10,exStorage, verStorage); + System.out.println("============================================================"); + testInsertPerformance(100, 100, exStorage, verStorage); + testInsertPerformance(100, 1000, exStorage, verStorage); + testInsertPerformance(100, 10000, exStorage, verStorage); + System.out.println("============================================================"); + testInsertPerformance(20, 4, exStorage, verStorage); + testInsertPerformance(20, 8, exStorage, verStorage); + testInsertPerformance(20, 10, exStorage, verStorage); + testInsertPerformance(20, 20, exStorage, verStorage); + testInsertPerformance(20, 40, exStorage, verStorage); + testInsertPerformance(20, 100, exStorage, verStorage); + testInsertPerformance(20, 400, exStorage, verStorage); + testInsertPerformance(20, 1000, exStorage, verStorage); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + public static void testInsertPerformanceInMermory(int round, int batchCount) { + // Map storageMap1 = new HashMap<>(); + // ExPolicyKVStorage exStorage = new ExistancePolicyKVStorageMap(storageMap1); + // Map storageMap2 = new HashMap<>(); + // VersioningKVStorage verStorage = new VersioningKVStorageMap(storageMap2); + MemoryKVStorage memoryKVStorage = new MemoryKVStorage(); + testInsertPerformance(round, batchCount, memoryKVStorage, memoryKVStorage); + } + + public static void testInsertPerformance(int round, int batchCount, ExPolicyKVStorage exStorage, + VersioningKVStorage verStorage) { + Random rand = new Random(); + + CryptoConfig cryptoConfig = new CryptoConfig(); + cryptoConfig.setHashAlgorithm(CryptoAlgorithm.SHA256); + cryptoConfig.setAutoVerifyHash(true); + + // generate base data sample; + String key; + byte[] data = new byte[64]; + rand.nextBytes(data); + + long startTs = System.currentTimeMillis(); + HashDigest rootHash; + int randomId = rand.nextInt(1000); + MerkleDataSet mds = new MerkleDataSet(cryptoConfig, MKL_KEY_PREFIX, exStorage, verStorage); + for (int i = 0; i < round; i++) { + for (int j = 0; j < batchCount; j++) { + key = "data_" + startTs + "_" + randomId + "_" + (i * batchCount + j); + long v = mds.getVersion(key); + mds.setValue(key, data, v); + } + mds.commit(); + rootHash = mds.getRootHash(); + mds = new MerkleDataSet(rootHash, cryptoConfig, MKL_KEY_PREFIX, exStorage, verStorage, false); + } + + long elapsedTs = System.currentTimeMillis() - startTs; + + System.out.println(String.format( + "Inserted %s keys[round=%s, batchCount=%s], takes %s ms! -- TPS=%.2f, KPS=%.2f;", round * batchCount, + round, batchCount, elapsedTs, round * 1000.0D / elapsedTs, round * batchCount * 1000.0D / elapsedTs)); + + // int exKeys = storageMap1.size(); + // int verKeys = storageMap2.size(); + // System.out.println(String.format( + // "Inserted %s keys[round=%s, batchCount=%s], takes %s ms! -- TPS=%.2f, + // KPS=%.2f, " + // + "Total Storage Keys=%s [ex.count=%s, ver.count=%s];", + // round * batchCount, round, batchCount, elapsedTs, round * 1000.0D / + // elapsedTs, + // round * batchCount * 1000.0D / elapsedTs, exKeys + verKeys, exKeys, + // verKeys)); + } +} diff --git a/source/test/test-ledger-core/src/main/resources/MerkleDataset_Performance_Result_20180922.txt b/source/test/test-ledger-core/src/main/resources/MerkleDataset_Performance_Result_20180922.txt new file mode 100644 index 00000000..5a0e3450 --- /dev/null +++ b/source/test/test-ledger-core/src/main/resources/MerkleDataset_Performance_Result_20180922.txt @@ -0,0 +1,119 @@ + +Environment:Mac Book Pro(IDE + Redis Server 3.2.9); + + +============================================================================================== +======================================= In Memory : 1 ======================================== +============================================================================================== + +Inserted 1000 keys[round=1000, batchCount=1], takes 311 ms! -- TPS=3215.43, KPS=3215.43; +Inserted 10000 keys[round=10000, batchCount=1], takes 894 ms! -- TPS=11185.68, KPS=11185.68; +Inserted 20000 keys[round=20000, batchCount=1], takes 1367 ms! -- TPS=14630.58, KPS=14630.58; +Inserted 40000 keys[round=40000, batchCount=1], takes 2706 ms! -- TPS=14781.97, KPS=14781.97; +============================================================ +Inserted 2000 keys[round=500, batchCount=4], takes 34 ms! -- TPS=14705.88, KPS=58823.53; +Inserted 4000 keys[round=1000, batchCount=4], takes 72 ms! -- TPS=13888.89, KPS=55555.56; +Inserted 40000 keys[round=10000, batchCount=4], takes 811 ms! -- TPS=12330.46, KPS=49321.82; +Inserted 80000 keys[round=20000, batchCount=4], takes 1957 ms! -- TPS=10219.72, KPS=40878.90; +============================================================ +Inserted 1000 keys[round=100, batchCount=10], takes 12 ms! -- TPS=8333.33, KPS=83333.33; +Inserted 10000 keys[round=1000, batchCount=10], takes 118 ms! -- TPS=8474.58, KPS=84745.76; +Inserted 20000 keys[round=2000, batchCount=10], takes 226 ms! -- TPS=8849.56, KPS=88495.58; +Inserted 40000 keys[round=4000, batchCount=10], takes 468 ms! -- TPS=8547.01, KPS=85470.09; +Inserted 40000 keys[round=2000, batchCount=20], takes 384 ms! -- TPS=5208.33, KPS=104166.67; +Inserted 4000 keys[round=200, batchCount=20], takes 31 ms! -- TPS=6451.61, KPS=129032.26; +Inserted 80000 keys[round=4000, batchCount=20], takes 723 ms! -- TPS=5532.50, KPS=110650.07; +Inserted 8000 keys[round=400, batchCount=20], takes 65 ms! -- TPS=6153.85, KPS=123076.92; +============================================================ +Inserted 10000 keys[round=100, batchCount=100], takes 73 ms! -- TPS=1369.86, KPS=136986.30; +Inserted 100000 keys[round=100, batchCount=1000], takes 711 ms! -- TPS=140.65, KPS=140646.98; +Inserted 1000000 keys[round=100, batchCount=10000], takes 9096 ms! -- TPS=10.99, KPS=109938.43; +============================================================ +Inserted 80 keys[round=20, batchCount=4], takes 1 ms! -- TPS=20000.00, KPS=80000.00; +Inserted 160 keys[round=20, batchCount=8], takes 1 ms! -- TPS=20000.00, KPS=160000.00; +Inserted 200 keys[round=20, batchCount=10], takes 1 ms! -- TPS=20000.00, KPS=200000.00; +Inserted 400 keys[round=20, batchCount=20], takes 3 ms! -- TPS=6666.67, KPS=133333.33; +Inserted 800 keys[round=20, batchCount=40], takes 6 ms! -- TPS=3333.33, KPS=133333.33; +Inserted 2000 keys[round=20, batchCount=100], takes 13 ms! -- TPS=1538.46, KPS=153846.15; +Inserted 8000 keys[round=20, batchCount=400], takes 55 ms! -- TPS=363.64, KPS=145454.55; +Inserted 20000 keys[round=20, batchCount=1000], takes 164 ms! -- TPS=121.95, KPS=121951.22; + + +============================================================================================== +======================= In Redis : 1 [存量记录数:0][最终记录数:5161945] ======================== +============================================================================================== + +Inserted 1000 keys[round=1000, batchCount=1], takes 897 ms! -- TPS=1114.83, KPS=1114.83; +Inserted 10000 keys[round=10000, batchCount=1], takes 6293 ms! -- TPS=1589.07, KPS=1589.07; +Inserted 20000 keys[round=20000, batchCount=1], takes 12515 ms! -- TPS=1598.08, KPS=1598.08; +Inserted 40000 keys[round=40000, batchCount=1], takes 26683 ms! -- TPS=1499.08, KPS=1499.08; +============================================================ +Inserted 2000 keys[round=500, batchCount=4], takes 633 ms! -- TPS=789.89, KPS=3159.56; +Inserted 4000 keys[round=1000, batchCount=4], takes 1251 ms! -- TPS=799.36, KPS=3197.44; +Inserted 40000 keys[round=10000, batchCount=4], takes 13840 ms! -- TPS=722.54, KPS=2890.17; +Inserted 80000 keys[round=20000, batchCount=4], takes 27632 ms! -- TPS=723.80, KPS=2895.19; +============================================================ +Inserted 1000 keys[round=100, batchCount=10], takes 277 ms! -- TPS=361.01, KPS=3610.11; +Inserted 10000 keys[round=1000, batchCount=10], takes 2787 ms! -- TPS=358.81, KPS=3588.09; +Inserted 20000 keys[round=2000, batchCount=10], takes 5596 ms! -- TPS=357.40, KPS=3573.98; +Inserted 40000 keys[round=4000, batchCount=10], takes 11133 ms! -- TPS=359.29, KPS=3592.92; +Inserted 40000 keys[round=2000, batchCount=20], takes 10680 ms! -- TPS=187.27, KPS=3745.32; +Inserted 4000 keys[round=200, batchCount=20], takes 1036 ms! -- TPS=193.05, KPS=3861.00; +Inserted 80000 keys[round=4000, batchCount=20], takes 21045 ms! -- TPS=190.07, KPS=3801.38; +Inserted 8000 keys[round=400, batchCount=20], takes 2125 ms! -- TPS=188.24, KPS=3764.71; +============================================================ +Inserted 10000 keys[round=100, batchCount=100], takes 2614 ms! -- TPS=38.26, KPS=3825.55; +Inserted 100000 keys[round=100, batchCount=1000], takes 25076 ms! -- TPS=3.99, KPS=3987.88; +Inserted 1000000 keys[round=100, batchCount=10000], takes 250569 ms! -- TPS=0.40, KPS=3990.92; +============================================================ +Inserted 80 keys[round=20, batchCount=4], takes 25 ms! -- TPS=800.00, KPS=3200.00; +Inserted 160 keys[round=20, batchCount=8], takes 45 ms! -- TPS=444.44, KPS=3555.56; +Inserted 200 keys[round=20, batchCount=10], takes 58 ms! -- TPS=344.83, KPS=3448.28; +Inserted 400 keys[round=20, batchCount=20], takes 125 ms! -- TPS=160.00, KPS=3200.00; +Inserted 800 keys[round=20, batchCount=40], takes 227 ms! -- TPS=88.11, KPS=3524.23; +Inserted 2000 keys[round=20, batchCount=100], takes 537 ms! -- TPS=37.24, KPS=3724.39; +Inserted 8000 keys[round=20, batchCount=400], takes 2054 ms! -- TPS=9.74, KPS=3894.84; +Inserted 20000 keys[round=20, batchCount=1000], takes 5252 ms! -- TPS=3.81, KPS=3808.07; + + +============================================================================================== +======================= In Redis : 2 [存量记录数:3246278][最终记录数:8408223] ================== +============================================================================================== + +Inserted 1000 keys[round=1000, batchCount=1], takes 1084 ms! -- TPS=922.51, KPS=922.51; +Inserted 10000 keys[round=10000, batchCount=1], takes 6716 ms! -- TPS=1488.98, KPS=1488.98; +Inserted 20000 keys[round=20000, batchCount=1], takes 13520 ms! -- TPS=1479.29, KPS=1479.29; +Inserted 40000 keys[round=40000, batchCount=1], takes 26724 ms! -- TPS=1496.78, KPS=1496.78; +============================================================ +Inserted 2000 keys[round=500, batchCount=4], takes 689 ms! -- TPS=725.69, KPS=2902.76; +Inserted 4000 keys[round=1000, batchCount=4], takes 1441 ms! -- TPS=693.96, KPS=2775.85; +Inserted 40000 keys[round=10000, batchCount=4], takes 14211 ms! -- TPS=703.68, KPS=2814.72; +Inserted 80000 keys[round=20000, batchCount=4], takes 29281 ms! -- TPS=683.04, KPS=2732.15; +============================================================ +Inserted 1000 keys[round=100, batchCount=10], takes 304 ms! -- TPS=328.95, KPS=3289.47; +Inserted 10000 keys[round=1000, batchCount=10], takes 3001 ms! -- TPS=333.22, KPS=3332.22; +Inserted 20000 keys[round=2000, batchCount=10], takes 5910 ms! -- TPS=338.41, KPS=3384.09; +Inserted 40000 keys[round=4000, batchCount=10], takes 12445 ms! -- TPS=321.41, KPS=3214.14; +Inserted 40000 keys[round=2000, batchCount=20], takes 10792 ms! -- TPS=185.32, KPS=3706.45; +Inserted 4000 keys[round=200, batchCount=20], takes 1102 ms! -- TPS=181.49, KPS=3629.76; +Inserted 80000 keys[round=4000, batchCount=20], takes 21885 ms! -- TPS=182.77, KPS=3655.47; +Inserted 8000 keys[round=400, batchCount=20], takes 2081 ms! -- TPS=192.22, KPS=3844.31; +============================================================ +Inserted 10000 keys[round=100, batchCount=100], takes 2484 ms! -- TPS=40.26, KPS=4025.76; +Inserted 100000 keys[round=100, batchCount=1000], takes 24897 ms! -- TPS=4.02, KPS=4016.55; +Inserted 1000000 keys[round=100, batchCount=10000], takes 252557 ms! -- TPS=0.40, KPS=3959.50; +============================================================ +Inserted 80 keys[round=20, batchCount=4], takes 26 ms! -- TPS=769.23, KPS=3076.92; +Inserted 160 keys[round=20, batchCount=8], takes 49 ms! -- TPS=408.16, KPS=3265.31; +Inserted 200 keys[round=20, batchCount=10], takes 62 ms! -- TPS=322.58, KPS=3225.81; +Inserted 400 keys[round=20, batchCount=20], takes 127 ms! -- TPS=157.48, KPS=3149.61; +Inserted 800 keys[round=20, batchCount=40], takes 227 ms! -- TPS=88.11, KPS=3524.23; +Inserted 2000 keys[round=20, batchCount=100], takes 560 ms! -- TPS=35.71, KPS=3571.43; +Inserted 8000 keys[round=20, batchCount=400], takes 2100 ms! -- TPS=9.52, KPS=3809.52; +Inserted 20000 keys[round=20, batchCount=1000], takes 5161 ms! -- TPS=3.88, KPS=3875.22; + + + + + + diff --git a/source/tools/pom.xml b/source/tools/pom.xml new file mode 100644 index 00000000..d5986244 --- /dev/null +++ b/source/tools/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + tools + pom + + tools-keygen + tools-keygen-booter + tools-initializer + tools-initializer-booter + tools-capability + + + \ No newline at end of file diff --git a/source/tools/tools-capability/pom.xml b/source/tools/tools-capability/pom.xml new file mode 100644 index 00000000..289fd670 --- /dev/null +++ b/source/tools/tools-capability/pom.xml @@ -0,0 +1,95 @@ + + + + + tools + com.jd.blockchain + 0.8.2.RELEASE + + 4.0.0 + + tools-capability + + tools-capability + + + UTF-8 + 1.8 + 1.8 + + + + + com.jd.blockchain + consensus-framework + ${project.version} + + + + com.jd.blockchain + consensus-mq + ${project.version} + + + + com.jd.blockchain + ledger-model + ${project.version} + + + + com.jd.blockchain + ledger-core + ${project.version} + + + + com.jd.blockchain + utils-common + ${project.version} + + + + com.jd.blockchain + tools-initializer + ${project.version} + + + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + diff --git a/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/CapabilityBooter.java b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/CapabilityBooter.java new file mode 100644 index 00000000..9f7f92a4 --- /dev/null +++ b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/CapabilityBooter.java @@ -0,0 +1,26 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.capability.CapabilityBooter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2019/1/3 下午5:35 + * Description: + */ +package com.jd.blockchain.capability; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * + * @author shaozhuguang + * @create 2019/1/3 + * @since 1.0.0 + */ +@SpringBootApplication +public class CapabilityBooter { + + public static void main(String[] args) { + SpringApplication.run(CapabilityBooter.class, args); + } +} \ No newline at end of file diff --git a/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/CapabilityEngine.java b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/CapabilityEngine.java new file mode 100644 index 00000000..d6eeeb19 --- /dev/null +++ b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/CapabilityEngine.java @@ -0,0 +1,108 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.capability.CapabilityEngine + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/27 上午10:16 + * Description: + */ +package com.jd.blockchain.capability; + +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import com.jd.blockchain.capability.service.RemoteTransactionService; +import com.jd.blockchain.capability.service.SettingsInit; +import com.jd.blockchain.capability.settings.CapabilitySettings; +import com.jd.blockchain.utils.ArgumentSet; + +/** + * + * @author shaozhuguang + * @create 2018/12/27 + * @since 1.0.0 + */ +@Component +public class CapabilityEngine implements CommandLineRunner { + + public static final String MQURL_ARG = "-s"; + + public static final String CONF_ARG = "-c"; + + public static final String TOPIC_ARG = "-t"; + + public static final String UR_AEG = "-ur"; + + public static final String DR_AEG = "-dr"; + + public static final String UR_DR_AEG = "-udr"; + + public static final String DR_KV_AEG = "-drw"; + + public static final String KV_AEG = "-kv"; + + public static void engine(String[] args) { + ArgumentSet arguments = ArgumentSet.resolve(args, + ArgumentSet.setting() + .prefix(MQURL_ARG) + .prefix(CONF_ARG) + .prefix(TOPIC_ARG) + .option(UR_AEG) + .option(DR_AEG) + .option(UR_DR_AEG) + .option(KV_AEG) + .option(DR_KV_AEG)); + try { + ArgumentSet.ArgEntry mqArg = arguments.getArg(MQURL_ARG); + if (mqArg != null) { + String mqUrl = mqArg.getValue(); + CapabilitySettings.MSG_QUEUE_URL = mqUrl; + } + + ArgumentSet.ArgEntry confArg = arguments.getArg(CONF_ARG); + if (confArg != null) { + String conf = confArg.getValue(); + SettingsInit.init(conf); + } else { + SettingsInit.init(CapabilitySettings.settingsConf); + } + + ArgumentSet.ArgEntry topicArg = arguments.getArg(TOPIC_ARG); + if (topicArg != null) { + String topic = topicArg.getValue(); + CapabilitySettings.TX_TOPIC = topic; + } + + RemoteTransactionService service = new RemoteTransactionService(); + + if (arguments.hasOption(UR_AEG)) { + // 单纯注册1亿个用户 + service.userRegister(CapabilitySettings.TX_TOTAL_SIZE); + } else if (arguments.hasOption(DR_AEG)) { + // 单纯注册1亿数据账户 + service.dataAccountRegister(CapabilitySettings.TX_TOTAL_SIZE); + } else if (arguments.hasOption(UR_DR_AEG)) { + // 先注册5千万用户再注册5千万数据账户 + service.userAndDataAccountRegister(CapabilitySettings.TX_HALF_SIZE, + CapabilitySettings.TX_HALF_SIZE); + } else if (arguments.hasOption(DR_KV_AEG)) { + // 先注册1万数据账户,再对每个数据账户写入1万个kv + service.dataAccountRegisterAndKvStorage(CapabilitySettings.DR_SIZE, + CapabilitySettings.KV_SIZE); + } else if (arguments.hasOption(KV_AEG)) { + // 单纯向其中某个数据账户写入KV + service.kvStorage(CapabilitySettings.KV_TOTAL_SIZE); + } else { + // 单纯向其中某个数据账户写入KV + service.kvStorage(CapabilitySettings.KV_TOTAL_SIZE); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Override + public void run(String... args) throws Exception { + engine(args); + } +} \ No newline at end of file diff --git a/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/service/RemoteTransactionService.java b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/service/RemoteTransactionService.java new file mode 100644 index 00000000..80ae2a97 --- /dev/null +++ b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/service/RemoteTransactionService.java @@ -0,0 +1,362 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.capability.service.RemoteTransactionService + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/26 下午5:22 + * Description: + */ +package com.jd.blockchain.capability.service; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLong; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.capability.settings.CapabilitySettings; +import com.jd.blockchain.consensus.mq.factory.MsgQueueFactory; +import com.jd.blockchain.consensus.mq.producer.MsgQueueProducer; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.BlockchainKeyGenerator; +import com.jd.blockchain.ledger.BlockchainKeyPair; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionRequestBuilder; +import com.jd.blockchain.ledger.data.TxBuilder; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * + * @author shaozhuguang + * @create 2018/12/26 + * @since 1.0.0 + */ + +public class RemoteTransactionService { + + private MsgQueueProducer txProducer; + + private final ExecutorService instanceFactory = Executors.newSingleThreadExecutor(); + + private final ArrayBlockingQueue> txBlockingQueue = new ArrayBlockingQueue(1); + + private final LinkedList dataAccountAddress = new LinkedList<>(); + + private Bytes defaultDataAccount; + + private static final AtomicLong keyPrefix = new AtomicLong(); + + public void userRegister(int count) throws Exception { + initTxProducer(); + int loop = 0; + if (count <= 0) { + userCreate(); + } else { + loop = count / CapabilitySettings.TX_SIZE_PER_SEND; + userCreate(loop); + } + // 从队列中获取数据 + if (count <= 0) { + for (;;) { + txRequestSend(); + } + } else { + for (int i = 0; i < loop; i++) { + txRequestSend(); + } + } + closeTxProducer(); + } + + public void dataAccountRegister(int count) throws Exception { + initTxProducer(); + int loop = 0; + if (count <= 0) { + dataAccountCreate(); + } else { + loop = count / CapabilitySettings.TX_SIZE_PER_SEND; + dataAccountCreate(loop); + } + // 从队列中获取数据 + if (count <= 0) { + for (;;) { + txRequestSend(); + } + } else { + for (int i = 0; i < loop; i++) { + txRequestSend(); + } + } + closeTxProducer(); + } + + public void userAndDataAccountRegister(int userCount, int dataAccountCount) throws Exception { + if (userCount <= 0 || dataAccountCount <= 0) { + throw new IllegalArgumentException("userCount and dataAccountCount can not be 0!!!"); + } + initTxProducer(); + int userLoop = userCount / CapabilitySettings.TX_SIZE_PER_SEND; + int dataAccountLoop = dataAccountCount / CapabilitySettings.TX_SIZE_PER_SEND; + userCreate(userLoop); + dataAccountCreate(dataAccountLoop); + for (int i = 0, totalLoop = userLoop + dataAccountCount; i < totalLoop; i++) { + txRequestSend(); + } + closeTxProducer(); + } + + public void dataAccountRegisterAndKvStorage(int dataAccountCount, int kvCount) throws Exception { + if (kvCount <= 0 || dataAccountCount <= 0) { + throw new IllegalArgumentException("userCount and dataAccountCount can not be 0!!!"); + } + initTxProducer(); + int dataAccountLoop = dataAccountCount / CapabilitySettings.TX_SIZE_PER_SEND; + dataAccountCreate(dataAccountLoop); + // 首先将数据账户写入 + for (int i = 0; i < dataAccountLoop; i++) { + txRequestSend(); + } + int kvLoop = kvCount / CapabilitySettings.TX_SIZE_PER_SEND; + // 然后将每个数据账户都写入指定数量的kv + Iterator iterator = dataAccountAddress.iterator(); + while(iterator.hasNext()){ + Bytes address = iterator.next(); + kvStorageCreate(kvCount, address); + } + for (int i = 0, loop = kvLoop * dataAccountCount; i < loop; i++) { + txRequestSend(); + } + + closeTxProducer(); + } + + public void kvStorage(int kvCount) throws Exception { + initTxProducer(); + + dataAccountDefaultCreate(); + try { + txRequestSend(); + // 确认结块成功 + Thread.sleep(10000); + } catch (Exception e) { + throw e; + } + int kvLoop = kvCount / CapabilitySettings.TX_SIZE_PER_SEND; + // 然后将每个数据账户都写入指定数量的kv + Bytes address = defaultDataAccount; + kvStorageCreate(kvLoop, address); + for (int i = 0; i < kvLoop; i++) { + txRequestSend(); + } + + closeTxProducer(); + } + + + + private void txRequestSend() throws Exception { + List txRequests = txBlockingQueue.take(); + if (txRequests != null && !txRequests.isEmpty()) { + Iterator iterator = txRequests.iterator(); + while(iterator.hasNext()){ + byte[] txRequest = iterator.next(); + try { + txProducer.publish(txRequest); + } catch (Exception e) { + e.printStackTrace(); + } + } + ConsoleUtils.info("[*] Transaction Request send success!!!"); + } + } + + private void initTxProducer() throws Exception { + txProducer = MsgQueueFactory.newProducer(CapabilitySettings.MSG_QUEUE_URL, CapabilitySettings.TX_TOPIC); + txProducer.connect(); + ConsoleUtils.info("[*] Transaction Producer start success!!!"); + } + + private void closeTxProducer() throws Exception{ + txProducer.close(); + } + + private void userCreate(int loop) { + instanceFactory.execute(() -> { + for (int index = 0; index < loop; index++) { + // 每次生产10000个,然后放入队列中 + try { + LinkedList txSerializeBytes = userActiveCreate(); + txBlockingQueue.put(txSerializeBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void userCreate() { + // 一直在生产用户 + instanceFactory.execute(() -> { + for (;;) { + // 每次生产10000个,然后放入队列中 + try { + LinkedList txSerializeBytes = userActiveCreate(); + txBlockingQueue.put(txSerializeBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + + private void dataAccountCreate(int loop) { + dataAccountCreate(loop, false); + } + + private void dataAccountCreate(int loop, final boolean isSave) { + instanceFactory.execute(() -> { + for (int index = 0; index < loop; index++) { + // 每次生产10000个,然后放入队列中 + try { + LinkedList txSerializeBytes = dataAccountActiveCreate(isSave); + txBlockingQueue.put(txSerializeBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void dataAccountDefaultCreate() { + instanceFactory.execute(() -> { + List currentBytes = new ArrayList<>(); + TransactionRequest txRequest = dataAccountRegisterRequest(CapabilitySettings.ledgerHash, CapabilitySettings.adminKey); + byte[] serializeBytes = BinaryEncodingUtils.encode(txRequest, TransactionRequest.class); + currentBytes.add(serializeBytes); + try { + txBlockingQueue.put(currentBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + }); + } + + private void dataAccountCreate() { + // 一直在生产用户 + instanceFactory.execute(() -> { + for (;;) { + // 每次生产10000个,然后放入队列中 + try { + LinkedList txSerializeBytes = dataAccountActiveCreate(); + txBlockingQueue.put(txSerializeBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private void kvStorageCreate(int loop, Bytes address) { + // 一直在生产用户 + instanceFactory.execute(() -> { + for (int index = 0; index < loop; index++) { + // 每次生产10000个,然后放入队列中 + try { + LinkedList txSerializeBytes = kvActiveCreate(address); + txBlockingQueue.put(txSerializeBytes); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + }); + } + + private LinkedList userActiveCreate() { + // 每次生产10000个,然后放入队列中 + LinkedList txSerializeBytes = new LinkedList<>(); + for (int i = 0; i < CapabilitySettings.TX_SIZE_PER_SEND; i++) { + TransactionRequest txRequest = userRegisterRequest(CapabilitySettings.ledgerHash, CapabilitySettings.adminKey); + byte[] serializeBytes = BinaryEncodingUtils.encode(txRequest, TransactionRequest.class); + txSerializeBytes.addFirst(serializeBytes); + } + return txSerializeBytes; + } + + private LinkedList dataAccountActiveCreate() { + return dataAccountActiveCreate(false); + } + + private LinkedList dataAccountActiveCreate(boolean isSave) { + // 每次生产10000个,然后放入队列中 + LinkedList txSerializeBytes = new LinkedList<>(); + for (int i = 0; i < CapabilitySettings.TX_SIZE_PER_SEND; i++) { + TransactionRequest txRequest = dataAccountRegisterRequest(CapabilitySettings.ledgerHash, CapabilitySettings.adminKey, isSave); + byte[] serializeBytes = BinaryEncodingUtils.encode(txRequest, TransactionRequest.class); + txSerializeBytes.addFirst(serializeBytes); + } + return txSerializeBytes; + } + + private LinkedList kvActiveCreate(Bytes address) { + // 每次生产10000个,然后放入队列中 + LinkedList txSerializeBytes = new LinkedList<>(); + for (int i = 0; i < CapabilitySettings.TX_SIZE_PER_SEND; i++) { + TransactionRequest txRequest = kvStorageRequest(address, CapabilitySettings.ledgerHash, CapabilitySettings.adminKey); + byte[] serializeBytes = BinaryEncodingUtils.encode(txRequest, TransactionRequest.class); + txSerializeBytes.addFirst(serializeBytes); + } + return txSerializeBytes; + } + + private TransactionRequest userRegisterRequest(HashDigest ledgerHash, CryptoKeyPair adminKey) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair userKey = BlockchainKeyGenerator.getInstance().generate(); + txbuilder.users().register(userKey.getIdentity()); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + return reqBuilder.buildRequest(); + } + + private TransactionRequest dataAccountRegisterRequest(HashDigest ledgerHash, CryptoKeyPair adminKey, boolean isSave) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair dataAccountKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainIdentity identity = dataAccountKey.getIdentity(); + txbuilder.dataAccounts().register(identity); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + if (isSave) { + dataAccountAddress.addFirst(identity.getAddress()); + } + return reqBuilder.buildRequest(); + } + + private TransactionRequest dataAccountRegisterRequest(HashDigest ledgerHash, CryptoKeyPair adminKey) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + BlockchainKeyPair dataAccountKey = BlockchainKeyGenerator.getInstance().generate(); + BlockchainIdentity identity = dataAccountKey.getIdentity(); + txbuilder.dataAccounts().register(identity); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + defaultDataAccount = identity.getAddress(); + return reqBuilder.buildRequest(); + } + + private TransactionRequest kvStorageRequest(Bytes address, HashDigest ledgerHash, CryptoKeyPair adminKey) { + TxBuilder txbuilder = new TxBuilder(ledgerHash); + long currValue = keyPrefix.getAndIncrement(); + txbuilder.dataAccount(address).set("key-" + currValue + "-" + System.currentTimeMillis(), + BytesUtils.toBytes("value-" + currValue), -1L); + TransactionRequestBuilder reqBuilder = txbuilder.prepareRequest(); + reqBuilder.signAsEndpoint(adminKey); + return reqBuilder.buildRequest(); + } +} \ No newline at end of file diff --git a/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/service/SettingsInit.java b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/service/SettingsInit.java new file mode 100644 index 00000000..33820bc1 --- /dev/null +++ b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/service/SettingsInit.java @@ -0,0 +1,149 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.capability.service.SettingsInit + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/27 下午2:26 + * Description: + */ +package com.jd.blockchain.capability.service; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.capability.settings.CapabilitySettings; +import com.jd.blockchain.consensus.action.ActionResponse; +import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; +import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.ContractCodeDeployOperation; +import com.jd.blockchain.ledger.ContractEventSendOperation; +import com.jd.blockchain.ledger.DataAccountKVSetOperation; +import com.jd.blockchain.ledger.DataAccountRegisterOperation; +import com.jd.blockchain.ledger.EndpointRequest; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.NodeRequest; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionContentBody; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionResponse; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.codec.Base58Utils; + +/** + * + * @author shaozhuguang + * @create 2018/12/27 + * @since 1.0.0 + */ + +public class SettingsInit { + + static { + DataContractRegistry.register(LedgerBlock.class); + DataContractRegistry.register(TransactionContent.class); + DataContractRegistry.register(TransactionContentBody.class); + DataContractRegistry.register(TransactionRequest.class); + DataContractRegistry.register(NodeRequest.class); + DataContractRegistry.register(EndpointRequest.class); + DataContractRegistry.register(TransactionResponse.class); + DataContractRegistry.register(DataAccountKVSetOperation.class); + DataContractRegistry.register(DataAccountKVSetOperation.KVWriteEntry.class); + + DataContractRegistry.register(Operation.class); + DataContractRegistry.register(ContractCodeDeployOperation.class); + DataContractRegistry.register(ContractEventSendOperation.class); + DataContractRegistry.register(DataAccountRegisterOperation.class); + DataContractRegistry.register(UserRegisterOperation.class); + + DataContractRegistry.register(ActionResponse.class); + + DataContractRegistry.register(BftsmartConsensusSettings.class); + DataContractRegistry.register(BftsmartNodeSettings.class); + + } + + public static final void init(String settingsFile) throws Exception { + + Settings settings = new Settings(); +// settings.ledgerHash = "6B3aa543AkotypMaLCeuWDTXFLuG9UKyZCSdJBPStJzEe"; + settings.ledgerHash = "6CB4tTkKyfshafJB1xk8deeZ8FxnjJsSnQLExi5Bq6Mum"; + settings.privKey = "177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY"; + settings.pubKey = "endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna"; + settings.pwd = "abc"; + init(settings); + +// File confFile = new File(settingsFile); +// if (settingsFile == null || settingsFile.length() == 0 || !confFile.exists()) { +// ClassPathResource resource = new ClassPathResource(CapabilitySettings.settingsConf); +// confFile = resource.getFile(); +// } +// if (!confFile.exists()) { +// Settings settings = new Settings(); +// settings.ledgerHash = "6B3aa543AkotypMaLCeuWDTXFLuG9UKyZCSdJBPStJzEe"; +// settings.privKey = "177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY"; +// settings.pubKey = "endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna"; +// settings.pwd = "abc"; +// init(settings); +// } else { +// List readLines = FileUtils.readLines(confFile); +// if (readLines != null && !readLines.isEmpty()) { +// Settings settings = new Settings(); +// for (String readLine : readLines) { +// if (readLine.startsWith("ledgerHash")) { +// settings.ledgerHash = readLine.split("=")[1]; +// } else if (readLine.startsWith("privKey")) { +// settings.privKey = readLine.split("=")[1]; +// } else if (readLine.startsWith("pubKey")) { +// settings.pubKey = readLine.split("=")[1]; +// } else if (readLine.startsWith("pwd")) { +// settings.pwd = readLine.split("=")[1]; +// } +// } +// init(settings); +// } else { +// throw new IllegalArgumentException("file is not exist !!!"); +// } +// } + } + + private static void init(Settings settings) { + // 处理ledgerHash + HashDigest hash = new HashDigest(Base58Utils.decode(settings.getLedgerHash())); + CapabilitySettings.ledgerHash = hash; + + // 处理用户 + PrivKey privKey = KeyGenCommand.decodePrivKeyWithRawPassword(settings.getPrivKey(), settings.getPwd()); + PubKey pubKey = KeyGenCommand.decodePubKey(settings.getPubKey()); + CapabilitySettings.adminKey = new CryptoKeyPair(pubKey, privKey); + } + + private static class Settings { + String ledgerHash; + + String privKey; + + String pubKey; + + String pwd; + + public String getLedgerHash() { + return ledgerHash; + } + + public String getPrivKey() { + return privKey; + } + + public String getPubKey() { + return pubKey; + } + + public String getPwd() { + return pwd; + } + } +} \ No newline at end of file diff --git a/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/settings/CapabilitySettings.java b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/settings/CapabilitySettings.java new file mode 100644 index 00000000..afa59315 --- /dev/null +++ b/source/tools/tools-capability/src/main/java/com/jd/blockchain/capability/settings/CapabilitySettings.java @@ -0,0 +1,47 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.capability.CapabilitySettings + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/12/26 下午5:38 + * Description: + */ +package com.jd.blockchain.capability.settings; + +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.hash.HashDigest; + +/** + * + * @author shaozhuguang + * @create 2018/12/26 + * @since 1.0.0 + */ + +public class CapabilitySettings { + + public static final int TX_TOTAL_SIZE = 100 * 1000 * 1000; +// public static final int TX_TOTAL_SIZE = 100 * 1000; + + public static final int TX_HALF_SIZE = 50 * 1000 * 1000; + + public static final int DR_SIZE = 10000; + + public static final int KV_SIZE = 10000; + + public static final int KV_TOTAL_SIZE = 100 * 1000 * 1000; + +// public static String MSG_QUEUE_URL = "nats://127.0.0.1:4222"; + public static String MSG_QUEUE_URL = "rabbit://127.0.0.1:5672"; + +// public static String TX_TOPIC = "tx-topic"; + public static String TX_TOPIC = "tx-two-topic"; + + public static final int TX_SIZE_PER_SEND = 10000; + + public static HashDigest ledgerHash; + + public static CryptoKeyPair adminKey; + + public static final String settingsConf = "settings.conf"; +} \ No newline at end of file diff --git a/source/tools/tools-capability/src/main/resources/META-INF/MANIFEST.MF b/source/tools/tools-capability/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..b08cf0d7 --- /dev/null +++ b/source/tools/tools-capability/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Created-By: 1.8.0 +Built-By: shaozhuguang@jd.com +Extension-Name: JDChain +Specification-Title: JDChain +Specification-Vendor: JD Software Foundation +Implementation-Vendor: JD Software Foundation +Implementation-URL: http://ledger.jd.com +Build-Jdk: 1.8.0 + diff --git a/source/tools/tools-capability/src/main/resources/settings.conf b/source/tools/tools-capability/src/main/resources/settings.conf new file mode 100644 index 00000000..216cc7cb --- /dev/null +++ b/source/tools/tools-capability/src/main/resources/settings.conf @@ -0,0 +1,4 @@ +ledgerHash=6B3aa543AkotypMaLCeuWDTXFLuG9UKyZCSdJBPStJzEe +privKey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY +pubKey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +pwd=abc \ No newline at end of file diff --git a/source/tools/tools-initializer-booter/ledger.init b/source/tools/tools-initializer-booter/ledger.init new file mode 100644 index 00000000..6cc31468 --- /dev/null +++ b/source/tools/tools-initializer-booter/ledger.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=true +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=8800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=true + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=8810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=8820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=true + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=8830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/tools/tools-initializer-booter/local.conf b/source/tools/tools-initializer-booter/local.conf new file mode 100644 index 00000000..ee8254fc --- /dev/null +++ b/source/tools/tools-initializer-booter/local.conf @@ -0,0 +1,22 @@ + +#当前参与方的 id; +local.parti.id=0 + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=./ + +#账本数据库的连接字符串; +ledger.db.uri=redis://127.0.0.1/0 + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识系统的参数配置;可选参数; +consensus.conf= + diff --git a/source/tools/tools-initializer-booter/pom.xml b/source/tools/tools-initializer-booter/pom.xml new file mode 100644 index 00000000..26bf0b36 --- /dev/null +++ b/source/tools/tools-initializer-booter/pom.xml @@ -0,0 +1,61 @@ + + 4.0.0 + + com.jd.blockchain + tools + 0.8.2.RELEASE + + tools-initializer-booter + + + + com.jd.blockchain + tools-initializer + ${project.version} + + + com.jd.blockchain + storage-redis + ${project.version} + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + src/main/resources/META-INF/MANIFEST.MF + + com.jd.blockchain.tools.initializer.boot.LedgerInitCommandBooter + true + . + false + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/tools/tools-initializer-booter/src/main/java/com/jd/blockchain/tools/initializer/boot/LedgerInitCommandBooter.java b/source/tools/tools-initializer-booter/src/main/java/com/jd/blockchain/tools/initializer/boot/LedgerInitCommandBooter.java new file mode 100644 index 00000000..0f333e1d --- /dev/null +++ b/source/tools/tools-initializer-booter/src/main/java/com/jd/blockchain/tools/initializer/boot/LedgerInitCommandBooter.java @@ -0,0 +1,90 @@ +package com.jd.blockchain.tools.initializer.boot; + +import com.jd.blockchain.tools.initializer.LedgerInitCommand; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +public class LedgerInitCommandBooter { + + public static void main(String[] args) { + // 加载当前包及../system包下的所有class + load(); + LedgerInitCommand.main(args); + } + + private static void load() { + List jarPaths = loadPaths(); + try { + if (jarPaths != null && !jarPaths.isEmpty()) { + loadJars(jarPaths); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void loadJars(List jarPaths) throws Exception { + Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + boolean accessible = method.isAccessible(); + try { + if (accessible == false) { + method.setAccessible(true); + } + // 获取系统类加载器 + URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader(); + for (File file : jarPaths) { + URL url = file.toURI().toURL(); + try { + method.invoke(classLoader, url); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } finally { + method.setAccessible(accessible); + } + } + + private static List loadPaths() { + List loadJarFiles = new ArrayList<>(); + URL url = LedgerInitCommandBooter.class + .getProtectionDomain() + .getCodeSource() + .getLocation(); + try { + String currPath = java.net.URLDecoder.decode(url.getPath(), "utf-8"); + if (currPath.endsWith(".jar")) { + currPath = currPath.substring(0, currPath.lastIndexOf("/") + 1); + } + File file = new File(currPath); + loadJarFiles.addAll(dirJars(file)); + // 获取上级路径 + String systemPath = file.getParent() + File.separator + "system"; + loadJarFiles.addAll(dirJars(new File(systemPath))); + } catch (Exception e) { + throw new RuntimeException(e); + } + return loadJarFiles; + } + + private static List dirJars(File dir) { + List jars = new ArrayList<>(); + if (dir.exists() && dir.isDirectory()) { + File[] jarArray = dir.listFiles(); + if (jarArray != null) { + for (File jar : jarArray) { + if (jar.getAbsolutePath().endsWith(".jar")) { + jars.add(jar); + } + } + } + } + return jars; + } +} diff --git a/source/tools/tools-initializer-booter/src/main/resources/META-INF/MANIFEST.MF b/source/tools/tools-initializer-booter/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..b08cf0d7 --- /dev/null +++ b/source/tools/tools-initializer-booter/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,10 @@ +Manifest-Version: 1.0 +Created-By: 1.8.0 +Built-By: shaozhuguang@jd.com +Extension-Name: JDChain +Specification-Title: JDChain +Specification-Vendor: JD Software Foundation +Implementation-Vendor: JD Software Foundation +Implementation-URL: http://ledger.jd.com +Build-Jdk: 1.8.0 + diff --git a/source/tools/tools-initializer/bftsmart.config b/source/tools/tools-initializer/bftsmart.config new file mode 100644 index 00000000..df69caf5 --- /dev/null +++ b/source/tools/tools-initializer/bftsmart.config @@ -0,0 +1,144 @@ +# 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 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 ### +############################################ + +#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 ###### +############################################ + +#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 + + +#Number of servers in the group +system.servers.num = 4 + +#Maximum number of faulty replicas +system.servers.f = 1 + +#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 + +#Configuration of all node servers; +#PubKey of node server with specified ID, with base58 encoding. +system.server.0.pubkey= +system.server.0.network.host=127.0.0.1 +system.server.0.network.port=8900 +system.server.0.network.secure=false + +system.server.1.pubkey= +system.server.1.network.host=127.0.0.1 +system.server.1.network.port=8910 +system.server.1.network.secure=false + +system.server.2.pubkey= +system.server.2.network.host=127.0.0.1 +system.server.2.network.port=8920 +system.server.2.network.secure=false + +system.server.3.pubkey= +system.server.3.network.host=127.0.0.1 +system.server.3.network.port=8920 +system.server.3.network.secure=false diff --git a/source/tools/tools-initializer/ledger.init b/source/tools/tools-initializer/ledger.init new file mode 100644 index 00000000..6cc31468 --- /dev/null +++ b/source/tools/tools-initializer/ledger.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=true +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=8800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=true + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=8810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=8820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=true + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=8830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/tools/tools-initializer/local.conf b/source/tools/tools-initializer/local.conf new file mode 100644 index 00000000..9df0303c --- /dev/null +++ b/source/tools/tools-initializer/local.conf @@ -0,0 +1,25 @@ + +#当前参与方的公钥; +local.parti.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=./ + +#账本数据库的连接字符串; +ledger.db.uri=redis://127.0.0.1/0 + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识协议的参数配置;必须参数; +consensus.conf=mq.config + +#共识协议的实现类型;必须参数; +consensus.service-provider=com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider + diff --git a/source/tools/tools-initializer/mq.config b/source/tools/tools-initializer/mq.config new file mode 100644 index 00000000..9795a9de --- /dev/null +++ b/source/tools/tools-initializer/mq.config @@ -0,0 +1,6 @@ +system.msg.queue.server=nats://127.0.0.1:4222 +system.msg.queue.topic.tx=tx-topic +system.msg.queue.topic.bl=bl-topic +system.msg.queue.topic.msg=msg-topic +system.msg.queue.block.txsize=1000 +system.msg.queue.block.maxdelay=2000 \ No newline at end of file diff --git a/source/tools/tools-initializer/pom.xml b/source/tools/tools-initializer/pom.xml new file mode 100644 index 00000000..d4fe5039 --- /dev/null +++ b/source/tools/tools-initializer/pom.xml @@ -0,0 +1,104 @@ + + 4.0.0 + + com.jd.blockchain + tools + 0.8.2.RELEASE + + tools-initializer + + + + com.jd.blockchain + binary-proto + ${project.version} + + + com.jd.blockchain + crypto-framework + ${project.version} + + + com.jd.blockchain + ledger-core + ${project.version} + + + com.jd.blockchain + consensus-framework + ${project.version} + + + com.jd.blockchain + consensus-bftsmart + ${project.version} + + + com.jd.blockchain + consensus-mq + ${project.version} + + + + com.jd.blockchain + storage-composite + ${project.version} + + + + + com.jd.blockchain + tools-keygen + ${project.version} + + + com.jd.blockchain + utils-http + ${project.version} + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-logging + + + + + + org.springframework.boot + spring-boot-starter-log4j2 + + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/ConsolePrompter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/ConsolePrompter.java new file mode 100644 index 00000000..01083537 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/ConsolePrompter.java @@ -0,0 +1,45 @@ +package com.jd.blockchain.tools.initializer; + +import com.jd.blockchain.utils.ConsoleUtils; + +public class ConsolePrompter implements Prompter { + + private boolean debug = true; + + @Override + public void info(String format, Object... args) { + ConsoleUtils.info(format, args); + } + + @Override + public void error(String format, Object... args) { + ConsoleUtils.error(format, args); + } + + @Override + public void error(Exception error, String format, Object... args) { + if (debug) { + error.printStackTrace(); + } + } + + @Override + public String confirm(String format, Object... args) { + return confirm("", format, args); + } + + @Override + public String confirm(String tag, String format, Object... args) { + String msg = String.format(format, args); + return ConsoleUtils.confirm("[%s] %s", tag, msg); + } + + public boolean isDebug() { + return debug; + } + + public void setDebug(boolean debug) { + this.debug = debug; + } + +} \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/DBConnectionConfig.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/DBConnectionConfig.java new file mode 100644 index 00000000..8ca98ffc --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/DBConnectionConfig.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.tools.initializer; + +public class DBConnectionConfig { + + private String connectionUri; + + private String password; + + public DBConnectionConfig() { + } + public DBConnectionConfig(String uri) { + this.connectionUri = uri; + } + + public String getUri() { + return connectionUri; + } + + public void setConnectionUri(String connectionUri) { + this.connectionUri = connectionUri; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + +} \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/InitializerConfiguration.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/InitializerConfiguration.java new file mode 100644 index 00000000..3ad076d9 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/InitializerConfiguration.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.tools.initializer; + +import org.springframework.context.annotation.Configuration; + +@Configuration +public interface InitializerConfiguration { + + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/InitializingStep.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/InitializingStep.java new file mode 100644 index 00000000..50fe9984 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/InitializingStep.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.tools.initializer; + +public enum InitializingStep { + + PERMISSION_READY, + + LEDGER_INIT_COMPLETED + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerBindingConfig.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerBindingConfig.java new file mode 100644 index 00000000..f8e9adcd --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerBindingConfig.java @@ -0,0 +1,357 @@ +package com.jd.blockchain.tools.initializer; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.io.RuntimeIOException; + +public class LedgerBindingConfig { + + public static final String CHARSET = "UTF-8"; + + public static final String LEDGER_HASH_SEPERATOR = ","; + + public static final String ATTR_SEPERATOR = "."; + + // Binding List; + public static final String LEDGER_BINDINS = "ledger.bindings"; + + // Binding Config Key Prefix; + public static final String BINDING_PREFIX = "binding"; + + // Participant Config Key Prefix; + public static final String PARTI_PREFIX = "parti."; + + // Participant Attribute Key; + public static final String PARTI_ADDRESS = PARTI_PREFIX + "address"; + public static final String PARTI_PK_PATH = PARTI_PREFIX + "pk-path"; + public static final String PARTI_PK = PARTI_PREFIX + "pk"; + public static final String PARTI_PASSWORD = PARTI_PREFIX + "pwd"; + + + // DB Connection Config Key Prefix; + public static final String DB_PREFIX = "db."; + + // DB Connction Attribute Key; + public static final String DB_CONN = DB_PREFIX + "uri"; + public static final String DB_PASSWORD = DB_PREFIX + "pwd"; + + // ------------------------------ + + private Map bindings = new LinkedHashMap<>(); + + public HashDigest[] getLedgerHashs() { + return bindings.keySet().toArray(new HashDigest[bindings.size()]); + } + + public BindingConfig getLedger(HashDigest hash) { + return bindings.get(hash); + } + + public void addLedgerBinding(HashDigest ledgerHash, BindingConfig binding) { + bindings.put(ledgerHash, binding); + } + + public void removeLedgerBinding(HashDigest ledgerHash) { + bindings.remove(ledgerHash); + } + + public void store(File file) { + try (FileOutputStream out = new FileOutputStream(file, false)) { + store(out); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public void store(OutputStream out) { + FileUtils.writeText(toPropertiesString(), out, CHARSET); + } + + private String toPropertiesString() { + StringBuilder builder = new StringBuilder(); + + HashDigest[] hashs = this.getLedgerHashs(); + writeLedgerHashs(builder, hashs); + writeLine(builder); + + for (int i = 0; i < hashs.length; i++) { + writeLine(builder, "#第 %s 个账本[%s]的配置;", i + 1, hashs[i].toBase58()); + BindingConfig binding = getLedger(hashs[i]); + writeParticipant(builder, hashs[i], binding); + writeDB(builder, hashs[i], binding); + writeLine(builder); + } + return builder.toString(); + } + + private void writeLedgerHashs(StringBuilder builder, HashDigest[] hashs) { + writeLine(builder, "#绑定的账本的hash列表;以逗号分隔;"); + + String[] base58Hashs = new String[hashs.length]; + for (int i = 0; i < base58Hashs.length; i++) { + base58Hashs[i] = hashs[i].toBase58(); + } + String base58HashList = String.join(", \\\r\n", base58Hashs); + writeLine(builder, "%s=%s", LEDGER_BINDINS, base58HashList); + writeLine(builder); + } + + private void writeParticipant(StringBuilder builder, HashDigest ledgerHash, BindingConfig binding) { + String ledgerPrefix = String.join(ATTR_SEPERATOR, BINDING_PREFIX, ledgerHash.toBase58()); + // 参与方配置; + String partiAddressKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_ADDRESS); + String partiPkPathKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_PK_PATH); + String partiPKKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_PK); + String partiPwdKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_PASSWORD); + + writeLine(builder, "#账本的当前共识参与方的节点地址 Address;"); + writeLine(builder, "%s=%s", partiAddressKey, stringOf(binding.getParticipant().getAddress())); + writeLine(builder, "#账本的当前共识参与方的私钥文件的保存路径;"); + writeLine(builder, "%s=%s", partiPkPathKey, stringOf(binding.getParticipant().getPkPath())); + writeLine(builder, "#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性;"); + writeLine(builder, "%s=%s", partiPKKey, stringOf(binding.getParticipant().getPk())); + writeLine(builder, "#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入;"); + writeLine(builder, "%s=%s", partiPwdKey, stringOf(binding.getParticipant().getPassword())); + writeLine(builder); + } + + private void writeDB(StringBuilder builder, HashDigest ledgerHash, BindingConfig binding) { + String ledgerPrefix = String.join(ATTR_SEPERATOR, BINDING_PREFIX, ledgerHash.toBase58()); + // 数据库存储配置; + String dbConnKey = String.join(ATTR_SEPERATOR, ledgerPrefix, DB_CONN); + String dbPwdKey = String.join(ATTR_SEPERATOR, ledgerPrefix, DB_PASSWORD); + + writeLine(builder, "#账本的存储数据库的连接字符串;"); + writeLine(builder, "%s=%s", dbConnKey, stringOf(binding.getDbConnection().getUri())); + writeLine(builder, "#账本的存储数据库的连接口令;"); + writeLine(builder, "%s=%s", dbPwdKey, stringOf(binding.getDbConnection().getPassword())); + writeLine(builder); + } + + private static String stringOf(Object obj) { + if (obj == null) { + return ""; + } + return obj.toString(); + } + + private static void writeLine(StringBuilder content, String format, Object... args) { + content.append(String.format(format, args)); + content.append("\r\n"); + } + + private static void writeLine(StringBuilder content) { + content.append("\r\n"); + } + + /** + * 解析配置; + * + * @param file + * @return + */ + public static LedgerBindingConfig resolve(File file) { + Properties props = FileUtils.readProperties(file, CHARSET); + return resolve(props); + } + + /** + * 解析配置; + * + * @param in + * @return + */ + public static LedgerBindingConfig resolve(InputStream in) { + Properties props = FileUtils.readProperties(in, CHARSET); + return resolve(props); + } + + /** + * 解析配置; + * + * @param props + * @return + */ + public static LedgerBindingConfig resolve(Properties props) { + LedgerBindingConfig conf = new LedgerBindingConfig(); + + // 解析哈希列表; + String ledgerHashListString = getProperty(props, LEDGER_BINDINS, true); + String[] base58Hashs = split(ledgerHashListString, LEDGER_HASH_SEPERATOR); + if (base58Hashs.length == 0) { + return conf; + } + HashDigest[] hashs = new HashDigest[base58Hashs.length]; + for (int i = 0; i < base58Hashs.length; i++) { + byte[] hashBytes = Base58Utils.decode(base58Hashs[i]); + hashs[i] = new HashDigest(hashBytes); + + BindingConfig bindingConf = resolveBinding(props, base58Hashs[i]); + conf.bindings.put(hashs[i], bindingConf); + } + + return conf; + } + + /** + * 解析 Binding 配置; + * + * @param props + * @param ledgerHash + * @return + */ + private static BindingConfig resolveBinding(Properties props, String ledgerHash) { + BindingConfig binding = new BindingConfig(); + + String ledgerPrefix = String.join(ATTR_SEPERATOR, BINDING_PREFIX, ledgerHash); + // 参与方配置; + String partiAddrKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_ADDRESS); + String partiPkPathKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_PK_PATH); + String partiPKKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_PK); + String partiPwdKey = String.join(ATTR_SEPERATOR, ledgerPrefix, PARTI_PASSWORD); + + String strPartiAddr = getProperty(props, partiAddrKey, true); + binding.participant.address = strPartiAddr; + binding.participant.pkPath = getProperty(props, partiPkPathKey, false); + binding.participant.pk = getProperty(props, partiPKKey, false); + binding.participant.password = getProperty(props, partiPwdKey, false); +// if (binding.participant.address < 0) { +// throw new IllegalArgumentException( +// String.format("Participant id less than 0 in ledger binding[%s]!", ledgerHash)); +// } + if (binding.participant.pkPath == null && binding.participant.pk == null) { + throw new IllegalArgumentException( + String.format("No priv key config of participant of ledger binding[%s]!", ledgerHash)); + } + + // 数据库存储配置; + String dbConnKey = String.join(ATTR_SEPERATOR, ledgerPrefix, DB_CONN); + String dbPwdKey = String.join(ATTR_SEPERATOR, ledgerPrefix, DB_PASSWORD); + + binding.dbConnection.setConnectionUri(getProperty(props, dbConnKey, true)); + binding.dbConnection.setPassword(getProperty(props, dbPwdKey, false)); + if (binding.dbConnection.getUri() == null) { + throw new IllegalArgumentException( + String.format("No db connection config of participant of ledger binding[%s]!", ledgerHash)); + } + + return binding; + } + + private static String[] split(String str, String seperator) { + String[] items = str.split(seperator); + List validItems = new ArrayList<>(); + for (int i = 0; i < items.length; i++) { + items[i] = items[i].trim(); + if (items[i].length() > 0) { + validItems.add(items[i]); + } + } + return validItems.toArray(new String[validItems.size()]); + } + + /** + * 返回指定属性的值; + * + *
+ * 当值不存在时,如果是必需参数,则抛出异常 {@link IllegalArgumentException},否则返回 null; + * + * @param props + * 属性表; + * @param key + * 属性的键; + * @param required + * 是否为必需参数; + * @return 长度大于 0 的字符串,或者 null; + */ + private static String getProperty(Properties props, String key, boolean required) { + String value = props.getProperty(key); + if (value == null) { + if (required) { + throw new IllegalArgumentException("Miss property[" + key + "]!"); + } + return null; + } + value = value.trim(); + if (value.length() == 0) { + if (required) { + throw new IllegalArgumentException("Miss property[" + key + "]!"); + } + return null; + } + return value; + } + + public static class BindingConfig { + + private ParticipantBindingConfig participant = new ParticipantBindingConfig(); + + private DBConnectionConfig dbConnection = new DBConnectionConfig(); + + public ParticipantBindingConfig getParticipant() { + return participant; + } + + public DBConnectionConfig getDbConnection() { + return dbConnection; + } + + } + + public static class ParticipantBindingConfig { + + private String address; + + private String pkPath; + + private String pk; + + private String password; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPkPath() { + return pkPath; + } + + public void setPkPath(String pkPath) { + this.pkPath = pkPath; + } + + public String getPk() { + return pk; + } + + public void setPk(String pk) { + this.pk = pk; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitCommand.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitCommand.java new file mode 100644 index 00000000..4d307b86 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitCommand.java @@ -0,0 +1,245 @@ +package com.jd.blockchain.tools.initializer; + +import java.io.File; +import java.util.Properties; + +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusProviders; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig.BindingConfig; +import com.jd.blockchain.tools.initializer.LedgerInitProperties.ConsensusParticipantConfig; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.ArgumentSet; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.ArgumentSet.ArgEntry; +import com.jd.blockchain.utils.ArgumentSet.Setting; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * 账本初始化器; + * + * @author huanghaiquan + * + */ +@SpringBootApplication +@EnableAutoConfiguration +@EnableConfigurationProperties +public class LedgerInitCommand { + + private static final String LEDGER_BINDING_FILE_NAME = "ledger-binding.conf"; + + // 当前参与方的本地配置文件的路径(local.conf); + private static final String LOCAL_ARG = "-l"; + + // 账本的初始化配置文件的路径(ledger.init); + private static final String INI_ARG = "-i"; + + // 是否输出调试信息; + private static final String DEBUG_OPT = "-debug"; + + /** + * 入口; + * + * @param args + */ + public static void main(String[] args) { + Setting argSetting = ArgumentSet.setting().prefix(LOCAL_ARG, INI_ARG).option(DEBUG_OPT); + ArgumentSet argset = ArgumentSet.resolve(args, argSetting); + + try { + ArgEntry localArg = argset.getArg(LOCAL_ARG); + if (localArg == null) { + ConsoleUtils.info("Miss local config file which can be specified with arg [%s]!!!", LOCAL_ARG); + + } + LocalConfig localConf = LocalConfig.resolve(localArg.getValue()); + + ArgEntry iniArg = argset.getArg(INI_ARG); + if (iniArg == null) { + ConsoleUtils.info("Miss ledger initializing config file which can be specified with arg [%s]!!!", + INI_ARG); + return; + } + + // // load ledger init setting; + LedgerInitProperties ledgerInitSetting = LedgerInitProperties.resolve(iniArg.getValue()); + String localNodePubKeyString = localConf.getLocal().getPubKeyString(); + PubKey localNodePubKey = KeyGenCommand.decodePubKey(localNodePubKeyString); + // 地址根据公钥生成 + String localNodeAddress = AddressEncoding.generateAddress(localNodePubKey).toBase58(); + + // load all pub keys; + int currId = -1; + for (int i = 0; i < ledgerInitSetting.getConsensusParticipantCount(); i++) { + ConsensusParticipantConfig pconf = ledgerInitSetting.getConsensusParticipant(i); + String currPartAddress = pconf.getAddress(); + if (currPartAddress == null) { + if (pconf.getPubKeyPath() != null) { + PubKey pubKey = KeyGenCommand.readPubKey(pconf.getPubKeyPath()); + pconf.setPubKey(pubKey); + currPartAddress = pconf.getAddress(); + } + } + if (localNodeAddress.equals(currPartAddress)) { + currId = i; + } + } + if (currId == -1) { + throw new IllegalStateException("The current node specified in local.conf is not found in ledger.init!"); + } + + String base58Pwd = localConf.getLocal().getPassword(); + if (base58Pwd == null) { + base58Pwd = KeyGenCommand.readPasswordString(); + } + PrivKey privKey = KeyGenCommand.decodePrivKey(localConf.getLocal().getPrivKeyString(), base58Pwd); + + // Load consensus properties; + Properties props = FileUtils.readProperties(localConf.getConsensusConfig()); + ConsensusProvider csProvider = ConsensusProviders.getProvider(localConf.getConsensusProvider()); + ConsensusSettings csSettings = csProvider.getSettingsFactory().getConsensusSettingsBuilder() + .createSettings(props); + + + // ConsensusProperties csProps = new ConsensusProperties(props); + + // Output ledger binding config of peer; + if (!FileUtils.existDirectory(localConf.getBindingOutDir())) { + FileUtils.makeDirectory(localConf.getBindingOutDir()); + } + File ledgerBindingFile = new File(localConf.getBindingOutDir(), LEDGER_BINDING_FILE_NAME); + LedgerBindingConfig conf; + if (ledgerBindingFile.exists()) { + conf = LedgerBindingConfig.resolve(ledgerBindingFile); + } else { + conf = new LedgerBindingConfig(); + } + + // 启动初始化; + LedgerInitCommand initCommand = new LedgerInitCommand(); + HashDigest newLedgerHash = initCommand.startInit(currId, privKey, base58Pwd, ledgerInitSetting, csSettings, csProvider, + localConf.getStoragedDb(), new ConsolePrompter(), conf); + + if (newLedgerHash != null) { + // success; + // so save ledger binding config to file system; + conf.store(ledgerBindingFile); + ConsoleUtils.info("\r\n------ Update Ledger binding configuration success! ------[%s]", + ledgerBindingFile.getAbsolutePath()); + } + + // ConsoleUtils.confirm("\r\n\r\n Press any key to quit. :>"); + + } catch (Exception e) { + ConsoleUtils.error("\r\nError!! -- %s\r\n", e.getMessage()); + if (argset.hasOption(DEBUG_OPT)) { + e.printStackTrace(); + } + + ConsoleUtils.error("\r\n Ledger init process has been broken by error!"); + } + + } + + private LedgerManager ledgerManager; + + public LedgerManager getLedgerManager() { + return ledgerManager; + } + + public LedgerInitCommand() { + } + + public HashDigest startInit(int currId, PrivKey privKey, String base58Pwd, LedgerInitProperties ledgerSetting, + ConsensusSettings csSettings, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, LedgerBindingConfig conf, Object... extBeans) { + if (currId < 0 || currId >= ledgerSetting.getConsensusParticipantCount()) { + ConsoleUtils.info( + "Your participant id is illegal which is less than 1 or great than the total participants count[%s]!!!", + ledgerSetting.getConsensusParticipantCount()); + return null; + } + + // generate binding config; + BindingConfig bindingConf = new BindingConfig(); + // bindingConf.setCsConfigFile(localConf.getConsensusConfig()); + bindingConf.getParticipant().setAddress(ledgerSetting.getConsensusParticipant(currId).getAddress()); + String encodedPrivKey = KeyGenCommand.encodePrivKey(privKey, base58Pwd); + bindingConf.getParticipant().setPk(encodedPrivKey); + bindingConf.getParticipant().setPassword(base58Pwd); + + bindingConf.getDbConnection().setConnectionUri(dbConnConfig.getUri()); + bindingConf.getDbConnection().setPassword(dbConnConfig.getPassword()); + + // bindingConf.getMqConnection().setServer(mqConnConfig.getServer()); + // bindingConf.getMqConnection().setTopic(mqConnConfig.getTopic()); + + // confirm continue; + prompter.info("\r\n\r\n This is participant [%s], the ledger initialization is ready to start!\r\n", currId); + // ConsoleUtils.confirm("Press any key to continue... "); + // prompter.confirm("Press any key to continue... "); + + // start web listener; + NetworkAddress serverAddress = ledgerSetting.getConsensusParticipant(currId).getInitializerAddress(); + String argServerAddress = String.format("--server.address=%s", serverAddress.getHost()); + String argServerPort = String.format("--server.port=%s", serverAddress.getPort()); + String[] innerArgs = { argServerAddress, argServerPort }; + + SpringApplication app = new SpringApplication(LedgerInitCommand.class); + if (extBeans != null && extBeans.length > 0) { + app.addInitializers((ApplicationContextInitializer) applicationContext -> { + ConfigurableListableBeanFactory beanFactory = applicationContext.getBeanFactory(); + for (Object bean : extBeans) { + beanFactory.registerSingleton(bean.toString(), bean); + } + }); + } + ConfigurableApplicationContext ctx = app.run(innerArgs); + this.ledgerManager = ctx.getBean(LedgerManager.class); + + prompter.info("\r\n------ Web listener[%s:%s] was started. ------\r\n", serverAddress.getHost(), + serverAddress.getPort()); + + try { + LedgerInitProcess initProc = ctx.getBean(LedgerInitProcess.class); + HashDigest ledgerHash = initProc.initialize(currId, privKey, ledgerSetting, csSettings, csProvider, + bindingConf.getDbConnection(), prompter); + + if (ledgerHash == null) { + // ledger init fail; + ConsoleUtils.error("\r\n------ Ledger initialize fail! ------\r\n"); + return null; + } else { + ConsoleUtils.info("\r\n------ Ledger initialize success! ------"); + ConsoleUtils.info("New Ledger Hash is :[%s]", ledgerHash.toBase58()); + + if (conf == null) { + conf = new LedgerBindingConfig(); + } + conf.addLedgerBinding(ledgerHash, bindingConf); + + return ledgerHash; + + } + } finally { + ctx.close(); + ConsoleUtils.info("\r\n------ Web listener[%s:%s] was closed. ------\r\n", serverAddress.getHost(), + serverAddress.getPort()); + } + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitException.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitException.java new file mode 100644 index 00000000..22a09f31 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitException.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.tools.initializer; + +import com.jd.blockchain.ledger.core.LedgerException; + +public class LedgerInitException extends LedgerException{ + + private static final long serialVersionUID = 103724923689027144L; + + public LedgerInitException(String message) { + super(message); + } + + public LedgerInitException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProcess.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProcess.java new file mode 100644 index 00000000..c610bfad --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProcess.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.tools.initializer; + +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.consensus.service.ConsensusServiceProvider; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.CryptoSetting; + +/** + * + * @author huanghaiquan + * + */ +public interface LedgerInitProcess { + + /** + * Init a new ledger; + * + * @param currentId + * @param privKey + * @param ledgerInitProps + * 账本初始化配置属性; + * @param consensusSettings + * 共识配置属性; + * @param consensusServiceProvider + * @param prompter + * @return 返回新账本的 hash;如果未初始化成功,则返回 null; + */ + /** + * Init a new ledger; + * @param currentId Id of current participant; + * @param privKey Private key of current participant; + * @param ledgerInitProps The settings about this initialization; + * @param consensusSettings The consensus settings + * @param consensusProvider + * @param dbConnConfig + * @param prompter + * @return + */ + HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, + ConsensusSettings consensusSettings, ConsensusProvider consensusProvider, + DBConnectionConfig dbConnConfig, Prompter prompter); + + /** + * @param currentId + * @param privKey + * @param ledgerInitProps + * @param consensusSettings + * @param consensusProvider + * @param dbConnConfig + * @param prompter + * @param cryptoSetting + * @return + */ + HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, + ConsensusSettings consensusSettings, ConsensusProvider consensusProvider, + DBConnectionConfig dbConnConfig, Prompter prompter, CryptoSetting cryptoSetting); + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java new file mode 100644 index 00000000..dd6270f5 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LedgerInitProperties.java @@ -0,0 +1,315 @@ +package com.jd.blockchain.tools.initializer; + +import java.io.ByteArrayOutputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import com.jd.blockchain.crypto.AddressEncoding; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.tools.keygen.KeyGenCommand; +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.net.NetworkAddress; + +import org.springframework.core.io.ClassPathResource; + +public class LedgerInitProperties { + + // 账本种子; + public static final String LEDGER_SEED = "ledger.seed"; + // 共识参与方的个数,后续以 part.id 分别标识每一个参与方的配置; + public static final String PART_COUNT = "cons_parti.count"; + // 共识参与方的名称的模式; + public static final String PART_ID_PATTERN = "cons_parti.%s"; + // 参与方的名称; + public static final String PART_NAME = "name"; + // 参与方的公钥文件路径; + public static final String PART_PUBKEY_PATH = "pubkey-path"; + // 参与方的公钥文件路径; + public static final String PART_PUBKEY = "pubkey"; + // //共识参与方的共识服务的主机地址; + // public static final String PART_CONSENSUS_HOST = "consensus.host"; + // // 共识参与方的共识服务的端口; + // public static final String PART_CONSENSUS_PORT = "consensus.port"; + // // 共识参与方的共识服务是否开启安全连接; + // public static final String PART_CONSENSUS_SECURE = "consensus.secure"; + // 共识参与方的账本初始服务的主机; + public static final String PART_INITIALIZER_HOST = "initializer.host"; + // 共识参与方的账本初始服务的端口; + public static final String PART_INITIALIZER_PORT = "initializer.port"; + // 共识参与方的账本初始服务是否开启安全连接; + public static final String PART_INITIALIZER_SECURE = "initializer.secure"; + + private byte[] ledgerSeed; + + private List consensusParticipants = new ArrayList<>(); + + public byte[] getLedgerSeed() { + return ledgerSeed; + } + + public static byte[] getHostSettingValue() throws Exception { + ClassPathResource hostConfigResource = new ClassPathResource("hosts.config"); + InputStream fis = hostConfigResource.getInputStream(); + ByteArrayOutputStream bos = null; + byte[] buffer = null; + try { + bos = new ByteArrayOutputStream(1000); + byte[] b = new byte[1000]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + // host file to bytes + buffer = bos.toByteArray(); + } catch (FileNotFoundException e) { + throw new Exception(e.getMessage(), e); + } catch (IOException e) { + throw new Exception(e.getMessage(), e); + } finally { + if (fis != null) { + fis.close(); + } + if (bos != null) { + bos.close(); + } + } + return buffer; + } + + public static byte[] getSystemSettingValue() throws Exception { + ClassPathResource systemConfigResource = new ClassPathResource("bftsmart.config"); + InputStream fis = systemConfigResource.getInputStream(); + ByteArrayOutputStream bos = null; + byte[] buffer = null; + try { + bos = new ByteArrayOutputStream(1000); + byte[] b = new byte[1000]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + // file to bytes + buffer = bos.toByteArray(); + } catch (FileNotFoundException e) { + throw new Exception(e.getMessage(), e); + } catch (IOException e) { + throw new Exception(e.getMessage(), e); + } finally { + if (fis != null) { + fis.close(); + } + if (bos != null) { + bos.close(); + } + } + return buffer; + } + + public int getConsensusParticipantCount() { + return consensusParticipants.size(); + } + + public List getConsensusParticipants() { + return consensusParticipants; + } + + public ConsensusParticipantConfig[] getConsensusParticipantArray() { + return consensusParticipants.toArray(new ConsensusParticipantConfig[consensusParticipants.size()]); + } + + /** + * 返回参与者; + * + * @param address + * 从 1 开始; 小于等于 {@link #getConsensusParticipantCount()}; + * @return + */ + public ConsensusParticipantConfig getConsensusParticipant(int id) { + for (ConsensusParticipantConfig p : consensusParticipants) { + if (p.getId()== id) { + return p; + } + } + return null; + } + + private LedgerInitProperties(byte[] ledgerSeed) { + this.ledgerSeed = ledgerSeed; + } + + public void addConsensusParticipant(ConsensusParticipantConfig participant) { + consensusParticipants.add(participant); + } + + private static String getKeyOfCsParti(int partId, String partPropKey) { + String partAddrStr = String.format(PART_ID_PATTERN, partId); + return String.format("%s.%s", partAddrStr, partPropKey); + } + + public static LedgerInitProperties resolve(String initSettingFile) { + Properties props = FileUtils.readProperties(initSettingFile, "UTF-8"); + return resolve(props); + } + + public static LedgerInitProperties resolve(InputStream in) { + Properties props = FileUtils.readProperties(in, "UTF-8"); + return resolve(props); + } + + private static LedgerInitProperties resolve(Properties props) { + String hexLedgerSeed = getProperty(props, LEDGER_SEED).replace("-", ""); + byte[] ledgerSeed = HexUtils.decode(hexLedgerSeed); + LedgerInitProperties setting = new LedgerInitProperties(ledgerSeed); + + int partCount = getInt(getProperty(props, PART_COUNT)); + if (partCount < 0) { + throw new IllegalArgumentException(String.format("Property[%s] is negative!", PART_COUNT)); + } + if (partCount < 4) { + throw new IllegalArgumentException(String.format("Property[%s] is less than 4!", PART_COUNT)); + } + for (int i = 0; i < partCount; i++) { + ConsensusParticipantConfig parti = new ConsensusParticipantConfig(); + + parti.setId(i); + + String nameKey = getKeyOfCsParti(i, PART_NAME); + parti.setName(getProperty(props, nameKey)); + + String pubkeyPathKey = getKeyOfCsParti(i, PART_PUBKEY_PATH); + parti.setPubKeyPath(getProperty(props, pubkeyPathKey)); + + String pubkeyKey = getKeyOfCsParti(i, PART_PUBKEY); + String base58PubKey = getProperty(props, pubkeyKey); + if (base58PubKey != null) { + PubKey pubKey = KeyGenCommand.decodePubKey(base58PubKey); + parti.setPubKey(pubKey); + } + + // String consensusHostKey = getKeyOfCsParti(i, PART_CONSENSUS_HOST); + // String consensusHost = getProperty(props, consensusHostKey); + // + // String consensusPortKey = getKeyOfCsParti(i, PART_CONSENSUS_PORT); + // int consensusPort = getInt(getProperty(props, consensusPortKey)); + // + // String consensusSecureKey = getKeyOfCsParti(i, PART_CONSENSUS_SECURE); + // boolean consensusSecure = Boolean.parseBoolean(getProperty(props, + // consensusSecureKey)); + // NetworkAddress consensusAddress = new NetworkAddress(consensusHost, + // consensusPort, consensusSecure); + // parti.setConsensusAddress(consensusAddress); + + String initializerHostKey = getKeyOfCsParti(i, PART_INITIALIZER_HOST); + String initializerHost = getProperty(props, initializerHostKey); + + String initializerPortKey = getKeyOfCsParti(i, PART_INITIALIZER_PORT); + int initializerPort = getInt(getProperty(props, initializerPortKey)); + + String initializerSecureKey = getKeyOfCsParti(i, PART_INITIALIZER_SECURE); + boolean initializerSecure = Boolean.parseBoolean(getProperty(props, initializerSecureKey)); + NetworkAddress initializerAddress = new NetworkAddress(initializerHost, initializerPort, initializerSecure); + parti.setInitializerAddress(initializerAddress); + + setting.addConsensusParticipant(parti); + } + + return setting; + } + + private static String getProperty(Properties props, String key) { + return getProperty(props, key, true); + } + + private static String getProperty(Properties props, String key, boolean required) { + String value = props.getProperty(key); + if (value == null) { + if (required) { + throw new IllegalArgumentException("Miss property[" + key + "]!"); + } + return null; + } + value = value.trim(); + return value.length() == 0 ? null : value; + } + + private static int getInt(String strInt) { + return Integer.parseInt(strInt.trim()); + } + + /** + * 参与方配置信息; + * + * @author huanghaiquan + * + */ + public static class ConsensusParticipantConfig implements ParticipantNode { + + private int id; + + private String address; + + private String name; + + private String pubKeyPath; + + private PubKey pubKey; + + // private NetworkAddress consensusAddress; + + private NetworkAddress initializerAddress; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + @Override + public String getAddress() { + return address; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPubKeyPath() { + return pubKeyPath; + } + + public void setPubKeyPath(String pubKeyPath) { + this.pubKeyPath = pubKeyPath; + } + + public NetworkAddress getInitializerAddress() { + return initializerAddress; + } + + public void setInitializerAddress(NetworkAddress initializerAddress) { + this.initializerAddress = initializerAddress; + } + + public PubKey getPubKey() { + return pubKey; + } + + public void setPubKey(PubKey pubKey) { + this.pubKey = pubKey; + this.address = AddressEncoding.generateAddress(pubKey).toBase58(); + } + + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java new file mode 100644 index 00000000..8696263c --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/LocalConfig.java @@ -0,0 +1,203 @@ +package com.jd.blockchain.tools.initializer; + +import java.io.File; +import java.io.InputStream; +import java.net.URI; +import java.nio.file.Path; +import java.util.Properties; + +import com.jd.blockchain.utils.PathUtils; +import com.jd.blockchain.utils.PropertiesUtils; +import com.jd.blockchain.utils.io.FileUtils; + +public class LocalConfig { + + // 当前参与方的 id; + public static final String LOCAL_PARTI_PUBKEY = "local.parti.pubkey"; + + // 当前参与方的私钥(密文编码); + public static final String LOCAL_PARTI_PRIVKEY = "local.parti.privkey"; + + // 当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; + public static final String LOCAL_PARTI_PWD = "local.parti.pwd"; + + // 账本初始化完成后生成的"账本绑定配置文件"的输出目录; + public static final String LEDGER_BINDING_OUT = "ledger.binding.out"; + + // 账本数据库的连接字符串; + public static final String LEDGER_DB_URI = "ledger.db.uri"; + + // 账本数据库的连接口令; + public static final String LEDGER_DB_PWD = "ledger.db.pwd"; + + // 账本消息队列的地址 +// public static final String LEDGER_MQ_SERVER = "ledger.mq.server"; +// +// // 账本消息队列的主题 +// public static final String LEDGER_MQ_TOPIC = "ledger.mq.topic"; + + // 共识系统的参数配置;必须参数; + public static final String CONSENSUS_CONF = "consensus.conf"; + + // 共识系统的参数配置;必须参数; + public static final String CONSENSUS_SERVICE_PROVIDER = "consensus.service-provider"; + + private LocalParticipantConfig local = new LocalParticipantConfig(); + + private String bindingOutDir; + + private DBConnectionConfig storagedDb = new DBConnectionConfig(); + +// private MQConnectionConfig handleMq = new MQConnectionConfig(); + + private String consensusConfig; + private String consensusProvider; + + public LocalParticipantConfig getLocal() { + return local; + } + + public void setLocal(LocalParticipantConfig local) { + this.local = local; + } + + public String getBindingOutDir() { + return bindingOutDir; + } + + public void setBindingOutDir(String bindingOutDir) { + this.bindingOutDir = bindingOutDir; + } + + public DBConnectionConfig getStoragedDb() { + return storagedDb; + } + + public void setStoragedDb(DBConnectionConfig storagedDb) { + this.storagedDb = storagedDb; + } + +// public MQConnectionConfig getHandleMq() { +// return handleMq; +// } +// +// public void setHandleMq(MQConnectionConfig handleMq) { +// this.handleMq = handleMq; +// } + + public String getConsensusConfig() { + return consensusConfig; + } + + public void setConsensusConfig(String consensusConfig) { + this.consensusConfig = consensusConfig; + } + + public static LocalConfig resolve(String initSettingFile) { + Properties props = FileUtils.readProperties(initSettingFile, "UTF-8"); + return resolve(props, initSettingFile); + } + + public static LocalConfig resolve(InputStream in) { + Properties props = FileUtils.readProperties(in, "UTF-8"); + return resolve(props, null); + } + + private static LocalConfig resolve(Properties props, String initSettingFile) { + + LocalConfig conf = new LocalConfig(); + + String pubKeyString = PropertiesUtils.getRequiredProperty(props, LOCAL_PARTI_PUBKEY); + conf.local.pubKeyString = pubKeyString; + + conf.local.privKeyString = PropertiesUtils.getRequiredProperty(props, LOCAL_PARTI_PRIVKEY); + conf.local.password = PropertiesUtils.getProperty(props, LOCAL_PARTI_PWD, false); + + conf.storagedDb.setConnectionUri(PropertiesUtils.getRequiredProperty(props, LEDGER_DB_URI)); + conf.storagedDb.setPassword(PropertiesUtils.getProperty(props, LEDGER_DB_PWD, false)); + + if (initSettingFile == null) { + conf.bindingOutDir = PropertiesUtils.getRequiredProperty(props, LEDGER_BINDING_OUT); + conf.consensusConfig = PropertiesUtils.getRequiredProperty(props, CONSENSUS_CONF); + } else { + String bindingOutDir = PropertiesUtils.getRequiredProperty(props, LEDGER_BINDING_OUT); + String consensusConfig = PropertiesUtils.getRequiredProperty(props, CONSENSUS_CONF); + String initSettingDir = PathUtils.concatPaths(initSettingFile, "../"); + conf.bindingOutDir = absolutePath(initSettingDir, bindingOutDir); + conf.consensusConfig = absolutePath(initSettingDir, consensusConfig); + } + + conf.consensusProvider = PropertiesUtils.getRequiredProperty(props, CONSENSUS_SERVICE_PROVIDER); + + return conf; + } + + private static String absolutePath(String currPath, String settingPath) { + String absolutePath = settingPath; + File settingFile = new File(settingPath); + if (!settingFile.isAbsolute()) { + absolutePath = PathUtils.concatPaths(currPath, settingPath); + } + return absolutePath; + } + + private static int getInt(String strInt) { + return Integer.parseInt(strInt.trim()); + } + + public String getConsensusProvider() { + return consensusProvider; + } + + public void setConsensusProvider(String consensusProvider) { + this.consensusProvider = consensusProvider; + } + + /** + * 当前参与方的本地配置信息; + * + * @author huanghaiquan + * + */ + public static class LocalParticipantConfig { + private String pubKeyString; + + private String privKeyString; + + private String password; + + public String getPubKeyString() { + return pubKeyString; + } + + public void setId(String pubKeyString) { + this.pubKeyString = pubKeyString; + } + + public String getPrivKeyString() { + return privKeyString; + } + + public void setPrivKeyString(String privKeyString) { + this.privKeyString = privKeyString; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + } + + public static void main(String[] args) { + String currPath = "/Users/zhanglin33/Ddisk/20mintest/0/config/init/local.conf"; +// String settingPath = "../"; + String settingPath = "bftsmart.config"; + String path = absolutePath(PathUtils.concatPaths(currPath, "../"), settingPath); + System.out.println(path); + + } +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/Prompter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/Prompter.java new file mode 100644 index 00000000..663a2c46 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/Prompter.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.tools.initializer; + +public interface Prompter { + + void info(String format, Object... args); + + void error(String format, Object... args); + + void error(Exception error, String format, Object... args); + + String confirm(String format, Object... args); + + String confirm(String tag, String format, Object... args); + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DataCodes.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DataCodes.java new file mode 100644 index 00000000..184745cb --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DataCodes.java @@ -0,0 +1,5 @@ +package com.jd.blockchain.tools.initializer.web; + +public interface DataCodes { + public static final int PERMISSION = 0x0010; +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DecisionRequestBodyConverter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DecisionRequestBodyConverter.java new file mode 100644 index 00000000..4b096f48 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DecisionRequestBodyConverter.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.utils.http.RequestBodyConverter; + +public class DecisionRequestBodyConverter implements RequestBodyConverter { + + @Override + public void write(Object param, OutputStream out) throws IOException { + if (param instanceof LedgerInitDecision) { + BinaryEncodingUtils.encode(param, LedgerInitDecision.class, out); + return; + } + } + +} \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DecisionResponseConverter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DecisionResponseConverter.java new file mode 100644 index 00000000..6dcbb3d3 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/DecisionResponseConverter.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.InputStream; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.tools.initializer.LedgerInitException; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; + +public class DecisionResponseConverter implements ResponseConverter { + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) + throws Exception { + LedgerInitResponse resp = LedgerInitResponse.resolve(responseStream); + if (resp.isError()) { + throw new LedgerInitException("Error occurred at remote participant! --" + resp.getErrorMessage()); + } + return BinaryEncodingUtils.decode(resp.getData()); + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/HttpInitConsensServiceFactory.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/HttpInitConsensServiceFactory.java new file mode 100644 index 00000000..cc1f87ad --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/HttpInitConsensServiceFactory.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.tools.initializer.web; + +import com.jd.blockchain.utils.http.agent.HttpServiceAgent; +import com.jd.blockchain.utils.http.agent.ServiceEndpoint; +import com.jd.blockchain.utils.net.NetworkAddress; + +public class HttpInitConsensServiceFactory implements InitConsensusServiceFactory { + + @Override + public LedgerInitConsensusService connect(NetworkAddress endpointAddress) { + ServiceEndpoint endpoint = new ServiceEndpoint(endpointAddress); + LedgerInitConsensusService initConsensus = HttpServiceAgent.createService(LedgerInitConsensusService.class, + endpoint); + return initConsensus; + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitConsensusServiceFactory.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitConsensusServiceFactory.java new file mode 100644 index 00000000..c6c4dad7 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitConsensusServiceFactory.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.tools.initializer.web; + +import com.jd.blockchain.utils.net.NetworkAddress; + +public interface InitConsensusServiceFactory { + + public LedgerInitConsensusService connect(NetworkAddress endpointAddress); + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitServiceExceptionHandler.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitServiceExceptionHandler.java new file mode 100644 index 00000000..3a7d295f --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitServiceExceptionHandler.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.tools.initializer.web; + +import javax.servlet.http.HttpServletRequest; + +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; + +/** + * 全局异常处理类 + */ +@RestControllerAdvice +public class InitServiceExceptionHandler { + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + @ExceptionHandler(value = Exception.class) + @ResponseBody + public LedgerInitResponse json(HttpServletRequest req, Exception ex) { +// logger.error("Error of web controllers! --" + ex.getMessage(), ex); + System.out.println("[InitServiceExceptionHandler] Error of web controllers! --" + ex.getMessage()); + return LedgerInitResponse.error(ex.getMessage()); + } + +} \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitWebSecurityConfiguration.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitWebSecurityConfiguration.java new file mode 100644 index 00000000..cef38324 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitWebSecurityConfiguration.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.tools.initializer.web; + +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; + +@Configuration +@EnableWebSecurity +public class InitWebSecurityConfiguration extends WebSecurityConfigurerAdapter { + + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().permitAll(); + http.csrf().disable(); + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitWebServerConfiguration.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitWebServerConfiguration.java new file mode 100644 index 00000000..ec918972 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/InitWebServerConfiguration.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.util.List; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; + +import com.jd.blockchain.ledger.core.impl.LedgerManager; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +@Configuration +@ComponentScan +public class InitWebServerConfiguration extends WebMvcConfigurerAdapter { + static { + JSONSerializeUtils.disableCircularReferenceDetect(); + JSONSerializeUtils.configStringSerializer(ByteArray.class); + } + + @Override + public void extendMessageConverters(List> converters) { + converters.add(0, new LedgerInitMessageConverter()); + } + + @Bean + public InitConsensusServiceFactory initCsServiceFactory() { + return new HttpInitConsensServiceFactory(); + } + + @Bean + public LedgerManager getLedgerManager() { + return new LedgerManager(); + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitConsensusService.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitConsensusService.java new file mode 100644 index 00000000..08e90802 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitConsensusService.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.tools.initializer.web; + +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.HttpService; +import com.jd.blockchain.utils.http.PathParam; +import com.jd.blockchain.utils.http.RequestBody; + +@HttpService +public interface LedgerInitConsensusService { + + /** + * 请求账本的初始化许可; + * + * @param requesterId + * 发起请求的参与者 id; + * @param signature + * 请求者的私钥对 “id” + “账本种子” 做出的签名;只有签名合法且参与者是初始化配置中的参与方才能获得有效返回,否则将被拒绝; + */ + @HttpAction(path = "/legerinit/permission/{requesterId}", method = HttpMethod.POST, contentType = LedgerInitMessageConverter.CONTENT_TYPE_VALUE, responseConverter = PermissionResponseConverter.class) + LedgerInitPermission requestPermission(@PathParam(name = "requesterId") int requesterId, + @RequestBody(converter = SignatureDigestRequestBodyConverter.class) SignatureDigest signature); + + /** + * 同步账本初始化决议; + * + * @param initDecision + * 调用者的账本初始化决议; + * @return 目标参与方的账本初始化决议;如果目标参与者尚未准备就绪, 则返回 null; + */ + @HttpAction(path = "/legerinit/decision", method = HttpMethod.POST, contentType = LedgerInitMessageConverter.CONTENT_TYPE_VALUE, responseConverter = DecisionResponseConverter.class) + LedgerInitDecision synchronizeDecision(@RequestBody(converter = DecisionRequestBodyConverter.class) LedgerInitDecision initDecision); + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitDecisionData.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitDecisionData.java new file mode 100644 index 00000000..aaef9f73 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitDecisionData.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.tools.initializer.web; + +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.core.LedgerInitDecision; + +public class LedgerInitDecisionData implements LedgerInitDecision { + + private int participantId; + + private HashDigest ledgerHash; + + private SignatureDigest signature; + + @Override + public int getParticipantId() { + return participantId; + } + + @Override + public HashDigest getLedgerHash() { + return ledgerHash; + } + + @Override + public SignatureDigest getSignature() { + return signature; + } + + public void setParticipantId(int participantId) { + this.participantId = participantId; + } + + public void setLedgerHash(HashDigest ledgerHash) { + this.ledgerHash = ledgerHash; + } + + public void setSignature(SignatureDigest signature) { + this.signature = signature; + } + + } \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitMessageConverter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitMessageConverter.java new file mode 100644 index 00000000..0de796bf --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitMessageConverter.java @@ -0,0 +1,113 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerInitPermissionData; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 针对二进制对象的序列化和反序列化的 HTTP 消息转换器; + * + * @author huanghaiquan + * + */ +public class LedgerInitMessageConverter implements HttpMessageConverter { + + public static final String CONTENT_TYPE_VALUE = "application/bin-obj"; + + public static final MediaType CONTENT_TYPE = MediaType.valueOf(CONTENT_TYPE_VALUE); + + private static final List SUPPORTED_MEDIA_TYPES = Collections.singletonList(CONTENT_TYPE); + + private static final Map, Class> SUPPORTED_CONTRACT_TYPES = new HashMap<>(); + + static { + DataContractRegistry.register(LedgerInitPermission.class); + DataContractRegistry.register(LedgerInitDecision.class); + + SUPPORTED_CONTRACT_TYPES.put(LedgerInitPermission.class, LedgerInitPermissionData.class); + SUPPORTED_CONTRACT_TYPES.put(LedgerInitDecision.class, LedgerInitDecisionData.class); + + // SUPPORTED_CONTRACT_TYPES.add(LedgerInitResponse.class); + // DataContractRegistry.register(LedgerInitResponse.class); + } + + private boolean isSupported(Class clazz) { + return getContractType(clazz) != null; + } + + private Class getContractType(Class clazz) { + for (Class itf : SUPPORTED_CONTRACT_TYPES.keySet()) { + if (itf.isAssignableFrom(clazz)) { + return itf; + } + } + return null; + } + + @Override + public boolean canRead(Class clazz, MediaType mediaType) { + return CONTENT_TYPE.includes(mediaType) + && (clazz.isPrimitive() || SignatureDigest.class == clazz || isSupported(clazz)); + } + + @Override + public boolean canWrite(Class clazz, MediaType mediaType) { + return CONTENT_TYPE.includes(mediaType) && (clazz.isPrimitive() || LedgerInitResponse.class.isAssignableFrom(clazz) || isSupported(clazz)); + } + + @Override + public List getSupportedMediaTypes() { + return SUPPORTED_MEDIA_TYPES; + } + + @Override + public Object read(Class clazz, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + if (SignatureDigest.class == clazz) { + byte[] signDigestBytes = BytesUtils.copyToBytes(inputMessage.getBody()); + return new SignatureDigest(signDigestBytes); + } + + Class contractType = getContractType(clazz); + Class implType = SUPPORTED_CONTRACT_TYPES.get(contractType); + return BinaryEncodingUtils.decode(inputMessage.getBody()); + } + + @Override + public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + LedgerInitResponse resp; + if (t == null) { + resp = LedgerInitResponse.success(null); + } else if (t instanceof LedgerInitResponse) { + resp = (LedgerInitResponse) t; + outputMessage.getBody().write(resp.toBytes()); + } else { + Class contractType = getContractType(t.getClass()); + if (contractType == null) { + throw new IllegalStateException("Unsupported type[" + t.getClass().getName() + "]!"); + } + byte[] data = BinaryEncodingUtils.encode(t, contractType); + resp = LedgerInitResponse.success(data); + outputMessage.getBody().write(resp.toBytes()); + } + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitResponse.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitResponse.java new file mode 100644 index 00000000..1f803963 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitResponse.java @@ -0,0 +1,85 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.Serializable; + +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.NumberMask; + +public class LedgerInitResponse implements BytesSerializable, Serializable { + + private static final long serialVersionUID = -475554045260920722L; + + private boolean error; + + private String errorMessage; + + private byte[] data; + + public boolean isError() { + return error; + } + + public String getErrorMessage() { + return errorMessage; + } + + public byte[] getData() { + return data; + } + + private LedgerInitResponse() { + } + + public static LedgerInitResponse error(String messageFormat, Object... args) { + LedgerInitResponse resp = new LedgerInitResponse(); + resp.error = true; + resp.errorMessage = String.format(messageFormat, args); + return resp; + } + + public static LedgerInitResponse success(byte[] data) { + LedgerInitResponse resp = new LedgerInitResponse(); + resp.error = false; + resp.data = data; + return resp; + } + + public static LedgerInitResponse resolve(InputStream in) { + LedgerInitResponse resp = new LedgerInitResponse(); + long uid = BytesUtils.readLong(in); + if (uid != serialVersionUID) { + throw new IllegalArgumentException("Illegal bytes of " + LedgerInitResponse.class.getName() + "!"); + } + resp.error = BytesUtils.readByte(in) == 1; + if (resp.error) { + byte[] errorMsgBytes = BytesEncoding.read(NumberMask.SHORT, in); + resp.errorMessage = BytesUtils.toString(errorMsgBytes, "UTF-8"); + } + resp.data = BytesEncoding.read(NumberMask.NORMAL, in); + return resp; + } + + public static LedgerInitResponse resolve(byte[] bytes) { + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + return resolve(in); + } + + @Override + public byte[] toBytes() { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesUtils.writeLong(serialVersionUID, out); + BytesUtils.writeByte(error ? (byte) 1 : (byte) 0, out); + if (error) { + byte[] errorBytes = ByteArray.fromString(errorMessage, "UTF-8"); + BytesEncoding.write(errorBytes, NumberMask.SHORT, out); + } + BytesEncoding.write(data, NumberMask.NORMAL, out); + return out.toByteArray(); + } +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java new file mode 100644 index 00000000..28c21cb8 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/LedgerInitializeWebController.java @@ -0,0 +1,768 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RestController; + +import com.jd.blockchain.binaryproto.DataContractRegistry; +import com.jd.blockchain.consensus.ConsensusProvider; +import com.jd.blockchain.consensus.ConsensusSettings; +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.ledger.BlockchainIdentity; +import com.jd.blockchain.ledger.BlockchainIdentityData; +import com.jd.blockchain.ledger.ParticipantNode; +import com.jd.blockchain.ledger.CryptoSetting; +import com.jd.blockchain.ledger.LedgerBlock; +import com.jd.blockchain.ledger.LedgerInitSetting; +import com.jd.blockchain.ledger.Operation; +import com.jd.blockchain.ledger.TransactionBuilder; +import com.jd.blockchain.ledger.TransactionContent; +import com.jd.blockchain.ledger.TransactionRequest; +import com.jd.blockchain.ledger.TransactionState; +import com.jd.blockchain.ledger.UserRegisterOperation; +import com.jd.blockchain.ledger.core.CryptoConfig; +import com.jd.blockchain.ledger.core.LedgerEditor; +import com.jd.blockchain.ledger.core.LedgerInitDecision; +import com.jd.blockchain.ledger.core.LedgerInitPermission; +import com.jd.blockchain.ledger.core.LedgerInitPermissionData; +import com.jd.blockchain.ledger.core.LedgerManage; +import com.jd.blockchain.ledger.core.LedgerTransactionContext; +import com.jd.blockchain.ledger.data.DigitalSignatureBlob; +import com.jd.blockchain.ledger.data.LedgerInitSettingData; +import com.jd.blockchain.ledger.data.TxBuilder; +import com.jd.blockchain.ledger.data.TxRequestBuilder; +import com.jd.blockchain.storage.service.DbConnection; +import com.jd.blockchain.storage.service.DbConnectionFactory; +import com.jd.blockchain.tools.initializer.DBConnectionConfig; +import com.jd.blockchain.tools.initializer.InitializingStep; +import com.jd.blockchain.tools.initializer.LedgerInitException; +import com.jd.blockchain.tools.initializer.LedgerInitProcess; +import com.jd.blockchain.tools.initializer.LedgerInitProperties; +import com.jd.blockchain.tools.initializer.LedgerInitProperties.ConsensusParticipantConfig; +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.concurrent.InvocationResult; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.net.NetworkAddress; +import com.jd.blockchain.tools.initializer.Prompter; + +/** + * 账本初始化控制器; + * + * @author huanghaiquan + * + */ +@RestController +public class LedgerInitializeWebController implements LedgerInitProcess, LedgerInitConsensusService { + + static { + DataContractRegistry.register(TransactionRequest.class); + } + + private static final CryptoAlgorithm SIGN_ALG = CryptoAlgorithm.ED25519; + + private volatile LedgerInitPermission localPermission; + + private TransactionContent initTxContent; + + private volatile int currentId = -1; + + private volatile LedgerInitSetting ledgerInitSetting; + + private volatile LedgerInitPermission[] permissions; + + private volatile NetworkAddress[] initializerAddresses; + + private volatile Prompter prompter; + + private volatile ConsensusProvider consensusProvider; + + private volatile LedgerBlock genesisBlock; + + private volatile LedgerInitDecision localDecision; + + private volatile DecisionResultHandle[] decisions; + + private volatile DbConnection dbConn; + + private volatile LedgerEditor ledgerEditor; + + @Autowired + private LedgerManage ledgerManager; + + @Autowired + private DbConnectionFactory dbConnFactory; + + @Autowired + private InitConsensusServiceFactory initCsServiceFactory; + + public LedgerInitializeWebController() { + } + + public LedgerInitializeWebController(LedgerManage ledgerManager, DbConnectionFactory dbConnFactory, + InitConsensusServiceFactory initCsServiceFactory) { + this.ledgerManager = ledgerManager; + this.dbConnFactory = dbConnFactory; + this.initCsServiceFactory = initCsServiceFactory; + } + + public int getId() { + return currentId; + } + + public TransactionContent getInitTxContent() { + return initTxContent; + } + + public LedgerInitPermission getLocalPermission() { + return localPermission; + } + + public LedgerInitDecision getLocalDecision() { + return localDecision; + } + + public void setPrompter(Prompter prompter) { + this.prompter = prompter; + } + + public ConsensusProvider getConsensusProvider() { + return consensusProvider; + } + + public void setConsensusProvider(ConsensusProvider consensusProvider) { + this.consensusProvider = consensusProvider; + } + + @Override + public HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, + ConsensusSettings csSettings, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter) { + return initialize(currentId, privKey, ledgerInitProps, csSettings, csProvider, dbConnConfig, prompter, + createDefaultCryptoSetting()); + } + + @Override + public HashDigest initialize(int currentId, PrivKey privKey, LedgerInitProperties ledgerInitProps, + ConsensusSettings consensusProps, ConsensusProvider csProvider, DBConnectionConfig dbConnConfig, + Prompter prompter, CryptoSetting cryptoSetting) { + + if (this.ledgerInitSetting != null) { + throw new IllegalStateException("ledger init process has already started."); + } + + setPrompter(prompter); + setConsensusProvider(csProvider); + prompter.info("Init settings and sign permision..."); + + prepareLocalPermission(currentId, privKey, ledgerInitProps, consensusProps, cryptoSetting); + + prompter.confirm(InitializingStep.PERMISSION_READY.toString(), + "Ledger init permission has already prepared! Any key to continue..."); + + prompter.info("Start consensus with each other participant..."); + + // 从其它参与者获得许可; + boolean allPermitted = consensusPermissions(currentId, privKey); + + if (allPermitted) { + prompter.info("All participants permitted!"); + } else { + prompter.error("Initialization is broken because of not all participants permitting!"); + return null; + } + + try { + // 连接数据库; + connectDb(dbConnConfig); + + // 生成账本; + makeLocalDecision(privKey); + + // 获取其它参与方的账本生成结果; + return consensusDecisions(privKey); + } finally { + closeDb(); + } + } + + public boolean consensusPermisions(PrivKey privKey) { + return consensusPermissions(currentId, privKey); + } + + public DbConnection connectDb(DBConnectionConfig dbConnConfig) { + this.dbConn = dbConnFactory.connect(dbConnConfig.getUri(), dbConnConfig.getPassword()); + return dbConn; + } + + public LedgerInitDecision makeLocalDecision(PrivKey privKey) { + // 生成账本; + this.ledgerEditor = ledgerManager.newLedger(this.ledgerInitSetting, dbConn.getStorageService()); + this.genesisBlock = initLedgerDataset(ledgerEditor); + + // 生成签名决定; + this.localDecision = makeDecision(currentId, genesisBlock.getHash(), privKey); + this.decisions = new DecisionResultHandle[this.ledgerInitSetting.getConsensusParticipants().length]; + for (int i = 0; i < decisions.length; i++) { + // 参与者的 id 是依次递增的; + this.decisions[i] = new DecisionResultHandle(i); + } + // 预置当前参与方的“决定”到列表,避免向自己发起请求; + this.decisions[currentId].setResult(localDecision); + return localDecision; + } + + public HashDigest consensusDecisions(PrivKey privKey) { + // 获取其它参与方的账本生成结果; + boolean allDecided = startRequestDecisions(privKey, prompter); + if (!allDecided) { + prompter.error( + "Rollback ledger initialization because of not all nodes make same decision! --[Current Participant=%s]", + currentId); + ledgerEditor.cancel(); + return null; + } + + // 执行提交提交; + ledgerEditor.commit(); + return genesisBlock.getHash(); + } + + public void closeDb() { + if (dbConn != null) { + DbConnection connection = dbConn; + dbConn = null; + try { + connection.close(); + } catch (IOException e) { + prompter.error(e, "Error occurred on closing db connection! --" + e.getMessage()); + } + } + } + + /** + * 在所有参与者之间进行第一阶段的共识:账本创建许可; + * + * @param privKey + * @return + */ + private boolean consensusPermissions(int currentId, PrivKey privKey) { + // 从其它参与者获得许可; + boolean allPermitted = false; + int retry = 0; + do { + allPermitted = startRequestPermissions(currentId, privKey); + if (!allPermitted) { + if (retry < 16) { + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + // ignore interrupted exception; + } + } else { + String r = prompter.confirm( + "\r\n Some permissions were rejected! Do you retry again? Press 'Y' to retry, any others to quit. :>"); + if (!"Y".equalsIgnoreCase(r)) { + throw new LedgerInitException("Ledger init process has broken!"); + } + retry = 0; + } + + retry++; + prompter.info("Retry requesting permissions...[%s]", retry); + } + } while (!allPermitted); + + return allPermitted; + } + + public CryptoSetting createDefaultCryptoSetting() { + CryptoConfig defCryptoSetting = new CryptoConfig(); + defCryptoSetting.setAutoVerifyHash(true); + defCryptoSetting.setHashAlgorithm(CryptoAlgorithm.SHA256); + + return defCryptoSetting; + } + + public LedgerInitPermission prepareLocalPermission(int currentId, PrivKey privKey, LedgerInitProperties ledgerProps, + ConsensusSettings consensusProps) { + CryptoSetting defCryptoSetting = createDefaultCryptoSetting(); + return prepareLocalPermission(currentId, privKey, ledgerProps, consensusProps, defCryptoSetting); + } + + public LedgerInitPermission prepareLocalPermission(int currentId, PrivKey privKey, LedgerInitProperties ledgerProps, + ConsensusSettings csSettings, CryptoSetting cryptoSetting) { + // 创建初始化配置; + LedgerInitSettingData initSetting = new LedgerInitSettingData(); + initSetting.setLedgerSeed(ledgerProps.getLedgerSeed()); + initSetting.setCryptoSetting(cryptoSetting); + ConsensusParticipantConfig[] parties = ledgerProps.getConsensusParticipantArray(); + ConsensusParticipantConfig[] orderedParties = sortAndVerify(parties); + initSetting.setConsensusParticipants(orderedParties); + + // 创建默认的共识配置; + try { + // ConsensusConfig csConfig = new ConsensusConfig(); + byte[] csSettingBytes = consensusProvider.getSettingsFactory().getConsensusSettingsEncoder() + .encode(csSettings); + initSetting.setConsensusProvider(consensusProvider.getName()); + initSetting.setConsensusSettings(new Bytes(csSettingBytes)); + } catch (Exception e) { + throw new LedgerInitException("Create default consensus config failed! --" + e.getMessage(), e); + } + + if (currentId < 0 || currentId >= orderedParties.length) { + throw new LedgerInitException("Your id is out of bound of participant list!"); + } + this.currentId = currentId; + this.ledgerInitSetting = initSetting; + + // 校验当前的公钥、私钥是否匹配; + byte[] testBytes = BytesUtils.toBytes(currentId); + SignatureDigest testSign = CryptoUtils.sign(SIGN_ALG).sign(privKey, testBytes); + PubKey myPubKey = orderedParties[currentId].getPubKey(); + if (!CryptoUtils.sign(SIGN_ALG).verify(testSign, myPubKey, testBytes)) { + throw new LedgerInitException("Your pub-key specified in the init-settings isn't match your priv-key!"); + } + this.initializerAddresses = new NetworkAddress[orderedParties.length]; + // 记录每个参与方的账本初始化服务地址; + for (int i = 0; i < orderedParties.length; i++) { + initializerAddresses[i] = orderedParties[i].getInitializerAddress(); + } + + // 生成初始化交易,并签署许可; + TransactionBuilder initTxBuilder = new TxBuilder(null);// 账本初始化交易的账本 hash 为 null; + initTxBuilder.ledgers().create(initSetting); + for (ParticipantNode p : initSetting.getConsensusParticipants()) { + // TODO:暂时只支持注册用户的初始化操作; + BlockchainIdentity superUserId = new BlockchainIdentityData(p.getPubKey()); + initTxBuilder.users().register(superUserId); + } + this.initTxContent = initTxBuilder.prepareContent(); + + // 对初始交易签名,生成当前参与者的账本初始化许可; + SignatureDigest permissionSign = TxRequestBuilder.sign(initTxContent, privKey); + LedgerInitPermissionData permission = new LedgerInitPermissionData(currentId, permissionSign); + + this.currentId = currentId; + this.permissions = new LedgerInitPermission[initSetting.getConsensusParticipants().length]; + this.permissions[currentId] = permission; + this.localPermission = permission; + + return permission; + } + + private LedgerInitDecision makeDecision(int participantId, HashDigest ledgerHash, PrivKey privKey) { + byte[] dataBytes = getDecisionBytes(participantId, ledgerHash); + SignatureDigest signature = CryptoUtils.sign(privKey.getAlgorithm()).sign(privKey, dataBytes); + + LedgerInitDecisionData decision = new LedgerInitDecisionData(); + decision.setParticipantId(participantId); + decision.setLedgerHash(ledgerHash); + decision.setSignature(signature); + return decision; + } + + private LedgerBlock initLedgerDataset(LedgerEditor ledgerEditor) { + // 初始化时,自动将参与方注册为账本的用户; + TxRequestBuilder txReqBuilder = new TxRequestBuilder(this.initTxContent); + ParticipantNode[] parties = this.ledgerInitSetting.getConsensusParticipants(); + for (int i = 0; i < parties.length; i++) { + PubKey pubKey = parties[i].getPubKey(); + SignatureDigest signDigest = this.permissions[i].getTransactionSignature(); + DigitalSignatureBlob digitalSignature = new DigitalSignatureBlob(pubKey, signDigest); + txReqBuilder.addNodeSignature(digitalSignature); + } + TransactionRequest txRequest = txReqBuilder.buildRequest(); + + LedgerTransactionContext txCtx = ledgerEditor.newTransaction(txRequest); + Operation[] ops = txRequest.getTransactionContent().getOperations(); + // 注册用户; 注:第一个操作是 LedgerInitOperation; + // TODO:暂时只支持注册用户的初始化操作; + for (int i = 1; i < ops.length; i++) { + UserRegisterOperation userRegOP = (UserRegisterOperation) ops[i]; + txCtx.getDataSet().getUserAccountSet().register(userRegOP.getUserID().getAddress(), + userRegOP.getUserID().getPubKey()); + } + + txCtx.commit(TransactionState.SUCCESS); + + return ledgerEditor.prepare(); + } + + /** + * 请求所有其它参与方的账本创建许可; + * + * @param privKey + * @return + */ + private boolean startRequestPermissions(int currentId, PrivKey privKey) { + SignatureDigest reqAuthSign = signPermissionRequest(currentId, privKey); + + ParticipantNode[] participants = ledgerInitSetting.getConsensusParticipants(); + + // 异步请求结果列表;不包括已经获得许可的参与方; + InvocationResult[] results = new InvocationResult[participants.length]; + int unpermittedCount = 0; + for (int i = 0; i < participants.length; i++) { + if (this.permissions[i] == null) { + unpermittedCount++; + } + } + + // 发起请求; + CountDownLatch latch = new CountDownLatch(unpermittedCount); + for (int i = 0; i < participants.length; i++) { + if (this.permissions[i] == null) { + results[i] = doRequestPermission(participants[i].getId(), reqAuthSign, latch); + } + } + + // 等待结果; + try { + while (!latch.await(5000, TimeUnit.MILLISECONDS)) { + List waitingIds = new ArrayList<>(); + for (int i = 0; i < results.length; i++) { + if (results[i] != null) { + if (results[i].getResult() == null) { + waitingIds.add("" + (i + 1)); + } + } + } + + prompter.info("\r\nWaiting for permissions of participants[%s] ...", String.join(",", waitingIds)); + } + } catch (InterruptedException e) { + throw new LedgerInitException( + "Process of requesting participant permissions was interrupted! --" + e.getMessage(), e); + } + + // 校验接入许可; + boolean allPermitted = true; + for (int i = 0; i < results.length; i++) { + if (results[i] == null) {// 忽略自己; + continue; + } + if (results[i].getError() != null) { + prompter.error(results[i].getError(), + "Error occurred on requesting permission from participant[Id=%s, name=%s]!", + participants[i].getAddress(), participants[i].getName()); + allPermitted = false; + continue; + } + PubKey pubKey = participants[i].getPubKey(); + LedgerInitPermission permission = (LedgerInitPermission) results[i].getResult(); + if (permission.getParticipantId() != participants[i].getId()) { + prompter.error("\r\nThe id of received permission isn't equal to it's participant ! --[Id=%s][name=%s]", + participants[i].getAddress(), participants[i].getName()); + allPermitted = false; + continue; + } + + if (!TxRequestBuilder.verifySignature(this.initTxContent, permission.getTransactionSignature(), pubKey)) { + prompter.error("Invalid permission from participant! --[Id=%s][name=%s]", participants[i].getAddress(), + participants[i].getName()); + allPermitted = false; + continue; + } + this.permissions[i] = permission; + } + return allPermitted; + } + + private byte[] getDecisionBytes(int participantId, HashDigest ledgerHash) { + return BytesUtils.concat(BytesUtils.toBytes(participantId), ledgerHash.toBytes()); + } + + private LedgerInitConsensusService connectToParticipant(int participantId) { + return initCsServiceFactory.connect(this.initializerAddresses[participantId]); + } + + public SignatureDigest signPermissionRequest(int requesterId, PrivKey privKey) { + byte[] reqAuthBytes = BytesUtils.concat(BytesUtils.toBytes(requesterId), ledgerInitSetting.getLedgerSeed()); + SignatureDigest reqAuthSign = CryptoUtils.sign(SIGN_ALG).sign(privKey, reqAuthBytes); + return reqAuthSign; + } + + /** + * 向指定的参与方请求其对于账本初始化参数的许可签名; + * + * @param targetId + * @param reqAuthSign + * @param latch + * @return + */ + private InvocationResult doRequestPermission(int targetId, SignatureDigest reqAuthSign, + CountDownLatch latch) { + InvocationResult result = new InvocationResult<>(); + try { + LedgerInitConsensusService initConsensus = connectToParticipant(targetId); + Thread thrd = new Thread(new Runnable() { + @Override + public void run() { + try { + LedgerInitPermission permission = initConsensus.requestPermission(currentId, reqAuthSign); + result.setResult(permission); + } catch (Exception e) { + result.setError(e); + } finally { + latch.countDown(); + } + } + }); + thrd.start(); + } catch (Exception e) { + result.setError(e); + } + return result; + } + + @RequestMapping(path = "/legerinit/permission/{requesterId}", method = RequestMethod.POST, produces = LedgerInitMessageConverter.CONTENT_TYPE_VALUE, consumes = LedgerInitMessageConverter.CONTENT_TYPE_VALUE) + @Override + public LedgerInitPermission requestPermission(@PathVariable(name = "requesterId") int requesterId, + @RequestBody SignatureDigest signature) { + if (requesterId == currentId) { + throw new LedgerInitException("There is a id conflict!"); + } + int retry = 0; + while (currentId == -1 || ledgerInitSetting == null || localPermission == null) { + // 本地尚未完成初始化; + if (retry < 30) { + try { + Thread.sleep(800); + } catch (InterruptedException e) { + // ignore interrupted exception; + } + } else { + return null; + } + retry++; + } + + ParticipantNode[] participants = ledgerInitSetting.getConsensusParticipants(); + if (requesterId < 0 || requesterId >= participants.length) { + throw new LedgerInitException("The id of requester is out of the bound of participant list!"); + } + byte[] requestCodeBytes = BytesUtils.concat(BytesUtils.toBytes(requesterId), ledgerInitSetting.getLedgerSeed()); + PubKey requesterPubKey = participants[requesterId].getPubKey(); + if (!CryptoUtils.sign(SIGN_ALG).verify(signature, requesterPubKey, requestCodeBytes)) { + throw new LedgerInitException("The requester signature is invalid!"); + } + return localPermission; + } + + /** + * 开始请求所有成员的账本创建决定; + */ + private boolean startRequestDecisions(PrivKey privKey, Prompter prompter) { + // 进行随机化选择请求的目标列表的顺序; + Random rand = new Random(); + DecisionResultHandle[] randDecHdls = Arrays.copyOf(this.decisions, this.decisions.length); + DecisionResultHandle temp; + for (int i = 0; i < randDecHdls.length; i++) { + int a = rand.nextInt(randDecHdls.length); + int b = rand.nextInt(randDecHdls.length); + temp = randDecHdls[a]; + randDecHdls[a] = randDecHdls[b]; + randDecHdls[b] = temp; + } + + // 请求; + boolean allDecided = false; + while (!allDecided) { + allDecided = true; + for (int i = 0; i < randDecHdls.length; i++) { + if (randDecHdls[i].getResult() != null) { + // 忽略当前参与方自己(在初始化“决定”时已经置为非空),以及已经收到主动提交“决定”的参与方; + continue; + } + boolean decided = doSynchronizeDecision(randDecHdls[i].PARTICIPANT_ID, privKey, randDecHdls[i], + prompter); + allDecided = allDecided & decided; + } + if (!allDecided) { + String r = prompter.confirm( + "\r\nSome decisions were rejected! Do you retry again? Press 'Y' to retry, any others to quit. :>"); + if (!"Y".equalsIgnoreCase(r)) { + return false; + } + } + } + + return allDecided; + } + + /** + * 与指定的参与方同步“账本初始化决定({@link LedgerInitDecision})”; + * + * @param targetId + * @param privKey + * @param resultHandle + * @param prompter + * @return + */ + private boolean doSynchronizeDecision(int targetId, PrivKey privKey, DecisionResultHandle resultHandle, + Prompter prompter) { + try { + LedgerInitConsensusService initConsensus = connectToParticipant(targetId); + LedgerInitDecision targetDecision = null; + int retry = 0; + do { + prompter.info("Start synchronizling decision from participant[%s] to participant[%s] ......", currentId, + targetId); + try { + targetDecision = initConsensus.synchronizeDecision(localDecision); + } catch (Exception e1) { + prompter.info("Error occurred on synchronizing decision . --%s", e1.getMessage()); + } + if (targetDecision == null) { + if (resultHandle.getResult() != null) { + // 已经验证过; + return true; + } + // 对方的账本初始化尚未就绪;隔5秒重试;重试超过3后提示确认是否继续重试; + if (retry == 16) { + String r = prompter.confirm( + "Target participant[%s] isn't ready to do decision! Do you want to retry again?\r\n" + + " Press 'Y' to retry, and any others to break this participant and to continue synchronizing with others. :>", + targetId); + if (!"Y".equalsIgnoreCase(r)) { + return false; + } + retry = 0; + } + prompter.info( + "Target participant[%s] isn't ready to do decision! Waiting 5 seconds and retry again...", + targetId); + try { + Thread.sleep(2000); + } catch (InterruptedException e) { + // ignore InterruptedException; + } + retry++; + } + } while (targetDecision == null); + + if (targetDecision.getParticipantId() != targetId && resultHandle.getResult() == null) { + prompter.error( + "The received id of participant isn't equal to id of request target participant! --[Id=%s]", + targetId); + return false; + } + + return validateAndRecordDecision(targetDecision, resultHandle, privKey.getAlgorithm()); + } catch (Exception e) { + prompter.error(e, "Error occurred on synchronizing decision from participant[%s] to participant[%s] ! --%s", + currentId, targetId, e.getMessage()); + return false; + } + } + + /** + * 校验并记录指定的参与方做出的决定; + *

+ * 注:对 {@link DecisionResultHandle#setResult(LedgerInitDecision)} + * 方法的调用不是线程安全的,但由于是在满足有效性校验之后才调用,具有幂等性,所以不必对该方法的多处调用进行同步; + * + * @param targetDecision + * @param resultHandle + * @param hashAlgorithm + * @return + */ + private synchronized boolean validateAndRecordDecision(LedgerInitDecision targetDecision, + DecisionResultHandle resultHandle, CryptoAlgorithm hashAlgorithm) { + if ((!localDecision.getLedgerHash().equals(targetDecision.getLedgerHash())) + && resultHandle.getResult() == null) { + // 如果结果已经被 + prompter.error( + "The received ledger hash of participant isn't equal to ledger hash of current participant! --[Id=%s]", + targetDecision.getParticipantId()); + return false; + } + + // 检查签名; + PubKey targetPubKey = ledgerInitSetting.getConsensusParticipants()[targetDecision.getParticipantId()] + .getPubKey(); + byte[] deciBytes = getDecisionBytes(targetDecision.getParticipantId(), targetDecision.getLedgerHash()); + if ((!CryptoUtils.sign(hashAlgorithm).verify(targetDecision.getSignature(), targetPubKey, deciBytes)) + && resultHandle.getResult() == null) { + prompter.error("The signature of received decision is invalid! --[Id=%s]", + targetDecision.getParticipantId()); + return false; + } + + resultHandle.setResult(targetDecision); + return true; + } + + @RequestMapping(path = "/legerinit/decision", method = RequestMethod.POST, produces = LedgerInitMessageConverter.CONTENT_TYPE_VALUE, consumes = LedgerInitMessageConverter.CONTENT_TYPE_VALUE) + @Override + public LedgerInitDecision synchronizeDecision(@RequestBody LedgerInitDecision initDecision) { + int remoteId = initDecision.getParticipantId(); + if (remoteId == currentId) { + throw new LedgerInitException( + String.format("Reject decision because of self-synchronization! --[Id=%s]", remoteId)); + } + if (this.genesisBlock == null) { + // 当前参与者尚未准备就绪,返回 null; + return null; + } + PubKey pubKey = ledgerInitSetting.getConsensusParticipants()[remoteId].getPubKey(); + DecisionResultHandle resultHandle = this.decisions[remoteId]; + if (!validateAndRecordDecision(initDecision, resultHandle, pubKey.getAlgorithm())) { + // 签名无效; + throw new LedgerInitException( + String.format("Reject decision because of invalid signature! --[Id=%s]", remoteId)); + } + return localDecision; + } + + /** + * 对参与者列表按照 id 进行升序排列,并校验id是否从 1 开始且没有跳跃; + * + * @param parties + * @return + */ + private ConsensusParticipantConfig[] sortAndVerify(ConsensusParticipantConfig[] parties) { + Arrays.sort(parties, new Comparator() { + @Override + public int compare(ConsensusParticipantConfig o1, ConsensusParticipantConfig o2) { + return o1.getId() - o2.getId(); + } + }); + for (int i = 0; i < parties.length; i++) { + if (parties[i].getId() != i) { + throw new LedgerInitException( + "The ids of participants are not match their positions in the participant-list!"); + } + } + return parties; + } + + private static class DecisionResultHandle extends InvocationResult { + + private final int PARTICIPANT_ID; + + public DecisionResultHandle(int participantId) { + this.PARTICIPANT_ID = participantId; + } + + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/PermissionResponseConverter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/PermissionResponseConverter.java new file mode 100644 index 00000000..a0f40417 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/PermissionResponseConverter.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.InputStream; + +import com.jd.blockchain.binaryproto.BinaryEncodingUtils; +import com.jd.blockchain.ledger.core.LedgerInitPermissionData; +import com.jd.blockchain.tools.initializer.LedgerInitException; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; + +public class PermissionResponseConverter implements ResponseConverter { + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) + throws Exception { + LedgerInitResponse resp = LedgerInitResponse.resolve(responseStream); + if (resp.isError()) { + throw new LedgerInitException("Error occurred at remote participant! --" + resp.getErrorMessage()); + } + return BinaryEncodingUtils.decode(resp.getData()); + } + +} diff --git a/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/SignatureDigestRequestBodyConverter.java b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/SignatureDigestRequestBodyConverter.java new file mode 100644 index 00000000..7abc1895 --- /dev/null +++ b/source/tools/tools-initializer/src/main/java/com/jd/blockchain/tools/initializer/web/SignatureDigestRequestBodyConverter.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.tools.initializer.web; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.crypto.asymmetric.SignatureDigest; +import com.jd.blockchain.utils.http.RequestBodyConverter; + +public class SignatureDigestRequestBodyConverter implements RequestBodyConverter { + + @Override + public void write(Object param, OutputStream out) throws IOException { + if (param instanceof SignatureDigest) { + out.write(((SignatureDigest)param).toBytes()); + return; + } + } + +} \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/resources/META-INF/spring.factories b/source/tools/tools-initializer/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..854f0848 --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +com.jd.blockchain.tools.initializer.InitializerConfiguration \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/resources/application.properties b/source/tools/tools-initializer/src/main/resources/application.properties new file mode 100644 index 00000000..2090d209 --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/application.properties @@ -0,0 +1,17 @@ +server.address=127.0.0.1 +server.port=8900 + +#server.ssl.key-store=classpath:mykeys.jks +#server.ssl.key-store-password=abc123 +#server.ssl.key-password=abc123 + +server.tomcat.accesslog.enabled=true + +debug=false + +#logging.file=logs/peer.log +logging.level.com.jd.blockchain=DEBUG +logging.level.org.org.springframework=DEBUG + +spring.mvc.favicon.enabled=false + diff --git a/source/tools/tools-initializer/src/main/resources/banner.txt b/source/tools/tools-initializer/src/main/resources/banner.txt new file mode 100644 index 00000000..c39618bd --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/banner.txt @@ -0,0 +1,13 @@ + + ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄ ▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄▄▄▄▄▄▄▄▄▄ ▄▄ ▄ +▐░░░░░░░░░░░▌▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░▌ ▐░▌ + ▀▀▀▀▀█░█▀▀▀ ▐░█▀▀▀▀▀▀▀█░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀▀▀ ▐░▌ ▐░▌▐░█▀▀▀▀▀▀▀█░▌ ▀▀▀▀█░█▀▀▀▀ ▐░▌░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌░▌ ▐░▌ ▐░█▀▀▀▀▀▀▀█░▌▐░█▀▀▀▀▀▀▀█░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌ + ▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ ▐░▌ ▐░▌ ▐░▌ ▐░▌▐░▌ + ▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░█▄▄▄▄▄▄▄█░▌▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌ ▐░█▄▄▄▄▄▄▄▄▄ ▐░▌ ▐░▌▐░▌ ▐░▌ ▄▄▄▄█░█▄▄▄▄ ▐░▌ ▐░▐░▌ +▐░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░▌ ▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░░░░░░░░░░░▌▐░▌ ▐░▌ ▐░░░░░░░░░░░▌▐░▌ ▐░▌▐░▌ ▐░▌▐░░░░░░░░░░░▌▐░▌ ▐░░▌ + ▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀ ▀ ▀ ▀▀▀▀▀▀▀▀▀▀▀ ▀ ▀▀ + diff --git a/source/tools/tools-initializer/src/main/resources/banner2.txt b/source/tools/tools-initializer/src/main/resources/banner2.txt new file mode 100644 index 00000000..f1c960f7 --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/banner2.txt @@ -0,0 +1,12 @@ + + 888888 8888888b. 888888b. 888 888 .d8888b. 888 d8b + "88b 888 "Y88b 888 "88b 888 888 d88P Y88b 888 Y8P + 888 888 888 888 .88P 888 888 888 888 888 + 888 888 888 8888888K. 888 .d88b. .d8888b 888 888 888 88888b. 8888b. 888 88888b. + 888 888 888 888 "Y88b 888 d88""88b d88P" 888 .88P 888 888 "88b "88b 888 888 "88b + 888 888 888 888 888 888 888 888 888 888888K 888 888 888 888 .d888888 888 888 888 + 88P 888 .d88P 888 d88P 888 Y88..88P Y88b. 888 "88b Y88b d88P 888 888 888 888 888 888 888 + 888 8888888P" 8888888P" 888 "Y88P" "Y8888P 888 888 "Y8888P" 888 888 "Y888888 888 888 888 + .d88P + .d88P" +888P" diff --git a/source/tools/tools-initializer/src/main/resources/banner3.txt b/source/tools/tools-initializer/src/main/resources/banner3.txt new file mode 100644 index 00000000..a8e93d02 --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/banner3.txt @@ -0,0 +1,12 @@ + + $$$$$\ $$$$$$$\ $$$$$$$\ $$\ $$\ $$$$$$\ $$\ $$\ + \__$$ |$$ __$$\ $$ __$$\ $$ | $$ | $$ __$$\ $$ | \__| + $$ |$$ | $$ | $$ | $$ |$$ | $$$$$$\ $$$$$$$\ $$ | $$\ $$ / \__|$$$$$$$\ $$$$$$\ $$\ $$$$$$$\ + $$ |$$ | $$ | $$$$$$$\ |$$ |$$ __$$\ $$ _____|$$ | $$ | $$ | $$ __$$\ \____$$\ $$ |$$ __$$\ +$$\ $$ |$$ | $$ | $$ __$$\ $$ |$$ / $$ |$$ / $$$$$$ / $$ | $$ | $$ | $$$$$$$ |$$ |$$ | $$ | +$$ | $$ |$$ | $$ | $$ | $$ |$$ |$$ | $$ |$$ | $$ _$$< $$ | $$\ $$ | $$ |$$ __$$ |$$ |$$ | $$ | +\$$$$$$ |$$$$$$$ | $$$$$$$ |$$ |\$$$$$$ |\$$$$$$$\ $$ | \$$\ \$$$$$$ |$$ | $$ |\$$$$$$$ |$$ |$$ | $$ | + \______/ \_______/ \_______/ \__| \______/ \_______|\__| \__| \______/ \__| \__| \_______|\__|\__| \__| + + + diff --git a/source/tools/tools-initializer/src/main/resources/local.conf b/source/tools/tools-initializer/src/main/resources/local.conf new file mode 100644 index 00000000..9df0303c --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/local.conf @@ -0,0 +1,25 @@ + +#当前参与方的公钥; +local.parti.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=./ + +#账本数据库的连接字符串; +ledger.db.uri=redis://127.0.0.1/0 + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识协议的参数配置;必须参数; +consensus.conf=mq.config + +#共识协议的实现类型;必须参数; +consensus.service-provider=com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider + diff --git a/source/tools/tools-initializer/src/main/resources/mq.config b/source/tools/tools-initializer/src/main/resources/mq.config new file mode 100644 index 00000000..9795a9de --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/mq.config @@ -0,0 +1,6 @@ +system.msg.queue.server=nats://127.0.0.1:4222 +system.msg.queue.topic.tx=tx-topic +system.msg.queue.topic.bl=bl-topic +system.msg.queue.topic.msg=msg-topic +system.msg.queue.block.txsize=1000 +system.msg.queue.block.maxdelay=2000 \ No newline at end of file diff --git a/source/tools/tools-initializer/src/main/resources/system.config b/source/tools/tools-initializer/src/main/resources/system.config new file mode 100644 index 00000000..2f2c9185 --- /dev/null +++ b/source/tools/tools-initializer/src/main/resources/system.config @@ -0,0 +1,126 @@ +# 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 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 ### +############################################ + +#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 ###### +############################################ + +#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 + + +#Number of servers in the group +system.servers.num = 4 + +#Maximum number of faulty replicas +system.servers.f = 1 + +#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 + +#Unique ID of node. +server.1.address= +server.1.pubkey= diff --git a/source/tools/tools-initializer/src/test/java/test/com/jd/blockchain/tools/initializer/ConsensusSettingTest.java b/source/tools/tools-initializer/src/test/java/test/com/jd/blockchain/tools/initializer/ConsensusSettingTest.java new file mode 100644 index 00000000..0500d122 --- /dev/null +++ b/source/tools/tools-initializer/src/test/java/test/com/jd/blockchain/tools/initializer/ConsensusSettingTest.java @@ -0,0 +1,52 @@ +package test.com.jd.blockchain.tools.initializer; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import java.io.*; + +/** + * Created by zhangshuang3 on 2018/9/11. + */ +public class ConsensusSettingTest { + + @Test + public void systemFileToBytes() throws Exception { + + ClassPathResource systemConfigResource = new ClassPathResource("bftsmart.config"); + InputStream fis = systemConfigResource.getInputStream(); + ByteArrayOutputStream bos = null; + FileOutputStream fos = null; + byte[] buffer = null; + + try { + bos = new ByteArrayOutputStream(1000); + byte[] b = new byte[1000]; + int n; + while ((n = fis.read(b)) != -1) { + bos.write(b, 0, n); + } + //file to bytes + buffer = bos.toByteArray(); + //bytes to file + File file = new File("bftsmart.config"); + fos = new FileOutputStream(file); + fos.write(buffer); + } catch (FileNotFoundException e) { + throw new Exception(e.getMessage(), e); + } catch (IOException e) { + throw new Exception(e.getMessage(), e); + } finally { + if (fis != null) { + fis.close(); + } + if (bos != null) { + bos.close(); + } + if (fos != null) { + fos.close(); + } + } + return; + } +} \ No newline at end of file diff --git a/source/tools/tools-initializer/src/test/java/test/com/jd/blockchain/tools/initializer/LedgerBindingConfigTest.java b/source/tools/tools-initializer/src/test/java/test/com/jd/blockchain/tools/initializer/LedgerBindingConfigTest.java new file mode 100644 index 00000000..4b29ccef --- /dev/null +++ b/source/tools/tools-initializer/src/test/java/test/com/jd/blockchain/tools/initializer/LedgerBindingConfigTest.java @@ -0,0 +1,78 @@ +package test.com.jd.blockchain.tools.initializer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; + +import com.jd.blockchain.crypto.hash.HashDigest; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig; +import com.jd.blockchain.tools.initializer.LedgerBindingConfig.BindingConfig; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; + +public class LedgerBindingConfigTest { + + @Test + public void testResolveAndStore() throws IOException { + ClassPathResource ledgerBindingConfigFile = new ClassPathResource("ledger-binding.conf"); + InputStream in = ledgerBindingConfigFile.getInputStream(); + try { + LedgerBindingConfig conf = LedgerBindingConfig.resolve(in); + assertLedgerBindingConfig(conf); + + conf.store(System.out); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + conf.store(out); + + ByteArrayInputStream newIn = new ByteArrayInputStream(out.toByteArray()); + LedgerBindingConfig newConf = LedgerBindingConfig.resolve(newIn); + + assertLedgerBindingConfig(newConf); + } finally { + in.close(); + } + + } + + /** + * 判断指定的对象跟测试模板是否一致; + * + * @param conf + */ + private void assertLedgerBindingConfig(LedgerBindingConfig conf) { + String[] expectedHashs = { "6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K", + "64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty" }; + HashDigest[] hashs = conf.getLedgerHashs(); + for (int i = 0; i < hashs.length; i++) { + assertEquals(expectedHashs[i], hashs[i].toBase58()); + } + + BindingConfig bindingConf_0 = conf.getLedger(hashs[0]); + assertEquals("1", bindingConf_0.getParticipant().getAddress()); + assertEquals("keys/jd-com.priv", bindingConf_0.getParticipant().getPkPath()); + assertEquals("AdSXsf5QJpy", bindingConf_0.getParticipant().getPk()); + assertNull(bindingConf_0.getParticipant().getPassword()); + + assertEquals("redis://ip:port/1", bindingConf_0.getDbConnection().getUri()); + assertEquals("kksfweffj", bindingConf_0.getDbConnection().getPassword()); + + BindingConfig bindingConf_1 = conf.getLedger(hashs[1]); + assertEquals("2", bindingConf_1.getParticipant().getAddress()); + assertEquals("keys/jd-com-1.priv", bindingConf_1.getParticipant().getPkPath()); + assertNull(bindingConf_1.getParticipant().getPk()); + assertEquals("kksafe", bindingConf_1.getParticipant().getPassword()); + + assertEquals("redis://ip:port/2", bindingConf_1.getDbConnection().getUri()); + assertNull(bindingConf_1.getDbConnection().getPassword()); + } + +} diff --git a/source/tools/tools-initializer/src/test/resources/ledger-binding.conf b/source/tools/tools-initializer/src/test/resources/ledger-binding.conf new file mode 100644 index 00000000..f04c2c72 --- /dev/null +++ b/source/tools/tools-initializer/src/test/resources/ledger-binding.conf @@ -0,0 +1,34 @@ + + +#绑定的账本的hash列表;以逗号分隔; +ledger.bindings=6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K, \ +64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty + +#第1个账本[6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K]的配置; +#账本的当前共识参与方的ID; +binding.6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K.parti.address=1 +#账本的当前共识参与方的私钥文件的保存路径; +binding.6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K.parti.pk-path=keys/jd-com.priv +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K.parti.pk=AdSXsf5QJpy +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K.parti.pwd= +#账本的存储数据库的连接字符串; +binding.6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K.db.uri=redis://ip:port/1 +#账本的存储数据库的连接口令; +binding.6HaDnSu4kY6vNAdSXsf5QJpyYxrtxxoH1tn8dDRvbRD8K.db.pwd=kksfweffj + + +#第2个账本[64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty]的配置; +#账本的当前共识参与方的ID; +binding.64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty.parti.address=2 +#账本的当前共识参与方的私钥文件的保存路径; +binding.64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty.parti.pk-path=keys/jd-com-1.priv +#账本的当前共识参与方的私钥内容(Base58编码);如果指定了,优先选用此属性,其次是 pk-path 属性; +binding.64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty.parti.pk= +#账本的当前共识参与方的私钥文件的读取口令;可为空;如果为空时,节点的启动过程中需要手动从控制台输入; +binding.64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty.parti.pwd=kksafe +#账本的存储数据库的连接字符串; +binding.64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty.db.uri=redis://ip:port/2 +#账本的存储数据库的连接口令; +binding.64hnH4a8n48LeEP5HU2bMWmNxUPcaZ1JRCehRwvuNS8Ty.db.pwd= \ No newline at end of file diff --git a/source/tools/tools-initializer/src/test/resources/ledger.init b/source/tools/tools-initializer/src/test/resources/ledger.init new file mode 100644 index 00000000..6cc31468 --- /dev/null +++ b/source/tools/tools-initializer/src/test/resources/ledger.init @@ -0,0 +1,85 @@ + +#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; +ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe + +#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; +ledger.name= + +#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; +cons_parti.count=4 + +#第0个参与方的名称; +cons_parti.0.name=jd.com +#第0个参与方的公钥文件路径; +cons_parti.0.pubkey-path=keys/jd-com.pub +#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.0.pubkey=endPsK36koyFr1D245Sa9j83vt6pZUdFBJoJRB3xAsWM6cwhRbna +#第0个参与方的共识服务的主机地址; +cons_parti.0.consensus.host=127.0.0.1 +#第0个参与方的共识服务的端口; +cons_parti.0.consensus.port=8900 +#第0个参与方的共识服务是否开启安全连接; +cons_parti.0.consensus.secure=true +#第0个参与方的账本初始服务的主机; +cons_parti.0.initializer.host=127.0.0.1 +#第0个参与方的账本初始服务的端口; +cons_parti.0.initializer.port=8800 +#第0个参与方的账本初始服务是否开启安全连接; +cons_parti.0.initializer.secure=true + +#第1个参与方的名称; +cons_parti.1.name=at.com +#第1个参与方的公钥文件路径; +cons_parti.1.pubkey-path=keys/at-com.pub +#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.1.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ +#第1个参与方的共识服务的主机地址; +cons_parti.1.consensus.host=127.0.0.1 +#第1个参与方的共识服务的端口; +cons_parti.1.consensus.port=8910 +#第1个参与方的共识服务是否开启安全连接; +cons_parti.1.consensus.secure=false +#第1个参与方的账本初始服务的主机; +cons_parti.1.initializer.host=127.0.0.1 +#第1个参与方的账本初始服务的端口; +cons_parti.1.initializer.port=8810 +#第1个参与方的账本初始服务是否开启安全连接; +cons_parti.1.initializer.secure=false + +#第2个参与方的名称; +cons_parti.2.name=bt.com +#第2个参与方的公钥文件路径; +cons_parti.2.pubkey-path=keys/bt-com.pub +#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.2.pubkey=endPsK36jEG281HMHeh6oSqzqLkT95DTnCM6REDURjdb2c67uR3R +#第2个参与方的共识服务的主机地址; +cons_parti.2.consensus.host=127.0.0.1 +#第2个参与方的共识服务的端口; +cons_parti.2.consensus.port=8920 +#第2个参与方的共识服务是否开启安全连接; +cons_parti.2.consensus.secure=false +#第2个参与方的账本初始服务的主机; +cons_parti.2.initializer.host=127.0.0.1 +#第2个参与方的账本初始服务的端口; +cons_parti.2.initializer.port=8820 +#第2个参与方的账本初始服务是否开启安全连接; +cons_parti.2.initializer.secure=true + +#第3个参与方的名称; +cons_parti.3.name=xt.com +#第3个参与方的公钥文件路径; +cons_parti.3.pubkey-path=keys/xt-com.pub +#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; +cons_parti.3.pubkey=endPsK36nse1dck4uF19zPvAMijCV336Y3zWdgb4rQG8QoRj5ktR +#第3个参与方的共识服务的主机地址; +cons_parti.3.consensus.host=127.0.0.1 +#第3个参与方的共识服务的端口; +cons_parti.3.consensus.port=8930 +#第3个参与方的共识服务是否开启安全连接; +cons_parti.3.consensus.secure=false +#第3个参与方的账本初始服务的主机; +cons_parti.3.initializer.host=127.0.0.1 +#第3个参与方的账本初始服务的端口; +cons_parti.3.initializer.port=8830 +#第3个参与方的账本初始服务是否开启安全连接; +cons_parti.3.initializer.secure=false diff --git a/source/tools/tools-initializer/src/test/resources/local.conf b/source/tools/tools-initializer/src/test/resources/local.conf new file mode 100644 index 00000000..9df0303c --- /dev/null +++ b/source/tools/tools-initializer/src/test/resources/local.conf @@ -0,0 +1,25 @@ + +#当前参与方的公钥; +local.parti.pubkey=endPsK36sC5JdPCDPDAXUwZtS3sxEmqEhFcC4whayAsTTh8Z6eoZ + +#当前参与方的私钥(密文编码); +local.parti.privkey=177gjsj5PHeCpbAtJE7qnbmhuZMHAEKuMsd45zHkv8F8AWBvTBbff8yRKdCyT3kwrmAjSnY + +#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; +local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY + +#账本初始化完成后生成的"账本绑定配置文件"的输出目录; +ledger.binding.out=./ + +#账本数据库的连接字符串; +ledger.db.uri=redis://127.0.0.1/0 + +#账本数据库的连接口令; +ledger.db.pwd= + +#共识协议的参数配置;必须参数; +consensus.conf=mq.config + +#共识协议的实现类型;必须参数; +consensus.service-provider=com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider + diff --git a/source/tools/tools-initializer/src/test/resources/mq.config b/source/tools/tools-initializer/src/test/resources/mq.config new file mode 100644 index 00000000..9795a9de --- /dev/null +++ b/source/tools/tools-initializer/src/test/resources/mq.config @@ -0,0 +1,6 @@ +system.msg.queue.server=nats://127.0.0.1:4222 +system.msg.queue.topic.tx=tx-topic +system.msg.queue.topic.bl=bl-topic +system.msg.queue.topic.msg=msg-topic +system.msg.queue.block.txsize=1000 +system.msg.queue.block.maxdelay=2000 \ No newline at end of file diff --git a/source/tools/tools-initializer/src/test/resources/system.config b/source/tools/tools-initializer/src/test/resources/system.config new file mode 100644 index 00000000..ada42b49 --- /dev/null +++ b/source/tools/tools-initializer/src/test/resources/system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/tools/tools-initializer/system.config b/source/tools/tools-initializer/system.config new file mode 100644 index 00000000..ada42b49 --- /dev/null +++ b/source/tools/tools-initializer/system.config @@ -0,0 +1,121 @@ +# 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 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 diff --git a/source/tools/tools-keygen-booter/pom.xml b/source/tools/tools-keygen-booter/pom.xml new file mode 100644 index 00000000..c0e91f9a --- /dev/null +++ b/source/tools/tools-keygen-booter/pom.xml @@ -0,0 +1,48 @@ + + 4.0.0 + + com.jd.blockchain + tools + 0.8.2.RELEASE + + tools-keygen-booter + + + + com.jd.blockchain + tools-keygen + ${project.version} + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + src/main/resources/META-INF/MANIFEST.MF + + com.jd.blockchain.tools.keygen.boot.KeyGenBooter + true + ../lib + false + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/tools/tools-keygen-booter/src/main/java/com/jd/blockchain/tools/keygen/boot/KeyGenBooter.java b/source/tools/tools-keygen-booter/src/main/java/com/jd/blockchain/tools/keygen/boot/KeyGenBooter.java new file mode 100644 index 00000000..c0ce182c --- /dev/null +++ b/source/tools/tools-keygen-booter/src/main/java/com/jd/blockchain/tools/keygen/boot/KeyGenBooter.java @@ -0,0 +1,96 @@ +package com.jd.blockchain.tools.keygen.boot; + +import com.jd.blockchain.tools.keygen.KeyGenCommand; + +import java.io.File; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.List; + +public class KeyGenBooter { + + private static final String charSet = "UTF-8"; + + private static final String jarSuffix = ".jar"; + + public static void main(String[] args) { + load(); + KeyGenCommand.main(args); + } + + + private static void load() { + List jarPaths = loadPaths(); + try { + if (!jarPaths.isEmpty()) { + loadJars(jarPaths); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + private static void loadJars(List jarPaths) throws Exception { + Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); + boolean accessible = method.isAccessible(); + try { + if (accessible == false) { + method.setAccessible(true); + } + // 获取系统类加载器 + URLClassLoader classLoader = (URLClassLoader) Thread + .currentThread() + .getContextClassLoader(); + for (File file : jarPaths) { + URL url = file.toURI().toURL(); + try { + method.invoke(classLoader, url); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + } finally { + method.setAccessible(accessible); + } + } + + private static List loadPaths() { + List loadJarFiles = new ArrayList<>(); + URL url = KeyGenBooter.class + .getProtectionDomain() + .getCodeSource() + .getLocation(); + try { + String currPath = java.net.URLDecoder.decode(url.getPath(), charSet); + if (currPath.endsWith(jarSuffix)) { + currPath = currPath.substring(0, currPath.lastIndexOf("/") + 1); + } + File file = new File(currPath); + loadJarFiles.addAll(dirJars(file)); + // 获取上级路径 + String systemPath = file.getParent() + File.separator + "system"; + loadJarFiles.addAll(dirJars(new File(systemPath))); + } catch (Exception e) { + throw new RuntimeException(e); + } + return loadJarFiles; + } + + private static List dirJars(File dir) { + List jars = new ArrayList<>(); + if (dir.exists() && dir.isDirectory()) { + File[] jarArray = dir.listFiles(); + if (jarArray != null) { + for (File jar : jarArray) { + if (jar.getAbsolutePath().endsWith(jarSuffix)) { + jars.add(jar); + } + } + } + } + return jars; + } +} diff --git a/source/tools/tools-keygen-booter/src/main/resources/META-INF/MANIFEST.MF b/source/tools/tools-keygen-booter/src/main/resources/META-INF/MANIFEST.MF new file mode 100644 index 00000000..bdb5fd98 --- /dev/null +++ b/source/tools/tools-keygen-booter/src/main/resources/META-INF/MANIFEST.MF @@ -0,0 +1,9 @@ +Manifest-Version: 1.0 +Created-By: 1.8.0 +Built-By: shaozhuguang@jd.com +Extension-Name: JDChain +Specification-Title: JDChain +Specification-Vendor: JD Software Foundation +Implementation-Vendor: JD Software Foundation +Implementation-URL: http://ledger.jd.com +Build-Jdk: 1.8.0 diff --git a/source/tools/tools-keygen/pom.xml b/source/tools/tools-keygen/pom.xml new file mode 100644 index 00000000..fa1755c9 --- /dev/null +++ b/source/tools/tools-keygen/pom.xml @@ -0,0 +1,33 @@ + + 4.0.0 + + com.jd.blockchain + tools + 0.8.2.RELEASE + + tools-keygen + + + + com.jd.blockchain + crypto-framework + ${project.version} + + + commons-io + commons-io + 2.4 + + + + + \ No newline at end of file diff --git a/source/tools/tools-keygen/src/main/java/com/jd/blockchain/tools/keygen/KeyGenCommand.java b/source/tools/tools-keygen/src/main/java/com/jd/blockchain/tools/keygen/KeyGenCommand.java new file mode 100644 index 00000000..4b9f135d --- /dev/null +++ b/source/tools/tools-keygen/src/main/java/com/jd/blockchain/tools/keygen/KeyGenCommand.java @@ -0,0 +1,357 @@ + +package com.jd.blockchain.tools.keygen; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.crypto.SecretKey; + +import com.jd.blockchain.crypto.CryptoAlgorithm; +import com.jd.blockchain.crypto.CryptoUtils; +import com.jd.blockchain.crypto.asymmetric.CryptoKeyPair; +import com.jd.blockchain.crypto.asymmetric.PrivKey; +import com.jd.blockchain.crypto.asymmetric.PubKey; +import com.jd.blockchain.utils.ArgumentSet; +import com.jd.blockchain.utils.ConsoleUtils; +import com.jd.blockchain.utils.ArgumentSet.ArgEntry; +import com.jd.blockchain.utils.ArgumentSet.Setting; +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.FileUtils; +import com.jd.blockchain.utils.security.AESUtils; +import com.jd.blockchain.utils.security.DecryptionException; +import com.jd.blockchain.utils.security.ShaUtils; + +public class KeyGenCommand { + + public static final byte[] PUB_KEY_FILE_MAGICNUM = { (byte) 0xFF, 112, 117, 98 }; + + public static final byte[] PRIV_KEY_FILE_MAGICNUM = { (byte) 0x00, 112, 114, 118 }; + + // 指定 -r 参数时为“读取模式”,显示密钥文件; -r 参数之后紧跟着指定要读取的公钥或者私钥文件的路径; + private static final String READ_ARG = "-r"; + + // 在“读取模式”下指定 -d 参数时要求输入密码,并显示解密后的私钥明文; + private static final String OPT_DECRYPTING = "-d"; + + // 未指定 -r 参数时为“生成模式”,输出密钥文件;通过 -o + // 参数指定文件的输出目录;并通过第一个名称参数指定密钥名称,密钥的保存文件以指定的名称命名; + private static final String OUT_DIR_ARG = "-o"; + + private static final String NAME_ARG = "-n"; + + // 指定local.conf的路径,以方便直接修改local.conf配置文件 + private static final String LOCAL_CONF_ARG = "-l"; + + // 是否输出调试信息; + private static final String OPT_DEBUG = "-debug"; + + /** + * 入口; + * + * @param args + */ + public static void main(String[] args) { + Setting setting = ArgumentSet.setting().prefix(READ_ARG, NAME_ARG, OUT_DIR_ARG, LOCAL_CONF_ARG).option(OPT_DECRYPTING, + OPT_DEBUG); + ArgumentSet argSet = ArgumentSet.resolve(args, setting); + try { + ArgEntry[] argEntries = argSet.getArgs(); + if (argEntries.length == 0) { + ConsoleUtils.info("Miss argument!\r\n" + + "-r : in reading mode if set this option, or in generating mode if not set.\r\n" + + "-d : decrypt priv key in reading mode. This is optional.\r\n" + "-n : name of key.\r\n" + + "-o : output dir of key under generating mode.\r\n"); + return; + } + + if (argSet.getArg(READ_ARG) != null) { + readKey(argSet.getArg(READ_ARG).getValue(), argSet.hasOption(OPT_DECRYPTING)); + } else { + ArgEntry name = argSet.getArg(NAME_ARG); + if (name == null) { + ConsoleUtils.info("Miss name of key!"); + return; + } + ArgEntry dirArg = argSet.getArg(OUT_DIR_ARG); + if (dirArg == null || dirArg.getValue() == null) { + ConsoleUtils.info("Miss storage dir of keys!"); + return; + } + if (!FileUtils.existDirectory(dirArg.getValue())) { + ConsoleUtils.info("The storage dir doesn't exist!"); + return; + } + ArgEntry localConfArg = argSet.getArg(LOCAL_CONF_ARG); + String localConfPath = localConfArg == null ? null : localConfArg.getValue(); + generateKeyPair(name.getValue(), dirArg.getValue(), localConfPath); + } + + } catch (Exception e) { + ConsoleUtils.info("Error!!! %s", e.getMessage()); + if (argSet.hasOption(OPT_DEBUG)) { + e.printStackTrace(); + } + } + + } + + /** + * 生成密钥,要求输入密码用于保护私钥文件; + * + * @param name + * @param outputDir + */ + private static void generateKeyPair(String name, String outputDir, String localConfPath) { + CryptoKeyPair kp = CryptoUtils.sign(CryptoAlgorithm.ED25519).generateKeyPair(); + + String base58PubKey = encodePubKey(kp.getPubKey()); + + byte[] pwdBytes = readPassword(); + String base58PrivKey = encodePrivKey(kp.getPrivKey(), pwdBytes); + + File pubKeyFile = new File(outputDir, String.format("%s.pub", name)); + File privKeyFile = new File(outputDir, String.format("%s.priv", name)); + FileUtils.writeText(base58PubKey, pubKeyFile); + FileUtils.writeText(base58PrivKey, privKeyFile); + + String base58PwdKey = null; + String savePwdStr = ConsoleUtils.confirm("Do you want to save encode password to file? Please input y or n ..."); + if (savePwdStr.equalsIgnoreCase("y") || savePwdStr.equalsIgnoreCase("yes")) { + base58PwdKey = Base58Utils.encode(pwdBytes); + File pwdFile = new File(outputDir, String.format("%s.pwd", name)); + FileUtils.writeText(base58PwdKey, pwdFile); + } else if (savePwdStr.equalsIgnoreCase("n") || savePwdStr.equalsIgnoreCase("no")) { + // do nothing + } else { + savePwdStr = ConsoleUtils.confirm("Please input y or n ..."); + if (savePwdStr.equalsIgnoreCase("y") || savePwdStr.equalsIgnoreCase("yes")) { + base58PwdKey = Base58Utils.encode(pwdBytes); + File pwdFile = new File(outputDir, String.format("%s.pwd", name)); + FileUtils.writeText(base58PwdKey, pwdFile); + } else { + // do nothing + } + } + if (localConfPath != null) { + File localConf = new File(localConfPath); + if (localConf.exists()) { + try { + List configs = org.apache.commons.io.FileUtils.readLines(localConf); + List modifyConfigs = new ArrayList<>(); + if (configs != null && !configs.isEmpty()) { + for (String configLine : configs) { + if (configLine.startsWith("local.parti.pubkey")) { + modifyConfigs.add("local.parti.pubkey=" + base58PubKey); + } else if (configLine.startsWith("local.parti.privkey")) { + modifyConfigs.add("local.parti.privkey=" + base58PrivKey); + } else if (configLine.startsWith("local.parti.pwd")) { + modifyConfigs.add("local.parti.pwd=" + base58PwdKey); + } else { + modifyConfigs.add(configLine); + } + } + } + org.apache.commons.io.FileUtils.writeLines(localConf, modifyConfigs, false); + } catch (Exception e) { + System.err.println("Error!!! --[" + e.getClass().getName() + "] " + e.getMessage()); + } + } + } + } + + public static String encodePubKey(PubKey pubKey) { + byte[] pubKeyBytes = BytesUtils.concat(PUB_KEY_FILE_MAGICNUM, pubKey.toBytes()); + String base58PubKey = Base58Utils.encode(pubKeyBytes); + return base58PubKey; + } + + public static PubKey decodePubKey(String base58PubKey) { + byte[] keyBytes = Base58Utils.decode(base58PubKey); + return decodePubKey(keyBytes); + } + + public static String encodePrivKey(PrivKey privKey, String base58Pwd) { + byte[] pwdBytes = Base58Utils.decode(base58Pwd); + return encodePrivKey(privKey, pwdBytes); + } + + public static String encodePrivKey(PrivKey privKey, byte[] pwdBytes) { + byte[] encodedPrivKeyBytes = encryptPrivKey(privKey, pwdBytes); + String base58PrivKey = Base58Utils.encode(encodedPrivKeyBytes); + return base58PrivKey; + } + + public static byte[] encryptPrivKey(PrivKey privKey, byte[] pwdBytes) { + SecretKey userKey = AESUtils.generateKey128(pwdBytes); + byte[] encryptedPrivKeyBytes = AESUtils.encrypt(privKey.toBytes(), userKey); + return BytesUtils.concat(PRIV_KEY_FILE_MAGICNUM, encryptedPrivKeyBytes); + } + + /** + * 读取密钥;
+ * 如果是私钥,则需要输入密码; + * + * @param keyFile + */ + public static void readKey(String keyFile, boolean decrypting) { + String base58KeyString = FileUtils.readText(keyFile); + byte[] keyBytes = Base58Utils.decode(base58KeyString); + if (BytesUtils.startsWith(keyBytes, PUB_KEY_FILE_MAGICNUM)) { + if (decrypting) { + // Try reading pubKey; + PubKey pubKey = doDecodePubKeyBytes(keyBytes); + ConsoleUtils.info( + "======================== pub key ========================\r\n" + + "[%s]\r\n" + + "Raw:[%s][%s]\r\n", + base58KeyString, pubKey.getAlgorithm(), Base58Utils.encode(pubKey.toBytes())); + }else { + ConsoleUtils.info( + "======================== pub key ========================\r\n" + + "[%s]\r\n", + base58KeyString); + } + return; + } else if (BytesUtils.startsWith(keyBytes, PRIV_KEY_FILE_MAGICNUM)) { + // Try reading privKye; + try { + if (decrypting) { + byte[] pwdBytes = readPassword(); + PrivKey privKey = decryptedPrivKeyBytes(keyBytes, pwdBytes); + ConsoleUtils.info("======================== priv key ========================\r\n" + + "[%s]\r\n" + + "Raw:[%s][%s]\r\n", + base58KeyString, privKey.getAlgorithm(), Base58Utils.encode(privKey.toBytes())); + } else { + ConsoleUtils.info("======================== priv key ========================\r\n[%s]\r\n", + base58KeyString); + } + } catch (DecryptionException e) { + ConsoleUtils.error("Invalid password!"); + } + return; + } else { + ConsoleUtils.error("Unknow key!!"); + return; + } + } + + private static PubKey doDecodePubKeyBytes(byte[] encodedPubKeyBytes) { + byte[] pubKeyBytes = Arrays.copyOfRange(encodedPubKeyBytes, PUB_KEY_FILE_MAGICNUM.length, + encodedPubKeyBytes.length); + return new PubKey(pubKeyBytes); + } + + public static PrivKey decryptedPrivKeyBytes(byte[] encodedPrivKeyBytes, byte[] pwdBytes) { + // Read privKye; + SecretKey userKey = AESUtils.generateKey128(pwdBytes); + byte[] encryptedKeyBytes = Arrays.copyOfRange(encodedPrivKeyBytes, PRIV_KEY_FILE_MAGICNUM.length, + encodedPrivKeyBytes.length); + try { + byte[] plainKeyBytes = AESUtils.decrypt(encryptedKeyBytes, userKey); + return new PrivKey(plainKeyBytes); + } catch (DecryptionException e) { + throw new DecryptionException("Invalid password!", e); + } + } + + public static PubKey readPubKey(String keyFile) { + String base58KeyString = FileUtils.readText(keyFile); + return decodePubKey(base58KeyString); + } + + public static PubKey decodePubKey(byte[] encodedPubKeyBytes) { + if (BytesUtils.startsWith(encodedPubKeyBytes, PUB_KEY_FILE_MAGICNUM)) { + // Read pubKey; + return doDecodePubKeyBytes(encodedPubKeyBytes); + } + + throw new IllegalArgumentException("The specified file is not a pub key file!"); + } + + /** + * 从控制台读取加密口令,以二进制数组形式返回原始口令的一次SHA256的结果; + * + * @return + */ + public static byte[] readPassword() { + byte[] pwdBytes = ConsoleUtils.readPassword(); + return ShaUtils.hash_256(pwdBytes); + } + + /** + * 对指定的原始密码进行编码生成用于加解密的密码; + * + * @param rawPassword + * @return + */ + public static byte[] encodePassword(String rawPassword) { + byte[] pwdBytes = BytesUtils.toBytes(rawPassword, "UTF-8"); + return ShaUtils.hash_256(pwdBytes); + } + + /** + * 对指定的原始密码进行编码生成用于加解密的密码; + * + * @param rawPassword + * @return + */ + public static String encodePasswordAsBase58(String rawPassword) { + return Base58Utils.encode(encodePassword(rawPassword)); + } + + /** + * 从控制台读取加密口令,以Base58字符串形式返回口令的一次SHA256的结果; + * + * @return + */ + public static String readPasswordString() { + return Base58Utils.encode(readPassword()); + } + + public static PrivKey readPrivKey(String keyFile, String base58Pwd) { + return readPrivKey(keyFile, Base58Utils.decode(base58Pwd)); + } + + /** + * 从文件读取私钥; + * + * @param keyFile + * @param pwdBytes + * @return + */ + public static PrivKey readPrivKey(String keyFile, byte[] pwdBytes) { + String base58KeyString = FileUtils.readText(keyFile); + byte[] keyBytes = Base58Utils.decode(base58KeyString); + if (!BytesUtils.startsWith(keyBytes, PRIV_KEY_FILE_MAGICNUM)) { + throw new IllegalArgumentException("The specified file is not a private key file!"); + } + return decryptedPrivKeyBytes(keyBytes, pwdBytes); + } + + public static PrivKey decodePrivKey(String base58Key, String base58Pwd) { + byte[] decryptedKey = Base58Utils.decode(base58Pwd); + return decodePrivKey(base58Key, decryptedKey); + } + + public static PrivKey decodePrivKey(String base58Key, byte[] pwdBytes) { + byte[] keyBytes = Base58Utils.decode(base58Key); + if (!BytesUtils.startsWith(keyBytes, PRIV_KEY_FILE_MAGICNUM)) { + throw new IllegalArgumentException("The specified file is not a private key file!"); + } + return decryptedPrivKeyBytes(keyBytes, pwdBytes); + } + + public static PrivKey decodePrivKeyWithRawPassword(String base58Key, String rawPassword) { + byte[] pwdBytes = encodePassword(rawPassword); + byte[] keyBytes = Base58Utils.decode(base58Key); + if (!BytesUtils.startsWith(keyBytes, PRIV_KEY_FILE_MAGICNUM)) { + throw new IllegalArgumentException("The specified file is not a private key file!"); + } + return decryptedPrivKeyBytes(keyBytes, pwdBytes); + } + +} diff --git a/source/utils/.gitignore b/source/utils/.gitignore new file mode 100644 index 00000000..bb88c990 --- /dev/null +++ b/source/utils/.gitignore @@ -0,0 +1,23 @@ +**.classpath +**.project +**/bin/ +**.class + +target/ + +*.bak +bin/ +*.iml +*.ipr +*.iws +.idea +.classpath +.project +.settings/ +.DS_Store +.springBeans +.externalToolBuilders/ + + +*.versionsBackup +.factorypath diff --git a/source/utils/pom.xml b/source/utils/pom.xml new file mode 100644 index 00000000..42ae182d --- /dev/null +++ b/source/utils/pom.xml @@ -0,0 +1,59 @@ + + 4.0.0 + + com.jd.blockchain + jdchain-root + 0.8.2.RELEASE + + utils + pom + + + utils-serialize + utils-web + utils-web-server + utils-common + utils-http + utils-test + + + + + junit + junit + test + + + org.mockito + mockito-core + test + + + + + + + net.i2p.crypto + eddsa + 0.1.0 + + + + + + + \ No newline at end of file diff --git a/source/utils/utils-common/.gitignore b/source/utils/utils-common/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/utils/utils-common/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/utils/utils-common/pom.xml b/source/utils/utils-common/pom.xml new file mode 100644 index 00000000..ce523d77 --- /dev/null +++ b/source/utils/utils-common/pom.xml @@ -0,0 +1,80 @@ + + 4.0.0 + + com.jd.blockchain + utils + 0.8.2.RELEASE + + utils-common + + + + + org.slf4j + slf4j-api + + + + commons-codec + commons-codec + + + + net.i2p.crypto + eddsa + + + org.bouncycastle + bcprov-jdk15on + + + org.springframework + spring-beans + + + + + + + org.slf4j + slf4j-log4j12 + test + + + org.apache.logging.log4j + log4j-api + test + + + org.apache.logging.log4j + log4j-core + test + + + + + + \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ArgumentSet.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ArgumentSet.java new file mode 100644 index 00000000..7b604f54 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ArgumentSet.java @@ -0,0 +1,161 @@ +package com.jd.blockchain.utils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +public class ArgumentSet { + +// private static Pattern ARG_PATTERN = Pattern.compile("\\-.+\\={1}.*"); + + private ArgEntry[] args; + + private Map prefixArgs = new HashMap<>(); + + private Set options = new HashSet<>(); + + private ArgumentSet(ArgEntry[] args) { + this.args = args; + for (ArgEntry arg : args) { + if (arg.prefix != null) { + prefixArgs.put(arg.prefix, arg); + } + if (arg.option != null) { + options.add(arg.option); + } + } + } + + public static boolean hasOption(String[] args, String option) { + boolean contains = false; + if (args != null) { + for (String a : args) { + if (option.equalsIgnoreCase(a)) { + contains = true; + break; + } + } + } + return contains; + } + + public static Setting setting() { + return new Setting(); + } + + public static ArgumentSet resolve(String[] args, Setting setting) { + if (args == null) { + return new ArgumentSet(new ArgEntry[0]); + } + List argEntries = new ArrayList<>(); + for (int i = 0; i < args.length; i++) { + if (setting.prefixes.contains(args[i])) { + ArgEntry ae = new ArgEntry(); + ae.prefix = args[i]; + if (i+1 >= args.length) { + throw new IllegalArgumentException(String.format("缺少 %s 参数!", args[i])); + } + i++; + ae.value = args[i]; + argEntries.add(ae); + continue; + } + + if(setting.options.contains(args[i])){ + ArgEntry ae = new ArgEntry(); + ae.option = args[i]; + argEntries.add(ae); + continue; + } + } + return new ArgumentSet(argEntries.toArray(new ArgEntry[argEntries.size()])); + } + + /** + * 按照原始顺序排列的参数列表; + * + * @return ArgEntry[] + */ + public ArgEntry[] getArgs() { + return args; + } + + public ArgEntry getArg(String prefix) { + return prefixArgs.get(prefix); + } + + public boolean hasOption(String option) { + return options.contains(option); + } + + /** + * @author huanghaiquan + * + */ + public static class ArgEntry { + + private String prefix; + + private String value; + + private String option; + + /** + * 前缀;
+ * 如果不是前缀参数,则为 null; + * + * @return String + */ + public String getPrefix() { + return prefix; + } + + /** + * 参数值;
+ * + * 如果只是选项参数,则返回 null; + * + * @return String + */ + public String getValue() { + return value; + } + + /** + * 选项; + * + * @return String + */ + public String getOption() { + return option; + } + + } + + public static class Setting { + + private Set prefixes = new HashSet<>(); + + private Set options = new HashSet<>(); + + private Setting() { + } + + + public Setting prefix(String... prefixes) { + this.prefixes.addAll(Arrays.asList(prefixes)); + return this; + } + + public Setting option(String... options) { + this.options.addAll(Arrays.asList(options)); + return this; + } + + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ArrayUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ArrayUtils.java new file mode 100644 index 00000000..bb04964e --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ArrayUtils.java @@ -0,0 +1,87 @@ +package com.jd.blockchain.utils; + +import java.lang.reflect.Array; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +/** + * @author haiq + * + */ +public abstract class ArrayUtils { + private ArrayUtils() { + } + + public static T[] singleton(T obj, Class clazz) { + @SuppressWarnings("unchecked") + T[] array = (T[]) Array.newInstance(clazz, 1); + array[0] = obj; + return array; + } + + public static T[] toArray(Iterator itr, Class clazz){ + List lst = new LinkedList(); + while (itr.hasNext()) { + T t = (T) itr.next(); + lst.add(t); + } + @SuppressWarnings("unchecked") + T[] array = (T[]) Array.newInstance(clazz, lst.size()); + lst.toArray(array); + return array; + } + + public static List asList(T[] array){ + return asList(array, 0, array.length); + } + + public static Set asSet(T[] array){ + if (array == null || array.length == 0) { + return Collections.emptySet(); + } + HashSet set = new HashSet(); + for (T t : array) { + set.add(t); + } + return set; + } + + public static SortedSet asSortedSet(T[] array){ + if (array == null || array.length == 0) { + return Collections.emptySortedSet(); + } + TreeSet set = new TreeSet(); + for (T t : array) { + set.add(t); + } + return set; + } + + public static List asList(T[] array, int fromIndex){ + return asList(array, fromIndex, array.length); + } + + public static List asList(T[] array, int fromIndex, int toIndex){ + if (toIndex < fromIndex) { + throw new IllegalArgumentException("The toIndex less than fromIndex!"); + } + if (fromIndex < 0) { + throw new IllegalArgumentException("The fromIndex is negative!"); + } + if (toIndex > array.length) { + throw new IllegalArgumentException("The toIndex great than the length of array!"); + } + + if (fromIndex == toIndex) { + return Collections.emptyList(); + } + return new ReadonlyArrayListWrapper(array, fromIndex, toIndex); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/AttributeMap.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/AttributeMap.java new file mode 100644 index 00000000..5722c50f --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/AttributeMap.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.utils; + +import java.util.Set; + +/** + * AttributeMap 定义字符串 key-value 属性表的通用访问接口; + * + * @author haiq + * + */ +public interface AttributeMap { + + /** + * 属性名称列表; + * + * @return String's set + */ + public Set getAttributeNames(); + + /** + * 是否包含指定名称的属性; + * @param name name; + * @return boolean + */ + public boolean containAttribute(String name); + + /** + * 返回指定名称的属性值; + * + * @param name name + * @return String + */ + public String getAttribute(String name); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Attributes.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Attributes.java new file mode 100644 index 00000000..82e19fcc --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Attributes.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.utils; + +import java.util.Properties; +import java.util.Set; + +public class Attributes extends Properties implements AttributeMap { + + private static final long serialVersionUID = 142263972661078077L; + + @Override + public Set getAttributeNames() { + return stringPropertyNames(); + } + + @Override + public boolean containAttribute(String name) { + return containsKey(name); + } + + @Override + public String getAttribute(String name) { + return getProperty(name); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java new file mode 100644 index 00000000..303f82ee --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BaseConstant.java @@ -0,0 +1,35 @@ +package com.jd.blockchain.utils; + +import java.io.File; + +/** + * + * @author zhaogw + * date 2018/5/4 14:20 + */ +public class BaseConstant { + public static final String DELIMETER_COMMA = ","; //逗号分隔符; + public static final String DELIMETER_SEMICOLON = ";"; //分号分隔符; + public static final String DELIMETER_SLASH = "/"; //斜线分隔符; + public static final String DELIMETER_DOT = "."; //点号分隔符; + public static final String DELIMETER_UNDERLINE = "_"; //斜线分隔符; + public static final String DELIMITER_EQUAL = "="; + public static final String DELIMETER_QUESTION = "?"; //逗号分隔符; + public static final String DELIMETER_DOUBLE_ALARM = "##"; //双警号分隔符; + public static final String CHARSET_UTF_8 = "utf-8"; //utf-8编码; + //合约系统使用的配置信息; + public static final String SYS_CONTRACT_CONF = "SYS_CONTRACT_CONF"; + public static final String SYS_CONTRACT_PROPS_NAME = "sys-contract.properties"; + public static final String CONTRACT_MAIN_CLASS_KEY = "contract"; + + public static final String CONTRACT_EVENT_PREFIX="@com.jd.blockchain.contract.model.ContractEvent(name="; + + // 编译时引用包黑名单 + public static final String PACKAGE_BLACKLIST = "BLACKLIST"; + + //根据列表读取时,每次允许的最大获取数量; + public static final int QUERY_LIST_MAX=100; + + public static final String CONTRACT_SERVICE_PROVIDER = "com.jd.blockchain.contract.jvm.JVMContractServiceProvider"; + public static final String SPRING_CF_LOCATION = "-sp"; +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BusinessException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BusinessException.java new file mode 100644 index 00000000..5dc7fffa --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/BusinessException.java @@ -0,0 +1,47 @@ +package com.jd.blockchain.utils; + +public class BusinessException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = -1934407327912767401L; + + private Integer errorCode; // 错误代码 + + public BusinessException() { + super(); + } + + public BusinessException(int errorCode) { + super(""); + this.errorCode = errorCode; + } + + public BusinessException(String message) { + super(message); + } + + public BusinessException(String message, Throwable throwable) { + super(message, throwable); + } + + public BusinessException(int code, String message) { + super(message); + this.errorCode = code; + } + + public BusinessException(int code, String message, Throwable throwable) { + super(message, throwable); + this.errorCode = code; + } + + public Integer getErrorCode() { + return errorCode; + } + + public void setErrorCode(int errorCode) { + this.errorCode = errorCode; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Bytes.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Bytes.java new file mode 100644 index 00000000..7b7921b8 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Bytes.java @@ -0,0 +1,248 @@ +package com.jd.blockchain.utils; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.RuntimeIOException; + +public class Bytes implements BytesSerializable { + + public static final Bytes EMPTY = new Bytes(BytesUtils.EMPTY_BYTES); + + private static final int MAX_CACHE = 256; + + private static Bytes[] INT_BYTES; + + private static Bytes[] LONG_BYTES; + + static { + INT_BYTES = new Bytes[MAX_CACHE]; + LONG_BYTES = new Bytes[MAX_CACHE]; + for (int i = 0; i < MAX_CACHE; i++) { + INT_BYTES[i] = new Bytes(BytesUtils.toBytes((int) i)); + LONG_BYTES[i] = new Bytes(BytesUtils.toBytes((long) i)); + } + } + + private final Bytes prefix; + + private final byte[] data; + + private final int hashCode; + + public int size() { + return prefix == null ? data.length : prefix.size() + data.length; + } + + public Bytes() { + prefix=null; + data = null; + hashCode = hashCode(1); + } + + public Bytes(byte[] data) { + if (data == null) { + throw new IllegalArgumentException("data is null!"); + } + this.prefix=null; + this.data = data; + hashCode = hashCode(1); + } + + public Bytes(Bytes prefix, byte[] data) { + if (data == null) { + throw new IllegalArgumentException("data is null!"); + } + this.prefix=prefix; + this.data = data; +// setPrefix(prefix); + hashCode = hashCode(1); + } + + public Bytes(Bytes prefix, Bytes data) { +// setData(data.toBytes()); +// setPrefix(prefix); + if (data == null) { + throw new IllegalArgumentException("data is null!"); + } + this.prefix=prefix; + this.data = data.toBytes(); + + hashCode = hashCode(1); + } + +// private void setData(byte[] data) { +// if (data == null) { +// throw new IllegalArgumentException("data is null!"); +// } +// this.data = data; +// } + + /** + * 返回当前的字节数组(不包含前缀对象); + * + * @return byte[] + */ + protected byte[] getDirectBytes() { + return data; + } + + public static Bytes fromString(String str) { + return new Bytes(BytesUtils.toBytes(str)); + } + + public static Bytes fromBase58(String str) { + return new Bytes(Base58Utils.decode(str)); + } + +// /** +// * 连接指定的前缀后面;此操作并不会更改“prefix”参数; +// * +// * @param prefix +// * @return +// */ +// private Bytes setPrefix(Bytes prefix) { +// this.prefix = prefix; +// return this; +// } + + public Bytes concat(Bytes key) { + return new Bytes(this, key); + } + + public Bytes concat(byte[] key) { + return new Bytes(this, key); + } + + public int writeTo(OutputStream out) { + int size = 0; + if (prefix != null) { + size = prefix.writeTo(out); + } + try { + out.write(data); + size += data.length; + return size; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + private int hashCode(int result) { + if (prefix != null) { + result = prefix.hashCode(result); + } + for (byte element : data) { + result = 31 * result + element; + } + + return result; + } + + // private static int hashCode(byte a[], int offset, int len) { + // if (a == null) + // return 0; + // + // int result = 1; + // for (int i = 0; i < len; i++) { + // result = 31 * result + a[offset + i]; + // } + // + // return result; + // } + + @Override + public int hashCode() { + return hashCode; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (!(obj instanceof Bytes)) { + return false; + } + Bytes oth = (Bytes) obj; + if (this.hashCode != oth.hashCode) { + return false; + } + boolean prefixIsEqual = false; + if (this.prefix == null && oth.prefix == null) { + prefixIsEqual = true; + } else if (this.prefix == null) { + prefixIsEqual = false; + } else { + prefixIsEqual = this.prefix.equals(oth.prefix); + } + if (!prefixIsEqual) { + return false; + } + return BytesUtils.equals(this.data, oth.data); + } + + public int copyTo(byte[] buffer, int offset, int len) { + if (len < 0) { + throw new IllegalArgumentException("Argument len is negative!"); + } + if (len == 0) { + return 0; + } + int s = 0; + if (prefix != null) { + s = prefix.copyTo(buffer, offset, len); + } + if (s < len) { + int l = len - s; + l = l < data.length ? l : data.length; + System.arraycopy(data, 0, buffer, offset + s, l); + s += l; + } + return s; + } + + @Override + public byte[] toBytes() { + if (prefix == null || prefix.size() == 0) { + return data; + } + int size = size(); + byte[] buffer = new byte[size]; + copyTo(buffer, 0, size); + return buffer; + } + + public String toBase58() { + return Base58Utils.encode(toBytes()); + } + + public static Bytes fromInt(int value) { + if (value > -1 && value < MAX_CACHE) { + return INT_BYTES[value]; + } + return new Bytes(BytesUtils.toBytes(value)); + } + + public static Bytes fromLong(long value) { + if (value > -1 && value < MAX_CACHE) { + return LONG_BYTES[(int) value]; + } + return new Bytes(BytesUtils.toBytes(value)); + } + + /** + * 返回 Base58 编码的字符; + */ + @Override + public String toString() { + return toBase58(); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ConsoleUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ConsoleUtils.java new file mode 100644 index 00000000..7811faa5 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ConsoleUtils.java @@ -0,0 +1,40 @@ +package com.jd.blockchain.utils; + +import java.io.Console; + +import com.jd.blockchain.utils.io.ByteArray; + +public class ConsoleUtils { + + public static byte[] readPassword() { + Console cs = getConsole(); + char[] pwdChars; + do { + pwdChars = cs.readPassword("\r\nInput password:"); + } while (pwdChars.length == 0); + String pwd = new String(pwdChars); + return ByteArray.fromString(pwd, "UTF-8"); + } + + public static void info(String format, Object... args) { + System.out.println(String.format(format, args)); + } + + public static void error(String format, Object... args) { + System.err.println(String.format(format, args)); + } + + public static String confirm(String fmt, Object...args) { + Console cs = getConsole(); + return cs.readLine(fmt, args); + } + + public static Console getConsole() { + Console cs = System.console(); + if (cs == null) { + throw new IllegalStateException("You are not running in console!"); + } + return cs; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/DataTypeUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/DataTypeUtils.java new file mode 100644 index 00000000..05952868 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/DataTypeUtils.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.utils; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import org.springframework.util.TypeUtils; + +public abstract class DataTypeUtils { + + private static Map, Class> wrapperTypes = new HashMap, Class>(); + + static{ + wrapperTypes.put(long.class, Long.class); + wrapperTypes.put(int.class, Integer.class); + wrapperTypes.put(char.class, Character.class); + wrapperTypes.put(byte.class, Byte.class); + wrapperTypes.put(boolean.class, Boolean.class); + } + + public static boolean isAssignable(Type lhsType, Type rhsType) { + boolean assignable = TypeUtils.isAssignable(lhsType, rhsType); + if (assignable) { + return true; + } + if (lhsType instanceof Class) { + Class lhsClass = (Class) lhsType; + + } + + return false; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Disposable.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Disposable.java new file mode 100644 index 00000000..5a72d94b --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Disposable.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.utils; + +public interface Disposable { + + public void dispose(); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/EmptyProperties.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/EmptyProperties.java new file mode 100644 index 00000000..285ce7fb --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/EmptyProperties.java @@ -0,0 +1,111 @@ +package com.jd.blockchain.utils; + +import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.InvalidPropertiesFormatException; +import java.util.Properties; +import java.util.Set; + +public class EmptyProperties extends Properties { + + private static final long serialVersionUID = 5941797426076447165L; + + public static Properties INSTANCE = new EmptyProperties(); + + private EmptyProperties() { + } + + @Override + public String getProperty(String key) { + return null; + } + + @Override + public String getProperty(String key, String defaultValue) { + return defaultValue; + } + + @Override + public Enumeration propertyNames() { + return Collections.enumeration(Collections.emptyList()); +// return Collections.emptyEnumeration(); + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public boolean containsKey(Object key) { + return false; + } + + @Override + public boolean containsValue(Object value) { + return false; + } + + @Override + public synchronized Object get(Object key) { + return null; + } + + @SuppressWarnings("unchecked") + @Override + public Set keySet() { + return Collections.EMPTY_SET; + } + + @SuppressWarnings("unchecked") + @Override + public Collection values() { + return Collections.EMPTY_SET; + } + + @SuppressWarnings("unchecked") + @Override + public Set> entrySet() { + return Collections.EMPTY_SET; + } + + @Override + public synchronized void load(InputStream inStream) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void load(Reader reader) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public void loadFromXML(InputStream in) throws IOException, InvalidPropertiesFormatException { + throw new UnsupportedOperationException(); + } + + @Override + public Object put(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + @Override + public Enumeration keys() { + return Collections.enumeration(Collections.emptyList()); + } + + @Override + public Object setProperty(String key, String value) { + throw new UnsupportedOperationException(); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/IllegalDataException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/IllegalDataException.java new file mode 100644 index 00000000..d7abaef2 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/IllegalDataException.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils; + +/** + * 无效数据异常; + * + * @author haiq + * + */ +public class IllegalDataException extends RuntimeException{ + + private static final long serialVersionUID = 5834019788898871654L; + + public IllegalDataException() { + } + + public IllegalDataException(String message) { + super(message); + } + + public IllegalDataException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/IteratorWrapper.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/IteratorWrapper.java new file mode 100644 index 00000000..7d898c30 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/IteratorWrapper.java @@ -0,0 +1,33 @@ +package com.jd.blockchain.utils; + +import java.util.Iterator; + +/** + * IteratorWrapper 是一个针对迭代器的适配器; + * + * @author haiq + * + * @param T1 + * @param T2 + */ +public abstract class IteratorWrapper implements Iterator { + + protected Iterator wrappedIterator; + + public IteratorWrapper(Iterator itr) { + this.wrappedIterator = itr; + } + + @Override + public boolean hasNext() { + return wrappedIterator.hasNext(); + } + + @Override + public T1 next() { + return wrap(wrappedIterator.next()); + } + + protected abstract T1 wrap(T2 t2); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PathUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PathUtils.java new file mode 100644 index 00000000..4ba89daa --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PathUtils.java @@ -0,0 +1,117 @@ +package com.jd.blockchain.utils; + +import org.springframework.util.StringUtils; + +public abstract class PathUtils { + + public static final String PATH_SEPERATOR = "/"; + public static final char PATH_SEPERATOR_CHAR = '/'; + + public static final String WINDOWS_PATH_SEPERATOR = "\\"; + + public static final String SCHEMA_SEPERATOR = ":"; + + /** + * 标准化指定的路径; + * + * 标准化的过程包括: + * 1、清理字符串中的 "."; + * 2、清除字符串中的 ".."以及路径中相应的上一级路径;(例如:ab/../cd 处理后变为 cd ) + * 2、将 windows 的分隔符"\"替换为标准分隔符"/"; + * 3、将连续重复的分隔符替换为单个分隔符; + * 4、去除结尾的分隔符; + * 5、去除其中的空白字符; + * + * 注:以冒号":"分隔的 schema 头将被保留; + * + * @param path path + * @return String + */ + public static String standardize(String path) { + path = StringUtils.trimAllWhitespace(path); + path = StringUtils.cleanPath(path); + path = cleanRepeatlySeperator(path); + if (path.endsWith(PATH_SEPERATOR)) { + return path.substring(0, path.length() - 1); + } + return path; + } + + /** + * 将指定的路径转换为绝对路径; + * + * 方法将检测指定的路径如果既没有以路径分隔符"/"开头,也没有冒号":"分隔的 schema 开头(例如 file:), + * + * 则在开头加上路径分隔符"/"返回; + * + * 注:方法不会检测路径是否标准,也不会自动将其标准化; + * + * @param path path + * @return String + */ + public static String absolute(String path) { + if (path.startsWith(PATH_SEPERATOR)) { + return path; + } + if (path.indexOf(SCHEMA_SEPERATOR) >= 0) { + return path; + } + return PATH_SEPERATOR + path; + } + + /** + * 清除路径中的重复分隔符; + * + * @param path path + * @return String + */ + public static String cleanRepeatlySeperator(String path) { + // 去除重复的分隔符; + String schema = ""; + String pathToProcess = path; + int index = path.indexOf("://"); + if (index >= 0) { + schema = path.substring(0, index + 3); + for (index = index + 3; index < path.length(); index++) { + if (path.charAt(index) != PATH_SEPERATOR_CHAR) { + break; + } + } + pathToProcess = path.substring(index); + } + StringBuilder pathToUse = new StringBuilder(); + boolean hit = false; + char ch; + for (int i = 0; i < pathToProcess.length(); i++) { + ch = pathToProcess.charAt(i); + if (ch == PATH_SEPERATOR_CHAR) { + if (hit) { + continue; + } else { + hit = true; + } + } else { + hit = false; + } + pathToUse.append(ch); + } + return schema + pathToUse; + } + + /** + * concatPaths + * @param paths path + * @return String + */ + public static String concatPaths(String... paths){ + if (paths == null || paths.length == 0) { + return ""; + } + StringBuilder path = new StringBuilder(); + for (String p : paths) { + path.append(p); + path.append(PATH_SEPERATOR_CHAR); + } + return standardize(path.toString()); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PrimitiveUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PrimitiveUtils.java new file mode 100644 index 00000000..496e4951 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PrimitiveUtils.java @@ -0,0 +1,418 @@ +package com.jd.blockchain.utils; + +import java.util.HashMap; +import java.util.Map; + +public class PrimitiveUtils { + + private static Map, Converter> converters = new HashMap, Converter>(); + + static { + converters.put(Boolean.class, new BooleanConverter()); + converters.put(boolean.class, new BooleanValueConverter()); + converters.put(Byte.class, new ByteConverter()); + converters.put(byte.class, new ByteValueConverter()); + converters.put(Character.class, new CharConverter()); + converters.put(char.class, new CharValueConverter()); + converters.put(Integer.class, new IntegerConverter()); + converters.put(int.class, new IntegerValueConverter()); + converters.put(Long.class, new LongConverter()); + converters.put(long.class, new LongValueConverter()); + converters.put(Double.class, new DoubleConverter()); + converters.put(double.class, new DoubleValueConverter()); + converters.put(Float.class, new FloatConverter()); + converters.put(float.class, new FloatValueConverter()); + } + + /** + * 判断指定的两个类型是否互为包装类; + * + * @param type1 type1 + * @param type2 type2 + * @return boolean + */ + public static boolean isWrapping(Class type1, Class type2) { + Converter converter = converters.get(type1); + return converter != null && converter.isWrapping(type2); + } + + /** + * 判断指定的类型是否是基本类型或者基本类型的包装类; + * + * @param clazz clazz + * @return boolean + */ + public static boolean isPrimitiveType(Class clazz) { + return converters.containsKey(clazz); + } + + /** + * 返回指定类型的默认值; + * + * 如果指定类型是原生类型,则返回默认的原始类型的值;否则,返回 null; + * + * @param clazz clazz + * @return Object + */ + public static Object getDefaultValue(Class clazz) { + Converter converter = converters.get(clazz); + if (converter == null) { + return null; + } + return converter.defaultValue(); + } + + /** + * 将指定的数据转为指定的类型;
+ * 如果指定的类型不是基本类型,则总是返回 null; + * @param data data + * @param clazz clazz + * @param class + * @return T + */ + @SuppressWarnings("unchecked") + public static T castTo(Object data, Class clazz) { + Converter converter = converters.get(clazz); + if (converter == null) { + return null; + } + return (T) converter.convert(data); + } + + private static interface Converter { + + Object defaultValue(); + + Object convert(Object data); + + boolean isWrapping(Class type); + + } + + private static class BooleanConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Boolean) { + return (Boolean) data; + } + return Boolean.parseBoolean(data.toString()); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == boolean.class || type == Boolean.class; + } + } + + private static class BooleanValueConverter extends BooleanConverter { + + @Override + public Object convert(Object data) { + Boolean value = (Boolean) super.convert(data); + if (value == null) { + return false; + } + return value; + } + + @Override + public Object defaultValue() { + return false; + } + } + + private static class CharConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Character) { + return (char) ((Character) data).charValue(); + } + if (data instanceof Byte) { + return (char) ((Byte) data).byteValue(); + } + if (data instanceof String) { + String str = (String) data; + if (str.length() == 1) { + return str.charAt(0); + } + } + throw new IllegalArgumentException("Cann't conver [" + data + "] to charater!"); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == char.class || type == Character.class; + } + } + + private static class CharValueConverter extends CharConverter { + + @Override + public Object convert(Object data) { + Character value = (Character) super.convert(data); + if (value == null) { + throw new IllegalArgumentException("Cann't convert null charater to char!"); + } + return value; + } + + @Override + public Object defaultValue() { + return (char) 0; + } + } + + private static class ByteConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Byte) { + return (int) ((Byte) data).byteValue(); + } + return Byte.parseByte(data.toString()); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == byte.class || type == Byte.class; + } + } + + private static class ByteValueConverter extends ByteConverter { + + @Override + public Object convert(Object data) { + Byte value = (Byte) super.convert(data); + if (value == null) { + return Byte.valueOf((byte) 0); + } + return value; + } + + @Override + public Object defaultValue() { + return (byte) 0; + } + } + + private static class IntegerConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Integer) { + return (Integer) data; + } + if (data instanceof Byte) { + return ((Byte) data).intValue(); + } + return Integer.parseInt(data.toString()); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == int.class || type == Integer.class; + } + } + + private static class IntegerValueConverter extends IntegerConverter { + + @Override + public Object convert(Object data) { + Integer value = (Integer) super.convert(data); + if (value == null) { + return 0; + } + return value; + } + + @Override + public Object defaultValue() { + return (int) 0; + } + } + + private static class LongConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Long) { + return (Long) data; + } + if (data instanceof Integer) { + return ((Integer) data).longValue(); + } + if (data instanceof Byte) { + return ((Byte) data).longValue(); + } + return Long.parseLong(data.toString()); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == long.class || type == Long.class; + } + } + + private static class LongValueConverter extends LongConverter { + + @Override + public Object convert(Object data) { + Long value = (Long) super.convert(data); + if (value == null) { + return Long.valueOf(0); + } + return value; + } + + @Override + public Object defaultValue() { + return 0L; + } + } + + private static class DoubleConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Double) { + return (Double) data; + } + if (data instanceof Float) { + return ((Float) data).doubleValue(); + } + if (data instanceof Long) { + return ((Long) data).doubleValue(); + } + if (data instanceof Integer) { + return ((Integer) data).doubleValue(); + } + if (data instanceof Byte) { + return ((Byte) data).doubleValue(); + } + return Double.parseDouble(data.toString()); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == double.class || type == Double.class; + } + } + + private static class DoubleValueConverter extends DoubleConverter { + + @Override + public Object convert(Object data) { + Long value = (Long) super.convert(data); + if (value == null) { + return Long.valueOf(0); + } + return value; + } + + @Override + public Object defaultValue() { + return (double) 0; + } + } + + private static class FloatConverter implements Converter { + + @Override + public Object convert(Object data) { + if (data == null) { + return null; + } + if (data instanceof Float) { + return (Float) data; + } + if (data instanceof Long) { + return ((Long) data).floatValue(); + } + if (data instanceof Integer) { + return ((Integer) data).floatValue(); + } + if (data instanceof Byte) { + return ((Byte) data).floatValue(); + } + return Float.parseFloat(data.toString()); + } + + @Override + public Object defaultValue() { + return null; + } + + @Override + public boolean isWrapping(Class type) { + return type == float.class || type == Float.class; + } + } + + private static class FloatValueConverter extends FloatConverter { + private static final Float DEFAULT_FLOAT = Float.valueOf(0); + + @Override + public Object convert(Object data) { + Float value = (Float) super.convert(data); + if (value == null) { + return DEFAULT_FLOAT; + } + return value; + } + + @Override + public Object defaultValue() { + return (float) 0; + } + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PropertiesUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PropertiesUtils.java new file mode 100644 index 00000000..ec6f5da9 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/PropertiesUtils.java @@ -0,0 +1,329 @@ +package com.jd.blockchain.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.util.Arrays; +import java.util.Properties; +import java.util.Set; + +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.beans.MutablePropertyValues; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; + +/** + * PropertiesUtils 定义了从 properties 文件到 pojo 对象的转换方法; + * + * 用于对充当配置文件的 properties 文件到定义了配置信息的 POJO 的转换; + * + * 支持 properties 的 key 到 POJO 的字段转换,支持层级的 key 的转换,例如: "user.name" 到 user 字段的对象的 + * name 属性; + * + * @author haiq + * + */ +public abstract class PropertiesUtils { + + private PropertiesUtils() { + } + + /** + * 创建配置对象的实例,并且从指定的属性表中初始化对应的实例字段; + * @param configClass configClass + * @param properties properties + * @param T + * @return T + */ + @SuppressWarnings("unchecked") + public static T createInstance(Class configClass, Properties properties) { + BeanWrapper confBean = new BeanWrapperImpl(configClass); + confBean.setAutoGrowNestedPaths(true); + + MutablePropertyValues values = new MutablePropertyValues(properties); + confBean.setPropertyValues(values, true); + + return (T) confBean.getWrappedInstance(); + } + + /** + * 创建配置对象的实例,并且从指定的属性表中初始化对应的实例字段; + * + * @param configClass + * 配置对象的类型; + * @param properties + * 属性表; + * @param propsPrefix + * 在属性表中与配置对象相关的属性的key的前缀; + * @param T + * @return T + */ + public static T createInstance(Class configClass, Properties properties, String propsPrefix) { + if (propsPrefix == null || propsPrefix.trim().length() == 0) { + return createInstance(configClass, properties); + } + propsPrefix = propsPrefix.trim(); + Properties configProperties = subset(properties, propsPrefix, true); + return createInstance(configClass, configProperties); + } + + /** + * 设置配置值; + * + * @param obj + * 配置对象;配置值将设置到此对象匹配的属性; + * @param configValues + * 配置值; + * @param propPrefix + * 自动加入的属性前缀; + */ + public static void setValues(Object obj, Properties configValues, String propPrefix) { + Properties values = new Properties(); + setValues(obj, values); + mergeFrom(configValues, values, propPrefix); + } + + /** + * 设置配置值; + * + * @param obj + * 配置对象;配置值将设置到此对象匹配的属性; + * @param configValues + * 配置值; + */ + public static void setValues(Object obj, Properties configValues) { + BeanWrapper confBean = new BeanWrapperImpl(obj); + confBean.setAutoGrowNestedPaths(true); + + MutablePropertyValues values = new MutablePropertyValues(configValues); + confBean.setPropertyValues(values, true); + } + + /** + * 从指定的路径加载配置; + * + * @param configClass + * 配置对象的类型; + * @param configFilePathPattern + * properties配置文件的路径;可以指定 spring 资源路径表达式; + * @param charset + * 字符集; + * @param class + * @return T + * @throws IOException exception + */ + public static T load(Class configClass, String configFilePathPattern, String charset) throws IOException { + Properties props = loadProperties(configFilePathPattern, charset); + return createInstance(configClass, props); + } + + /** + * 从指定的路径加载配置; + * + * @param obj + * 配置对象;配置文件的值将设置到此对象匹配的属性; + * @param configFilePathPattern + * properties配置文件的路径;可以指定 spring 资源路径表达式; + * @param charset + * 字符集; + * @throws IOException exception + */ + public static void load(Object obj, String configFilePathPattern, String charset) throws IOException { + Properties props = loadProperties(configFilePathPattern, charset); + setValues(obj, props); + } + + public static Properties loadProperties(String configFilePathPattern, String charset) throws IOException { + ResourcePatternResolver resResolver = new PathMatchingResourcePatternResolver(); + Resource configResource = resResolver.getResource(configFilePathPattern); + InputStream in = configResource.getInputStream(); + try { + return load(in, charset); + } finally { + in.close(); + } + } + + public static Properties loadProperties(File configFile, String charset) throws IOException { + FileSystemResource resource = new FileSystemResource(configFile); + InputStream in = resource.getInputStream(); + try { + return load(in, charset); + } finally { + in.close(); + } + } + + public static Properties load(InputStream in, String charset) throws IOException { + Properties props = new Properties(); + InputStreamReader reader = new InputStreamReader(in, charset); + try { + props.load(reader); + } finally { + reader.close(); + } + return props; + } + + public static Properties load(byte[] bytes, String charset) throws IOException { + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + return load(in, charset); + } + + /** + * 合并两个 properties ; + * + * @param props + * 要将其它值合并进来的属性集合;操作将对其产生修改; + * @param from + * 属性值将要合并进入其它属性集合;操作不对其产生修改; + */ + public static void mergeFrom(Properties props, Properties from) { + mergeFrom(props, from, null); + } + + /** + * 合并两个 properties ; + * + * @param props + * 要将其它值合并进来的属性集合;操作将对其产生修改; + * @param from + * 属性值将要合并进入其它属性集合;操作不对其产生修改; + * @param propertyNamePrefix + * 属性名称前缀; + */ + public static void mergeFrom(Properties props, Properties from, String propertyNamePrefix) { + if (propertyNamePrefix == null || propertyNamePrefix.length() == 0) { + for (String name : from.stringPropertyNames()) { + props.setProperty(name, from.getProperty(name)); + } + } else { + for (String name : from.stringPropertyNames()) { + props.setProperty(propertyNamePrefix + name, from.getProperty(name)); + } + } + } + + /** + * 获取指定 properties 中以指定的前缀开头的子集; + * + * @param props + * 要抽取的属性集合; + * @param propertyNamePrefix + * 属性名称前缀; + * @param trimPrefix + * 是否在复制的新的属性集合去掉指定的前缀; + * @return properties + */ + public static Properties subset(Properties props, String propertyNamePrefix, boolean trimPrefix) { + Properties subProperties = new Properties(); + Set names = props.stringPropertyNames(); + String newName; + for (String name : names) { + if (name.startsWith(propertyNamePrefix)) { + newName = name; + if (trimPrefix) { + newName = name.substring(propertyNamePrefix.length()); + } + subProperties.setProperty(newName, props.getProperty(name)); + } + } + return subProperties; + } + + public static Properties cloneFrom(Properties props) { + Properties newProps = new Properties(); + Set names = props.stringPropertyNames(); + for (String name : names) { + newProps.setProperty(name, props.getProperty(name)); + } + return newProps; + } + + public static byte[] toBytes(Properties props, String charsetName) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutputStreamWriter writer = new OutputStreamWriter(out, charsetName); + try { + props.store(writer, null); + writer.flush(); + } finally { + writer.close(); + } + return out.toByteArray(); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static int getInt(Properties props, String key) { + String value = getRequiredProperty(props, key); + return Integer.parseInt(value); + } + + public static boolean getBoolean(Properties props, String key) { + String value = getRequiredProperty(props, key); + return Boolean.parseBoolean(value); + } + + /** + * 返回指定的属性;
+ * 如果不存在,或者返回值为空(null 或 空白字符),则抛出 {@link IllegalArgumentException} 异常; + * + * @param props props + * @param key key + * @return String + */ + public static String getRequiredProperty(Properties props, String key) { + return getProperty(props, key, true); + } + + public static String getProperty(Properties props, String key, boolean required) { + String value = props.getProperty(key); + if (value == null) { + if (required) { + throw new IllegalArgumentException("Miss property[" + key + "]!"); + } + return null; + } + value = value.trim(); + if (value.length() == 0) { + if (required) { + throw new IllegalArgumentException("Miss property[" + key + "]!"); + } + return null; + } + return value; + + } + + public static Property[] getOrderedValues(Properties props) { + Property[] values = new Property[props.size()]; + String[] propNames = props.stringPropertyNames().toArray(new String[props.size()]); + Arrays.sort(propNames, (n1, n2) -> n1.compareTo(n2)); + for (int i = 0; i < propNames.length; i++) { + values[i] = new Property(propNames[i], props.getProperty(propNames[i])); + } + return values; + } + + public static Properties createProperties(Property[] propValues) { + Properties props = new Properties(); + setValues(props, propValues); + return props; + } + + public static Properties setValues(Properties props, Property[] propValues) { + for (Property p : propValues) { + props.setProperty(p.getName(), p.getValue()); + } + return props; + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Property.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Property.java new file mode 100644 index 00000000..802d68ee --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Property.java @@ -0,0 +1,64 @@ +package com.jd.blockchain.utils; + +import java.io.ByteArrayInputStream; + +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesUtils; + +public class Property implements BytesSerializable { + + private String name; + + private String value; + + public Property() { + } + + public Property(String name, String value) { + this.name = name; + this.value = value; + } + + public Property(byte[] nameValueBytes) { + decode(nameValueBytes); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public byte[] toBytes() { + byte[] nameBytes = BytesUtils.toBytes(name); + byte[] valueBytes = value == null ? BytesUtils.EMPTY_BYTES : BytesUtils.toBytes(value); + int totalSize = BytesEncoding.getOutputSizeInNormal(nameBytes.length) + + BytesEncoding.getOutputSizeInNormal(valueBytes.length); + byte[] totalBytes = new byte[totalSize]; + int offset = 0; + offset += BytesEncoding.writeInNormal(nameBytes, totalBytes, offset); + offset += BytesEncoding.writeInNormal(valueBytes, totalBytes, offset); + return totalBytes; + } + + private void decode(byte[] nameValueBytes) { + ByteArrayInputStream in = new ByteArrayInputStream(nameValueBytes); + byte[] nameBytes = BytesEncoding.readInNormal(in); + byte[] valueBytes = BytesEncoding.readInNormal(in); + this.name = BytesUtils.toString(nameBytes); + this.value = valueBytes.length == 0 ? "" : BytesUtils.toString(valueBytes); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/QueryUtil.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/QueryUtil.java new file mode 100644 index 00000000..ea858ff2 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/QueryUtil.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.utils; + +import static com.jd.blockchain.utils.BaseConstant.QUERY_LIST_MAX; + +/** + * @author zhaogw + * date 2019/2/22 17:00 + */ +public class QueryUtil { + + /** + * confirm the fromIndex and count by 3 factors; + * @param fromIndex fromIndex + * @param count count + * @param maxNum maxNum + * @return int[] + */ + public static int[] calFromIndexAndCount(int fromIndex, int count, int maxNum){ + int [] rtn = new int[2]; + if (fromIndex < 0 || fromIndex >= maxNum) { + fromIndex = 0; + } + + //must < maxNum; + if(count > maxNum){ + count = maxNum; + } + //must < threshold; + if(count > QUERY_LIST_MAX){ + count = QUERY_LIST_MAX; + } + //if count is empty, get the small; + if (count == -1) { + fromIndex = 0; + //count must <=100; + count = maxNum > QUERY_LIST_MAX ? QUERY_LIST_MAX : maxNum; + } + //count is ok, then calculate the plus condition; + if (fromIndex + count >= maxNum) { + count = maxNum - fromIndex; + } + //now if count<-1, then deduce: make trouble;so set count=0; + if(count < -1){ + count = 0; + } + rtn[0] = fromIndex; + rtn[1] = count; + return rtn; + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ReadonlyArrayListWrapper.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ReadonlyArrayListWrapper.java new file mode 100644 index 00000000..668ccd41 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ReadonlyArrayListWrapper.java @@ -0,0 +1,55 @@ +package com.jd.blockchain.utils; + +import java.util.AbstractList; + +/** + * ReadonlyArrayListWrapper 将数组包装为只读的 List; + * @author haiq + * + * @param class + */ +public class ReadonlyArrayListWrapper extends AbstractList{ + + private T[] array; + + private int fromIndex; + + private int toIndex; + + public ReadonlyArrayListWrapper(T[] array) { + this(array, 0, array.length); + } + + public ReadonlyArrayListWrapper(T[] array, int fromIndex) { + this(array, fromIndex, array.length); + } + + public ReadonlyArrayListWrapper(T[] array, int fromIndex, int toIndex) { + if (toIndex < fromIndex) { + throw new IllegalArgumentException("The toIndex less than fromIndex!"); + } + if (fromIndex < 0) { + throw new IllegalArgumentException("The fromIndex is negative!"); + } + if (toIndex > array.length) { + throw new IllegalArgumentException("The toIndex great than the length of array!"); + } + this.array = array; + this.fromIndex = fromIndex; + this.toIndex = toIndex; + } + + @Override + public T get(int index) { + if (index < 0 || index >= size()) { + throw new IndexOutOfBoundsException(); + } + return array[fromIndex + index]; + } + + @Override + public int size() { + return toIndex- fromIndex; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ServiceFactory.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ServiceFactory.java new file mode 100644 index 00000000..5209f455 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ServiceFactory.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.utils; + +import java.io.Closeable; + +public interface ServiceFactory extends Closeable{ + + T getService(Class serviceClazz); + +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Transactional.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Transactional.java new file mode 100644 index 00000000..fc2a85a9 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/Transactional.java @@ -0,0 +1,36 @@ +package com.jd.blockchain.utils; + +/** + * {@link Transactional} 表示一种事务特性,即对一个对象的状态的更改是临时性的,直到成功地调用 {@link #commit()} + * 方法提交变更,或者调用 {@link #cancel()} 方法取消变更; + * + * @author huanghaiquan + * + */ +public interface Transactional { + + /** + * 是否发生了变更;
+ * + * 自上一次调用 {@link #commit()} 或 {@link #cancel()} 之后,对象的状态如果发生了变更,则返回 true,否则返回 + * false; + * + * @return boolean + */ + boolean isUpdated(); + + /** + * 提交所有变更;
+ * + * 如果执行前 {@link #isUpdated()} 返回 true,则提交之后 {@link #isUpdated()} 返回 false; + */ + void commit(); + + /** + * 取消所有变更;
+ * + * 如果执行前 {@link #isUpdated()} 返回 true,则取消之后 {@link #isUpdated()} 返回 false; + */ + void cancel(); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ValueType.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ValueType.java new file mode 100644 index 00000000..ccabc600 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/ValueType.java @@ -0,0 +1,108 @@ +package com.jd.blockchain.utils; + +/** + * 键值操作的数据类型; + * + * @author huanghaiquan + * + */ +public enum ValueType { + + /** + * 空; + */ + NIL((byte) 0x00), + + /** + * 布尔型; + */ + BOOLEAN((byte) 0x10), + + /** + * 数值型: + */ + + INT8((byte) 0x11), + + INT16((byte) 0x12), + + INT32((byte) 0x13), + + INT64((byte) 0x14), + + /** + * 日期时间; + */ + DATETIME((byte) 0x15), + + /** + * 文本数据; + */ + TEXT((byte) 0x20), + + /** + * 文本数据; + */ + JSON((byte) 0x21), + + /** + * 文本数据; + */ + XML((byte) 0x22), + + /** + * 二进制数据; + */ + BYTES((byte) 0x40), + + /** + * 大整数; + */ + BIG_INT((byte) 0x41), + + /** + * 图片; + */ + IMG((byte) 0x42), + + /** + * 视频; + */ + VIDEO((byte) 0x43), + + /** + * 位置; + */ + LOCATION((byte) 0x44); + + +// /** +// * 引用;
+// * +// * 表示引用区块链系统中的某一个特定的对象,用以下形式的 URI 表示; +// * +// * state://ledger/account/key/version
+// * 或
+// * proof:state://account_merkle_path/key_merkle_path +// * +// * proof:tx:// +// * +// */ +// REFERENCE((byte) 0x80); + + public final byte CODE; + + private ValueType(byte code) { + this.CODE = code; + } + + public static ValueType valueOf(byte code) { + for (ValueType dataType : ValueType.values()) { + if (dataType.CODE == code) { + return dataType; + } + } + throw new IllegalArgumentException("Unsupported code[" + code + "] of DataType!"); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/Base58Utils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/Base58Utils.java new file mode 100644 index 00000000..584d7c04 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/Base58Utils.java @@ -0,0 +1,170 @@ +package com.jd.blockchain.utils.codec; + +public class Base58Utils { + + private static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + .toCharArray(); + private static final int BASE_58 = ALPHABET.length; + private static final int BASE_256 = 256; + + 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; + } + } + + + + public static String encode(byte[] input) { + if (input.length == 0) { + // paying with the same coin + return ""; + } + + // + // Make a copy of the input since we are going to modify it. + // + 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 any + // + 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); + return new String(output); + } + + public static byte[] decode(String input) { + if(HexUtils.isHex(input)){ + return HexUtils.decode(input); + } + if (input.length() == 0) { + // paying with the same coin + 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 RuntimeException("Not a Base58 input ddd: " + input); + } + + 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); + } + + 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 * BASE_256 + digit256; + + number[i] = (byte) (temp / BASE_58); + + remainder = temp % BASE_58; + } + + return (byte) remainder; + } + + 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 * BASE_58 + digit58; + + number58[i] = (byte) (temp / BASE_256); + + remainder = temp % BASE_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; + } +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/DataDecodeException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/DataDecodeException.java new file mode 100644 index 00000000..30968f48 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/DataDecodeException.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.codec; + +/** + * 无效数据异常; + * + * @author haiq + * + */ +public class DataDecodeException extends RuntimeException{ + + private static final long serialVersionUID = 5834019788898871654L; + + public DataDecodeException() { + } + + public DataDecodeException(String message) { + super(message); + } + + public DataDecodeException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/HexUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/HexUtils.java new file mode 100644 index 00000000..480ecbe7 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/codec/HexUtils.java @@ -0,0 +1,49 @@ +package com.jd.blockchain.utils.codec; + +import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; + +import org.apache.commons.codec.DecoderException; +import org.apache.commons.codec.binary.Hex; + +public class HexUtils { + + public static byte[] decode(String hexString) { + try { + return Hex.decodeHex(hexString.toCharArray()); + } catch (DecoderException e) { + throw new DataDecodeException(e.getMessage(), e); + } + } + + public static String encode(byte[] bytes) { + return Hex.encodeHexString(bytes); + } + + public static String encode(ByteBuffer bytes) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buf = new byte[64]; + int len; + while (bytes.remaining() > 0) { + len = Math.min(buf.length, bytes.remaining()); + bytes.get(buf, 0, len); + out.write(buf, 0, len); + } + return Hex.encodeHexString(out.toByteArray()); + } + + /** + * 判断是否16进制字符串 + * + * @param hexString hexString + * @return boolean + */ + public static boolean isHex(String hexString) { + String regex = "^[A-Fa-f0-9]+$"; + if (hexString.matches(regex)) { + return true; + } + return false; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncFuture.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncFuture.java new file mode 100644 index 00000000..4e02e151 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncFuture.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.utils.concurrent; + +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; + +/** + * 提供对异步操作的结果描述; + * @param class + */ +public interface AsyncFuture { + + /** + * 返回异步操作的结果;
+ * + * 注:此方法将堵塞当前线程直至异步操作完成并返回结果; + * + * @return v + */ + V get(); + + V get(long timeout, TimeUnit unit); + + /** + * 操作是否已完成; + * + * 当操作成功返回或者异常返回时,都表示为已完成; + * + * @return boolean + */ + boolean isDone(); + + /** + * 操作是否已成功; + * + * @return boolean + */ + boolean isExceptionally(); + + public AsyncFuture thenAccept(Consumer action); + + public AsyncFuture thenAcceptAsync(Consumer action); + + public AsyncFuture thenAcceptAsync(Consumer action, Executor executor); + + public AsyncFuture thenRun(Runnable action); + + public AsyncFuture thenRunAsync(Runnable action); + + public AsyncFuture thenRunAsync(Runnable action, Executor executor); + + public AsyncFuture whenComplete(AsyncHandle action); + + public AsyncFuture whenCompleteAsync(AsyncHandle action); + + public AsyncFuture whenCompleteAsync(AsyncHandle action, Executor executor); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncFutureListener.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncFutureListener.java new file mode 100644 index 00000000..d2e979cb --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncFutureListener.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.utils.concurrent; + +public interface AsyncFutureListener { + + public void complete(AsyncFuture future); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncHandle.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncHandle.java new file mode 100644 index 00000000..cda5bd57 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncHandle.java @@ -0,0 +1,14 @@ +package com.jd.blockchain.utils.concurrent; + +import java.util.function.BiConsumer; + +public interface AsyncHandle extends BiConsumer { + + void complete(T returnValue, Throwable error); + + + @Override + default void accept(T t, Throwable u) { + complete(t, u); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncResult.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncResult.java new file mode 100644 index 00000000..2d4ebcda --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/AsyncResult.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.utils.concurrent; +//package my.utils.concurrent; +// +//public interface AsyncResult { +// +// public T getResult(); +// +// /** +// * 操作是否已完成; +// * +// * 当操作成功返回或者异常返回时,都表示为已完成; +// * +// * @return +// */ +// public boolean isDone(); +// +// /** +// * 操作是否已成功; +// * +// * @return +// */ +// public boolean isSuccess(); +// +// public String getErrorCode(); +// +// /** +// * 返回操作异常; +// * +// * 当未完成(isDone方法返回false)或操作正常结束时,返回 null; +// * +// * @return +// */ +// public Throwable getException(); +// +// /** +// * 等待异步操作完成后返回; +// * +// * 等待过程不触发中断; +// */ +// public void awaitUninterruptibly(); +// +// /** +// * 等待异步操作完成后返回; +// * +// * 等待过程不触发中断; +// * +// * @param timeoutMillis +// * 超时毫秒数; +// * @return true 表示操作已完成; false 表示超时返回; +// */ +// public boolean awaitUninterruptibly(long timeoutMillis); +// +// /** +// * 注册监听器; +// * +// * @param listener +// */ +// public void addListener(AsyncCallback callback); +// +//} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/CompletableAsyncFuture.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/CompletableAsyncFuture.java new file mode 100644 index 00000000..60495059 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/CompletableAsyncFuture.java @@ -0,0 +1,189 @@ +package com.jd.blockchain.utils.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class CompletableAsyncFuture implements AsyncFuture { + + private CompletableFuture cf; + + public CompletableAsyncFuture() { + this.cf = new CompletableFuture(); + } + + private CompletableAsyncFuture(CompletableFuture cf) { + this.cf = cf; + } + + public static CompletableAsyncFuture completeFuture(T value) { + CompletableFuture cf = CompletableFuture.completedFuture(value); + return new CompletableAsyncFuture<>(cf); + } + + public static CompletableAsyncFuture runAsync(Runnable runnable){ + CompletableFuture cf = CompletableFuture.runAsync(runnable); + return new CompletableAsyncFuture<>(cf); + } + + public static CompletableAsyncFuture runAsync(Runnable runnable, Executor executor){ + CompletableFuture cf = CompletableFuture.runAsync(runnable, executor); + return new CompletableAsyncFuture<>(cf); + } + + public static CompletableAsyncFuture callAsync(Callable callable){ + CompletableFuture cf = CompletableFuture.supplyAsync(new Supplier() { + @Override + public T get() { + try { + return callable.call(); + } catch (Exception e) { + throw new RuntimeExecutionException(e.getMessage(), e); + } + } + }); + return new CompletableAsyncFuture<>(cf); + } + + public static CompletableAsyncFuture callAsync(Callable callable, Executor executor){ + CompletableFuture cf = CompletableFuture.supplyAsync(new Supplier() { + @Override + public T get() { + try { + return callable.call(); + } catch (Exception e) { + throw new RuntimeExecutionException(e.getMessage(), e); + } + } + }, executor); + return new CompletableAsyncFuture<>(cf); + } + + public static CompletableAsyncFuture callAsync(Supplier supplier){ + CompletableFuture cf = CompletableFuture.supplyAsync(supplier); + return new CompletableAsyncFuture<>(cf); + } + + public static CompletableAsyncFuture callAsync(Supplier supplier, Executor executor){ + CompletableFuture cf = CompletableFuture.supplyAsync(supplier, executor); + return new CompletableAsyncFuture<>(cf); + } + + /** + * 如果尚未完成,则设置 {@link #get()} 方法的返回值,并置为已完成状态; + * + * @param value + * 正常返回的结果; + * @return true - 表示此操作使得状态从“未完成”状态转为“已完成”状态; + */ + public boolean complete(T value) { + return cf.complete(value); + } + + public boolean error(Throwable ex) { + return cf.completeExceptionally(ex); + } + + @Override + public T get() { + try { + return cf.get(); + } catch (InterruptedException e) { + throw new RuntimeInterruptedException(e.getMessage(), e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause == null) { + cause = e; + } + throw new RuntimeExecutionException(cause.getMessage(), cause); + } + } + + @Override + public T get(long timeout, TimeUnit unit) { + try { + return cf.get(timeout, unit); + } catch (InterruptedException e) { + throw new RuntimeInterruptedException(e.getMessage(), e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + if (cause == null) { + cause = e; + } + throw new RuntimeExecutionException(cause.getMessage(), cause); + } catch (TimeoutException e) { + throw new RuntimeTimeoutException(e.getMessage(), e); + } + } + + @Override + public boolean isDone() { + return cf.isDone(); + } + + @Override + public boolean isExceptionally() { + return cf.isCompletedExceptionally(); + } + + @Override + public AsyncFuture thenAccept(Consumer action) { + cf.thenAccept(action); + return this; + } + + @Override + public AsyncFuture thenAcceptAsync(Consumer action) { + cf.thenAcceptAsync(action); + return this; + } + + @Override + public AsyncFuture thenAcceptAsync(Consumer action, Executor executor) { + cf.thenAcceptAsync(action, executor); + return this; + } + + @Override + public AsyncFuture thenRun(Runnable action) { + cf.thenRun(action); + return this; + } + + @Override + public AsyncFuture thenRunAsync(Runnable action) { + cf.thenRunAsync(action); + return this; + } + + @Override + public AsyncFuture thenRunAsync(Runnable action, Executor executor) { + cf.thenRunAsync(action, executor); + return this; + } + + @Override + public AsyncFuture whenComplete(AsyncHandle action) { + cf.whenComplete(action); + return this; + } + + @Override + public AsyncFuture whenCompleteAsync(AsyncHandle action) { + cf.whenCompleteAsync(action); + return this; + } + + @Override + public AsyncFuture whenCompleteAsync(AsyncHandle action, Executor executor) { + cf.whenCompleteAsync(action, executor); + cf.whenCompleteAsync((a, b) -> action.accept(a, b), executor); + return this; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/InvocationResult.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/InvocationResult.java new file mode 100644 index 00000000..84533ba7 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/InvocationResult.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.utils.concurrent; + +public class InvocationResult { + + private volatile T result; + + private volatile Exception error; + + public T getResult() { + return result; + } + + public void setResult(T result) { + this.result = result; + } + + public Exception getError() { + return error; + } + + public void setError(Exception error) { + this.error = error; + } + +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/NamedThreadFactory.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/NamedThreadFactory.java new file mode 100644 index 00000000..5d5b35b8 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/NamedThreadFactory.java @@ -0,0 +1,75 @@ +package com.jd.blockchain.utils.concurrent; + +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +/** + * 可对线程命名的线程工厂; + * + * @author haiq + * + */ +public class NamedThreadFactory implements ThreadFactory { + + private String name; + + private boolean indexThread = false; + + private AtomicLong index; + + private boolean deamon; + + /** + * 创建 NamedThreadFactory 实例; + * + * @param name + * 名称; + */ + public NamedThreadFactory(String name) { + this(name, false, false); + } + + /** + * 创建 NamedThreadFactory 实例; + * + * @param name + * 名称; + * @param deamon + * 是否守护线程; + */ + public NamedThreadFactory(String name, boolean deamon) { + this(name, false, deamon); + } + + /** + * 创建 NamedThreadFactory 实例; + * + * @param name + * 名称; + * @param indexThread + * 是否记录创建的线程个数;如果是,则通过在指定的线程名称前加上索引值构成最终的线程名称,诸如:"threadName-0,threadName-1"; + * @param deamon + * 是否守护线程; + */ + public NamedThreadFactory(String name, boolean indexThread, boolean deamon) { + this.name = name; + this.indexThread = indexThread; + if (indexThread) { + index = new AtomicLong(0); + } + this.deamon = deamon; + } + + @Override + public Thread newThread(Runnable r) { + String thrdName = name; + if (indexThread) { + long i = index.incrementAndGet(); + thrdName = name +"-" + i; + } + Thread thrd = new Thread(r, thrdName); + thrd.setDaemon(deamon); + return thrd; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeExecutionException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeExecutionException.java new file mode 100644 index 00000000..0bcde672 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeExecutionException.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.concurrent; + +/** + * 未预期的中断异常; + * + * @author haiq + * + */ +public class RuntimeExecutionException extends RuntimeException { + + private static final long serialVersionUID = -4404758941888371638L; + + public RuntimeExecutionException() { + } + + public RuntimeExecutionException(String message) { + super(message); + } + + public RuntimeExecutionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeInterruptedException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeInterruptedException.java new file mode 100644 index 00000000..c6ff9623 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeInterruptedException.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.concurrent; + +/** + * 未预期的中断异常; + * + * @author haiq + * + */ +public class RuntimeInterruptedException extends RuntimeException { + + private static final long serialVersionUID = -4404758941888371638L; + + public RuntimeInterruptedException() { + } + + public RuntimeInterruptedException(String message) { + super(message); + } + + public RuntimeInterruptedException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeTimeoutException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeTimeoutException.java new file mode 100644 index 00000000..64a09ac4 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/RuntimeTimeoutException.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.concurrent; + +/** + * 未预期的超时异常; + * + * @author haiq + * + */ +public class RuntimeTimeoutException extends RuntimeException { + + private static final long serialVersionUID = -4404758941888371638L; + + public RuntimeTimeoutException() { + } + + public RuntimeTimeoutException(String message) { + super(message); + } + + public RuntimeTimeoutException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/SyncFutureAdaptor.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/SyncFutureAdaptor.java new file mode 100644 index 00000000..e2655c65 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/SyncFutureAdaptor.java @@ -0,0 +1,135 @@ +package com.jd.blockchain.utils.concurrent; +//package my.utils.concurrent; +// +///** +// * 用于适配同步操作的 AsyncFuture 实现; +// * +// * @author haiq +// * +// * @param +// */ +//public class SyncFutureAdaptor implements AsyncFuture { +// +// private TSource source; +// +// private boolean success = false; +// private Throwable exception; +// private String errorCode; +// +// /** +// * 创建 SyncFutureAdaptor 实例; +// * +// * @param source +// * 操作对象; +// * @param exception +// * 操作完成后引发的异常;如果指定为 null 则表示操作成功返回而没有引发异常; +// * @param errorCode 错误码 +// */ +// private SyncFutureAdaptor(TSource source, Throwable exception, String errorCode) { +// this.source = source; +// this.success = exception == null; +// this.exception = exception; +// this.errorCode = errorCode; +// } +// +// /** +// * 创建表示成功完成操作的 AsyncFuture 实例; +// * +// * @param source +// * 执行操作的对象; +// * @return AsyncFuture 实例; +// */ +// public static AsyncFuture createSuccessFuture(T source) { +// return new SyncFutureAdaptor(source, null, null); +// } +// +// /** +// * 创建表示操作引发异常返回的 AsyncFuture 实例; +// * +// * @param source +// * 执行操作的对象; +// * @param exception +// * 操作引发的异常;不允许为 null; +// * @return AsyncFuture 实例; +// */ +// public static AsyncFuture createErrorFuture(T source, Throwable exception) { +// if (exception == null) { +// throw new IllegalArgumentException("Exception is null!"); +// } +// return new SyncFutureAdaptor(source, exception, null); +// } +// +// /** +// * 创建表示操作引发异常返回的 AsyncFuture 实例; +// * +// * @param source +// * 执行操作的对象; +// * @param errorCode +// * 操作引发的错误代码; +// * @return AsyncFuture 实例; +// */ +// public static AsyncFuture createErrorFuture(T source, String errorCode) { +// if (errorCode == null || errorCode.length() == 0) { +// throw new IllegalArgumentException("ErrorCode is empty!"); +// } +// return new SyncFutureAdaptor(source, null, errorCode); +// } +// +// @Override +// public TSource get() { +// return source; +// } +// +// @Override +// public boolean isDone() { +// return true; +// } +// +// @Override +// public boolean isSuccess() { +// return success; +// } +// +// @Override +// public Throwable getException() { +// return exception; +// } +// +// // @Override +// // public void addListener(AsyncFutureListener> +// // listener) { +// // +// // } +// +// @Override +// public void await() throws InterruptedException { +// return; +// } +// +// @Override +// public boolean await(long timeoutMillis) throws InterruptedException { +// return true; +// } +// +// @Override +// public void awaitUninterruptibly() { +// return; +// } +// +// @Override +// public boolean awaitUninterruptibly(long timeoutMillis) { +// return true; +// } +// +// @Override +// public void addListener(AsyncFutureListener listener) { +// // 同步操作已经完成; +// listener.complete(this); +// } +// +// @Override +// public String getErrorCode() { +// return this.errorCode; +// } +// +//} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/ThreadInvoker.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/ThreadInvoker.java new file mode 100644 index 00000000..fcd83083 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/ThreadInvoker.java @@ -0,0 +1,116 @@ +package com.jd.blockchain.utils.concurrent; + +/** + * 在新线程执行调用的工具类;
+ * + * 注意:一个实例只能调用一次,并且不应在多线程间共享; + * + * @author huanghaiquan + * + * @param class + */ +public abstract class ThreadInvoker { + + private volatile T retn; + + private volatile Exception error; + + private volatile boolean started = false; + + public Exception getError() { + return error; + } + + public T getRetn() { + return retn; + } + + protected abstract T invoke() throws Exception; + + /** + * Start invoke in new thread, and wait for the invoking thread to die; + * + * @return t + */ + public T startAndWait() { + Thread thrd = doStart(); + try { + thrd.join(); + } catch (InterruptedException e) { + throw new IllegalStateException(e.getMessage(), e); + } + if (error != null) { + throw new IllegalStateException(error.getMessage(), error); + } + return retn; + } + + /** + * Start invoke in new thread; + * + * @return AsyncCallback t; + */ + public AsyncCallback start() { + Thread thrd = doStart(); + return new AsyncCallback(thrd, this); + } + + public synchronized void reset() { + if (started) { + throw new IllegalStateException("Cann't reset when this invoking is running."); + } + retn = null; + error = null; + } + + private synchronized Thread doStart() { + if (started) { + throw new IllegalStateException("Invoker thread has started. Cann't start again until it's over."); + } + if (retn != null || error != null) { + throw new IllegalStateException("Cann't start again until the result of last invoking is reseted."); + } + started = true; + Thread thrd = new Thread(new Runnable() { + @Override + public void run() { + try { + retn = invoke(); + } catch (Exception e) { + error = e; + } finally { + started = false; + } + } + }); + thrd.start(); + return thrd; + } + + public static class AsyncCallback { + private Thread thrd; + + private ThreadInvoker invoker; + + public AsyncCallback(Thread thrd, ThreadInvoker invoker) { + this.thrd = thrd; + this.invoker = invoker; + } + + /** + * 等待调用返回; + * @return class t; + */ + public T waitReturn() { + try { + thrd.join(); + } catch (InterruptedException e) { + throw new IllegalStateException(e.getMessage(), e); + } + if (invoker.error != null) { + throw new IllegalStateException(invoker.error.getMessage(), invoker.error); + } + return invoker.retn; + } + } +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/ThreadUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/ThreadUtils.java new file mode 100644 index 00000000..fdbdadd8 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/concurrent/ThreadUtils.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.concurrent; + +public abstract class ThreadUtils { + + /** + * 以不可中断的方式使当前线程 sleep 指定时长; + * + * @param millis 要等待的时长;单位毫秒; + */ + public static void sleepUninterrupted(long millis){ + long start = System.currentTimeMillis(); + long elapseTime = 0; + while(elapseTime < millis){ + try { + Thread.sleep(elapseTime); + } catch (InterruptedException e) { + // ignore ; + } + elapseTime = System.currentTimeMillis() - start; + } + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/console/CommandConsole.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/console/CommandConsole.java new file mode 100644 index 00000000..2aadfe77 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/console/CommandConsole.java @@ -0,0 +1,141 @@ +package com.jd.blockchain.utils.console; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; + +public class CommandConsole { + + private Map processors = new ConcurrentHashMap(); + + private CommondProcessor defaultProcessor; + + private boolean monitoring = false; + + private BufferedReader in; + + private PrintStream out; + + private String prompt; + + public PrintStream out() { + return out; + } + + /** + * @param input + * 命令输入流; + * @param output + * 结果输出流; + * @param prompt + * 提示符; + */ + public CommandConsole(InputStream input, PrintStream output, String prompt) { + try { + this.in = new BufferedReader(new InputStreamReader(input, "GBK")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e.getMessage(), e); + } + this.out = output; + this.prompt = prompt; + + // 定义默认的命令处理器; + defaultProcessor = new CommondProcessor() { + @Override + public void onEnter(String command, String[] args, CommandConsole console) { + console.out().println("Unsupported command [" + command + "]!"); + } + }; + } + + public void setDefaultCommandProcessor(CommondProcessor processor) { + if (processor == null) { + throw new IllegalArgumentException("Null argument!"); + } + this.defaultProcessor = processor; + } + + /** + * 注册命令处理器; + * + * 一个命令只能有一个处理器,重复注册多个处理器将引发异常; + * + * @param command 命令文本; 自动忽略大小写; + * @param processor processor + */ + public synchronized void register(String command, CommondProcessor processor) { + if (StringUtils.containsWhitespace(command)) { + throw new IllegalArgumentException("Can't register command with white space character!"); + } + if (processors.containsKey(command)) { + throw new IllegalStateException("The command[" + command + "] has been registered!"); + } + processors.put(command.toLowerCase(), processor); + } + + /** + * 开始监控命令; + * + * 方法将堵塞当前线程; + * + */ + public synchronized void open() { + monitoring = true; + while (monitoring) { + // 输出提示; + out.println(); + out.print(prompt); + + // 读入命令; + String command; + try { + command = in.readLine(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + if (command == null) { + continue; + } + String[] cmdArgs = StringUtils.tokenizeToStringArray(command, " "); + if (cmdArgs.length == 0) { + continue; + } + String cmd = cmdArgs[0]; + String[] args; + if (cmdArgs.length > 1) { + args = Arrays.copyOfRange(cmdArgs, 1, cmdArgs.length); + } else { + args = new String[0]; + } + CommondProcessor processor = processors.get(cmd.toLowerCase()); + try { + if (processor != null) { + processor.onEnter(cmd, args, this); + } else { + defaultProcessor.onEnter(cmd, args, this); + } + } catch (Exception e) { + out.println("Error!!--" + e.getMessage()); + logger.error(e.getMessage(), e); + } + } + } + + /** + * 停止命令控制台; + */ + public synchronized void close() { + monitoring = false; + } + private Logger logger = LoggerFactory.getLogger(CommandConsole.class); +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/console/CommondProcessor.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/console/CommondProcessor.java new file mode 100644 index 00000000..24b16601 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/console/CommondProcessor.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.utils.console; + +import java.io.PrintStream; + +/** + * 命令处理器; + * + * @author haiq + * + */ +public interface CommondProcessor { + + public void onEnter(String command, String[] args, CommandConsole console); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/DefaultExceptionHandle.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/DefaultExceptionHandle.java new file mode 100644 index 00000000..998e619c --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/DefaultExceptionHandle.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.utils.event; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.jd.blockchain.utils.console.CommandConsole; + +public class DefaultExceptionHandle implements ExceptionHandle { + + @Override + public void handle(Exception ex, TListener listener, Method method, Object[] args) { + String argsValue = null; + if (args!=null) { + StringBuilder argsFormat = new StringBuilder(); + for (int i = 0; i < args.length; i++) { + argsFormat.append("arg["); + argsFormat.append(i); + argsFormat.append("]=%s;"); + } + argsValue = String.format(argsFormat.toString(), args); + } + String message = String.format("Error occurred on firing event!--[listener.class=%s][method=%s][args=%s]--[%s]%s", + listener.getClass().getName(), method.getName(), argsValue, ex.getClass().getName(), ex.getMessage()); +// System.err.println(message); + logger.error(message, ex); + } + private Logger logger = LoggerFactory.getLogger(DefaultExceptionHandle.class); +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/EventMulticaster.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/EventMulticaster.java new file mode 100644 index 00000000..d3de5554 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/EventMulticaster.java @@ -0,0 +1,125 @@ +package com.jd.blockchain.utils.event; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.slf4j.Logger; +import org.springframework.util.ReflectionUtils; + +import com.jd.blockchain.utils.Disposable; + +/** + * 将针对一个特定的 listener 的方法调用事件广播到多个 listener对应的方法; + * + * 只支持无返回值的,非 Object 类的方法; + * + * @author haiq + * + * @param TListener + */ +public class EventMulticaster implements Disposable { + + private TListener listenerProxy; + + private Method[] supportedMethods; + + private List listeners = new CopyOnWriteArrayList(); + + private ExceptionHandle exHandle; + + public EventMulticaster(Class listenerClass) { + this(listenerClass, (ExceptionHandle) null); + } + + public EventMulticaster(Class listenerClass, Logger errorLogger) { + this(listenerClass, new ExceptionLoggingHandle(errorLogger)); + } + + @SuppressWarnings("unchecked") + public EventMulticaster(Class listenerClass, ExceptionHandle exHandle) { + // 初始化错误处理器; + this.exHandle = exHandle == null ? new DefaultExceptionHandle() : exHandle; + + // 解析出不支持的方法; + Method[] methods = ReflectionUtils.getAllDeclaredMethods(listenerClass); + List supMths = new LinkedList(); + for (Method method : methods) { + if (method.getDeclaringClass() == Object.class) { + // 不支持 Object 方法; + continue; + } + if (method.getReturnType() != void.class) { + // 不支持带返回值的方法; + continue; + } + supMths.add(method); + } + supportedMethods = supMths.toArray(new Method[supMths.size()]); + + // 生成代理类; + listenerProxy = (TListener) Proxy.newProxyInstance(listenerClass.getClassLoader(), + new Class[] { listenerClass }, new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + multicast(proxy, method, args); + return null; + } + }); + } + + private void multicast(Object proxy, Method method, Object[] args) { + boolean supported = false; + for (Method mth : supportedMethods) { + if (mth.equals(method)) { + supported = true; + break; + } + } + if (supported) { + doNotify(listeners, method, args); + } else { + // 调用了不支持的方法; + throw new UnsupportedOperationException("Unsupported method for event multicasting!"); + } + } + + protected void doNotify(List listeners, Method method, Object[] args){ + for (TListener listener : listeners) { + doNotifySingle(listener, method, args); + } + } + + protected void doNotifySingle(TListener listener, Method method, Object[] args){ + try { + ReflectionUtils.invokeMethod(method, listener, args); + } catch (Exception e) { + exHandle.handle(e, listener, method, args); + } + } + + public void addListener(TListener listener) { + listeners.add(listener); + } + + public void removeListener(TListener listener) { + listeners.remove(listener); + } + + public TListener broadcast() { + return listenerProxy; + } + + @Override + public void dispose() { + listeners.clear(); + listeners = null; + listenerProxy = null; + supportedMethods = null; + exHandle = null; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/ExceptionHandle.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/ExceptionHandle.java new file mode 100644 index 00000000..d39d75f0 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/ExceptionHandle.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.utils.event; + +import java.lang.reflect.Method; + +/** + * ExceptionHandle 定义了在对事件监听器进行事件通知时出现的异常的处理接口; + * + * @author haiq + * + * @param TListener + */ +public interface ExceptionHandle { + + /** + * 处理监听器异常; + * + * @param ex + * 异常; + * @param listener + * 发生异常的监听器实例; + * @param method + * 发生异常的方法; + * @param args + * 方法参数; + */ + public void handle(Exception ex, TListener listener, Method method, Object[] args); +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/ExceptionLoggingHandle.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/ExceptionLoggingHandle.java new file mode 100644 index 00000000..772030d2 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/ExceptionLoggingHandle.java @@ -0,0 +1,32 @@ +package com.jd.blockchain.utils.event; + +import java.lang.reflect.Method; + +import org.slf4j.Logger; + +public class ExceptionLoggingHandle implements ExceptionHandle { + + private Logger logger; + + public ExceptionLoggingHandle(Logger logger) { + this.logger = logger; + } + + @Override + public void handle(Exception ex, TListener listener, Method method, Object[] args) { + String argsValue = null; + if (args!=null) { + StringBuilder argsFormat = new StringBuilder(); + for (int i = 0; i < args.length; i++) { + argsFormat.append("arg["); + argsFormat.append(i); + argsFormat.append("]=%s;"); + } + argsValue = String.format(argsFormat.toString(), args); + } + String message = String.format("Error occurred on firing event!--[listener.class=%s][method=%s][args=%s]--[%s]%s", + listener.getClass().getName(), method.getName(), argsValue, ex.getClass().getName(), ex.getMessage()); + logger.error(message, ex); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/RethrowExceptionHandler.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/RethrowExceptionHandler.java new file mode 100644 index 00000000..84d27018 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/event/RethrowExceptionHandler.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.event; + +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; + +import org.slf4j.Logger; + +public class RethrowExceptionHandler extends ExceptionLoggingHandle { + + public RethrowExceptionHandler(Logger logger) { + super(logger); + } + + @Override + public void handle(Exception ex, TListener listener, Method method, Object[] args) { + super.handle(ex, listener, method, args); + if (ex instanceof RuntimeException) { + throw (RuntimeException)ex; + } + throw new UndeclaredThrowableException(ex); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/hash/Hashing.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/hash/Hashing.java new file mode 100644 index 00000000..6d3203ae --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/hash/Hashing.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.utils.hash; + +import com.jd.blockchain.utils.hash.MurmurHash3; + +public interface Hashing { + + public static final Hashing MURMUR3_HASH = new Hashing() { + + public int hash32(CharSequence id) { + return MurmurHash3.murmurhash3_x86_32(id, 0, id.length(), 1024); + } + }; + + public int hash32(CharSequence id); + + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/hash/MurmurHash3.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/hash/MurmurHash3.java new file mode 100644 index 00000000..5c28e751 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/hash/MurmurHash3.java @@ -0,0 +1,320 @@ +package com.jd.blockchain.utils.hash; + +/** + * The MurmurHash3 algorithm was created by Austin Appleby and placed in the public domain. + * This java port was authored by Yonik Seeley and also placed into the public domain. + * The author hereby disclaims copyright to this source code. + *

+ * This produces exactly the same hash values as the final C++ + * version of MurmurHash3 and is thus suitable for producing the same hash values across + * platforms. + *

+ * The 32 bit x86 version of this hash should be the fastest variant for relatively short keys like ids. + * murmurhash3_x64_128 is a good choice for longer strings or if you need more than 32 bits of hash. + *

+ * Note - The x86 and x64 versions do _not_ produce the same results, as the + * algorithms are optimized for their respective platforms. + *

+ * See http://github.com/yonik/java_util for future updates to this file. + */ +public final class MurmurHash3 { + + /** 128 bits of state */ + public static final class LongPair { + public long val1; + public long val2; + } + + public static final int fmix32(int h) { + h ^= h >>> 16; + h *= 0x85ebca6b; + h ^= h >>> 13; + h *= 0xc2b2ae35; + h ^= h >>> 16; + return h; + } + + public static final long fmix64(long k) { + k ^= k >>> 33; + k *= 0xff51afd7ed558ccdL; + k ^= k >>> 33; + k *= 0xc4ceb9fe1a85ec53L; + k ^= k >>> 33; + return k; + } + + /** + * Gets a long from a byte buffer in little endian byte order. + * @param buf buf + * @param offset offset + * @return long + */ + public static final long getLongLittleEndian(byte[] buf, int offset) { + return ((long)buf[offset+7] << 56) // no mask needed + | ((buf[offset+6] & 0xffL) << 48) + | ((buf[offset+5] & 0xffL) << 40) + | ((buf[offset+4] & 0xffL) << 32) + | ((buf[offset+3] & 0xffL) << 24) + | ((buf[offset+2] & 0xffL) << 16) + | ((buf[offset+1] & 0xffL) << 8) + | ((buf[offset ] & 0xffL)); // no shift needed + } + + + /** + * Returns the MurmurHash3_x86_32 hash. + * @param data data + * @param offset offset + * @param len len + * @param seed seed + * @return int + */ + public static int murmurhash3_x86_32(byte[] data, int offset, int len, int seed) { + + final int c1 = 0xcc9e2d51; + final int c2 = 0x1b873593; + + int h1 = seed; + int roundedEnd = offset + (len & 0xfffffffc); // round down to 4 byte block + + for (int i=offset; i>> 17); // ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + } + + // tail + int k1 = 0; + + switch(len & 0x03) { + case 3: + k1 = (data[roundedEnd + 2] & 0xff) << 16; + // fallthrough + case 2: + k1 |= (data[roundedEnd + 1] & 0xff) << 8; + // fallthrough + case 1: + k1 |= (data[roundedEnd] & 0xff); + k1 *= c1; + k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); + k1 *= c2; + h1 ^= k1; + } + + // finalization + h1 ^= len; + + // fmix(h1); + h1 ^= h1 >>> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >>> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >>> 16; + + return h1; + } + + /** + * Returns the MurmurHash3_x86_32 hash of the UTF-8 bytes of the String without actually encoding + * the string to a temporary buffer. This is more than 2x faster than hashing the result + * of String.getBytes(). + * @param data data + * @param offset offset + * @param len len + * @param seed seed + * @return int + */ + public static int murmurhash3_x86_32(CharSequence data, int offset, int len, int seed) { + + final int c1 = 0xcc9e2d51; + final int c2 = 0x1b873593; + + int h1 = seed; + + int pos = offset; + int end = offset + len; + int k1 = 0; + int k2 = 0; + int shift = 0; + int bits = 0; + int nBytes = 0; // length in UTF8 bytes + + + while (pos < end) { + int code = data.charAt(pos++); + if (code < 0x80) { + k2 = code; + bits = 8; + + /*** + // optimized ascii implementation (currently slower!!! code size?) + if (shift == 24) { + k1 = k1 | (code << 24); + k1 *= c1; + k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); + k1 *= c2; + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + shift = 0; + nBytes += 4; + k1 = 0; + } else { + k1 |= code << shift; + shift += 8; + } + continue; + ***/ + + } + else if (code < 0x800) { + k2 = (0xC0 | (code >> 6)) + | ((0x80 | (code & 0x3F)) << 8); + bits = 16; + } + else if (code < 0xD800 || code > 0xDFFF || pos>=end) { + // we check for pos>=end to encode an unpaired surrogate as 3 bytes. + k2 = (0xE0 | (code >> 12)) + | ((0x80 | ((code >> 6) & 0x3F)) << 8) + | ((0x80 | (code & 0x3F)) << 16); + bits = 24; + } else { + // surrogate pair + // int utf32 = pos < end ? (int) data.charAt(pos++) : 0; + int utf32 = (int) data.charAt(pos++); + utf32 = ((code - 0xD7C0) << 10) + (utf32 & 0x3FF); + k2 = (0xff & (0xF0 | (utf32 >> 18))) + | ((0x80 | ((utf32 >> 12) & 0x3F))) << 8 + | ((0x80 | ((utf32 >> 6) & 0x3F))) << 16 + | (0x80 | (utf32 & 0x3F)) << 24; + bits = 32; + } + + + k1 |= k2 << shift; + + // int used_bits = 32 - shift; // how many bits of k2 were used in k1. + // int unused_bits = bits - used_bits; // (bits-(32-shift)) == bits+shift-32 == bits-newshift + + shift += bits; + if (shift >= 32) { + // mix after we have a complete word + + k1 *= c1; + k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >>> 19); // ROTL32(h1,13); + h1 = h1*5+0xe6546b64; + + shift -= 32; + // unfortunately, java won't let you shift 32 bits off, so we need to check for 0 + if (shift != 0) { + k1 = k2 >>> (bits-shift); // bits used == bits - newshift + } else { + k1 = 0; + } + nBytes += 4; + } + + } // inner + + // handle tail + if (shift > 0) { + nBytes += shift >> 3; + k1 *= c1; + k1 = (k1 << 15) | (k1 >>> 17); // ROTL32(k1,15); + k1 *= c2; + h1 ^= k1; + } + + // finalization + h1 ^= nBytes; + + // fmix(h1); + h1 ^= h1 >>> 16; + h1 *= 0x85ebca6b; + h1 ^= h1 >>> 13; + h1 *= 0xc2b2ae35; + h1 ^= h1 >>> 16; + + return h1; + } + + + /** + * Returns the MurmurHash3_x64_128 hash, placing the result in "out". + * @param key key + * @param offset offset + * @param len len + * @param seed seed + * @param out out + */ + public static void murmurhash3_x64_128(byte[] key, int offset, int len, int seed, LongPair out) { + // The original algorithm does have a 32 bit unsigned seed. + // We have to mask to match the behavior of the unsigned types and prevent sign extension. + long h1 = seed & 0x00000000FFFFFFFFL; + long h2 = seed & 0x00000000FFFFFFFFL; + + final long c1 = 0x87c37b91114253d5L; + final long c2 = 0x4cf5ad432745937fL; + + int roundedEnd = offset + (len & 0xFFFFFFF0); // round down to 16 byte block + for (int i=offset; i -1; i--) { + if (indexes[i] == baseChars[i].length) { + if (i > 0) { + // 进位; + indexes[i - 1]++; + } else { + // 溢出;忽略; + } + indexes[i] = 0; + } + } + return key.toString(); + } + + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/ByteArray.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/ByteArray.java new file mode 100644 index 00000000..322762cb --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/ByteArray.java @@ -0,0 +1,317 @@ +package com.jd.blockchain.utils.io; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Externalizable; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.Arrays; +import org.springframework.util.Base64Utils; + +import com.jd.blockchain.utils.codec.Base58Utils; +import com.jd.blockchain.utils.codec.HexUtils; + +/** + * ByteArray 二进制字节块是对字节数组的包装,目的是提供一种不可变的二进制数据结构; + * + * @author huanghaiquan + * + */ +public class ByteArray implements Externalizable { + + public static final ByteArray EMPTY = ByteArray.wrap(new byte[0]); + + private byte[] bytes; + + private int hashCode; + + private ByteArray readonlyWrapper = null; + + private ByteArray(byte[] bytes) { + this(bytes, false); + } + + private ByteArray(byte[] bytes, boolean readonly) { + this.bytes = bytes; + this.hashCode = Arrays.hashCode(bytes); +// this.readonly = readonly; + } + + public int size() { + return bytes.length; + } + + public byte get(int i) { + return bytes[i]; + } + +// public boolean isReadonly() { +// return readonly; +// } + + public void copy(int srcPos, byte[] dest, int destPos, int length) { + System.arraycopy(bytes, srcPos, dest, destPos, length); + } + + public ByteArray() {} + + @Override + public boolean equals(Object obj) { + if (super.equals(obj)) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ByteArray)) { + return false; + } + ByteArray target = (ByteArray) obj; + if (this.bytes == target.bytes) { + return true; + } + return BytesUtils.equals(target.bytes, this.bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } + + /** + * 对指定的字节数组的副本进行包装; + * + * @param bytes bytes + * @return ByteArray + */ + public static ByteArray wrapCopy(byte[] bytes) { + byte[] replica = Arrays.copyOf(bytes, bytes.length); + return new ByteArray(replica); + } + + /** + * 对指定的字节数组直接进行包装; + * + * @param bytes bytes + * @return ByteArray + */ + public static ByteArray wrap(byte[] bytes) { + return new ByteArray(bytes); + } + + /** + * 对指定的字节数组直接进行包装; + * + * @param oneByte oneByte + * @return ByteArray + */ + public static ByteArray wrap(byte oneByte) { + return new ByteArray(new byte[] { oneByte }); + } + + /** + * 对指定的字节数组的副本进行包装; + * + * @param bytes bytes + * @return ByteArray + */ + public static ByteArray wrapCopyReadonly(byte[] bytes) { + byte[] replica = Arrays.copyOf(bytes, bytes.length); + return new ByteArray(replica, true); + } + + /** + * 对指定的字节数组直接进行包装; + * + * @param bytes bytes + * @return ByteArray + */ + public static ByteArray wrapReadonly(byte[] bytes) { + return new ByteArray(bytes, true); + } + + /** + * 对指定的字节数组直接进行包装; + * + * @param oneByte oneByte + * @return ByteArray + */ + public static ByteArray wrapReadonly(byte oneByte) { + return new ByteArray(new byte[] { oneByte }, true); + } + + /** + * 将字节内容完整写入指定的输出流; + * @param out out + * @return int + */ + public int writeTo(OutputStream out) { + try { + out.write(bytes); + return bytes.length; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public InputStream asInputStream() { + return new ByteArrayInputStream(bytes); + } + + public ByteBuffer asReadOnlyBuffer() { + return ByteBuffer.wrap(bytes).asReadOnlyBuffer(); + } + +// public ByteArray asReadonly() { +// if (readonly) { +// return this; +// } +// if (readonlyWrapper == null) { +// readonlyWrapper = new ByteArray(bytes, true); +// } +// return readonlyWrapper; +// } + + /** + * 返回原始的二进制数组的副本;
+ * + * 注:使用者要注意此操作代理的内存性能的影响; + * + * @return byte array; + */ + public byte[] bytesCopy() { + return Arrays.copyOf(bytes, bytes.length); + } + + /** + * 返回原始的字节数组; + * + *
+ * 如果字节数组被标识为只读,调用此方法将引发异常 IllegalStateException; + * + * 注:应谨慎操作此方法返回的字节数组,通常的原则是不应更改返回的字节数组,用于只读的情形; + * + * @return byte array; + */ + public byte[] bytes() { +// if (readonly) { +// throw new IllegalStateException("This byte array is readonly!"); +// } + return bytes; + } + + public String toHex() { + return toHex(bytes); + } + + public String toBase64() { + return toBase64(bytes); + } + + public String toBase58() { + return toBase58(bytes); + } + + /** + * 返回 ByteArray 的 Base58 字符; + */ + @Override + public String toString() { + return toBase58(); + } + + public static ByteArray parseHex(String hexString) { + return wrap(fromHex(hexString)); + } + + public static ByteArray parseBase58(String base58String) { + return wrap(fromBase58(base58String)); + } + + public static ByteArray parseBase64(String base64String) { + return wrap(fromBase64(base64String)); + } + + public static ByteArray parseString(String str, String charset) { + return wrap(fromString(str, charset)); + } + + public static String toHex(byte[] bytes) { + return HexUtils.encode(bytes); + } + + public static String toBase58(byte[] bytes) { + return Base58Utils.encode(bytes); + } + public static String toBase64(byte[] bytes) { + return Base64Utils.encodeToUrlSafeString(bytes); + } + + public String toString(String charset) { + return BytesUtils.toString(bytes, charset); + } + + public static byte[] fromHex(String hexString) { + return HexUtils.decode(hexString); + } + + public static byte[] fromBase58(String base58String) { + return Base58Utils.decode(base58String); + } + + public static byte[] fromBase64(String base64String) { + return Base64Utils.decodeFromUrlSafeString(base64String); + } + + public static byte[] fromString(String str, String charset) { + return BytesUtils.toBytes(str, charset); + } + + /** + * The object implements the writeExternal method to save its contents + * by calling the methods of DataOutput for its primitive values or + * calling the writeObject method of ObjectOutput for objects, strings, + * and arrays. + * + * @param out the stream to write the object to + * @throws IOException Includes any I/O exceptions that may occur + * @serialData Overriding methods should use this tag to describe + * the data layout of this Externalizable object. + * List the sequence of element types and, if possible, + * relate the element to a public/protected field and/or + * method of this Externalizable class. + */ + @Override + public void writeExternal(ObjectOutput out) throws IOException { + ByteArrayOutputStream os = new ByteArrayOutputStream(); + writeTo(os); + byte[] bts = os.toByteArray(); + out.writeInt(bts.length); + out.write(bts); + } + + /** + * The object implements the readExternal method to restore its + * contents by calling the methods of DataInput for primitive + * types and readObject for objects, strings and arrays. The + * readExternal method must read the values in the same sequence + * and with the same types as were written by writeExternal. + * + * @param in the stream to read data from in order to restore the object + * @throws IOException if I/O errors occur + * @throws ClassNotFoundException If the class for an object being + * restored cannot be found. + */ + @Override + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + int len = in.readInt(); + byte[] bts = new byte[len]; + in.readFully(bts); + + this.bytes = bts; + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesBlob.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesBlob.java new file mode 100644 index 00000000..356b8a5e --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesBlob.java @@ -0,0 +1,5 @@ +package com.jd.blockchain.utils.io; + +public interface BytesBlob extends BytesWriter, BytesReader, BytesSerializable{ + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesChunkReader.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesChunkReader.java new file mode 100644 index 00000000..625d0048 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesChunkReader.java @@ -0,0 +1,162 @@ +package com.jd.blockchain.utils.io; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; + +import com.jd.blockchain.utils.IllegalDataException; + +/** + * 字节块读取; + * + * 执行与 {@link BytesChunkWriter} 的写入过程相对应的读取逻辑,分块读出内容; + * + * 在创建 BytesChunkReader 实例时,会先校验魔术字节,如果模式字节校验失败,将抛出 IllegalDataException ; + * + * 注:BytesChunkReader 不是线程安全的; + * + * @author haiq + * + */ +public class BytesChunkReader implements Closeable { + + private byte[] magicBytes; + + private InputStream in; + + /** + * 创建 BytesChunkReader 实例; + * + * 如果模式字节校验失败,将抛出 IllegalDataException ; + * + * @param magicString + * 魔术字符;将被以 UTF-8 编码转码为二进制字节参与校验; + * @param in + * 输入流; + * @throws IOException exception + */ + public BytesChunkReader(String magicString, InputStream in) throws IOException { + this.magicBytes = magicString.getBytes("UTF-8"); + this.in = in; + + checkMagic(); + } + + /** + * 创建 BytesChunkReader 实例; + * + * 如果模式字节校验失败,将抛出 IllegalDataException ; + * + * @param magicBytes + * 魔术字节; + * @param in + * 输入流; + * @throws IOException exception + */ + public BytesChunkReader(byte[] magicBytes, InputStream in) throws IOException { + this.magicBytes = magicBytes; + this.in = in; + + checkMagic(); + } + + private void checkMagic() throws IOException { + byte[] buff = new byte[magicBytes.length]; + int len = in.read(buff); + if (len <= 0) { + throw new IllegalDataException("No data to read!"); + } + if (len < magicBytes.length) { + throw new IllegalDataException("Mismatch magic bytes!"); + } + if (!BytesUtils.equals(magicBytes, buff)) { + throw new IllegalDataException("Mismatch magic bytes!"); + } + } + + /** + * 读取下一个数据块; + * @return 数据块;如果已经没有数据块可读,则返回 null,并关闭输入流; + * @throws IOException exception + */ + public byte[] read() throws IOException { + int len = readNextLengthHeader(); + if (len < 0) { + // No chunk; + return null; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int reallyLen = BytesUtils.copy(in, out, len); + if (reallyLen < len) { + throw new IllegalDataException( + "No enough data as the length header indicated to read from the input stream !"); + } + return out.toByteArray();// ByteArrayOutputStream is not necessary to + // close; + } + + /** + * 读取下一个数据块; + * @param out 输出流; + * @return 数据块;如果已经没有数据块可读,则返回 -1,并关闭输入流; + * @throws IOException exception + */ + public int read(OutputStream out) throws IOException { + int len = readNextLengthHeader(); + if (len < 0) { + // No chunk; + return -1; + } + int cpLen = BytesUtils.copy(in, out, len); + if (cpLen < len) { + throw new IllegalDataException( + "No enough data as the length header indicated to read from the input stream!"); + } + return len; + } + + /** + * 返回下一个长度头; + * + * 如果已到结尾,则返回 -1,并关闭输入流; + * + * @return + * @throws IOException + */ + private int readNextLengthHeader() throws IOException{ + int len = BytesUtils.readInt(in); + if (len <= 0) { + // No chunk; + close(); + return -1; + } + return len; + } + + @Override + public void close() throws IOException { + in.close(); + } + + public static byte[][] extract(byte[] magicBytes, byte[] compactedBytes) throws IOException{ + ByteArrayInputStream in = new ByteArrayInputStream(compactedBytes); + BytesChunkReader reader = new BytesChunkReader(magicBytes, in); + List dataBytesList; + try { + dataBytesList = new ArrayList(); + byte[] dataBytes = null; + while ((dataBytes = reader.read()) != null) { + dataBytesList.add(dataBytes); + } + } finally{ + reader.close(); + } + + return dataBytesList.toArray(new byte[dataBytesList.size()][]); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesChunkWriter.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesChunkWriter.java new file mode 100644 index 00000000..28016337 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesChunkWriter.java @@ -0,0 +1,157 @@ +package com.jd.blockchain.utils.io; + +import java.io.ByteArrayOutputStream; +import java.io.Closeable; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +/** + * 字节块写入; + * + * BytesChunkWriter 定义了魔术字节(magic bytes)作为字节块流的开端,写入在所有的内容之前; + * + * 在写入每一个字节块之前,先把块长度作为一个 4 字节的二进制数组写入,然后跟着写入块的内容; + * + * 当完成全部写入之后,写入 -1 的 4 字节二进制格式作为结尾标识; + * + * 注:BytesChunkReader 不是线程安全的; + * + * @author haiq + * + */ +public class BytesChunkWriter implements Closeable { + + private static byte[] END_BYTES = BytesUtils.toBytes(-1); + + private byte[] magicBytes; + + private OutputStream out; + + private boolean enclose = false; + + /** + * 创建一个 BytesChunkWriter 实例; + * + * @param magicString + * 魔术字符;作为字节块流的起始标识; + * @param out + * 要写入的流; + * @throws IOException exception + */ + public BytesChunkWriter(String magicString, OutputStream out) throws IOException { + try { + this.out = out; + this.magicBytes = magicString.getBytes("UTF-8"); + writeMagic(); + } catch (UnsupportedEncodingException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 创建一个 BytesChunkWriter 实例; + * + * @param magicBytes + * 魔术字符;作为字节块流的起始标识; + * @param out out + * @throws IOException exception + */ + public BytesChunkWriter(byte[] magicBytes, OutputStream out) throws IOException { + this.magicBytes = magicBytes; + this.out = out; + writeMagic(); + } + + private void writeMagic() throws IOException { + if (magicBytes.length == 0) { + throw new IllegalArgumentException("The magicBytes is empty!"); + } + out.write(magicBytes); + } + + /** + * 写入字节块; + * + * 在写入字节块之前先写入 4 个字节的块长度头,然后写入内容; + * + * @param bytes bytes + * @throws IOException exception + */ + public void write(byte[] bytes) throws IOException{ + checkEncosed(); + byte[] lenHeader = BytesUtils.toBytes(bytes.length); + out.write(lenHeader); + out.write(bytes); + } + + /** + * 从指定的流读入指定长度的块并写入流; + * + * 如果读入的长度不足指定的值,将抛出 IllegalArgumentException 异常; + * + * @param len 块长度; + * @param in 要读入数据的流; + * @throws IOException exception + */ + public void write(int len, InputStream in) throws IOException{ + if (len < 1) { + throw new IllegalArgumentException("The len must be positive!"); + } + byte[] lenHeader = BytesUtils.toBytes(len); + out.write(lenHeader); + int wrLen = BytesUtils.copy(in, out, len); + if (wrLen < len) { + throw new IllegalArgumentException("The length of the input stream is less than the specified len of chunk!"); + } + } + + public void flush() throws IOException{ + out.flush(); + } + + private void encloseChunk() throws IOException{ + if (enclose) { + return; + } + out.write(END_BYTES); + enclose = true; + } + + private void checkEncosed(){ + if (enclose) { + throw new IllegalStateException("This BytesChunkWriter instance is enclosed!"); + } + } + + /** + * 结束并关闭流; + * + * @throws IOException exception + */ + @Override + public void close() throws IOException { + if (!enclose) { + encloseChunk(); + flush(); + } + out.close(); + } + + public static byte[] compact(byte[] magicBytes, byte[]... dataBytes) throws IOException{ + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesChunkWriter writer = new BytesChunkWriter(magicBytes, out); + try { + if (dataBytes != null) { + for (byte[] bs : dataBytes) { + writer.write(bs);; + } + } + writer.flush(); + } finally{ + writer.close(); + } + return out.toByteArray(); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesDeserializable.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesDeserializable.java new file mode 100644 index 00000000..dff8ade1 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesDeserializable.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.utils.io; + +public interface BytesDeserializable { + + /** + * 以字节数组形式获取字节块的副本; + * @param bytes butes + */ + void fromBytes(byte[] bytes); + +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesEncoder.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesEncoder.java new file mode 100644 index 00000000..d4e6613c --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesEncoder.java @@ -0,0 +1,8 @@ +package com.jd.blockchain.utils.io; + +public interface BytesEncoder { + + byte[] encode(T data); + + T decode(byte[] bytes); +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesEncoding.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesEncoding.java new file mode 100644 index 00000000..a9ae8bad --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesEncoding.java @@ -0,0 +1,171 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class BytesEncoding { + + public static int writeInNormal(byte[] data, OutputStream out) { + return write(data, NumberMask.NORMAL, out); + } + + public static int writeInShort(byte[] data, OutputStream out) { + return write(data, NumberMask.SHORT, out); + } + + public static int writeInTiny(byte[] data, OutputStream out) { + return write(data, NumberMask.TINY, out); + } + + /** + * 写入字节数据; + * + * 先写入字节长度的头部,再写入数据; + * + * 如果字节数据的长度为 0,则只写入一个空的头部; + * + * @param data data + * @param dataLengthMask dataLengthMask + * @param out out + * @return 返回写入的字节数; + */ + public static int write(byte[] data, NumberMask dataLengthMask, OutputStream out) { + try { + int s = dataLengthMask.writeMask(data == null ? 0 : data.length, out); + if (data != null) { + out.write(data, 0, data.length); + s += data.length; + } + return s; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * getOutputSizeInNormal + * @param dataSize dataSize + * @return int + */ + public static int getOutputSizeInNormal(int dataSize) { + return NumberMask.NORMAL.getMaskLength(dataSize) + dataSize; + } + + public static int writeInNormal(byte[] data, byte[] buffer) { + return write(NumberMask.NORMAL, data, 0, buffer, 0, data.length); + } + + public static int writeInNormal(byte[] data, byte[] buffer, int bufferOffset) { + return write(NumberMask.NORMAL, data, 0, buffer, bufferOffset, data.length); + } + + public static int writeInNormal(byte[] data, byte[] buffer, int bufferOffset, int length) { + return write(NumberMask.NORMAL, data, 0, buffer, bufferOffset, length); + } + + public static int writeInNormal(byte[] data, int dataOffset, byte[] buffer, int bufferOffset, int length) { + return write(NumberMask.NORMAL, data, dataOffset, buffer, bufferOffset, length); + } + + public static int write(NumberMask dataLengthMask, byte[] data, byte[] buffer) { + return write(dataLengthMask, data, 0, buffer, 0, data.length); + } + + public static int write(NumberMask dataLengthMask, byte[] data, byte[] buffer, int bufferOffset) { + return write(dataLengthMask, data, 0, buffer, bufferOffset, data.length); + } + + public static int write(NumberMask dataLengthMask, byte[] data, byte[] buffer, int bufferOffset, int length) { + return write(dataLengthMask, data, 0, buffer, bufferOffset, length); + } + + public static int write(NumberMask dataLengthMask, byte[] data, int dataOffset, byte[] buffer, int bufferOffset, + int length) { + int s = dataLengthMask.writeMask(data == null ? 0 : data.length, buffer, bufferOffset); + bufferOffset += s; + if (data != null) { + System.arraycopy(data, dataOffset, buffer, bufferOffset, length); + s += length; + } + return s; + } + + public static byte[] readInTiny(byte[] buffer, int offset) { + return read(NumberMask.TINY, buffer, offset); + } + + public static byte[] readInShort(byte[] buffer, int offset) { + return read(NumberMask.SHORT, buffer, offset); + } + + public static byte[] readInNormal(byte[] buffer, int offset) { + return read(NumberMask.NORMAL, buffer, offset); + } + + public static byte[] read(NumberMask dataLengthMask, byte[] buffer, int offset) { + int size = dataLengthMask.resolveMaskedNumber(buffer, offset); + int maskLen = dataLengthMask.resolveMaskLength(buffer[offset]); + offset += maskLen; + byte[] data = new byte[size]; + System.arraycopy(buffer, offset, data, 0, size); + return data; + } + + public static int write(ByteArray data, NumberMask dataLengthMask, OutputStream out) { + int s = dataLengthMask.writeMask(data == null ? 0 : data.size(), out); + if (data != null) { + s += data.writeTo(out); + } + return s; + } + + public static byte[] readInTiny(InputStream in) { + return read(NumberMask.TINY, in); + } + + public static byte[] readInNormal(InputStream in) { + return read(NumberMask.NORMAL, in); + } + + public static byte[] readInShort(InputStream in) { + return read(NumberMask.SHORT, in); + } + + /** + * 读取头部和内容; + * 如果头部标识的数据长度为 0,则返回一个长度为 0 的字节数组; + * @param dataLengthMask dataLengthMask + * @param in in + * @return byte[] + */ + public static byte[] read(NumberMask dataLengthMask, InputStream in) { + try { + int size = dataLengthMask.resolveMaskedNumber(in); + if (size == 0) { + return BytesUtils.EMPTY_BYTES; + } + byte[] data = new byte[size]; + int len = in.read(data, 0, size); + if (len < size) { + throw new IllegalArgumentException("No enough bytes was read as the size header indicated!"); + } + return data; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * 读取头部和内容; + * @param headerMask headerMask + * @param in in + * @return byteArray + * @throws IOException exception + */ + public static ByteArray readAsByteArray(NumberMask headerMask, InputStream in) throws IOException { + byte[] data = read(headerMask, in); + return ByteArray.wrap(data); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesInputStream.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesInputStream.java new file mode 100644 index 00000000..e492c9d8 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesInputStream.java @@ -0,0 +1,240 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.InputStream; + +public class BytesInputStream extends InputStream { + + private byte[] bytes; + + private int posistion; + + private int tail; + + public BytesInputStream(byte[] bytes, int offset, int size) { + this.bytes = bytes; + this.posistion = offset; + this.tail = offset + size; + } + + protected int getPosition() { + return posistion; + } + + protected byte[] getOriginBytes() { + return bytes; + } + + /** + * 返回剩余的字节数; + * + * @return int + */ + public int getSize() { + return tail - posistion; + } + + /** + * 读取接下来的 1 个字节并返回; + * + * @return byte + */ + public byte readByte() { + int off = this.posistion; + checkBoundary(off, 1); + posistion++; + return bytes[off]; + } + + /** + * 读取接下来的 2 个字节并返回16位字符; + * + * @return char + */ + public char readChar() { + int off = posistion; + checkBoundary(off, 2); + char ch = BytesUtils.toChar(bytes, off); + posistion += 2; + return ch; + } + + /** + * 读取接下来的 2 个字节并返回16位整数; + * + * @return short + */ + public short readShort() { + int off = posistion; + checkBoundary(off, 2); + short n = BytesUtils.toShort(bytes, off); + posistion += 2; + return n; + } + + /** + * 读取接下来的 4 个字节并返回32位整数; + * + * @return int + */ + public int readInt() { + int off = posistion; + checkBoundary(off, 4); + int n = BytesUtils.toInt(bytes, off); + posistion += 4; + return n; + } + + /** + * 读取接下来的 8 个字节并返回64位整数; + * + * @return long + */ + public long readLong() { + int off = posistion; + checkBoundary(off, 8); + long n = BytesUtils.toLong(bytes, off); + posistion += 8; + return n; + } + + /** + * 读取指定数量的字节并返回对应的字符串; + * + * @param size size + * @return String + */ + public String readString(int size) { + int off = posistion; + checkBoundary(off, size); + String s = BytesUtils.toString(bytes, off, size); + posistion += size; + return s; + } + + /** + * 读取指定数量的字节并返回对应的字符串; + * @param size size + * @param charset charset + * @return String + */ + public String readString(int size, String charset) { + int off = posistion; + checkBoundary(off, size); + String s = BytesUtils.toString(bytes, off, size, charset); + posistion += size; + return s; + } + + /** + * 读取指定数量的字节并返回; + * + * @param size size + * @return 返回指定数量的字节拷贝; + */ + public byte[] readBytes(int size) { + byte[] copy = new byte[size]; + readBytes(copy, 0, size); + return copy; + } + + public int readBytes(byte[] buffer, int offset, int size) { + int off = posistion; + int s = tail - posistion; + s = s < size ? s : size; + checkBoundary(off, s); + System.arraycopy(bytes, off, buffer, offset, s); + posistion += s; + return s; + } + + /** + * 从当前位置开始读取, 返回剩余字节的片段;; + * 注:此操作不影响游标位置; + * @return BytesSlice + */ + public BytesSlice getSlice() { + if (tail == posistion) { + return BytesSlice.EMPTY; + } + return getSlice(tail - posistion); + } + + /** + * 从当前位置开始读取, 返回指定数量的字节片段; + * + *
+ * 注:此操作不影响游标位置; + * + * @param size size + * @return byteSlice + */ + public BytesSlice getSlice(int size) { + int off = posistion; + checkBoundary(off, size); + BytesSlice copy = new BytesSlice(bytes, off, size); + return copy; + } + + /** + * 从当前位置开始读取, 返回指定数量的字节片段;
+ * + * 注:此操作将数据游标位置向后移动指定数量的字节;; + * + * @param size size + * @return byteSlice + */ + public BytesSlice readSlice(int size) { + BytesSlice copy = getSlice(size); + posistion += size; + return copy; + } + + public void skip(int size) { + checkBoundary(posistion, size); + posistion += size; + } + + private void checkBoundary(int off, int len) { + // assert off >= posistion && off + len <= tail : "The accessing index is out of + // BytesInputStream's bounds!"; + if (off < posistion || off + len > tail) { + throw new IndexOutOfBoundsException("The accessing index is out of BytesInputStream's bounds!"); + } + } + + /** + * 注:遵循 JDK 接口要求,读取一个字节以 int 类型返回; + */ + @Override + public int read() throws IOException { + return readByte() & 0xFF; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + return readBytes(b, off, len); + } + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + @Override + public int available() throws IOException { + return getSize(); + } + + @Override + public long skip(long n) throws IOException { + if (n < 0) { + throw new IllegalArgumentException("Specified a negative number of bytes to be skipped!"); + } + if (n >= Integer.MAX_VALUE) { + throw new IllegalArgumentException("The number of bytes to be skipped is out of max value!"); + } + skip((int) n); + return n; + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesMap.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesMap.java new file mode 100644 index 00000000..21bf2afc --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesMap.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.utils.io; + +import java.util.Set; + +public interface BytesMap { + + Set keySet(); + + byte[] getValue(T key); +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesOutputBuffer.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesOutputBuffer.java new file mode 100644 index 00000000..6a9065ab --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesOutputBuffer.java @@ -0,0 +1,162 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * 字节输出缓冲区;
+ * + * 提供一种避免字节数组在内存复制的缓冲写入的实现;
+ * + * BytesOutputBuffer 是线程安全的; + * + * @author huanghaiquan + * + */ +public class BytesOutputBuffer { + + private static final int DEFAULT_CAPACITY = 8; + + private volatile int size = 0; + + private int capacity = 0; + + private int cursor = 0; + + private byte[][] buffers; + + public int getSize() { + return size; + } + + public BytesOutputBuffer() { + this(DEFAULT_CAPACITY); + } + + public BytesOutputBuffer(int initCapacity) { + if (initCapacity < 0) { + throw new IllegalArgumentException("Init capacity is negative!"); + } + this.capacity = initCapacity; + buffers = new byte[initCapacity][]; + } + + /** + * 直接写入;
+ * + * 此方法直接引用参数指定的数组作为缓冲区的组成部分,调用者需要避免在执行之后对参数引用的字节数组再做任何更改;
+ * + * 设计此方法的目的是提供一种通过避免字节数组复制操作来提升写入性能的方法; + * + * @param data data + */ + public synchronized void write(byte[] data) { + if (data == null) { + throw new IllegalArgumentException("data is null!"); + } + int idx = cursor; + if (idx == capacity) { + growCapacity(); + } + buffers[idx] = data; + cursor++; + size += data.length; + } + + private void growCapacity() { + int newCapacity = capacity + capacity / 2; + if (newCapacity == capacity) { + newCapacity++; + } + byte[][] newBuffers = new byte[newCapacity][]; + System.arraycopy(buffers, 0, newBuffers, 0, capacity); + buffers = newBuffers; + capacity = newCapacity; + } + + /** + * @param buffer buffer + */ + public void write(BytesOutputBuffer buffer) { + byte[][] fromBuffers = buffer.buffers; + for (int i = 0; i < buffer.cursor; i++) { + write(fromBuffers[i]); + } + } + + /** + * 复制写入;
+ * + * 此方法复制参数指定的数组的一个副本作为写入缓冲区的组成部分; + * + * @param data data + */ + public void writeCopy(byte[] data) { + int len = data.length; + byte[] copy = new byte[len]; + System.arraycopy(data, 0, copy, 0, len); + write(copy); + } + + /** + * * 复制写入;
+ * + * 此方法复制参数指定的数组的一个副本作为写入缓冲区的组成部分; + * @param data data + * @param offset offset + * @param len len + */ + public void writeCopy(byte[] data, int offset, int len) { + byte[] copy = new byte[len]; + System.arraycopy(data, offset, copy, 0, len); + write(copy); + } + + /** + * 把结果输出到指定的缓冲区,并返回写入的长度; + *

+ * 如果指定的缓冲区的空间足够,则将写入全部数据,返回值等于 {@link #getSize()} ;
+ * 如果指定的缓冲区的空间足够,则写满为止,返回实际写入的数据大小;
+ * + * 调用者可以把返回值与 {@link #getSize()} 比较,以判断是否完成写入; + * + * @param outBuffer outBuffer + * @param offset offset + * @return int + */ + public synchronized int writeTo(byte[] outBuffer, int offset) { + int len = Math.min(size, outBuffer.length - offset); + int t = len; + int s; + for (int i = 0; i < cursor & t > 0; i++) { + s = Math.min(buffers[i].length, t); + System.arraycopy(buffers[i], 0, outBuffer, offset, s); + offset += s; + t -= s; + } + return len; + } + + public synchronized int writeTo(OutputStream out) { + try { + for (int i = 0; i < cursor; i++) { + out.write(buffers[i]); + } + return size; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * 返回所有内容的副本; + * + * @return byte array; + */ + public byte[] toBytes() { + byte[] data =new byte[size]; + writeTo(data, 0); + return data; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesReader.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesReader.java new file mode 100644 index 00000000..46bfbb50 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesReader.java @@ -0,0 +1,10 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.InputStream; + +public interface BytesReader { + + void resolvFrom(InputStream in) throws IOException; + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSerializable.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSerializable.java new file mode 100644 index 00000000..8b2f656e --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSerializable.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.utils.io; + +public interface BytesSerializable { + + /** + * 以字节数组形式获取字节块的副本; + * @return byte[] + */ + byte[] toBytes(); + +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSlice.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSlice.java new file mode 100644 index 00000000..24ef4191 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSlice.java @@ -0,0 +1,193 @@ +package com.jd.blockchain.utils.io; + +/** + * @author huanghaiquan + * + */ +public class BytesSlice implements BytesSerializable { + + public static final BytesSlice EMPTY = new BytesSlice(new byte[0], 0, 0); + + private byte[] bytes; + + private int dataOffset; + + private int size; + + public BytesSlice() { + } + + public BytesSlice(byte[] bytes) { + this(bytes, 0, bytes.length); + } + + public BytesSlice(byte[] bytes, int offset) { + this(bytes, offset, bytes.length - offset); + } + + public BytesSlice(byte[] bytes, int offset, int size) { + if (offset + size > bytes.length) { + throw new IndexOutOfBoundsException(); + } + this.bytes = bytes; + this.dataOffset = offset; + this.size = size; + } + + public boolean isEmpty() { + return size == 0; + } + + public int getSize() { + return size; + } + + /** + * 返回首个字节; + * + * @return byte + */ + public byte getByte() { + return getByte(0); + } + + /** + * 返回首个字节; + * @param offset offset + * @return byte + */ + public byte getByte(int offset) { + int off = this.dataOffset + offset; + checkBoundary(off, 1); + return bytes[off]; + } + + public char getChar() { + return getChar(0); + } + + public char getChar(int offset) { + int off = this.dataOffset + offset; + checkBoundary(off, 2); + return BytesUtils.toChar(bytes, off); + } + + public short getShort() { + return getShort(0); + } + + public short getShort(int offset) { + int off = this.dataOffset + offset; + checkBoundary(off, 2); + return BytesUtils.toShort(bytes, off); + } + + public int getInt() { + return getInt(0); + } + + /** + * 从指定的偏移量开始,读取连续 4 个字节,转换为 int 类型的数值返回; + * + * @param offset offset + * @return int + */ + public int getInt(int offset) { + int off = this.dataOffset + offset; + checkBoundary(off, 4); + return BytesUtils.toInt(bytes, off); + } + + public long getLong() { + return getLong(0); + } + + /** + * 从指定的偏移量开始,读取连续 8 个字节,转换为 long 类型的数值返回; + * + * @param offset offset + * @return long + */ + public long getLong(int offset) { + int off = this.dataOffset + offset; + checkBoundary(off, 8); + return BytesUtils.toLong(bytes, off); + } + + public String getString() { + return BytesUtils.toString(bytes, dataOffset, size); + } + + public BytesInputStream getInputStream() { + return getInputStream(0); + } + + public BytesInputStream getInputStream(int offset) { + int off = this.dataOffset + offset; + int s = size; + checkBoundary(off, s); + return new BytesInputStream(bytes, off, s); + } + + // public InputStream asInputStream() { + // return new ByteArrayInputStream(bytes, dataOffset, size); + // } + + public byte[] getBytesCopy() { + if (size == 0) { + return BytesUtils.EMPTY_BYTES; + } + byte[] copy = new byte[size]; + System.arraycopy(bytes, dataOffset, copy, 0, size); + return copy; + } + + public byte[] getBytesCopy(int offset) { + return getBytesCopy(offset, getSize() - offset) ; + } + + public byte[] getBytesCopy(int offset, int size) { + int newOffset = dataOffset + offset; + checkBoundary(newOffset, size); + + if (size == 0) { + return BytesUtils.EMPTY_BYTES; + } + byte[] copy = new byte[size]; + System.arraycopy(bytes, newOffset, copy, 0, size); + return copy; + } + + protected byte[] getOriginBytes() { + return bytes; + } + + protected int getOriginOffset() { + return dataOffset; + } + + protected void checkBoundary(int offset, int len) { + // assert offset >= dataOffset + // && offset + len <= dataOffset + this.size : "The accessing index is out of + // BytesSlice's bounds!"; + if (offset < dataOffset || offset + len > dataOffset + this.size) { + throw new IndexOutOfBoundsException("The accessing index is out of BytesSlice's bounds!"); + } + } + + public BytesSlice getSlice(int offset) { + return getSlice(offset, getSize() - offset); + } + + public BytesSlice getSlice(int offset, int size) { + int newOffset = dataOffset + offset; + checkBoundary(newOffset, size); + return new BytesSlice(bytes, newOffset, size); + } + + @Override + public byte[] toBytes() { + return getBytesCopy(); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSliceArrayWrapper.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSliceArrayWrapper.java new file mode 100644 index 00000000..0774c6d2 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSliceArrayWrapper.java @@ -0,0 +1,48 @@ +package com.jd.blockchain.utils.io; + +public class BytesSliceArrayWrapper implements BytesSlices { + + private BytesSlice slice; + + /** + * @param dataBytes + * 数据; + * @param offset + * 初始的偏移量; + * @param dataOffset + * 数据的起始偏移量; + * @param count + * 数据片段的总数; + * @param size + * 单个数据片段的大小; + */ + private BytesSliceArrayWrapper(BytesSlice slice) { + this.slice = slice; + } + + @Override + public int getTotalSize() { + return slice.getSize(); + } + + @Override + public int getCount() { + return 1; + } + + @Override + public BytesSlice getDataSlice(int id) { + if (id != 0) { + throw new IndexOutOfBoundsException("The specified idx is out of bound!"); + } + return slice; + } + + public static BytesSlices wrap(BytesSlice slice) { + if (slice instanceof BytesSlices) { + return (BytesSlices)slice; + } + return new BytesSliceArrayWrapper(slice); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSlices.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSlices.java new file mode 100644 index 00000000..2ac975ec --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesSlices.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.utils.io; + +public interface BytesSlices { + + /** + * 总的字节数;包含各个子数据片段之间间隔的头部字节; + * @return int + */ + int getTotalSize(); + + /** + * 包含的子片段的数量; + * + * @return int + */ + int getCount(); + + /** + * 返回一个子数据片段; + * @param idx 子数据片段的编号;大于等于 0 ,小于总数 {@link #getCount()}; + * @return bytesSlice + */ + BytesSlice getDataSlice(int idx); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java new file mode 100644 index 00000000..bc740a3c --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesUtils.java @@ -0,0 +1,630 @@ +package com.jd.blockchain.utils.io; + +import java.io.*; + +import com.jd.blockchain.utils.IllegalDataException; + +/** + * 二进制工具类; + * + * @author haiq + * + */ +public class BytesUtils { + + public static final String DEFAULT_CHARSET = "UTF-8"; + + public static final byte[] EMPTY_BYTES = {}; + + public static final int MAX_BUFFER_SIZE = 1024 * 1024 * 1024; + public static final int BUFFER_SIZE = 64; + + private BytesUtils() { + } + + /** + * 比较指定的两个字节数组是否一致; + *

+ * + * 此方法不处理两者其中之一为 null 的情形,因为无法定义相等性,所以将引发 {@link NullPointerException} 异常; + * + * @param bytes1 bytes1 + * @param bytes2 bytes2 + * @return boolean + */ + public static boolean equals(byte[] bytes1, byte[] bytes2) { + if (bytes1 == bytes2) { + return true; + } + if (bytes1.length != bytes2.length) { + return false; + } + for (int i = 0; i < bytes1.length; i++) { + if (bytes1[i] != bytes2[i]) { + return false; + } + } + return true; + } + + public static byte[] toBytes(BytesWriter bytesWriter) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + bytesWriter.writeTo(out); + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + + } + + /** + * 将输入流的所有内容都读入到字节数组返回; + * 如果输入流的长度超出 MAX_BUFFER_SIZE 定义的值,则抛出 IllegalArgumentException ; + * @param in in + * @return byte[] + */ + public static byte[] copyToBytes(InputStream in) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] buffer = new byte[BUFFER_SIZE]; + int len = 0; + long size = 0; + while ((len = in.read(buffer)) > 0) { + size += len; + if (size > MAX_BUFFER_SIZE) { + throw new IllegalArgumentException( + "The size of the InputStream exceed the max buffer size [" + MAX_BUFFER_SIZE + "]!"); + } + out.write(buffer, 0, len); + } + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static int copy(InputStream in, OutputStream out) throws IOException { + return copy(in, out, Integer.MAX_VALUE); + } + + /** + * 将输入流复制到输出流; + * + * @param in + * 输入流; + * @param out + * 输出流; + * @param maxSize + * 最大字节大小; + * @return 返回实际复制的字节数; + * @throws IOException exception + */ + public static int copy(InputStream in, OutputStream out, int maxSize) throws IOException { + byte[] buffer = new byte[BUFFER_SIZE]; + int len = 0; + int left = maxSize; + int readLen = buffer.length; + while (left > 0) { + readLen = Math.min(left, buffer.length); + len = in.read(buffer, 0, readLen); + if (len > 0) { + out.write(buffer, 0, len); + left = left - len; + } else { + break; + } + } + return maxSize - left; + } + + /** + * 将 int 值转为4字节的二进制数组; + * + * @param value value + * @return 转换后的二进制数组,高位在前,低位在后; + */ + public static byte[] toBytes(int value) { + byte[] bytes = new byte[4]; + toBytes(value, bytes, 0); + return bytes; + } + + public static byte[] toBytes(short value) { + byte[] bytes = new byte[2]; + toBytes(value, bytes, 0); + return bytes; + } + + /** + * 将 long 值转为8字节的二进制数组; + * + * @param value value + * @return 转换后的二进制数组,高位在前,低位在后; + */ + public static byte[] toBytes(long value) { + byte[] bytes = new byte[8]; + toBytes(value, bytes, 0); + return bytes; + } + + /** + * 将 int 值转为4字节的二进制数组; + * + * @param value + * 要转换的int整数; + * @param bytes + * 要保存转换结果的二进制数组;转换结果将从高位至低位的顺序写入数组从 0 开始的4个元素; + */ + public static void toBytes(short value, byte[] bytes) { + toBytes(value, bytes, 0); + } + + public static int toBytes(int value, byte[] bytes) { + return toBytes(value, bytes, 0); + } + + /** + * 将 int 值转为4字节的二进制数组; + * + * @param value + * 要转换的int整数; + * @param bytes + * 要保存转换结果的二进制数组;转换结果将从高位至低位的顺序写入数组从 offset 指定位置开始的4个元素; + * @param offset + * 写入转换结果的起始位置; + * @return 返回写入的长度; + */ + public static int toBytes(int value, byte[] bytes, int offset) { + bytes[offset] = (byte) ((value >>> 24) & 0x00FF); + bytes[offset + 1] = (byte) ((value >>> 16) & 0x00FF); + bytes[offset + 2] = (byte) ((value >>> 8) & 0x00FF); + bytes[offset + 3] = (byte) (value & 0x00FF); + return 4; + } + +// public static int toBytes(int value, OutputStream out) { +// try { +// out.write((value >>> 24) & 0x00FF); +// out.write((value >>> 16) & 0x00FF); +// out.write((value >>> 8) & 0x00FF); +// out.write(value & 0x00FF); +// return 4; +// } catch (IOException e) { +// throw new RuntimeIOException(e.getMessage(), e); +// } +// } + + public static void toBytes(short value, byte[] bytes, int offset) { + bytes[offset] = (byte) ((value >>> 8) & 0x00FF); + bytes[offset + 1] = (byte) (value & 0x00FF); + } + + + public static void toBytes(char value, byte[] bytes, int offset) { + bytes[offset] = (byte) ((value >>> 8) & 0x00FF); + bytes[offset + 1] = (byte) (value & 0x00FF); + } + + /** + * 将 long 值转为8字节的二进制数组; + * + * @param value + * 要转换的long整数; + * @param bytes + * 要保存转换结果的二进制数组;转换结果将从高位至低位的顺序写入数组从 offset 指定位置开始的8个元素; + * @param offset + * 写入转换结果的起始位置; + * @return 返回写入的长度; + */ + public static int toBytes(long value, byte[] bytes, int offset) { + bytes[offset] = (byte) ((value >>> 56) & 0x00FF); + bytes[offset + 1] = (byte) ((value >>> 48) & 0x00FF); + bytes[offset + 2] = (byte) ((value >>> 40) & 0x00FF); + bytes[offset + 3] = (byte) ((value >>> 32) & 0x00FF); + bytes[offset + 4] = (byte) ((value >>> 24) & 0x00FF); + bytes[offset + 5] = (byte) ((value >>> 16) & 0x00FF); + bytes[offset + 6] = (byte) ((value >>> 8) & 0x00FF); + bytes[offset + 7] = (byte) (value & 0x00FF); + return 8; + } + +// public static int toBytes(long value, OutputStream out) { +// try { +// out.write((int) ((value >>> 56) & 0x00FF)); +// out.write((int) ((value >>> 48) & 0x00FF)); +// out.write((int) ((value >>> 40) & 0x00FF)); +// out.write((int) ((value >>> 32) & 0x00FF)); +// out.write((int) ((value >>> 24) & 0x00FF)); +// out.write((int) ((value >>> 16) & 0x00FF)); +// out.write((int) ((value >>> 8) & 0x00FF)); +// out.write((int) (value & 0x00FF)); +// return 8; +// } catch (IOException e) { +// throw new RuntimeIOException(e.getMessage(), e); +// } +// } + + public static byte[] toBytes(String str) { + return toBytes(str, DEFAULT_CHARSET); + } + + public static byte[] toBytes(String str, String charset) { + try { + byte[] bytes = str.getBytes(charset); + return bytes; + } catch (UnsupportedEncodingException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static String toString(byte[] bytes) { + return toString(bytes, DEFAULT_CHARSET); + } + + public static String toString(byte[] bytes, int offset) { + return toString(bytes, offset, bytes.length - offset, DEFAULT_CHARSET); + } + + public static String toString(byte[] bytes, int offset, int len) { + return toString(bytes, offset, len, DEFAULT_CHARSET); + } + + public static String toString(byte[] bytes, String charset) { + return toString(bytes, 0, bytes.length, charset); + } + + public static String toString(byte[] bytes, int offset, int len, String charset) { + try { + if (bytes == null) { + return null; + } + if (len == 0) { + return ""; + } + return new String(bytes, offset, len, charset); + } catch (UnsupportedEncodingException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * 按从高位到低位的顺序将指定二进制数组从位置 0 开始的 4 个字节转换为 int 整数; + * + * @param bytes + * 要转换的二进制数组; + * @return 转换后的 int 整数; + */ + public static int toInt(byte[] bytes) { + return toInt(bytes, 0); + // value = (value | (bytes[0] & 0xFF)) << 8; + // value = (value | (bytes[1] & 0xFF)) << 8; + // value = (value | (bytes[2] & 0xFF)) << 8; + // value = value | (bytes[3] & 0xFF); + // + // return value; + } + + /** + * 按从高位到低位的顺序将指定二进制数组从 offset 参数指定的位置开始的 2 个字节转换为 short 整数; + * + * @param bytes + * 要转换的二进制数组; + * @param offset + * 要读取数据的开始位置 + * @return 转换后的 short 整数; + */ + public static short toShort(byte[] bytes, int offset) { + short value = 0; + value = (short) ((value | (bytes[offset] & 0xFF)) << 8); + value = (short) (value | (bytes[offset + 1] & 0xFF)); + + return value; + } + + + public static char toChar(byte[] bytes, int offset) { + char value = 0; + value = (char) ((value | (bytes[offset] & 0xFF)) << 8); + value = (char) (value | (bytes[offset + 1] & 0xFF)); + + return value; + } + + + + /** + * 按从高位到低位的顺序将指定二进制数组从 offset 参数指定的位置开始的 4 个字节转换为 int 整数; + * + * @param bytes + * 要转换的二进制数组; + * @param offset + * 要读取数据的开始位置 + * @return 转换后的 int 整数; + */ + public static int toInt(byte[] bytes, int offset) { + // int value = 0; + // value = (value | (bytes[offset] & 0xFF)) << 8; + // value = (value | (bytes[offset + 1] & 0xFF)) << 8; + // value = (value | (bytes[offset + 2] & 0xFF)) << 8; + // value = value | (bytes[offset + 3] & 0xFF); + // + // return value; + return toInt(bytes, offset, 4); + } + + /** + * 按从高位到低位的顺序将指定二进制数组从 offset 参数指定的位置开始的 4 个字节转换为 int 整数; + * + * @param bytes + * 要转换的二进制数组; + * @param offset + * 要读取数据的开始位置 + * @return 转换后的 int 整数; + * + * @param len + * 长度;len 必须满足: len 大于等于 1 且小于等于4; + * @return 转换后的 int 整数; + */ + public static int toInt(byte[] bytes, int offset, int len) { + // if (len < 1 || len > 4) { + // throw new IllegalArgumentException("Len less than 1 or greate than 4!"); + // } + // int value = 0; + // for (int i = 0; i < len; i++) { + // value = value | ((bytes[offset + i] & 0xFF) << (8 * (3 - i))); + // } + // + // return value; + return toInt(bytes, offset, len, true); + } + + /** + * 按从高位到低位的顺序将指定二进制数组从 offset 参数指定的位置开始的 4 个字节转换为 int 整数; + * + * @param bytes + * 要转换的二进制数组; + * @param offset + * 要读取数据的开始位置 + * @return 转换后的 int 整数; + * + * @param len + * 长度;len 必须满足: len 大于等于 1 且小于等于4; + * @param highAlign + * 是否高位对齐;
+ * true 表示参数 bytes 的首个字节对应为整数的最高8位;
+ * false 表示参数 bytes 的最后字节对应为整数的最低8位; + * @return 转换后的 int 整数; + */ + public static int toInt(byte[] bytes, int offset, int len, boolean highAlign) { + if (len < 1 || len > 4) { + throw new IllegalArgumentException("Len less than 1 or greate than 4!"); + } + int value = 0; + if (highAlign) { + for (int i = 0; i < len; i++) { + value = value | ((bytes[offset + i] & 0xFF) << (8 * (3 - i))); + } + } else { + for (int i = 0; i < len; i++) { + value = value | ((bytes[offset + i] & 0xFF) << (8 * (len - 1 - i))); + } + } + + return value; + } + + public static long toLong(byte[] bytes) { + return toLong(bytes, 0); + } + + /** + * 按从高位到低位的顺序将指定二进制数组从 offset 参数指定的位置开始的 8个字节转换为 long 整数; + * + * @param bytes + * 要转换的二进制数组; + * @param offset + * 要读取数据的开始位置 + * @return 转换后的 long 整数; + */ + public static long toLong(byte[] bytes, int offset) { + long value = 0; + value = (value | (bytes[offset] & 0xFF)) << 8; + value = (value | (bytes[offset + 1] & 0xFF)) << 8; + value = (value | (bytes[offset + 2] & 0xFF)) << 8; + value = (value | (bytes[offset + 3] & 0xFF)) << 8; + value = (value | (bytes[offset + 4] & 0xFF)) << 8; + value = (value | (bytes[offset + 5] & 0xFF)) << 8; + value = (value | (bytes[offset + 6] & 0xFF)) << 8; + value = value | (bytes[offset + 7] & 0xFF); + + return value; + } + + /** + * 从指定的输入流中读入4个字节,由前到后按由高位到低位的方式转为 int 整数; + * @param in in + * @return int + */ + public static int readInt(InputStream in) { + try { + int value = 0; + for (int i = 0; i < 4; i++) { + value = value | ((in.read() & 0xFF) << (8 * (3 - i))); + } + return value; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static int writeInt(int value, OutputStream out) { + // byte[] bytes = toBytes(value); + // try { + // out.write(bytes); + // } catch (IOException e) { + // throw new RuntimeIOException(e.getMessage(), e); + // } + + try { + out.write((value >>> 24) & 0x00FF); + out.write((value >>> 16) & 0x00FF); + out.write((value >>> 8) & 0x00FF); + out.write(value & 0x00FF); + return 4; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static long readLong(InputStream in) { + // try { + // byte[] buf = new byte[8]; + // if (in.read(buf) < 8) { + // throw new IllegalDataException( + // "No enough data to read as long integer from the specified input stream!"); + // } + // return toLong(buf); + // } catch (IOException e) { + // throw new RuntimeIOException(e.getMessage(), e); + // } + + try { + long value = 0; + int v; + for (int i = 0; i < 7; i++) { + v = in.read(); + if (v < 0) { + throw new IllegalDataException( + "No enough data to read as long integer from the specified input stream!"); + } + value = (value | (v & 0xFF)) << 8; + } + value = value | (in.read() & 0xFF); + + return value; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static int writeLong(long value, OutputStream out) { + // byte[] bytes = toBytes(value); + // try { + // out.write(bytes); + // } catch (IOException e) { + // throw new RuntimeIOException(e.getMessage(), e); + // } + + try { + out.write((int) ((value >>> 56) & 0x00FF)); + out.write((int) ((value >>> 48) & 0x00FF)); + out.write((int) ((value >>> 40) & 0x00FF)); + out.write((int) ((value >>> 32) & 0x00FF)); + out.write((int) ((value >>> 24) & 0x00FF)); + out.write((int) ((value >>> 16) & 0x00FF)); + out.write((int) ((value >>> 8) & 0x00FF)); + out.write((int) (value & 0x00FF)); + return 8; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static byte readByte(InputStream in) { + try { + int value = in.read(); + if (value < 0) { + throw new IllegalDataException("No byte to read from the input stream!"); + } + return (byte) value; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static byte[] readBytes(InputStream in) { + ByteArrayOutputStream outStream = new ByteArrayOutputStream(); + try { + byte[] buffer = new byte[BUFFER_SIZE]; + int len = -1; + while ((len = in.read(buffer)) != -1) { + outStream.write(buffer, 0, len); + } + outStream.close(); + return outStream.toByteArray(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static void writeByte(byte value, OutputStream out) { + try { + out.write(value); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static byte[] concat(byte[]... bytesList) { + int size = 0; + for (byte[] bs : bytesList) { + size += bs.length; + } + byte[] bytesAll = new byte[size]; + size = 0; + for (byte[] bs : bytesList) { + System.arraycopy(bs, 0, bytesAll, size, bs.length); + size += bs.length; + } + + return bytesAll; + } + + public static long toLong(ByteArray byteArray) { + return toLong(byteArray.bytes()); + } + + /** + * 从字节数组获取对象 + * + * @param objBytes objBytes + * @return object + * @throws Exception exception + */ +// public static Object getObjectFromBytes(byte[] objBytes) throws Exception { +// if (objBytes == null || objBytes.length == 0) { +// return null; +// } +// ByteArrayInputStream bi = new ByteArrayInputStream(objBytes); +// ObjectInputStream oi = new ObjectInputStream(bi); +// return oi.readObject(); +// } + + /** + * 从对象获取一个字节数组; + * + * @param obj obj + * @return byte array + * @throws Exception exception + */ + public static byte[] getBytesFromObject(Object obj) throws Exception { + if (obj == null) { + return null; + } + ByteArrayOutputStream bo = new ByteArrayOutputStream(); + ObjectOutputStream oo = new ObjectOutputStream(bo); + oo.writeObject(obj); + return bo.toByteArray(); + } + + public static boolean startsWith(byte[] srcBytes, byte[] prefixBytes) { + for (int i = 0; i < prefixBytes.length; i++) { + if (prefixBytes[i] != srcBytes[i]) { + return false; + } + } + return true; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesWriter.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesWriter.java new file mode 100644 index 00000000..83248269 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/BytesWriter.java @@ -0,0 +1,8 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.OutputStream; + +public interface BytesWriter { + void writeTo(OutputStream out) throws IOException; +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/DynamicBytesSliceArray.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/DynamicBytesSliceArray.java new file mode 100644 index 00000000..a1580616 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/DynamicBytesSliceArray.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.utils.io; + +public class DynamicBytesSliceArray implements BytesSlices { + + private byte[] dataBytes; + + private int totalSize; + + private int[] offsets; + + private int[] sizes; + + private DynamicBytesSliceArray(byte[] dataBytes, int totalSize, int[] offsets, int[] sizes) { + this.dataBytes = dataBytes; + this.totalSize = totalSize; + this.offsets = offsets; + this.sizes = sizes; + } + + @Override + public int getTotalSize() { + return totalSize; + } + + @Override + public int getCount() { + return sizes.length; + } + + @Override + public BytesSlice getDataSlice(int idx) { + return new BytesSlice(dataBytes, offsets[idx], sizes[idx]); + } + +// public static DynamicBytesSliceArray resolve(byte[] dataBytes, int offset) { +// return resolve(new BytesSlice(dataBytes, 0, dataBytes.length), 0); +// } + + public static DynamicBytesSliceArray resolve(BytesInputStream bytesStream) { + int p1 = bytesStream.getPosition(); + int count = NumberMask.NORMAL.resolveMaskedNumber(bytesStream); + + int[] offsets = new int[count]; + int[] sizes = new int[count]; + + int size; + for (int i = 0; i < count; i++) { + size = NumberMask.NORMAL.resolveMaskedNumber(bytesStream); + sizes[i] = size; + offsets[i] = bytesStream.getPosition(); + bytesStream.skip(size); + } + int totalSize = bytesStream.getPosition() - p1; + return new DynamicBytesSliceArray(bytesStream.getOriginBytes(), totalSize, offsets, sizes); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/EmptyInputStream.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/EmptyInputStream.java new file mode 100644 index 00000000..26992c72 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/EmptyInputStream.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.InputStream; + +public class EmptyInputStream extends InputStream{ + + public static final EmptyInputStream INSTANCE = new EmptyInputStream(); + + private EmptyInputStream() { + } + + @Override + public int read() throws IOException { + return -1; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/FileUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/FileUtils.java new file mode 100644 index 00000000..1d82d10d --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/FileUtils.java @@ -0,0 +1,449 @@ +package com.jd.blockchain.utils.io; + +import java.io.*; +import java.util.ArrayList; +import java.util.Properties; + +/** + * @author haiq + * + */ +public class FileUtils { + + public static final String DEFAULT_CHARSET = "UTF-8"; + + public static boolean existFile(String filePath) { + File file = new File(filePath); + return file.isFile(); + } + + public static boolean existDirectory(String dir) { + File file = new File(dir); + return file.isDirectory(); + } + + public static boolean makeDirectory(String dir) { + File file = new File(dir); + return file.mkdirs(); + } + + /** + * 返回完整的绝对路径; + * + * @param path path + * @return String + */ + public static String getFullPath(String path) { + try { + File file = new File(path); + return file.getCanonicalPath(); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 以默认字符集(UTF-8)读取指定文件的首行; + * + * @param file file + * @return String + * @throws IOException exception + */ + public static String readFirstLine(File file) throws IOException { + return readFirstLine(file, DEFAULT_CHARSET); + } + + /** + * 读取指定文件的首行; + * + * @param file file + * @param charset + * 字符集; + * @return 返回首行非空行;返回结果不会自动截取两头的空字符串; + * @throws IOException exception + */ + public static String readFirstLine(File file, String charset) throws IOException { + FileInputStream in = new FileInputStream(file); + try { + InputStreamReader reader = new InputStreamReader(in, charset); + return getFirstLine(reader); + } finally { + in.close(); + } + } + + public static String[] readLines(File file) { + return readLines(file, DEFAULT_CHARSET); + } + + /** + * 返回指定文件的所有行; + * + * @param file file + * @param charset + * 字符集; + * @return 返回首行非空行;返回结果不会自动截取两头的空字符串; + */ + public static String[] readLines(File file, String charset) { + try (FileInputStream in = new FileInputStream(file); + InputStreamReader reader = new InputStreamReader(in, charset);) { + return getLines(reader); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static void writeLines(String[] lines, File file) { + writeLines(lines, file, DEFAULT_CHARSET); + } + + public static void writeLines(String[] lines, File file, String charset) { + try (FileOutputStream out = new FileOutputStream(file, false); + OutputStreamWriter writer = new OutputStreamWriter(out, charset); + BufferedWriter bfw = new BufferedWriter(writer);) { + for (String line : lines) { + writer.write(line); + writer.write("\r\n"); + } + + bfw.flush(); + writer.flush(); + out.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static String getFirstLine(Reader reader) throws IOException { + BufferedReader bfr = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + try { + String line = null; + while ((line = bfr.readLine()) != null) { + return line; + } + return null; + } finally { + bfr.close(); + } + } + + public static String[] getLines(Reader reader) throws IOException { + BufferedReader bfr = reader instanceof BufferedReader ? (BufferedReader) reader : new BufferedReader(reader); + try { + ArrayList lines = new ArrayList(); + String line = null; + while ((line = bfr.readLine()) != null) { + lines.add(line); + } + return lines.toArray(new String[lines.size()]); + } finally { + bfr.close(); + } + } + + /** + * 以默认字符集(UTF-8)将指定的文本保存到指定的文件中; + * + * @param file + * 要保存的文件; + * @param text + * 文本内容; + */ + public static void writeText(String text, File file) { + writeText(text, file, DEFAULT_CHARSET); + } + + /** + * 将指定的文本保存到指定的文件中; + * @param text + * 文本内容; + * @param file + * 要保存的文件; + * @param charset + * 字符集; + */ + public static void writeText(String text, File file, String charset) { + try (FileOutputStream out = new FileOutputStream(file, false)) { + writeText(text, out, charset); + out.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static void writeBytes(byte[] content, File file) { + try (FileOutputStream out = new FileOutputStream(file, false)) { + out.write(content); + out.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static void appendBytes(byte[] content, File file) { + try (FileOutputStream out = new FileOutputStream(file, true)) { + out.write(content); + out.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static void writeText(String text, OutputStream out, String charset) { + try (OutputStreamWriter writer = new OutputStreamWriter(out, charset);) { + writer.write(text); + writer.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static void writeProperties(Properties props, OutputStream out) { + writeProperties(props, out, DEFAULT_CHARSET); + } + + public static void writeProperties(Properties props, OutputStream out, String charset) { + try (OutputStreamWriter writer = new OutputStreamWriter(out, charset);) { + props.store(writer, null); + writer.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * 以默认字符集(UTF-8)从文件读取文本; + * @param file file + * @return String + */ + public static String readText(String file) { + return readText(new File(file), DEFAULT_CHARSET); + } + + /** + * 从文件读取文本; + * @param file file + * @param charset charset + * @return String + */ + public static String readText(String file, String charset) { + return readText(new File(file), charset); + } + + /** + * 以默认字符集(UTF-8)从文件读取文本; + * + * @param file file + * @return String + */ + public static String readText(File file) { + return readText(file, DEFAULT_CHARSET); + } + + /** + * 从文件读取文本; + * + * @param file file + * @param charset charset + * @return String + */ + public static String readText(File file, String charset) { + try { + FileInputStream in = new FileInputStream(file); + try { + return readText(in, charset); + } finally { + in.close(); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static String readText(InputStream in) throws IOException { + return readText(in, DEFAULT_CHARSET); + } + + /** + * 从流读取文本; + * + * @param in in + * @param charset charset + * @return String + * @throws IOException exception + */ + public static String readText(InputStream in, String charset) throws IOException { + InputStreamReader reader = new InputStreamReader(in, charset); + try { + StringBuilder content = new StringBuilder(); + char[] buffer = new char[64]; + int len = 0; + while ((len = reader.read(buffer)) > 0) { + content.append(buffer, 0, len); + } + return content.toString(); + } finally { + reader.close(); + } + } + + public static byte[] readBytes(String file) { + try { + FileInputStream in = new FileInputStream(file); + try { + return BytesUtils.copyToBytes(in); + } finally { + in.close(); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static byte[] readBytes(File file) { + try { + FileInputStream in = new FileInputStream(file); + try { + return BytesUtils.copyToBytes(in); + } finally { + in.close(); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static Properties readProperties(String systemConfig) { + return readProperties(systemConfig, DEFAULT_CHARSET); + } + + public static Properties readProperties(String file, String charset) { + try { + FileInputStream in = new FileInputStream(file); + try { + return readProperties(in, charset); + } finally { + in.close(); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static Properties readProperties(File file, String charset) { + try { + FileInputStream in = new FileInputStream(file); + try { + return readProperties(in, charset); + } finally { + in.close(); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static Properties readProperties(InputStream in) { + return readProperties(in, DEFAULT_CHARSET); + } + + public static Properties readProperties(InputStream in, String charset) { + try { + InputStreamReader reader = new InputStreamReader(in, charset); + try { + Properties props = new Properties(); + props.load(reader); + return props; + } finally { + reader.close(); + } + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * 根据byte数组,生成文件 filePath 文件路径 fileName 文件名称(需要带后缀,如*.jar) + */ + public static File getFile(byte[] bfile, String filePath, String fileName) { + BufferedOutputStream bos = null; + FileOutputStream fos = null; + File file = null; + try { + File dir = new File(filePath); + if (!dir.exists()) {// 判断文件目录是否存在 + dir.mkdirs(); + } + file = new File(filePath + File.separator + fileName); + fos = new FileOutputStream(file); + bos = new BufferedOutputStream(fos); + bos.write(bfile); + } catch (Exception e) { + e.printStackTrace(); + } finally { + if (bos != null) { + try { + bos.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + if (fos != null) { + try { + fos.close(); + } catch (IOException e1) { + e1.printStackTrace(); + } + } + } + return file; + } + + public static String getCurrentDir() { + try { + return new File("./").getCanonicalPath(); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static void deleteFile(String dir) { + deleteFile(dir, false); + } + + public static void deleteFile(File file) { + deleteFile(file, false); + } + + public static void deleteFile(String dir, boolean silent) { + File directory = new File(dir); + deleteFile(directory, silent); + } + + /** + * 删除文件; + * + * @param file + * @param silent + * 是否静默删除;如果为 true ,则吞噬删除过程中的异常,意味着方法即便正常返回时也有可能删除不完全; + */ + public static void deleteFile(File file, boolean silent) { + if (file.isFile()) { + try { + file.delete(); + return; + } catch (Exception e) { + if (!silent) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + } + File[] files = file.listFiles(); + if (files == null) { + return; + } + for (File f : files) { + deleteFile(f, silent); + } + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/FixedBytesSliceArray.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/FixedBytesSliceArray.java new file mode 100644 index 00000000..68983284 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/FixedBytesSliceArray.java @@ -0,0 +1,70 @@ +package com.jd.blockchain.utils.io; + +public class FixedBytesSliceArray implements BytesSlices { + + private byte[] dataBytes; + + private int totalSize; + + private int dataOffset; + + private int itemCount; + + private int itemSize; + + /** + * @param dataBytes + * 数据; + * @param totalSize + * 初始的偏移量; + * @param dataOffset + * 数据的起始偏移量; + * @param itemCount + * 数据片段的总数; + * @param itemSize + * 单个数据片段的大小; + */ + private FixedBytesSliceArray(byte[] dataBytes, int totalSize, int dataOffset, int itemCount, int itemSize) { + if ((dataOffset + itemCount * itemSize) > dataBytes.length) { + throw new IllegalArgumentException("The tail index of all slices is out of bound of data bytes!"); + } + this.dataBytes = dataBytes; + // this.length = (dataOffset + count * size) - totalSize; + this.totalSize = totalSize; + this.dataOffset = dataOffset; + this.itemCount = itemCount; + this.itemSize = itemSize; + } + + @Override + public int getTotalSize() { + return totalSize; + } + + @Override + public int getCount() { + return itemCount; + } + + @Override + public BytesSlice getDataSlice(int idx) { + if (idx < 0 || idx >= itemCount) { + throw new IllegalArgumentException("The specified idx is out of bound!"); + } + return new BytesSlice(dataBytes, dataOffset + idx * itemSize, itemSize); + } + + public static FixedBytesSliceArray resolve(BytesInputStream bytesStream, int itemSize) { + int p1 = bytesStream.getPosition(); + + int itemCount = NumberMask.NORMAL.resolveMaskedNumber(bytesStream); + + int dataOffset = bytesStream.getPosition(); + + bytesStream.skip(itemCount * itemSize); + int totalSize = bytesStream.getPosition() - p1; + + return new FixedBytesSliceArray(bytesStream.getOriginBytes(), totalSize, dataOffset, itemCount, itemSize); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/ForwardReadonlyInputStream.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/ForwardReadonlyInputStream.java new file mode 100644 index 00000000..0df903fd --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/ForwardReadonlyInputStream.java @@ -0,0 +1,40 @@ +package com.jd.blockchain.utils.io; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * + * 通过 {@link ForwardReadonlyInputStream} 对输入流进行包装,限制只能对输入流进行向前读取的操作,不允许调用 {@link #mark(int)}、 {@link #reset()}、{@link #close()}操作; + * + * @author huanghaiquan + * + */ +public class ForwardReadonlyInputStream extends FilterInputStream{ + + protected ForwardReadonlyInputStream(InputStream in) { + super(in); + } + + + @Override + public boolean markSupported() { + return false; + } + + @Override + public synchronized void mark(int readlimit) { + // forward readonly, so do nothing instead here; + } + + @Override + public synchronized void reset() throws IOException { + // forward readonly, so do nothing instead here; + } + + @Override + public void close() throws IOException { + // forward readonly, cann't be closed, so do nothing instead here; + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/NumberMask.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/NumberMask.java new file mode 100644 index 00000000..a77c5e7d --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/NumberMask.java @@ -0,0 +1,378 @@ +package com.jd.blockchain.utils.io; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * {@link NumberMask} 数值掩码;
+ * + * {@link NumberMask} 定义了使用有限的字节表示一个特定范围的正整数的格式;
+ * + * {@link NumberMask} 用于表示数值的字节长度是动态的,根据数值的范围而定;
+ * + * 这个特点使得 {@link NumberMask} 适用于表示小数据片的头部尺寸;尤其当一个数据块是由大量表示不同属性的小数据片构成时,使用 + * {@link NumberMask} 可以得到更紧凑的字节流; + * + *

+ * 注:{@link NumberMask} 处理的数值范围处于 32 位整数(int)的范围,不处理 64 位整数(long)的情况;
+ * 这样的设计一方面是因为 {@link NumberMask#NORMAL} 已经可以表示通常情况下的足够大的数值范围(1G),而 64 + * 位整数(long)表示的长度已经远远超出单台计算机能够处理的内存数量;
+ * 另一方面是,采用嵌套的方式使用{@link NumberMask}也可以达到表示无限大小的数据的目的(如:使用 {@link NumberMask} + * 表示数据片数量,对每一个数据片再用一个{@link NumberMask}表示其数据长度); + * + * @author huanghaiquan + * + */ +public enum NumberMask { + + /** + * 最短的头部,占用1字节;
+ * + * 表示字节内容的长度小于 256 (2^8); + */ + TINY((byte) 0), + + /** + * 短的头部,最多占用2字节;
+ * + *

+	 * 记录字节数据大小的头部占用的字节数是动态的,根据数据的大小而定,最少1个字节,最大2个字节;
+	 * 
+	 * 使用首个字节的最高两位作为标识位指示头部的长度;
+	 * 
+	 * 当字节内容的长度小于 128 (2^7),则头部占用1个字节,最高位标识为 0 ;
+	 * 
+	 * 当字节内容的长度小于 32768 (2^15, 32KB),则头部占用2个字节,最高位标识为 1 ;
+	 * 
+	 * 
+	 * 
+ */ + SHORT((byte) 1), + + /** + * 短的头部,最多占用4字节;
+ * + *
+	 * 记录字节数据大小的头部占用的字节数是动态的,根据数据的大小而定,最少1个字节,最大4个字节;
+	 * 
+	 * 使用首个字节的最高两位作为标识位指示头部的长度;
+	 * 
+	 * 当字节内容的长度小于 64 (2^6),则头部占用1个字节,最高位标识为 0 ;
+	 * 
+	 * 当字节内容的长度小于 16384 (2^14, 16KB),则头部占用2个字节,最高位标识为 1 ;
+	 * 
+	 * 当字节内容的长度小于 4194304 (2^22, 4MB),则头部占用3个字节,最高位标识为 2 ;
+	 * 
+	 * 当字节内容的长度小于 1073741824 (2^30, 1GB),则头部占用4个字节,最高位标识为 3 ;
+	 * 
+	 * 
+ */ + NORMAL((byte) 2); + + // 不考虑 long 的情况,因为 long 的数值表示的长度已经远远超出单台计算机能够处理的内存数量; + + // /** + // * 短的头部,最多占用8字节; + // */ + // LONG((byte) 3); + + /** + * 掩码位的个数; + */ + public final byte BIT_COUNT; + + /** + * 头部长度的最大值; + */ + public final int MAX_HEADER_LENGTH; + + public final int MAX_BOUNDARY_SIZE; + + /** + * 此常量对于 TINY、SHORT、NORMAL 有效; + */ + public final int BOUNDARY_SIZE_0; + public final int BOUNDARY_SIZE_1; + public final int BOUNDARY_SIZE_2; + public final int BOUNDARY_SIZE_3; + + private int[] boundarySizes; + + private NumberMask(byte bitCount) { + this.BIT_COUNT = bitCount; + this.MAX_HEADER_LENGTH = 1 << bitCount; + this.boundarySizes = new int[MAX_HEADER_LENGTH]; + for (byte i = 0; i < MAX_HEADER_LENGTH; i++) { + boundarySizes[i] = computeBoundarySize((byte) (i + 1)); + } + + this.MAX_BOUNDARY_SIZE = boundarySizes[MAX_HEADER_LENGTH - 1]; + if (bitCount == 0) { + // TINY; + BOUNDARY_SIZE_0 = boundarySizes[0]; + BOUNDARY_SIZE_1 = -1; + BOUNDARY_SIZE_2 = -1; + BOUNDARY_SIZE_3 = -1; + } else if (bitCount == 1) { + // SHORT; + BOUNDARY_SIZE_0 = boundarySizes[0]; + BOUNDARY_SIZE_1 = boundarySizes[1]; + BOUNDARY_SIZE_2 = -1; + BOUNDARY_SIZE_3 = -1; + } else if (bitCount == 2) { + // NORMAL; + BOUNDARY_SIZE_0 = boundarySizes[0]; + BOUNDARY_SIZE_1 = boundarySizes[1]; + BOUNDARY_SIZE_2 = boundarySizes[2]; + BOUNDARY_SIZE_3 = boundarySizes[3]; + } else { + throw new IllegalArgumentException("Illegal bitCount!"); + } + } + + /** + * 在指定的头部长度下能够表示的数据大小的临界值(不含); + * + * @param headerLength + * 值范围必须大于 0 ,且小于等于 {@link #MAX_HEADER_LENGTH} + * @return + */ + public int getBoundarySize(int headerLength) { +// if (headerLength < 1) { +// throw new IllegalArgumentException("Header length is less than one!"); +// } +// if (headerLength > MAX_HEADER_LENGTH) { +// throw new IllegalArgumentException( +// "Header length is great than MAX_HEADER_LENGTH[" + MAX_HEADER_LENGTH + "]!"); +// } + return boundarySizes[headerLength - 1]; + } + + private int computeBoundarySize(int headerLength) { + // 不考虑 long 的情况; + // long boundarySize = 1L << (headerLength * 8 - BIT_COUNT); + + int boundarySize = 1 << (headerLength * 8 - BIT_COUNT); + return boundarySize; + } + + /** + * 获取能够表示指定的数值的掩码长度,即掩码所需的字节数;
+ * + * @param number + * 要表示的数值;如果值范围超出掩码的有效范围,将抛出 {@link IllegalArgumentException} 异常; + * @return + */ + public int getMaskLength(int number) { + if (number > -1) { + if (number < BOUNDARY_SIZE_0) { + return 1; + } + if (number < BOUNDARY_SIZE_1) { + return 2; + } + if (number < BOUNDARY_SIZE_2) { + return 3; + } + if (number < BOUNDARY_SIZE_3) { + return 4; + } + } + throw new IllegalArgumentException("Number is out of the illegal range! --[number=" + number + "]"); + } + + /** + * 生成指定数值的掩码; + * + * @param number + * 要表示的数值;如果值范围超出掩码的有效范围,将抛出 {@link IllegalArgumentException} 异常; + * @return + */ + public byte[] generateMask(int number) { + // 计算掩码占用的字节长度; + int maskLen = getMaskLength(number); + byte[] maskBytes = new byte[maskLen]; + writeMask(number, maskLen, maskBytes, 0); + return maskBytes; + } + + public int writeMask(int number, byte[] buffer, int offset) { + // 计算掩码占用的字节长度; + int maskLen = getMaskLength(number); + return writeMask(number, maskLen, buffer, offset); + } + + private int writeMask(int number, int maskLen, byte[] buffer, int offset) { + // 计算掩码占用的字节长度; + for (int i = maskLen; i > 0; i--) { + buffer[offset + i - 1] = (byte) ((number >>> 8 * (maskLen - i)) & 0xFF); + } + + // 计算头字节的标识位; + byte indicatorByte = (byte) ((maskLen - 1) << (8 - BIT_COUNT)); + // 设置标识位; + buffer[offset] = (byte) (indicatorByte | buffer[offset]); + return maskLen; + } + + /** + * 生成指定数值的掩码并写入到指定的输出流; + * + * @param number + * @param out + * @return 写入的字节数; + */ + public int writeMask(int number, OutputStream out) { + // 生成数据尺寸掩码; + byte[] maskBytes = generateMask(number); + + try { + out.write(maskBytes); + return maskBytes.length; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + /** + * 解析掩码的头字节获得该掩码实例的完整长度; + * + * @param bytes + * 掩码的头字节;即掩码的字节序列的首个字节; + * @return 返回掩码实例的完整长度;
+ * 注:在字节流中,对首字节解析获取该值后减 1,可以得到该掩码后续要读取的字节长度; + */ + public int resolveMaskLength(BytesSlice bytes) { + return resolveMaskLength(bytes.getByte()); + } + + public int resolveMaskLength(BytesSlice bytes, int offset) { + return resolveMaskLength(bytes.getByte(offset)); + } + + /** + * 解析掩码的头字节获得该掩码实例的完整长度; + * + * @param headByte + * 掩码的头字节;即掩码的字节序列的首个字节; + * @return 返回掩码实例的完整长度;
+ * 注:在字节流中,对首字节解析获取该值后减 1,可以得到该掩码后续要读取的字节长度; + */ + public int resolveMaskLength(byte headByte) { + int len = ((headByte & 0xFF) >>> (8 - BIT_COUNT)) + 1; + if (len < 1) { + throw new IllegalArgumentException( + "Illegal length [" + len + "] was resolved from the head byte of NumberMask!"); + } + if (len > MAX_HEADER_LENGTH) { + throw new IllegalArgumentException( + "Illegal length [" + len + "] was resolved from the head byte of NumberMask!"); + } + return len; + } + + public int resolveMaskedNumber(byte[] markBytes) { + return resolveMaskedNumber(markBytes, 0); + } + + /** + * 从字节中解析掩码表示的数值; + * + * @param markBytes + * @param headPos + * @return + */ + public int resolveMaskedNumber(byte[] markBytes, int headPos) { + int maskLen = resolveMaskLength(markBytes[headPos]); + + // 清除首字节的标识位; + byte numberHead = (byte) (markBytes[headPos] & (0xFF >>> BIT_COUNT)); + + // 转换字节大小; + int number = numberHead & 0xFF; + for (int i = 1; i < maskLen; i++) { + number = (number << 8) | (markBytes[headPos + i] & 0xFF); + } + + return number; + } + + /** + * 从字节中解析掩码表示的数值; + * @param bytes bytes + * @return int + */ + public int resolveMaskedNumber(BytesSlice bytes) { + return resolveMaskedNumber(bytes, 0); + } + + /** + * 从字节中解析掩码表示的数值; + * @param bytes bytes + * @param offset offset + * @return int + */ + public int resolveMaskedNumber(BytesSlice bytes, int offset) { + byte headByte = bytes.getByte(offset); + int maskLen = resolveMaskLength(headByte); + + // 清除首字节的标识位; + byte numberHead = (byte) (headByte & (0xFF >>> BIT_COUNT)); + + // 转换字节大小; + int number = numberHead & 0xFF; + for (int i = 1; i < maskLen; i++) { + number = (number << 8) | (bytes.getByte(offset + i) & 0xFF); + } + + return number; + } + + /** + * 从字节中解析掩码表示的数值; + * @param bytesStream + * @return int + */ + public int resolveMaskedNumber(BytesInputStream bytesStream) { + byte headByte = bytesStream.readByte(); + int maskLen = resolveMaskLength(headByte); + + // 清除首字节的标识位; + byte numberHead = (byte) (headByte & (0xFF >>> BIT_COUNT)); + + // 转换字节大小; + int number = numberHead & 0xFF; + for (int i = 1; i < maskLen; i++) { + number = (number << 8) | (bytesStream.readByte() & 0xFF); + } + + return number; + } + + /** + * 从字节流解析掩码表示的数值; + * + * @param in + * @return + */ + public int resolveMaskedNumber(InputStream in) { + try { + byte[] buff = new byte[MAX_HEADER_LENGTH]; + // 解析头字节; + int len = in.read(buff, 0, 1); + if (len < 1) { + throw new IllegalArgumentException("No enough bytes for the size header's indicator byte!"); + } + int maskLen = resolveMaskLength(buff[0]); + if (maskLen > 1) { + in.read(buff, 1, maskLen - 1); + } + + return resolveMaskedNumber(buff, 0); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/RuntimeIOException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/RuntimeIOException.java new file mode 100644 index 00000000..704284bf --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/RuntimeIOException.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.utils.io; + +public class RuntimeIOException extends RuntimeException{ + + private static final long serialVersionUID = 6863237161295632635L; + + public RuntimeIOException(String message) { + super(message); + } + + public RuntimeIOException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/SingleBytesSliceArray.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/SingleBytesSliceArray.java new file mode 100644 index 00000000..152fdbd7 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/SingleBytesSliceArray.java @@ -0,0 +1,67 @@ +package com.jd.blockchain.utils.io; + +public class SingleBytesSliceArray extends BytesSlice implements BytesSlices { + + private int totalSize; + + /** + * @param dataBytes + * 数据; + * @param offset + * 初始的偏移量; + * @param dataOffset + * 数据的起始偏移量; + * @param count + * 数据片段的总数; + * @param size + * 单个数据片段的大小; + */ + /** + * @param dataBytes + * @param offset + * @param dataOffset + * @param dataSize + */ + private SingleBytesSliceArray(byte[] dataBytes, int totalSize, int dataOffset, int dataSize) { + super(dataBytes, dataOffset, dataSize); + this.totalSize = totalSize; + } + + @Override + public int getTotalSize() { + return totalSize; + } + + @Override + public int getCount() { + return 1; + } + + @Override + public BytesSlice getDataSlice(int idx) { + if (idx != 0) { + throw new IllegalArgumentException("The specified idx is out of bound!"); + } + return this; + } + + public static SingleBytesSliceArray resolveDynamic(BytesInputStream bytesStream) { + int p1 = bytesStream.getPosition(); + int size = NumberMask.NORMAL.resolveMaskedNumber(bytesStream); + int dataOffset = bytesStream.getPosition(); + bytesStream.skip(size); + int totalSize = bytesStream.getPosition() - p1; + return new SingleBytesSliceArray(bytesStream.getOriginBytes(), totalSize, dataOffset, size); + } + + public static SingleBytesSliceArray create(BytesInputStream bytesStream, int itemSize) { + int offset = bytesStream.getPosition(); + bytesStream.skip(itemSize); + return new SingleBytesSliceArray(bytesStream.getOriginBytes(), itemSize, offset, itemSize); + } + + public static SingleBytesSliceArray create(byte[] dataBytes, int offset, int size) { + return new SingleBytesSliceArray(dataBytes, size, offset, size); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/UnclosableOutputStream.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/UnclosableOutputStream.java new file mode 100644 index 00000000..e55daeb3 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/io/UnclosableOutputStream.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.io; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +/** + * {@link UnclosableOutputStream} 限制了对输出流执行关闭操作,调用 {@link #close()} 方法将不起任何作用,用在需要防止对输出流的使用中误关闭的情形; + * + * @author huanghaiquan + * + */ +public class UnclosableOutputStream extends FilterOutputStream { + + public UnclosableOutputStream(OutputStream out) { + super(out); + } + + @Override + public void close() throws IOException { + // do nothing for avoiding closing the inner outputstream; + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/net/NetworkAddress.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/net/NetworkAddress.java new file mode 100644 index 00000000..5e969bef --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/net/NetworkAddress.java @@ -0,0 +1,110 @@ +package com.jd.blockchain.utils.net; + +import java.io.Serializable; +import java.util.Arrays; + +import com.jd.blockchain.utils.io.BytesOutputBuffer; +import com.jd.blockchain.utils.io.BytesSerializable; +import com.jd.blockchain.utils.io.BytesUtils; + +/** + * 网络地址; + * + * @author haiq + * + */ +public class NetworkAddress implements BytesSerializable, Serializable { + + private static final long serialVersionUID = -4565279525154132393L; + + private String host; + + private int port; + + private boolean secure; + + public NetworkAddress() { + } + + public NetworkAddress(String host, int port) { + this(host, port, false); + } + + public NetworkAddress(String host, int port, boolean secure) { + this.host = host; + this.port = port; + this.secure = secure; + } + + /** + * 用于二进制反序列化的构造器; + * + * @param serializeBytes + */ + public NetworkAddress(byte[] serializeBytes) { + secure = serializeBytes[0] == (byte) 1; + port = BytesUtils.toInt(serializeBytes, 1); + host = BytesUtils.toString(serializeBytes, 5); + } + + public String getHost() { + return host; + } + + public int getPort() { + return port; + } + + public boolean isSecure() { + return secure; + } + + public void setHost(String host) { + this.host = host; + } + + public void setPort(int port) { + this.port = port; + } + + public void setSecure(boolean secure) { + this.secure = secure; + } + + @Override + public String toString() { + return secure ? String.format("secure://%s:%s", host, port) : String.format("%s:%s", host, port); + } + + @Override + public int hashCode() { + return Arrays.hashCode(new Object[] { host, port, secure }); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (obj instanceof NetworkAddress) { + NetworkAddress other = (NetworkAddress) obj; + return this.host.equals(other.host) && this.port == other.port && this.secure == other.secure; + } + return false; + } + + @Override + public byte[] toBytes() { + BytesOutputBuffer buffer = new BytesOutputBuffer(); + byte[] bf1 = new byte[5]; + bf1[0] = secure ? (byte) 1 : (byte) 0; + BytesUtils.toBytes(port, bf1, 1); + buffer.write(bf1); + buffer.write(BytesUtils.toBytes(host)); + return buffer.toBytes(); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/net/NetworkException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/net/NetworkException.java new file mode 100644 index 00000000..fd94cc36 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/net/NetworkException.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.utils.net; + +public class NetworkException extends RuntimeException{ + + private static final long serialVersionUID = 2231122547918937867L; + + public NetworkException() { + } + public NetworkException(String message) { + super(message); + } + public NetworkException(String message, Throwable cause) { + super(message, cause); + } + + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/AESUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/AESUtils.java new file mode 100644 index 00000000..ec1e2563 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/AESUtils.java @@ -0,0 +1,169 @@ +package com.jd.blockchain.utils.security; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +import com.jd.blockchain.utils.codec.HexUtils; + +/** + * AES 加密算法工具类; + * + * @author haiq + * + */ +public class AESUtils { + + /** + * 用指定的种子生成 128 位的秘钥;
+ * + * 如果指定的种子为空(null 或长度为 0 ),则生成随机的秘钥; + * + * @param seed + * 种子; + * @return + */ + public static String generateKey128_Hex(byte[] seed) { + byte[] keyBytes = generateKey128_Bytes(seed); + return HexUtils.encode(keyBytes); + } + + /** + * 用指定的种子生成 128 位的秘钥; + * + * @param seed + * 种子; + * @return + */ + public static byte[] generateKey128_Bytes(byte[] seed) { + SecretKey key = generateKey128(seed); + return key.getEncoded(); + } + + /** + * 用指定的种子生成 128 位的秘钥; + * + * @param seed + * 种子; 不允许为空; + * @return + */ + public static SecretKey generateKey128(byte[] seed) { + if (seed == null || seed.length == 0) { + throw new IllegalArgumentException("Empty seed!"); + } + // 注:AES 算法只支持 128 位,不支持 192, 256 位的秘钥加密; + byte[] hashBytes = ShaUtils.hash_128(seed); + return new SecretKeySpec(hashBytes, "AES"); + + //注:由于同一个种子有可能产生不同的随机数序列,不能基于随机数机制来生成;by huanghaiquan at 2017-08-25; +// byte[] random = RandomUtils.generateRandomBytes(16, seed); +// return new SecretKeySpec(random, "AES"); + } + + /** + * 生成 128 位的随机秘钥; + * + * @return + */ + public static SecretKey generateKey128() { + byte[] randBytes = RandomUtils.generateRandomBytes(16); + return new SecretKeySpec(randBytes, "AES"); + } + + /** + * 生成以 16 进制编码的 128 位的随机秘钥; + * + * @return + */ + public static String generateKey128_Hex() { + byte[] keyBytes = generateKey128_Bytes(); + return HexUtils.encode(keyBytes); + } + + public static byte[] generateKey128_Bytes() { + SecretKey key = generateKey128(); + return key.getEncoded(); + } + + /** + * 用指定的 16 进制的AES秘钥进行加密; + * + * @param content + * @param key + * 16进制编码的 AES 秘钥; + * @return + */ + public static byte[] encrypt(byte[] content, String key) { + return encrypt(content, HexUtils.decode(key)); + } + + public static byte[] encrypt(byte[] content, byte[] secretKey) { + SecretKey aesKey = new SecretKeySpec(secretKey, "AES"); + return encrypt(content, aesKey); + } + + /** + * + * @param plainBytes + * @param key + * @return + */ + public static byte[] encrypt(byte[] plainBytes, SecretKey key) { + try { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.ENCRYPT_MODE, key); + return cipher.doFinal(plainBytes); + } catch (InvalidKeyException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (NoSuchPaddingException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (BadPaddingException e) { + throw new EncryptionException(e.getMessage(), e); + } + } + + /** + * 解密; + * + * @param encryptedBytes + * @param key + * @return + */ + public static byte[] decrypt(byte[] encryptedBytes, String key) { + return decrypt(encryptedBytes, HexUtils.decode(key)); + } + + public static byte[] decrypt(byte[] encryptedBytes, byte[] key) { + SecretKey aesKey = new SecretKeySpec(key, "AES"); + return decrypt(encryptedBytes, aesKey); + } + + public static byte[] decrypt(byte[] encryptedBytes, SecretKey secretKey) { + try { + Cipher cipher = Cipher.getInstance("AES"); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(encryptedBytes); + } catch (InvalidKeyException e) { + throw new DecryptionException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new DecryptionException(e.getMessage(), e); + } catch (NoSuchPaddingException e) { + throw new DecryptionException(e.getMessage(), e); + } catch (IllegalBlockSizeException e) { + throw new DecryptionException(e.getMessage(), e); + } catch (BadPaddingException e) { + throw new DecryptionException(e.getMessage(), e); + } + } + +} \ No newline at end of file diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/AuthenticationException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/AuthenticationException.java new file mode 100644 index 00000000..2b235712 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/AuthenticationException.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.security; + +/** + * 认证异常; + * + * @author haiq + * + */ +public class AuthenticationException extends RuntimeException { + + private static final long serialVersionUID = 2188866951704920121L; + + public AuthenticationException(String message) { + super(message); + } + + public AuthenticationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/DESUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/DESUtils.java new file mode 100644 index 00000000..fb74c378 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/DESUtils.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.utils.security; + +import javax.crypto.Cipher; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.DESKeySpec; + +/** + * DES 算法的工具类; + * + * @author haiq + * + */ +public class DESUtils { + + private static final String DES = "DES"; + private static final String DEFAULT_CIPHER_ALGORITHM = "DES/ECB/PKCS5Padding"; + + /** + * 加密 + * + * @param code + * @param keyBytes + * {key+adress}.length=8*n + * @return + * @throws Exception + */ + public static byte[] encrypt(String code, byte[] keyBytes) throws Exception { + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); + DESKeySpec keySpec = new DESKeySpec(keyBytes); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); + SecretKey secretKey = keyFactory.generateSecret(keySpec); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + return cipher.doFinal(code.getBytes()); + } + + /** DES解密 */ + public static byte[] decrypt(byte[] codeBytes, byte[] keyBytes) throws Exception { + Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM); + DESKeySpec keySpec = new DESKeySpec(keyBytes); + SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(DES); + SecretKey secretKey = keyFactory.generateSecret(keySpec); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + return cipher.doFinal(codeBytes); + } + + /** + * 生成key + * + * @return + * @throws Exception + */ + public static byte[] initKey() throws Exception { + KeyGenerator kg = KeyGenerator.getInstance(DES); + kg.init(56); + SecretKey secretKey = kg.generateKey(); + return secretKey.getEncoded(); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/DecryptionException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/DecryptionException.java new file mode 100644 index 00000000..f582853c --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/DecryptionException.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.security; + +/** + * 解密异常; + * + * @author haiq + * + */ +public class DecryptionException extends RuntimeException { + + private static final long serialVersionUID = 2188866951704920121L; + + public DecryptionException(String message) { + super(message); + } + + public DecryptionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/Ed25519Utils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/Ed25519Utils.java new file mode 100644 index 00000000..07dc93f8 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/Ed25519Utils.java @@ -0,0 +1,85 @@ +package com.jd.blockchain.utils.security; + +import java.nio.ByteBuffer; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.SignatureException; + +import net.i2p.crypto.eddsa.EdDSAEngine; +import net.i2p.crypto.eddsa.EdDSAPrivateKey; +import net.i2p.crypto.eddsa.EdDSAPublicKey; +import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable; +import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec; +import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec; +import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec; + +public class Ed25519Utils { + + /** + * 用指定的私钥执行签名; + * + * @param data + * @param privateKey + * @return 返回签名摘要; + */ + public static byte[] sign_512(byte[] data, byte[] privateKey) { + return sign_512(ByteBuffer.wrap(data), privateKey); + } + + /** + * 用指定的私钥执行签名; + * + * @param data + * @param privateKey + * @return 返回签名摘要; + */ + public static byte[] sign_512(ByteBuffer data, byte[] privateKey) { + try { + java.security.Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512); + EdDSAPrivateKeySpec privateKeySpec = new EdDSAPrivateKeySpec(privateKey, spec); + PrivateKey privKey = new EdDSAPrivateKey(privateKeySpec); + sgr.initSign(privKey); + sgr.update(data); + return sgr.sign(); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } catch (SignatureException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 验证签名; + * + * @param plainData + * 明文; + * @param pubKeyBytes + * 公钥; + * @param signatureBytes + * 摘要; + * @return + */ + public static boolean verify(byte[] plainData, byte[] pubKeyBytes, byte[] signatureBytes) { + try { + java.security.Signature sgr = new EdDSAEngine(MessageDigest.getInstance("SHA-512")); + EdDSAParameterSpec spec = EdDSANamedCurveTable.getByName("ed25519-sha-512"); + EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(pubKeyBytes, spec); + EdDSAPublicKey pubKey = new EdDSAPublicKey(pubKeySpec); + sgr.initVerify(pubKey); + sgr.update(plainData); + return sgr.verify(signatureBytes); + } catch (InvalidKeyException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } catch (SignatureException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/EncryptionException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/EncryptionException.java new file mode 100644 index 00000000..8086d896 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/EncryptionException.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.security; + +/** + * 加密异常; + * + * @author haiq + * + */ +public class EncryptionException extends RuntimeException { + + private static final long serialVersionUID = 2188866951704920121L; + + public EncryptionException(String message) { + super(message); + } + + public EncryptionException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyGenerationException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyGenerationException.java new file mode 100644 index 00000000..a27b8fc3 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyGenerationException.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.utils.security; + +public class KeyGenerationException extends RuntimeException { + + private static final long serialVersionUID = 1729427276871753983L; + + public KeyGenerationException() { + } + + public KeyGenerationException(String message) { + super(message); + } + + public KeyGenerationException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyStoreException.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyStoreException.java new file mode 100644 index 00000000..b12f1306 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyStoreException.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.utils.security; + +public class KeyStoreException extends RuntimeException { + + private static final long serialVersionUID = 1729427276871753983L; + + public KeyStoreException() { + } + + public KeyStoreException(String message) { + super(message); + } + + public KeyStoreException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyStoreUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyStoreUtils.java new file mode 100644 index 00000000..da71c818 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/KeyStoreUtils.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.utils.security; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.security.KeyStore; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; + +public class KeyStoreUtils { + + public static final String P12_KEYSTORE_TYPE = "PKCS12"; + + public static void validateP12(byte[] certicate, String password) throws KeyStoreException{ + try { + ByteArrayInputStream certStream = new ByteArrayInputStream(certicate); + char[] pwdChars = password.toCharArray(); + KeyStore ks = KeyStore.getInstance(P12_KEYSTORE_TYPE); + ks.load(certStream, pwdChars); + } catch (java.security.KeyStoreException e) { + throw new KeyStoreException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new KeyStoreException(e.getMessage(), e); + } catch (CertificateException e) { + throw new KeyStoreException(e.getMessage(), e); + } catch (IOException e) { + throw new KeyStoreException(e.getMessage(), e); + } + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RSAKeyPair.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RSAKeyPair.java new file mode 100644 index 00000000..fd9fe3da --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RSAKeyPair.java @@ -0,0 +1,41 @@ +package com.jd.blockchain.utils.security; + +import java.nio.ByteBuffer; + +import com.jd.blockchain.utils.codec.Base58Utils; + +public class RSAKeyPair { + + private byte[] publicKey; + + private String publicKey_Base58; + + private byte[] privateKey; + + private String privateKey_Base58; + + public RSAKeyPair(byte[] publicKey, byte[] privateKey) { + this.publicKey = publicKey; + this.privateKey = privateKey; + + this.publicKey_Base58 = Base58Utils.encode(publicKey); + this.privateKey_Base58 = Base58Utils.encode(privateKey); + } + + public ByteBuffer getPublicKey() { + return ByteBuffer.wrap(publicKey).asReadOnlyBuffer(); + } + + public ByteBuffer getPrivateKey() { + return ByteBuffer.wrap(privateKey).asReadOnlyBuffer(); + } + + public String getPublicKey_Base58() { + return publicKey_Base58; + } + + public String getPrivateKey_Base58() { + return privateKey_Base58; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RSAUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RSAUtils.java new file mode 100644 index 00000000..b86f2e5b --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RSAUtils.java @@ -0,0 +1,131 @@ +package com.jd.blockchain.utils.security; + +import java.security.InvalidKeyException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.interfaces.RSAPrivateKey; +import java.security.interfaces.RSAPublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; + +import com.jd.blockchain.utils.codec.Base58Utils; + +/** + * RSA 加密算法的工具类; + * + * @author haiq + * + */ +public class RSAUtils { + public static final String ALG_RSA = "RSA"; + public static final String KEYPAIR_PUBKEY = "pubKey"; + public static final String KEYPAIR_PRIKEY = "priKey"; + + public static RSAKeyPair generateKey512() { + try { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALG_RSA); + keyPairGen.initialize(512, new SecureRandom()); + KeyPair keyPair = keyPairGen.generateKeyPair(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + + return new RSAKeyPair(publicKey.getEncoded(), privateKey.getEncoded()); + } catch (NoSuchAlgorithmException e) { + throw new KeyGenerationException(e.getMessage(), e); + } + } + + public static RSAKeyPair generateKey2048() { + try { + KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(ALG_RSA); + keyPairGen.initialize(2048, new SecureRandom()); + KeyPair keyPair = keyPairGen.generateKeyPair(); + RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate(); + RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic(); + + return new RSAKeyPair(publicKey.getEncoded(), privateKey.getEncoded()); + } catch (NoSuchAlgorithmException e) { + throw new KeyGenerationException(e.getMessage(), e); + } + } + + public static byte[] encryptByPublicKey_Base58(byte[] data, String publicKey_Base58) { + byte[] publicKey = Base58Utils.decode(publicKey_Base58); + return encryptByPublicKey(data, publicKey); + } + + public static byte[] encryptByPublicKey(byte[] data, byte[] publicKey) { + if (publicKey == null) { + throw new IllegalArgumentException("Public key is empty!"); + } + try { + RSAPublicKey pubKey = loadPublicKey(publicKey); + Cipher cipher = Cipher.getInstance(ALG_RSA); + cipher.init(Cipher.ENCRYPT_MODE, pubKey); + return cipher.doFinal(data); + } catch (InvalidKeyException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (InvalidKeySpecException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (NoSuchPaddingException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (BadPaddingException e) { + throw new EncryptionException(e.getMessage(), e); + } + } + + public static byte[] decryptByPrivateKey_Base58(byte[] data, String privateKey_Base58) { + byte[] privateKey = Base58Utils.decode(privateKey_Base58); + return decryptByPrivateKey(data, privateKey); + } + + public static byte[] decryptByPrivateKey(byte[] cipherData, byte[] privateKey) { + if (privateKey == null) { + throw new IllegalArgumentException("Private key is empty!"); + } + try { + RSAPrivateKey privKey = loadPrivateKey(privateKey); + Cipher cipher = Cipher.getInstance(ALG_RSA); + cipher.init(Cipher.DECRYPT_MODE, privKey); + return cipher.doFinal(cipherData); + } catch (InvalidKeyException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (InvalidKeySpecException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (NoSuchPaddingException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (IllegalBlockSizeException e) { + throw new EncryptionException(e.getMessage(), e); + } catch (BadPaddingException e) { + throw new EncryptionException(e.getMessage(), e); + } + } + + private static RSAPublicKey loadPublicKey(byte[] pubKey) throws NoSuchAlgorithmException, InvalidKeySpecException { + KeyFactory keyFactory = KeyFactory.getInstance(ALG_RSA); + X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKey); + return (RSAPublicKey) keyFactory.generatePublic(keySpec); + } + + private static RSAPrivateKey loadPrivateKey(byte[] priKey) + throws NoSuchAlgorithmException, InvalidKeySpecException { + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(priKey); + KeyFactory keyFactory = KeyFactory.getInstance(ALG_RSA); + return (RSAPrivateKey) keyFactory.generatePrivate(keySpec); + } +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RandomUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RandomUtils.java new file mode 100644 index 00000000..576ea7a4 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RandomUtils.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.utils.security; + +import java.security.SecureRandom; + +/** + * 随机数生成工具类; + * + * @author haiq + * + */ +public class RandomUtils { + + public static byte[] generateRandomBytes(int sizeOfRandom) { + return generateRandomBytes(sizeOfRandom, null); + } + + /** + * 用指定的种子生成秘钥; + *

+ * + * 注:需要注意,同一个种子有可能产生不同的随机数序列; + * + * @param sizeOfRandom + * 要输出的随机数的长度;(单位:字节) + * @param seed + * 随机种子; + * @return 随机数;长度等于 sizeOfRandom 参数指定的值; + */ + public static byte[] generateRandomBytes(int sizeOfRandom, byte[] seed) { + SecureRandom sr = null; + if (seed == null || seed.length == 0) { + // 随机; + sr = new SecureRandom(); + } else { + sr = new SecureRandom(seed); + } + byte[] randomBytes = new byte[sizeOfRandom]; + sr.nextBytes(randomBytes); + return randomBytes; + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RipeMD160Utils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RipeMD160Utils.java new file mode 100644 index 00000000..c3173dbd --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/RipeMD160Utils.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.utils.security; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Security; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +public class RipeMD160Utils { + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + public static byte[] hash(byte[] bytes) { + try { + MessageDigest md160 = MessageDigest.getInstance("RIPEMD160"); + return md160.digest(bytes); + } catch (NoSuchAlgorithmException e) { + throw new EncryptionException(e.getMessage(), e); + } + + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/SHA256Hash.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/SHA256Hash.java new file mode 100644 index 00000000..6faa2a86 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/SHA256Hash.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.utils.security; + +public interface SHA256Hash { + + /** + * 追加要一起计算哈希的数据; + * + * @param bytes + */ + void update(byte[] bytes); + + /** + * 追加要一起计算哈希的数据; + * + * @param bytes + */ + void update(byte[] bytes, int offset, int len); + + /** + * 完成哈希计算并返回结果; + * + * @return + */ + byte[] complete(); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/ShaUtils.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/ShaUtils.java new file mode 100644 index 00000000..34ee84f6 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/security/ShaUtils.java @@ -0,0 +1,150 @@ +package com.jd.blockchain.utils.security; + +import java.io.IOException; +import java.io.InputStream; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +/** + * 摘要工具类 + * + * @author haiq + * + */ +public class ShaUtils { + + /** + * 对指定的字节数组进行 SHA128 哈希; + * @param bytes bytes + * @return 返回长度为 16 的字节数组; + */ + public static byte[] hash_128(byte[] bytes) { + byte[] hash256Bytes = hash_256(bytes); + return Arrays.copyOf(hash256Bytes, 16); + } + + /** + * 对指定的字节数组进行 SHA256 哈希; + * @param bytes bytes + * @return 返回长度为 32 的字节数组; + */ + public static byte[] hash_256(byte[] bytes) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + md.update(bytes); + return md.digest(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 对指定的字节数组进行 SHA256 哈希; + * @param bytes bytes + * @param outputBuffer outputBuffer + * @return 返回长度为 32 的字节数组; + */ + public static int hash_256(byte[] bytes, byte[] outputBuffer) { + return hash_256(bytes, outputBuffer, 0, outputBuffer.length); + } + + /** + * 对指定的字节数组进行 SHA256 哈希; + * @param bytes bytes + * @param outputBuffer outputBuffer + * @param offset offset + * @param length length + * @return 返回长度为 32 的字节数组; + */ + public static int hash_256(byte[] bytes, byte[] outputBuffer, int offset, int length) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + md.update(bytes); + return md.digest(outputBuffer, offset, length); + } catch (NoSuchAlgorithmException | DigestException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static byte[] hash_256(InputStream input) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + byte[] buff = new byte[64]; + int len = 0; + while ((len = input.read(buff)) > 0) { + md.update(buff, 0, len); + } + return md.digest(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static SHA256Hash hash_256() { + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + return new SHA256HashImpl(md); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + // /** + // * 组装待签名数据 + // * + // * @param userAgent + // * 客户端 + // * @param url + // * 请求路径 + // * @param requestBody + // * 请求体 可为空 + // * @return + // */ + // @Deprecated + // public static byte[] getData(byte[] userAgent, byte[] url, byte[] + // requestBody) { + // + // byte[] bytes = new byte[userAgent.length + url.length + (requestBody == null + // ? 0 : requestBody.length)]; + // System.arraycopy(userAgent, 0, bytes, 0, userAgent.length); + // System.arraycopy(url, 0, bytes, userAgent.length, url.length); + // if (requestBody != null) { + // System.arraycopy(requestBody, 0, bytes, userAgent.length + url.length, + // requestBody.length); + // } + // return bytes; + // } + + private static class SHA256HashImpl implements SHA256Hash { + + private MessageDigest md; + + public SHA256HashImpl(MessageDigest md) { + this.md = md; + } + + @Override + public void update(byte[] bytes) { + md.update(bytes, 0, bytes.length); + } + + @Override + public void update(byte[] bytes, int offset, int len) { + md.update(bytes, offset, len); + } + + @Override + public byte[] complete() { + return md.digest(); + } + + } + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/transfer/AsyncSendable.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/transfer/AsyncSendable.java new file mode 100644 index 00000000..1e399fa3 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/transfer/AsyncSendable.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.utils.transfer; + +import com.jd.blockchain.utils.concurrent.AsyncFuture; + +/** + * AsyncMessageSendable 是对异步发送操作的抽象; + * + * @author haiq + * + * @param + */ +public interface AsyncSendable { + + /** + * 异步发送消息; + * + * @param message + * @return + */ + public AsyncFuture asyncSend(TData message); + +} diff --git a/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/transfer/Sendable.java b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/transfer/Sendable.java new file mode 100644 index 00000000..27573193 --- /dev/null +++ b/source/utils/utils-common/src/main/java/com/jd/blockchain/utils/transfer/Sendable.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.utils.transfer; + +/** + * Sendable 是发送操作的抽象; + * + * @author haiq + * + */ +public interface Sendable { + + /** + * 发送消息; + * + * @param message + */ + public void send(TData message); + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/ArgumentSetTest.java b/source/utils/utils-common/src/test/java/test/my/utils/ArgumentSetTest.java new file mode 100644 index 00000000..95a10767 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/ArgumentSetTest.java @@ -0,0 +1,23 @@ +package test.my.utils; + +import static org.junit.Assert.*; + +import org.junit.Test; + +import com.jd.blockchain.utils.ArgumentSet; + +public class ArgumentSetTest { + + @Test + public void test() { + String[] args = { "-n", "my", "-o", "./keys" }; + ArgumentSet argSet = ArgumentSet.resolve(args, ArgumentSet.setting().prefix("-n", "-o")); + + assertNotNull(argSet.getArg("-n")); + assertEquals("my",argSet.getArg("-n").getValue()); + assertNotNull(argSet.getArg("-o")); + assertEquals("./keys",argSet.getArg("-o").getValue()); + + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/PropertiesUtilsTest.java b/source/utils/utils-common/src/test/java/test/my/utils/PropertiesUtilsTest.java new file mode 100644 index 00000000..881bca26 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/PropertiesUtilsTest.java @@ -0,0 +1,132 @@ +package test.my.utils; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.Properties; + +import org.junit.Test; + +import com.jd.blockchain.utils.PropertiesUtils; + +public class PropertiesUtilsTest { + + public static class TestData { + + private String name; + + private int id; + + private boolean enable; + + private long life; + + private HomeAddress homeAddress; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public long getLife() { + return life; + } + + public void setLife(long life) { + this.life = life; + } + + public HomeAddress getHomeAddress() { + return homeAddress; + } + + public void setHomeAddress(HomeAddress homeAddress) { + this.homeAddress = homeAddress; + } + + } + + public static class HomeAddress { + + private int number; + + private String street; + + public int getNumber() { + return number; + } + + public void setNumber(int number) { + this.number = number; + } + + public String getStreet() { + return street; + } + + public void setStreet(String street) { + this.street = street; + } + } + + @Test + public void test() { + Properties props = new Properties(); + props.setProperty("name", "john"); + props.setProperty("id", "10"); + props.setProperty("enable", "true"); + props.setProperty("life", "120088835993666666"); + props.setProperty("remark", "dfdafew"); + props.setProperty("homeAddress.number", "18"); + props.setProperty("homeAddress.street", "newyork"); + + TestData data = PropertiesUtils.createInstance(TestData.class, props); + assertNotNull(data); + assertEquals("john", data.getName()); + assertEquals(10, data.getId()); + assertEquals(true, data.isEnable()); + assertEquals(120088835993666666L, data.getLife()); + assertNotNull(data.getHomeAddress()); + assertEquals(18, data.getHomeAddress().getNumber()); + assertEquals("newyork", data.getHomeAddress().getStreet()); + + Properties props1 = new Properties(); + props1.setProperty("abc.name", "john"); + props1.setProperty("abc.id", "10"); + props1.setProperty("abc.enable", "true"); + props1.setProperty("abc.life", "120088835993666666"); + props1.setProperty("remark", "dfdafew"); + props1.setProperty("abc.homeAddress.number", "18"); + + TestData data1 = PropertiesUtils.createInstance(TestData.class, props1, "abc."); + assertNotNull(data1); + assertEquals("john", data1.getName()); + assertEquals(10, data1.getId()); + assertEquals(true, data1.isEnable()); + assertEquals(120088835993666666L, data1.getLife()); + assertNotNull(data1.getHomeAddress()); + assertEquals(18, data1.getHomeAddress().getNumber()); + assertNull(data1.getHomeAddress().getStreet()); + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/concurrent/FutureTest.java b/source/utils/utils-common/src/test/java/test/my/utils/concurrent/FutureTest.java new file mode 100644 index 00000000..d60c43b9 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/concurrent/FutureTest.java @@ -0,0 +1,10 @@ +package test.my.utils.concurrent; + +public class FutureTest { + + public static void main(String[] args) { + // TODO Auto-generated method stub + + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/io/BytesEncodingTest.java b/source/utils/utils-common/src/test/java/test/my/utils/io/BytesEncodingTest.java new file mode 100644 index 00000000..f7548505 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/io/BytesEncodingTest.java @@ -0,0 +1,86 @@ +package test.my.utils.io; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import org.junit.Test; + +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesEncoding; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.NumberMask; + +public class BytesEncodingTest { + + @Test + public void testWriteAndRead() throws UnsupportedEncodingException { + // 针对正常数据的测试; + byte[] data = UUID.randomUUID().toString().getBytes("UTF-8"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int s = BytesEncoding.write(data, NumberMask.TINY, out); + + byte[] outBytes = out.toByteArray(); + assertEquals(outBytes.length, s); + assertEquals(data.length + 1, s); + + ByteArrayInputStream in = new ByteArrayInputStream(outBytes); + byte[] resovledBytes = BytesEncoding.read(NumberMask.TINY, in); + + assertTrue(BytesUtils.equals(data, resovledBytes)); + + // 针对 null 的测试; + data = null; + + out = new ByteArrayOutputStream(); + s = BytesEncoding.write(data, NumberMask.TINY, out); + + outBytes = out.toByteArray(); + assertEquals(outBytes.length, s); + assertEquals(1, s); + + in = new ByteArrayInputStream(outBytes); + resovledBytes = BytesEncoding.read(NumberMask.TINY, in); + + assertTrue(BytesUtils.equals(BytesUtils.EMPTY_BYTES, resovledBytes)); + } + + @Test + public void testWriteAndRead1() throws UnsupportedEncodingException { + // 针对正常数据的测试; + ByteArray data = ByteArray.parseString(UUID.randomUUID().toString(), "UTF-8"); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + int s = BytesEncoding.write(data, NumberMask.TINY, out); + + byte[] outBytes = out.toByteArray(); + assertEquals(outBytes.length, s); + assertEquals(data.size() + 1, s); + + ByteArrayInputStream in = new ByteArrayInputStream(outBytes); + byte[] resovledBytes = BytesEncoding.read(NumberMask.TINY, in); + + assertTrue(BytesUtils.equals(data.bytes(), resovledBytes)); + + // 针对 null 的测试; + data = null; + + out = new ByteArrayOutputStream(); + s = BytesEncoding.write(data, NumberMask.TINY, out); + + outBytes = out.toByteArray(); + assertEquals(outBytes.length, s); + assertEquals(1, s); + + in = new ByteArrayInputStream(outBytes); + resovledBytes = BytesEncoding.read(NumberMask.TINY, in); + + assertTrue(BytesUtils.equals(BytesUtils.EMPTY_BYTES, resovledBytes)); + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/io/BytesTest.java b/source/utils/utils-common/src/test/java/test/my/utils/io/BytesTest.java new file mode 100644 index 00000000..b408c0f4 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/io/BytesTest.java @@ -0,0 +1,25 @@ +package test.my.utils.io; + +import static org.junit.Assert.*; + +import java.util.Arrays; + +import org.junit.Test; + +import com.jd.blockchain.utils.Bytes; +import com.jd.blockchain.utils.io.BytesUtils; + +public class BytesTest { + + @Test + public void test() { + Bytes prefix = Bytes.fromString("A"); + byte[] textBytes = BytesUtils.toBytes("B"); + Bytes key1 = new Bytes(prefix, textBytes); + byte[] key1Bytes = key1.toBytes(); + assertEquals(prefix.size() + textBytes.length, key1.size()); + assertEquals(prefix.size() + textBytes.length, key1Bytes.length); + assertEquals(Arrays.hashCode(key1Bytes), key1.hashCode()); + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/io/BytesUtilsTest.java b/source/utils/utils-common/src/test/java/test/my/utils/io/BytesUtilsTest.java new file mode 100644 index 00000000..e3afd01b --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/io/BytesUtilsTest.java @@ -0,0 +1,257 @@ +package test.my.utils.io; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Random; + +import org.bouncycastle.pqc.math.linearalgebra.ByteUtils; +import org.junit.Test; + +import com.jd.blockchain.utils.io.BytesUtils; + +public class BytesUtilsTest { + + @Test + public void testByteOutputInput() { + // 边界测试; + assertByteOutputInput((byte) 0); + assertByteOutputInput((byte) 1); + assertByteOutputInput((byte) -1); + assertByteOutputInput((byte) 128); + assertByteOutputInput((byte) 255); + assertByteOutputInput(Byte.MAX_VALUE); + assertByteOutputInput(Byte.MIN_VALUE); + + byte[] emptyBytes = {}; + ByteArrayInputStream emptyIn = new ByteArrayInputStream(emptyBytes); + Exception err = null; + try { + BytesUtils.readByte(emptyIn); + } catch (Exception e) { + err = e; + } + assertTrue(err != null); + } + + private void assertByteOutputInput(byte value) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesUtils.writeByte(value, out); + byte[] bytes = out.toByteArray(); + assertEquals(bytes[0], value); + + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + byte val1 = BytesUtils.readByte(in); + + assertEquals(value, val1); + } + + @Test + public void testIntegerToBytes() { + assertIntTest(0); + assertIntTest(Integer.MAX_VALUE); + assertIntTest(Integer.MIN_VALUE); + assertIntTest(255); + assertIntTest(256); + } + + private void assertIntTest(int i) { + byte[] bytes = BytesUtils.toBytes(i); + int reallyInt = BytesUtils.toInt(bytes); + assertEquals(i, reallyInt); + } + + @Test + public void testIntTestLowAlign() { + int n = 65535; + byte[] bytes = BytesUtils.toBytes(n); + int reallyInt = BytesUtils.toInt(bytes, 2, 2, false); + assertEquals(n, reallyInt); + + n = 0; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 2, 2, false); + assertEquals(n, reallyInt); + + n = 255; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 2, 2, false); + assertEquals(n, reallyInt); + + n = 256; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 2, 2, false); + assertEquals(n, reallyInt); + + n = 255; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 3, 1, false); + assertEquals(n, reallyInt); + + n = 16777215; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 1, 3, false); + assertEquals(n, reallyInt); + + n = 65535; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 1, 3, false); + assertEquals(n, reallyInt); + + n = 255; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 1, 3, false); + assertEquals(n, reallyInt); + + n = 0; + bytes = BytesUtils.toBytes(n); + reallyInt = BytesUtils.toInt(bytes, 1, 3, false); + assertEquals(n, reallyInt); + } + + @Test + public void testLongIntegerToBytes() { + assertLongTest(0); + assertLongTest(Integer.MAX_VALUE); + assertLongTest(Integer.MIN_VALUE); + assertLongTest(Long.MAX_VALUE); + assertLongTest(Long.MIN_VALUE); + assertLongTest(255); + assertLongTest(256); + } + + private void assertLongTest(long i) { + byte[] bytes = BytesUtils.toBytes(i); + long reallyInt = BytesUtils.toLong(bytes); + assertEquals(i, reallyInt); + } + + @Test + public void testIntegerByteCasting() { + testIntByteCasting(0); + testIntByteCasting(-1); + testIntByteCasting(1); + testIntByteCasting(255); + testIntByteCasting(256); + testIntByteCasting(Integer.MAX_VALUE); + testIntByteCasting(Integer.MIN_VALUE); + } + + private void testIntByteCasting(int v) { + byte b1 = (byte) (v & 0xFF); + byte b2 = (byte) v; + assertTrue(b1 == b2); + } + + @Test + public void testWriteByte() { + assertByteWriting((byte) 0); + assertByteWriting((byte) 128); + assertByteWriting((byte) 1); + assertByteWriting((byte) 0x0F); + assertByteWriting((byte) 0xF0); + assertByteWriting((byte) 255); + } + + private void assertByteWriting(byte bt) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + out.write(bt); + byte[] buff = out.toByteArray(); + assertEquals(bt, buff[0]); + + ByteArrayInputStream in = new ByteArrayInputStream(buff); + int val = in.read(); + byte btIn = (byte) (val & 0xFF); + byte btIn1 = (byte) (val); + + assertEquals(bt, btIn); + assertEquals(bt, btIn1); + } + + @Test + public void testShortCasting() { + doTestShortCasting((short) 0); + doTestShortCasting((short) -1); + doTestShortCasting((short) 1); + doTestShortCasting((short) 255); + doTestShortCasting((short) -255); + doTestShortCasting(Short.MIN_VALUE); + doTestShortCasting(Short.MAX_VALUE); + } + + private void doTestShortCasting(short value) { + byte[] bytes = BytesUtils.toBytes(value); + assertEquals(2, bytes.length); + + short deValue = BytesUtils.toShort(bytes, 0); + assertEquals(value, deValue); + } + + @Test + public void testConcatBytes() { + Random rand = new Random(); + byte[] bs1 = new byte[64]; + byte[] bs2 = new byte[0]; + byte[] bs3 = new byte[512]; + + rand.nextBytes(bs1); + rand.nextBytes(bs3); + + byte[] bsAll = BytesUtils.concat(bs1, bs2, bs3); + assertEquals(bs1.length + bs2.length + bs3.length, bsAll.length); + for (int i = 0; i < bs1.length; i++) { + assertEquals(bs1[i], bsAll[i]); + } + for (int i = 0; i < bs3.length; i++) { + assertEquals(bs3[i], bsAll[bs1.length + i]); + } + } + + @Test + public void testStreamingReadWrite() { + testStreamingReadWriteInt(0); + testStreamingReadWriteInt(1); + testStreamingReadWriteInt(2); + testStreamingReadWriteInt(256); + testStreamingReadWriteInt(128); + testStreamingReadWriteInt(-1); + testStreamingReadWriteInt(-128); + testStreamingReadWriteInt(Integer.MAX_VALUE); + testStreamingReadWriteInt(Integer.MIN_VALUE); + + testStreamingReadWriteLong(0); + testStreamingReadWriteLong(1); + testStreamingReadWriteLong(2); + testStreamingReadWriteLong(256); + testStreamingReadWriteLong(128); + testStreamingReadWriteLong(-1); + testStreamingReadWriteLong(-128); + testStreamingReadWriteLong(Integer.MAX_VALUE); + testStreamingReadWriteLong(Integer.MIN_VALUE); + testStreamingReadWriteLong(Long.MAX_VALUE); + testStreamingReadWriteLong(Long.MIN_VALUE); + } + + private void testStreamingReadWriteInt(int value) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesUtils.writeInt(value, out); + byte[] bytes0 = BytesUtils.toBytes(value); + assertTrue(BytesUtils.equals(bytes0, out.toByteArray())); + + ByteArrayInputStream in = new ByteArrayInputStream(bytes0); + int v = BytesUtils.readInt(in); + assertEquals(value, v); + } + + private void testStreamingReadWriteLong(long value) { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BytesUtils.writeLong(value, out); + byte[] bytes0 = BytesUtils.toBytes(value); + assertTrue(BytesUtils.equals(bytes0, out.toByteArray())); + + ByteArrayInputStream in = new ByteArrayInputStream(bytes0); + long v = BytesUtils.readLong(in); + assertEquals(value, v); + } +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/security/AESUtilsTest.java b/source/utils/utils-common/src/test/java/test/my/utils/security/AESUtilsTest.java new file mode 100644 index 00000000..ee907916 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/security/AESUtilsTest.java @@ -0,0 +1,153 @@ +package test.my.utils.security; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.util.Random; +import java.util.UUID; + +import org.junit.Test; + +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.security.AESUtils; +import com.jd.blockchain.utils.security.ShaUtils; + +public class AESUtilsTest { + + @Test + public void testAESKeyGeneration() throws UnsupportedEncodingException { + String seedStr = UUID.randomUUID().toString(); + byte[] seed = seedStr.getBytes("UTF-8"); + System.out.println("seed.lenght=" + seed.length); + + // 验证:同一个种子,生成的秘钥总是相同的; + String key1 = AESUtils.generateKey128_Hex(seed); + String key2 = AESUtils.generateKey128_Hex(seed); + String key3 = AESUtils.generateKey128_Hex(seed); + + assertEquals(key1, key2); + assertEquals(key1, key3); + + // 验证:不同的种子,生成的秘钥总是不同的; + byte[] seed2 = UUID.randomUUID().toString().getBytes("UTF-8"); + String newKey = AESUtils.generateKey128_Hex(seed2); + assertNotEquals(key1, newKey); + + // 验证:随机秘钥总是不同的; + String randomKey1 = AESUtils.generateKey128_Hex(); + String randomKey2 = AESUtils.generateKey128_Hex(); + String randomKey3 = AESUtils.generateKey128_Hex(); + assertNotEquals(randomKey1, randomKey2); + assertNotEquals(randomKey1, randomKey3); + assertNotEquals(randomKey2, randomKey3); + + // 验证:更长的种子也可以支持; + byte[] longSeed = (seedStr + seedStr + seedStr + seedStr).getBytes("UTF-8"); + System.out.println("longSeed.length=" + longSeed.length); + String keyFromLongSeed = AESUtils.generateKey128_Hex(longSeed); + String keyFromLongSeed2 = AESUtils.generateKey128_Hex(longSeed); + assertNotEquals(key1, keyFromLongSeed2); + assertEquals(keyFromLongSeed, keyFromLongSeed2); + + // 验证:对更长的种子进行 hash 后再生成 key; + byte[] hashSeed = ShaUtils.hash_256(longSeed); + System.out.println("hashSeed.length=" + hashSeed.length); + String keyFromHashSeed = AESUtils.generateKey128_Hex(hashSeed); + String keyFromHashSeed2 = AESUtils.generateKey128_Hex(hashSeed); + + assertNotEquals(key1, keyFromHashSeed); + assertNotEquals(keyFromLongSeed, keyFromHashSeed); + assertEquals(keyFromHashSeed, keyFromHashSeed2); + } + + @Test + public void testAESEncryptionWithRandomKey() throws UnsupportedEncodingException { + String keyString = AESUtils.generateKey128_Hex(); + + byte[] origBytes = UUID.randomUUID().toString().getBytes("UTF-8"); + + byte[] enBytes = AESUtils.encrypt(origBytes, keyString); + byte[] deBytes = AESUtils.decrypt(enBytes, keyString); + + assertEquals(origBytes.length, deBytes.length); + for (int i = 0; i < origBytes.length; i++) { + assertEquals(origBytes[i], deBytes[i]); + } + } + + /** + * 根据同一个 seed 生成的秘钥,能够加密,并解密恢复原始内容; + * + * @throws UnsupportedEncodingException + */ + @Test + public void testAESEncryptionWithSeed() throws UnsupportedEncodingException { + byte[] seed = UUID.randomUUID().toString().getBytes("UTF-8"); + byte[] encryptedKey = AESUtils.generateKey128_Bytes(seed); + + byte[] plainBytes = UUID.randomUUID().toString().getBytes("UTF-8"); + String plainContent = HexUtils.encode(plainBytes); + byte[] encryptedBytes = AESUtils.encrypt(plainBytes, encryptedKey); + + byte[] decryptedKey = AESUtils.generateKey128_Bytes(seed); + byte[] decryptedBytes = AESUtils.decrypt(encryptedBytes, decryptedKey); + String decryptedContent = HexUtils.encode(decryptedBytes); + + assertEquals(plainContent, decryptedContent); + } + + @Test + public void testAESEnryptionConsistance() throws UnsupportedEncodingException { + byte[] seed = UUID.randomUUID().toString().getBytes("UTF-8"); + byte[] encryptedKey = AESUtils.generateKey128_Bytes(seed); + + byte[] plainBytes = UUID.randomUUID().toString().getBytes("UTF-8"); + + byte[] encryptedBytes = AESUtils.encrypt(plainBytes, encryptedKey); + byte[] encryptedBytes1 = AESUtils.encrypt(plainBytes, encryptedKey); + byte[] encryptedBytes2 = AESUtils.encrypt(plainBytes, encryptedKey); + byte[] encryptedBytes3 = AESUtils.encrypt(plainBytes, encryptedKey); + + assertTrue(BytesUtils.equals(encryptedBytes, encryptedBytes1)); + assertTrue(BytesUtils.equals(encryptedBytes1, encryptedBytes2)); + assertTrue(BytesUtils.equals(encryptedBytes2, encryptedBytes3)); + } + + /** + * 测试对任意长度的明文数据进行加解密的正确性; + */ + @Test + public void testLongBytes() { + testLongBytes_128(101); + testLongBytes_128(171); + testLongBytes_128(1023); + testLongBytes_128(1024); + testLongBytes_128(1025); + } + + /** + * 测试在 128 长度密码下对任意长度的输入进行加密解密的正确性; + * @param size + */ + private void testLongBytes_128(int size) { + byte[] data = new byte[size]; + Random rand = new Random(); + rand.nextBytes(data); + + System.out.println("(" + size + ")Bytes data=[" + HexUtils.encode(data) + "]"); + + byte[] key = AESUtils.generateKey128_Bytes(); + + byte[] encData = AESUtils.encrypt(data, key); + + byte[] decData = AESUtils.decrypt(encData, key); + + assertTrue(BytesUtils.equals(data, decData)); + } + + + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/security/RSAUtilsTest.java b/source/utils/utils-common/src/test/java/test/my/utils/security/RSAUtilsTest.java new file mode 100644 index 00000000..45caf3fb --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/security/RSAUtilsTest.java @@ -0,0 +1,60 @@ +package test.my.utils.security; + +import static org.junit.Assert.*; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import org.junit.Test; + +import com.jd.blockchain.utils.codec.HexUtils; +import com.jd.blockchain.utils.security.AESUtils; +import com.jd.blockchain.utils.security.RSAKeyPair; +import com.jd.blockchain.utils.security.RSAUtils; + +public class RSAUtilsTest { + + @Test + public void testEncription() throws UnsupportedEncodingException { + // 测试:公钥加密,私钥解密; + RSAKeyPair keyPair = RSAUtils.generateKey512(); + System.out.println("New-PUB-KEY=" + keyPair.getPublicKey_Base58()); + System.out.println("New-PRIV-KEY=" + keyPair.getPrivateKey_Base58()); + + String content = UUID.randomUUID().toString(); + byte[] contentBytes = content.getBytes("UTF-8"); + + System.out.println("要加密的数据长度:" + contentBytes.length); + byte[] enContentBytes = RSAUtils.encryptByPublicKey_Base58(contentBytes, keyPair.getPublicKey_Base58()); + + byte[] deContentBytes = RSAUtils.decryptByPrivateKey_Base58(enContentBytes, keyPair.getPrivateKey_Base58()); + String deContent = new String(deContentBytes, "UTF-8"); + + assertEquals(content, deContent); + } + + public static final String PUB_KEY = "rbanryFCC6yJnCRCQgVWyYXvqkSBz4k8KgpbVavFUopm6EcXY7217hqU7sa5iEvG2RbYUqcswYvdVikJqWADXNh6XDs1AB6MWHdhNrh4ha7rAVfDM6H9ho3KxHWXKSAc"; + + public static final String PRIV_KEY = "2YxGPrDW2hwdAfrXymm5pJb1ftXzKmPqYBqYdgQWAcBRzMk8nDM7sGeDf3cJZEQaremdVbfQqUrhRtz2MV31TX8qABm3ChTsszGUmoxvybJPaVd513adZwLyo54NS1GuyLxPyD2N32uqYnKtKojTrxfWZmoLpACmoqhMzt68Sn69ULd84j24tt8VBpSWEH1Xsr9hCtBB65HT3f6FH5X5HYMU6ueaPrx8Sh4zFiEW5eLM4xJEPLjf3iaWatN9YV9QPeyvavicNYCtmJWQ6sv13xKR9DscWVJ61EjSxTQQMQe1sA9Anewjmk1bRUbnoKebM2kaGK9Q3Gg7qx8QwoAvRfPP9A6pyUPPaE33WuKDKZ2QPzYQ531TXJA8hYwnCvfcixp3cJqGtFNEkhBu7H4DSqchnWfVJ62HJFBB2L96Fxkiudyqh8ejjsweWVKPw1aYoNzreEnRpMHsLoxAN6eRFA3NV"; + + public static final String DATA_HEX = "f61610cba4c92c268a2bd73e963a270b"; + + public static final String CHIPER_DATA = "b127ffbbf6d570cea43aba7494bf04e1d1263d130b922cde32d558de3fdd88f2c205ba8151fdfcae7bb5c075fad10524cfde89b6b3be4ad8b2fb0db38901ea9c"; + + @Test + public void testEncryptAndDecrypt() { + byte[] contentBytes = HexUtils.decode(DATA_HEX); + System.out.println("要加密的数据长度:" + contentBytes.length); + + byte[] enContentBytes = RSAUtils.encryptByPublicKey_Base58(contentBytes, PUB_KEY); + System.out.println("CHIPER-DATA=[" + HexUtils.encode(enContentBytes) + "]"); + + byte[] deContentBytes = RSAUtils.decryptByPrivateKey_Base58(enContentBytes, PRIV_KEY); + String deContent = HexUtils.encode(deContentBytes); + System.out.println("DECRYPT-DATA=[" + HexUtils.encode(enContentBytes) + "]"); + + assertEquals(DATA_HEX, deContent); + + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/security/RandomUtilsTest.java b/source/utils/utils-common/src/test/java/test/my/utils/security/RandomUtilsTest.java new file mode 100644 index 00000000..f226cc17 --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/security/RandomUtilsTest.java @@ -0,0 +1,51 @@ +package test.my.utils.security; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import org.junit.Test; + +import com.jd.blockchain.utils.security.RandomUtils; + +public class RandomUtilsTest { + + @Test + public void testGenerateRandomBytesWithDifferntSeed() throws UnsupportedEncodingException { + String seedStr = UUID.randomUUID().toString(); + byte[] seed = seedStr.getBytes("UTF-8"); + String seedStr2 = UUID.randomUUID().toString(); + byte[] seed2 = seedStr2.getBytes("UTF-8"); + System.out.println("seed.lenght=" + seed.length); + + byte[] random1 = RandomUtils.generateRandomBytes(16, seed); + byte[] random2 = RandomUtils.generateRandomBytes(16, seed2); + + assertNoEqualBytes(random1, random2); + + random1 = RandomUtils.generateRandomBytes(32, seed); + random2 = RandomUtils.generateRandomBytes(32, seed2); + + assertNoEqualBytes(random1, random2); + + random1 = RandomUtils.generateRandomBytes(64, seed); + random2 = RandomUtils.generateRandomBytes(64, seed2); + + assertNoEqualBytes(random1, random2); + } + + private void assertNoEqualBytes(byte[] random1, byte[] random2) { + boolean notEqual = random1.length != random2.length; + assertEquals(random1.length, random2.length); + for (int i = 0; i < random1.length; i++) { + notEqual = notEqual | (random1[i] != random2[i]); + if (notEqual) { + break; + } + } + assertTrue(notEqual); + } + +} diff --git a/source/utils/utils-common/src/test/java/test/my/utils/security/ShaUtilsTest.java b/source/utils/utils-common/src/test/java/test/my/utils/security/ShaUtilsTest.java new file mode 100644 index 00000000..dfd6cf3b --- /dev/null +++ b/source/utils/utils-common/src/test/java/test/my/utils/security/ShaUtilsTest.java @@ -0,0 +1,102 @@ +package test.my.utils.security; + +import static org.junit.Assert.*; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Random; +import java.util.UUID; + +import org.junit.Test; + +import com.jd.blockchain.utils.security.RandomUtils; +import com.jd.blockchain.utils.security.ShaUtils; + +public class ShaUtilsTest { + +// @Test +// public void testHash_128() throws UnsupportedEncodingException { +// String text = "the text with fixed size"; +// byte[] hashBytes = ShaUtils.hash_128(text.getBytes("UTF-8")); +// assertEquals(16, hashBytes.length); +// +// text = UUID.randomUUID().toString(); +// hashBytes = ShaUtils.hash_128(text.getBytes("UTF-8")); +// assertEquals(16, hashBytes.length); +// +// StringBuilder bigText = new StringBuilder(); +// for (int i = 0; i < 256; i++) { +// bigText.append((char)(97+(i% 20))); +// } +// hashBytes = ShaUtils.hash_128(bigText.toString().getBytes("UTF-8")); +// assertEquals(16, hashBytes.length); +// } + + @Test + public void testHash_256ByteArray() throws UnsupportedEncodingException { + String text = "the text with fixed size"; + byte[] hashBytes = ShaUtils.hash_256(text.getBytes("UTF-8")); + assertEquals(32, hashBytes.length); + + text = UUID.randomUUID().toString(); + hashBytes = ShaUtils.hash_256(text.getBytes("UTF-8")); + assertEquals(32, hashBytes.length); + + StringBuilder bigText = new StringBuilder(); + for (int i = 0; i < 512; i++) { + bigText.append((char)(97+(i% 20))); + } + hashBytes = ShaUtils.hash_256(bigText.toString().getBytes("UTF-8")); + assertEquals(32, hashBytes.length); + } + + + /** + * 验证采用不同的缓存数组大小进行 hash 计算是否会影响其计算结果;
+ * + * 注:显然,算法本身的效果是不会受算法调用方式的影响的;测试的结果也表明这一点; + */ + @Test + public void testHash_256_withDiffBuffSize() { + byte[] randBytes = RandomUtils.generateRandomBytes(256); + + ByteArrayInputStream in = new ByteArrayInputStream(randBytes); + byte[] hash1 = hash_256(in, 32); + + in = new ByteArrayInputStream(randBytes); + byte[] hash2 = hash_256(in, 32); + + in = new ByteArrayInputStream(randBytes); + byte[] hash3 = hash_256(in, 64); + + in = new ByteArrayInputStream(randBytes); + byte[] hash4 = hash_256(in, 128); + + assertArrayEquals(hash1, hash2); + assertArrayEquals(hash1, hash3); + assertArrayEquals(hash1, hash4); + } + + + private static byte[] hash_256(InputStream input, int buffSize) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + byte[] buff =new byte[buffSize]; + int len = 0; + while((len=input.read(buff)) > 0){ + md.update(buff, 0, len); + } + return md.digest(); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } +} diff --git a/source/utils/utils-http/.gitignore b/source/utils/utils-http/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/utils/utils-http/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/utils/utils-http/pom.xml b/source/utils/utils-http/pom.xml new file mode 100644 index 00000000..ef13d9e8 --- /dev/null +++ b/source/utils/utils-http/pom.xml @@ -0,0 +1,71 @@ + + 4.0.0 + + com.jd.blockchain + utils + 0.8.2.RELEASE + + + + 0.5.0-SNAPSHOT + 0.5.0-SNAPSHOT + + + utils-http + + + com.jd.blockchain + utils-common + ${project.version} + + + com.jd.blockchain + utils-serialize + ${project.version} + + + com.jd.blockchain + utils-web-server + test + ${project.version} + + + + org.apache.httpcomponents + httpclient + + + org.springframework + spring-core + + + org.springframework + spring-beans + + + + + + + \ No newline at end of file diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpAction.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpAction.java new file mode 100644 index 00000000..aae7edcf --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpAction.java @@ -0,0 +1,66 @@ +package com.jd.blockchain.utils.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * HTTP 服务方法; + * + * @author haiq + * + */ +@Target({ ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +public @interface HttpAction { + + /** + * 请求路径; + * + * 默认为空; + * + * 此时按照 service 的 path 属性+方法名 产生最终的请求路径; + * + * @return + */ + public String path() default ""; + + /** + * HTTP 请求方法; + * + * @return + */ + public HttpMethod method(); + + /** + * HTTP 的 Content-Type; + * @return + */ + public String contentType() default ""; + + + + /** + * 自定义的返回值转换器;必须实现 RequestParamFilter 接口; + * + * @return + */ + public Class requestParamFilter() default RequestParamFilter.class; + + /** + * 自定义的返回值转换器;必须实现 ResponseConverter 接口; + * + * @return + */ + public Class responseConverter() default ResponseConverter.class; + + /** + * 当检测到 http 错误时是否在引发的 HttpStatusException 中包含回复的内容; + * + * 默认为 false; + * + * @return + */ + public boolean resolveContentOnHttpError() default false; +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpMethod.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpMethod.java new file mode 100644 index 00000000..820ca410 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpMethod.java @@ -0,0 +1,19 @@ +package com.jd.blockchain.utils.http; + +/** + * HTTP 请求方法; + * + * @author haiq + * + */ +public enum HttpMethod { + + GET, + + POST, + + PUT, + + DELETE + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpService.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpService.java new file mode 100644 index 00000000..e4bbe2e2 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpService.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.utils.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +public @interface HttpService { + + /** + * 服务的路径; + *
+ * 它是所有服务方法的根路径,默认为 /; + * + * @return + */ + public String path() default "/"; + + /** + * 默认的回复转换器; + *
+ * 当服务方法未指明自定义的回复转换器时,如果采用此默认回复转换器,如果服务的默认回复转换器也未设置,则采用系统预定义的默认回复转换器; + * + * @return + */ + public ClassdefaultResponseConverter() default ResponseConverter.class; + + /** + * 回复转换器工厂; + *
+ * 当服务方法上未指定的回复转换器,同时服务上也未指定默认回复转换器,则通过此属性指定的回复转换器工厂创建每个方法的回复转换器; + *
+ * 如果此属性也未设置,则采用系统预定义的默认回复转换器; + + * @return + */ + public Class responseConverterFactory() default ResponseBodyConverterFactory.class; + + /** + * 默认的请求体参数转换器; + *
+ * 当 {@link RequestBody} 标注未指定转换器实现时,则采用此处的默认配置; + * + * @return + */ + public Class defaultRequestBodyConverter() default RequestBodyConverter.class; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceConsts.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceConsts.java new file mode 100644 index 00000000..723eaaf6 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceConsts.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.utils.http; + +public interface HttpServiceConsts { + + public static final String CHARSET = "utf-8"; + + public static final String PATH_SEPERATOR = "/"; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceContext.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceContext.java new file mode 100644 index 00000000..c7300a3a --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceContext.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.http; + +/** + * HttpServiceContext 定义了 HTTP 服务代理上下文的信息; + * + * @author haiq + * + */ +public interface HttpServiceContext { + + /** + * 服务接口的类型; + * @return + */ + Class getServiceClasss(); + + /** + * 创建服务代理实例时由调用者指定的绑定对象; + * @return + */ + Object getProxyBindingData(); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceException.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceException.java new file mode 100644 index 00000000..cf76003b --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpServiceException.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.utils.http; + +/** + * HTTP 服务异常; + * + * 通过 HTTP Service Agent 调用产生的异常都将包装为 HttpServiceException 抛出; + * + * 注:操作方法通过 throws 关键字声明的异常除外,这些异常将原样抛出; + * + * @author haiq + * + */ +public class HttpServiceException extends RuntimeException{ + + private static final long serialVersionUID = 7316207586307377240L; + + public HttpServiceException() { + } + + public HttpServiceException(String message) { + super(message); + } + + public HttpServiceException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpStatusException.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpStatusException.java new file mode 100644 index 00000000..02c5bbc6 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/HttpStatusException.java @@ -0,0 +1,39 @@ +package com.jd.blockchain.utils.http; + +/** + * 用于描述调用请求发生的 http 状态码400以上的错误; + * + * @author haiq + * + */ +public class HttpStatusException extends HttpServiceException { + + private static final long serialVersionUID = 9123750807777784421L; + + private int httpCode; + +// private String content; +// +// public HttpStatusException(int httpCode, String message) { +// this(httpCode, message, null); +// } + + public HttpStatusException(int httpCode, String message) { + super(message); + this.httpCode = httpCode; +// this.content = content; + } + + /** + * http 状态吗; + * @return + */ + public int getHttpCode() { + return httpCode; + } + +// public String getContent() { +// return content; +// } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/NamedParam.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/NamedParam.java new file mode 100644 index 00000000..eec11802 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/NamedParam.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.utils.http; + +public class NamedParam { + private String name; + + private String value; + + public NamedParam(String name, String value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public String getValue() { + return value; + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/NamedParamMap.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/NamedParamMap.java new file mode 100644 index 00000000..49308a0c --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/NamedParamMap.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.utils.http; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class NamedParamMap { + + private Map> params = new LinkedHashMap<>(); + + public NamedParam[] getParams() { + List allParams = new ArrayList<>(); + for (List namedParams : params.values()) { + allParams.addAll(namedParams); + } + return allParams.toArray(new NamedParam[allParams.size()]); + } + + public NamedParam addParam(String name, String value) { + List values = params.get(name); + if (values == null) { + values = new ArrayList<>(); + params.put(name, values); + } + NamedParam p = new NamedParam(name, value); + values.add(p); + return p; + } + + public boolean isEmpty() { + return params.isEmpty(); + } + + /** + * 合并; + * @param extParams + * @param prefix 附加的新参数的前缀; + */ + public void merge(NamedParamMap extParams) { + merge(extParams, null); + } + + /** + * 合并; + * @param extParams + * @param prefix 附加的新参数的前缀; + */ + public void merge(NamedParamMap extParams, String prefix) { + if (prefix == null || prefix.length() == 0) { + NamedParam[] paramArr = extParams.getParams(); + for (NamedParam np : paramArr) { + addParam(np.getName(), np.getValue()); + } + } else { + NamedParam[] paramArr = extParams.getParams(); + for (NamedParam np : paramArr) { + addParam(prefix + np.getName(), np.getValue()); + } + } + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/PathParam.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/PathParam.java new file mode 100644 index 00000000..ac60f0c8 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/PathParam.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.utils.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.jd.blockchain.utils.http.converters.ObjectToStringConverter; + +/** + * 标识方法的一个参数映射为请求路径的一部分; + * + * 一般情况下,应该使用 String 类型作为路径参数并以此类进行标注; + * + * 但如果被标注的参数的类型不是 String 类型而是其它,则通过 toString 方法来获得实际的路径参数值; + * + * @author haiq + * + */ +@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PathParam { + + /** + * 路径中的变量名; + * + * @return + */ + public String name(); + + /** + * 参数值转换器的类型; + * + * 指定的参数值转换器必须实现 StringConverter 接口; + * + * 如果未指定,则默认通过 toString() 方法获取参数最终的文本值; + * + * @return + */ + public Class converter() default ObjectToStringConverter.class; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/PropertiesConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/PropertiesConverter.java new file mode 100644 index 00000000..18ea8f6a --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/PropertiesConverter.java @@ -0,0 +1,7 @@ +package com.jd.blockchain.utils.http; + +public interface PropertiesConverter { + + public NamedParamMap toProperties(Object arg); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestBody.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestBody.java new file mode 100644 index 00000000..6117d999 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestBody.java @@ -0,0 +1,37 @@ +package com.jd.blockchain.utils.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 标识一个方法参数如何映射为 http 请求的 body ; + * + * 注意:在一个方法中,最多只允许有一个参数被标识为 RequestBody; + * + * @author haiq + * + */ +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestBody { + + public boolean required() default true; + + /** + * 参数值转换器的类型; + * + * 指定的参数值转换器必须实现 RequestBodyConverter 接口; + * + * 如果未指定, + * + * 对于 InputStream 或 byte 数组类型,则直接输出字节内容; + * + * 对于除此之外的其它类型,则通过 Object.toString() 方法获取文本值按照指定的编码; + * + * @return + */ + public Class converter() default RequestBodyConverter.class; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestBodyConverter.java new file mode 100644 index 00000000..f71cbc80 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestBodyConverter.java @@ -0,0 +1,25 @@ +package com.jd.blockchain.utils.http; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * 标注了 {@link RequestBody} 的参数的转换器; + * + *
+ * 定义了如何将某个参数从特定类型转换为用于发送 http 请求体的输入流; + * + *
+ * 如果将多个参数都标注了 {@link RequestBody} ,则会按顺序对每一个参数调用其对应的转换器; + * + *
+ * 注:任何时候,实现者都应避免在完成一个参数写入之后主动关闭输出流; + * + * @author haiq + * + */ +public interface RequestBodyConverter { + + void write(Object param, OutputStream out) throws IOException; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParam.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParam.java new file mode 100644 index 00000000..63c57fe0 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParam.java @@ -0,0 +1,74 @@ +package com.jd.blockchain.utils.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.jd.blockchain.utils.http.converters.ObjectToStringConverter; + +/** + * 用于标识一个方法参数或一个类的成员该如何体现为 http 请求中的一个参数;

+ * + * 如果采用 HTTP GET 方法发起请求,则 RequestParam 标识的参数将以查询参数的方式通过 URL 提交;

+ * + * 如果采用 HTTP POST 方法发起请求,则 RequestParam 标识的参数将以表单字段的方式提交;

+ * + * @author haiq + * + */ +@Target({ElementType.PARAMETER, ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestParam { + + /** + * 参数名; + * + * @return + */ + public String name(); + + /** + * 参数是否必须提供;

+ * + * 如果必须的参数的值为 null ,将引发异常;

+ * + * 如果非必须的参数的值为 null,则忽略此参数;

+ * + * 默认为 true; + * + * @return + */ + public boolean required() default true; + + /** + * 是否为数组; + * + *
+ * + * 如果设为 true,则参数必须为数组类型,此参数将输出为“列表参数”; + * + * @return + */ + public boolean array() default false; + + /** + * 忽略值; + * + * 仅当 require 为 false 时有效,指示当参数的值为 null 或者与 ignoreValue 相等时将忽略参数; + * + * @return + */ + public String ignoreValue() default ""; + + /** + * 参数值转换器的类型; + * + * 指定的参数值转换器必须实现 StringConverter 接口; + * + * 如果未指定,则默认通过 toString() 方法获取参数最终的文本值; + * + * @return + */ + public Class converter() default ObjectToStringConverter.class; +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParamFilter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParamFilter.java new file mode 100644 index 00000000..ffe0f697 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParamFilter.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.utils.http; + +/** + * 请求参数过滤器; + * + * @author haiq + * + */ +public interface RequestParamFilter { + + void filter(HttpMethod requestMethod, NamedParamMap requestParams); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParamMap.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParamMap.java new file mode 100644 index 00000000..3fe6c5c8 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/RequestParamMap.java @@ -0,0 +1,58 @@ +package com.jd.blockchain.utils.http; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * 用于标识一个 POJO 类型的参数将其成员转换为请求参数表; + * + * 此标注应用在方法参数中,并将参数对象中标注为 RequestParam 的属性作为请求参数加入参数表; + * + * @author haiq + * + */ +@Target({ ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +public @interface RequestParamMap { + + /** + * 属性前缀; + * + * 当一个方法中通过 RequestParams 定义了多个参数表时,可通过定义不同的前缀以防止属性名称冲突; + * + * @return + */ + public String prefix() default ""; + + /** + * 属性分隔符; + * + * 最终的参数名由前缀+分隔符+原始参数名构成; + * + * @return + */ + public String seperator() default "."; + + /** + * 参数是否必须提供; + * + * 运行时如果必须的参数为 null 将引发异常; + * + * 默认为 true; + * + * @return + */ + public boolean required() default true; + + /** + * 自定义的转换器; + * + * 必须实现 PropertiesConverter 接口; + * + * @return + */ + public Classconverter() default PropertiesConverter.class; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/ResponseBodyConverterFactory.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/ResponseBodyConverterFactory.java new file mode 100644 index 00000000..c11c8a15 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/ResponseBodyConverterFactory.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.utils.http; + +import java.lang.reflect.Method; + +public interface ResponseBodyConverterFactory { + + ResponseConverter createResponseConverter(HttpAction actionDef, Method mth); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/ResponseConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/ResponseConverter.java new file mode 100644 index 00000000..ba496404 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/ResponseConverter.java @@ -0,0 +1,22 @@ +package com.jd.blockchain.utils.http; + +import java.io.InputStream; + +import com.jd.blockchain.utils.http.agent.ServiceRequest; + +/** + * 回复结果转换器; + * + * 用于定义如何从 http 回复的文本结果转换为一个特定的对象; + * + * 当 ResponseConvert 抛出的异常的类型存在于服务接口的操作方法声明 的异常列表中,则异常将被直接返回给调用者; + * + * @author haiq + * + */ +public interface ResponseConverter { + + // TODO 支持按 HTTP 状态进行解析; + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) throws Exception; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/StringConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/StringConverter.java new file mode 100644 index 00000000..be7f5395 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/StringConverter.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.utils.http; + +/** + * 字符串转换器; + * + * 定义了如何将某个对象转换为字符串; + * + * @author haiq + * + */ +public interface StringConverter { + + String toString(Object args); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ArgDefEntry.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ArgDefEntry.java new file mode 100644 index 00000000..3ed7c15c --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ArgDefEntry.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.utils.http.agent; + +class ArgDefEntry { + private int index; + + private Class argType; + + private TDef definition; + + public int getIndex() { + return index; + } + + public Class getArgType() { + return argType; + } + + public TDef getDefinition() { + return definition; + } + + public ArgDefEntry(int index, Class argType, TDef definition) { + this.index = index; + this.argType = argType; + this.definition = definition; + } + + +} \ No newline at end of file diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationAlgs.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationAlgs.java new file mode 100644 index 00000000..1397dafe --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationAlgs.java @@ -0,0 +1,11 @@ +package com.jd.blockchain.utils.http.agent; + +/** + * 定义了rest 接口认证算法常量值 + * @author haiq + * + */ +public class AuthorizationAlgs { + + public final static String DEFAULT = "DEFAULT"; +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationHeader.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationHeader.java new file mode 100644 index 00000000..04018364 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationHeader.java @@ -0,0 +1,59 @@ +package com.jd.blockchain.utils.http.agent; + + +/*** + * Http Authorization Header; + * + * @author haiq + * + */ +public class AuthorizationHeader implements RequestHeader{ + + /**@see com.jd.blockchain.utils.http.agent.AuthorizationAlgs*/ + private String alg; //算法类型 + + private String senderName; //开发者注册的用户名 + + private String secretKey; //秘钥 + + public AuthorizationHeader(){ + + } + + public AuthorizationHeader(String senderName, String secretKey){ + this(AuthorizationAlgs.DEFAULT, senderName, secretKey); + } + + public AuthorizationHeader(String alg, String senderName, String secretKey){ + this.alg = alg; + this.senderName = senderName; + this.secretKey = secretKey; + } + + public String getAlg() { + return alg; + } + + public String getSenderName() { + return senderName; + } + + public String getSecretKey() { + return secretKey; + } + + @Override + public String getName() { + return "Authorization"; + } + + @Override + public String getValue() { + StringBuilder authBuilder = new StringBuilder(); + authBuilder.append(alg).append(" ").append(senderName).append(":") + .append(secretKey); + return authBuilder.toString(); + } + + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationHeaderResovler.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationHeaderResovler.java new file mode 100644 index 00000000..fa92569f --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/AuthorizationHeaderResovler.java @@ -0,0 +1,13 @@ +package com.jd.blockchain.utils.http.agent; + +/** + * AuthorizationHeaderResovler 是一个根据实际的请求生成认证头部的策略接口; + * + * @author haiq + * + */ +public interface AuthorizationHeaderResovler { + + public AuthorizationHeader generateHeader(ServiceRequest request); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/CustomHeader.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/CustomHeader.java new file mode 100644 index 00000000..ef362fa3 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/CustomHeader.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.http.agent; + +public class CustomHeader implements RequestHeader{ + + private String name; + + private String value; + + public CustomHeader(String name, String value) { + this.name = name; + this.value = value; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/DefaultResponseConverterFactory.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/DefaultResponseConverterFactory.java new file mode 100644 index 00000000..9f2483fc --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/DefaultResponseConverterFactory.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.utils.http.agent; + +import java.lang.reflect.Method; + +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.ResponseBodyConverterFactory; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.converters.ByteArrayResponseConverter; +import com.jd.blockchain.utils.http.converters.JsonResponseConverter; +import com.jd.blockchain.utils.http.converters.StringResponseConverter; + +public class DefaultResponseConverterFactory implements ResponseBodyConverterFactory { + + public static final DefaultResponseConverterFactory INSTANCE = new DefaultResponseConverterFactory(); + + private DefaultResponseConverterFactory() { + } + + @Override + public ResponseConverter createResponseConverter(HttpAction actionDef, Method mth) { + Class retnClazz = mth.getReturnType(); + // create default response converter; + if (byte[].class == retnClazz) { + return ByteArrayResponseConverter.INSTANCE; + } + if (String.class == retnClazz) { + return StringResponseConverter.INSTANCE; + } + + // TODO:未处理 基本类型、输入输出流; + return new JsonResponseConverter(retnClazz); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpClientPool.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpClientPool.java new file mode 100644 index 00000000..6fe29a5c --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpClientPool.java @@ -0,0 +1,270 @@ +/** + * 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.utils.http.agent; + +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.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.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 TIME_OUT = 10 * 1000; + + private static final int CONNECT_TIME_OUT = 10 * 1000; + + private static final int SOCKET_TIME_OUT = 10 * 1000; + + 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 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 registry = RegistryBuilder + . 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 params) { + List nameValuePairs = new ArrayList(); + Set 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(); + } + } + + /** + * POST请求 + * + * @param url + * @param params + * @return String + * @throws IOException + */ + public static String post(String url, Map params) throws IOException { + HttpPost httpPost = new HttpPost(url); + config(httpPost); + setPostParams(httpPost, params); + 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; + } +} \ No newline at end of file diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpServiceAgent.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpServiceAgent.java new file mode 100644 index 00000000..ec8d386d --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpServiceAgent.java @@ -0,0 +1,1051 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.URI; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.http.Consts; +import org.apache.http.Header; +import org.apache.http.HttpHeaders; +import org.apache.http.NameValuePair; +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.HttpPut; +import org.apache.http.client.methods.HttpUriRequest; +import org.apache.http.entity.ByteArrayEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.message.BasicNameValuePair; +import org.springframework.beans.BeanUtils; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.HttpService; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.HttpServiceException; +import com.jd.blockchain.utils.http.HttpStatusException; +import com.jd.blockchain.utils.http.NamedParam; +import com.jd.blockchain.utils.http.NamedParamMap; +import com.jd.blockchain.utils.http.PathParam; +import com.jd.blockchain.utils.http.RequestBody; +import com.jd.blockchain.utils.http.RequestBodyConverter; +import com.jd.blockchain.utils.http.RequestParam; +import com.jd.blockchain.utils.http.RequestParamFilter; +import com.jd.blockchain.utils.http.RequestParamMap; +import com.jd.blockchain.utils.http.ResponseBodyConverterFactory; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.StringConverter; +import com.jd.blockchain.utils.http.converters.NullResponseConverter; +import com.jd.blockchain.utils.http.converters.StringResponseConverter; + +/** + * http 服务代理; + * + * @author haiq + * + */ +public class HttpServiceAgent { + + private static final NamedParam[] EMPTY_PARAMS = {}; + + private static Map, HttpServiceAgent> serviceAgentMap = new ConcurrentHashMap, HttpServiceAgent>(); + + private Class serviceClass; + + private RequestBodyConverter defaultRequestBodyConverter; + + private ResponseConverter defaultResponseConverter; + + private ResponseBodyConverterFactory responseConverterFactory; + + private AuthorizationHeaderResovler authorizationHeaderResolver; + + private Map actions = new HashMap<>(); + + private HttpServiceAgent(Class serviceClass, + AuthorizationHeaderResovler authResolver) { + this.serviceClass = serviceClass; + this.authorizationHeaderResolver = authResolver; + + resolveService(); + } + + /** + * 创建映射指定服务接口的 HTTP 服务代理; + * + * @param serviceClass + * 服务的接口类型; + * @param serviceEndpoint + * 连接到服务提供者服务器的相关设置; + * @param authorizationHeader + * 安全认证头部; + * @return + */ + public static T createService(Class serviceClass, ServiceEndpoint serviceEndpoint, + RequestHeader... authorizationHeaders) { + return createService(serviceClass, serviceEndpoint, null, null, authorizationHeaders); + } + + /** + * 创建映射指定服务接口的 HTTP 服务代理; + * + * @param serviceClass + * 服务的接口类型; + * @param serviceEndpoint + * 服务终结点; + * @return + */ + public static T createService(Class serviceClass, ServiceEndpoint serviceEndpoint) { + return createService(serviceClass, serviceEndpoint, (AuthorizationHeaderResovler) null); + } + + /** + * 创建映射指定服务接口的 HTTP 服务代理; + * + * @param serviceClass + * 定义了服务的接口类型; + * @param serviceEndpoint + * 服务终结点; + * @param authorizationHeaderResolver + * 安全认证头部的解析器; + * @return + */ + public static T createService(Class serviceClass, ServiceEndpoint serviceEndpoint, + AuthorizationHeaderResovler authorizationHeaderResolver) { + return createService(serviceClass, serviceEndpoint, null, authorizationHeaderResolver); + } + + /** + * 创建映射指定服务接口的 HTTP 服务代理; + * + * @param serviceClass + * 定义了服务的接口类型; + * @param serviceEndpoint + * 服务终结点; + * @param connectionManager + * 连接管理器; + * @param authorizationHeaderResolver + * 安全认证头部的解析器; + * @param headers + * 请求头部; + * @return + */ + public static T createService(Class serviceClass, ServiceEndpoint serviceEndpoint, + ServiceConnectionManager connectionManager, AuthorizationHeaderResovler authorizationHeaderResolver, + RequestHeader... headers) { + ServiceConnection connection = null; + if (connectionManager == null) { + connection = ServiceConnectionManager.connect(serviceEndpoint); + } else { + connection = connectionManager.create(serviceEndpoint); + } + return createService(serviceClass, connection, authorizationHeaderResolver, headers); + } + + /** + * 创建映射指定服务接口的 HTTP 服务代理; + * + * @param serviceClass + * 定义了服务的接口类型; + * @param serviceEndpoint + * 服务终结点; + * @param connectionManager + * 连接管理器; + * @param authorizationHeaderResolver + * 安全认证头部的解析器; + * @param headers + * 请求头部; + * @return + */ + public static T createService(Class serviceClass, ServiceConnection connection, + AuthorizationHeaderResovler authorizationHeaderResolver, RequestHeader... headers) { + return createService(serviceClass, connection, authorizationHeaderResolver, headers, null); + } + + /** + * 创建映射指定服务接口的 HTTP 服务代理; + * + * @param serviceClass + * 定义了服务的接口类型; + * @param serviceEndpoint + * 服务终结点; + * @param connectionManager + * 连接管理器; + * @param authorizationHeaderResolver + * 安全认证头部的解析器; + * @param headers + * 请求头部; + * @param bindingData + * 由调用者指定的绑定对象;
+ * 该对象将被关联到 HttpServiceContext 上;调用者可以通过此对象将某些数据对象传递到调用过程的一些处理组件上,例如 + * {@link ResponseConverter}; + * @return + */ + public static T createService(Class serviceClass, ServiceConnection connection, + AuthorizationHeaderResovler authorizationHeaderResolver, RequestHeader[] headers, Object bindingData) { + if (serviceClass == null) { + throw new IllegalArgumentException("Service class is null!"); + } + if (!(connection instanceof HttpServiceConnection)) { + throw new IllegalArgumentException( + "Illegal service connection! It must be created by the ServiceConnectionManager!"); + } + HttpServiceConnection httpConnection = (HttpServiceConnection) connection; + // 避免反复解析同一个服务类型; + HttpServiceAgent agent = serviceAgentMap.get(serviceClass); + if (agent == null) { + synchronized (serviceClass) { + agent = serviceAgentMap.get(serviceClass); + if (agent == null) { + agent = new HttpServiceAgent(serviceClass, authorizationHeaderResolver); + serviceAgentMap.put(serviceClass, agent); + } + } + } + + // CloseableHttpClient httpClient = createHttpClient(serviceEndpoint, + // connectionManager); + + ServiceInvocationHandler invocationHandler = new ServiceInvocationHandler(agent, httpConnection, headers, + bindingData); + + @SuppressWarnings("unchecked") + T serviceProxy = (T) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class[] { serviceClass }, + invocationHandler); + return serviceProxy; + } + + private void resolveService() { + // 处理服务路径; + HttpService serviceAnno = serviceClass.getAnnotation(HttpService.class); + if (serviceAnno == null) { + throw new IllegalHttpServiceDefinitionException( + "The specific service was not tag with HttpService annotation!"); + } + String servicePath = serviceAnno.path(); + servicePath = StringUtils.cleanPath(servicePath); + if (StringUtils.isEmpty(servicePath)) { + throw new IllegalHttpServiceDefinitionException( + "Illegal path or no path was specified for the HttpService!-- path=" + serviceAnno.path()); + } + + // 实例化服务的默认请求体参数转换器; + Class defaultRequestBodyConverterClazz = serviceAnno.defaultRequestBodyConverter(); + if (defaultRequestBodyConverterClazz != null && defaultRequestBodyConverterClazz != RequestBodyConverter.class) { + if (RequestBodyConverter.class.isAssignableFrom(defaultRequestBodyConverterClazz)) { + defaultRequestBodyConverter = (RequestBodyConverter) BeanUtils.instantiate(defaultRequestBodyConverterClazz); + } else { + throw new IllegalHttpServiceDefinitionException( + "The specified default request body converter of service doesn't implement the interface " + + RequestBodyConverter.class.getName() + "!"); + } + } + + // 实例化服务的默认回复转换器; + Class defaultResponseConverterClazz = serviceAnno.defaultResponseConverter(); + if (defaultResponseConverterClazz != null && defaultResponseConverterClazz != ResponseConverter.class) { + if (ResponseConverter.class.isAssignableFrom(defaultResponseConverterClazz)) { + defaultResponseConverter = (ResponseConverter) BeanUtils.instantiate(defaultResponseConverterClazz); + } else { + throw new IllegalHttpServiceDefinitionException( + "The specified default response converter of service doesn't implement the interface " + + ResponseConverter.class.getName() + "!"); + } + } + Class responseConverterFactoryClazz = serviceAnno.responseConverterFactory(); + if (responseConverterFactoryClazz != null && responseConverterFactoryClazz != ResponseBodyConverterFactory.class) { + if (ResponseBodyConverterFactory.class.isAssignableFrom(responseConverterFactoryClazz)) { + this.responseConverterFactory = (ResponseBodyConverterFactory) BeanUtils + .instantiate(responseConverterFactoryClazz); + } else { + throw new IllegalHttpServiceDefinitionException( + "The specified response converter factory of service doesn't implement the interface " + + ResponseBodyConverterFactory.class.getName() + "!"); + } + + } + + // 解析服务操作; +// Method[] mths = ReflectionUtils.getAllDeclaredMethods(serviceClass); + Method[] mths = serviceClass.getMethods(); + for (Method mth : mths) { + ServiceActionContext actionContext = resolveAction( mth, servicePath); + if (actionContext != null) { + actions.put(mth, actionContext); + } + } + } + + /** + * 解析指定的方法; + * + * 如果指定的方法不符合解析策略则返回 null; + * + * @param serviceEndpoint + * @param mth + * @param servicePath + * @return + */ + private ServiceActionContext resolveAction(Method mth, String servicePath) { + // 生成路径模板; + HttpAction actionAnno = mth.getAnnotation(HttpAction.class); + if (actionAnno == null) { + // 未声明的方法不做解析; + return null; + } + String actionPath = StringUtils.cleanPath(actionAnno.path()); + if (StringUtils.isEmpty(actionPath)) { + actionPath = mth.getName(); + } + RequestPathTemplate pathTemplate = new RequestPathTemplate(servicePath, actionPath); + + // 校验请求你方法; + if (actionAnno.method() == null) { + throw new IllegalHttpServiceDefinitionException("The http method of action was not specified!"); + } + + String contentType = actionAnno.contentType(); + if (contentType != null) { + contentType = contentType.trim(); + if (contentType.length() == 0) { + contentType = null; + } + } + + RequestParamFilter reqParamFilter = createRequestParamFilter(actionAnno); + ResponseConverter responseConverter = createResponseConverter(actionAnno, mth); + + // 获取参数定义; + // 参数列表中, RequestBody 最多只能定义一个; + RequestBodyResolverComposite bodyResolverComposite = new RequestBodyResolverComposite(); + Class[] paramTypes = mth.getParameterTypes(); + Annotation[][] paramAnnos = mth.getParameterAnnotations(); + + List> reqParamAnnos = new LinkedList>(); + List> reqParamMapAnnos = new LinkedList>(); + List> pathParamAnnos = new LinkedList>(); + for (int i = 0; i < paramTypes.length; i++) { + RequestBody reqBodyAnno = findAnnotation(RequestBody.class, paramAnnos[i]); + RequestParam reqParamAnno = findAnnotation(RequestParam.class, paramAnnos[i]); + RequestParamMap reqParamsAnno = findAnnotation(RequestParamMap.class, paramAnnos[i]); + PathParam pathParamAnno = findAnnotation(PathParam.class, paramAnnos[i]); + if (hasConflictiveAnnotation(reqBodyAnno, reqParamAnno, reqParamsAnno, pathParamAnno)) { + // 存在冲突的定义; + throw new IllegalHttpServiceDefinitionException( + "The argument[" + i + "] of action has conflictive definition!"); + } +// if (bodyResolver != null && reqBodyAnno != null) { +// throw new IllegalHttpServiceDefinitionException("Define more than one request body for the action!"); +// } + if (reqBodyAnno != null) { + RequestBodyResolver reqBodyResolver = createBodyResolver(new ArgDefEntry(i, paramTypes[i], reqBodyAnno)); + bodyResolverComposite.addRequestBodyResolver(reqBodyResolver); + } + if (reqParamAnno != null) { + reqParamAnnos.add(new ArgDefEntry(i, paramTypes[i], reqParamAnno)); + } + if (reqParamsAnno != null) { + reqParamMapAnnos.add(new ArgDefEntry(i, paramTypes[i], reqParamsAnno)); + } + if (pathParamAnno != null) { + pathParamAnnos.add(new ArgDefEntry(i, paramTypes[i], pathParamAnno)); + } + } + RequestParamResolver reqParamResolver = createRequestParamResolver(reqParamAnnos, reqParamMapAnnos); + PathParamResolver pathParamResolver = createPathParamResolver(pathParamAnnos); +// if (bodyResolverComposite == null) { +// bodyResolverComposite = RequestBodyResolvers.NULL_BODY_RESOLVER; +// } + + // 获取声明的异常列表; + Class[] thrownExceptionTypes = mth.getExceptionTypes(); + + ServiceActionContext actionContext = new ServiceActionContext(mth, actionAnno.method(), contentType, + pathTemplate, pathParamResolver, reqParamFilter, reqParamResolver, bodyResolverComposite, responseConverter, + thrownExceptionTypes, actionAnno.resolveContentOnHttpError()); + return actionContext; + } + + @SuppressWarnings("unchecked") + private static T findAnnotation(Class clazz, Annotation[] annos) { + for (Annotation annotation : annos) { + if (clazz.isAssignableFrom(annotation.getClass())) { + return (T) annotation; + } + } + return null; + } + + private RequestParamFilter createRequestParamFilter(HttpAction actionDef) { + Class reqParamFilterClass = actionDef.requestParamFilter(); + if (reqParamFilterClass == null || reqParamFilterClass == RequestParamFilter.class) { + return NullRequestParamFilter.INSTANCE; + } + if (RequestParamFilter.class.isAssignableFrom(reqParamFilterClass)) { + return (RequestParamFilter) BeanUtils.instantiate(reqParamFilterClass); + } else { + throw new IllegalHttpServiceDefinitionException( + "The specified RequestParamFilter doesn't implement the interface " + + RequestParamFilter.class.getName() + "!"); + } + } + + /** + * 创建回复结果转换器; + * + * @param actionDef + * @param retnClazz + * @return + */ + private ResponseConverter createResponseConverter(HttpAction actionDef, Method mth) { + Class retnClazz = mth.getReturnType(); + if (Void.class.equals(retnClazz)) { + return NullResponseConverter.INSTANCE; + } + Class respConverterClass = actionDef.responseConverter(); + if (respConverterClass == null || respConverterClass == ResponseConverter.class) { + // 未设置方法级别的回复转换器; + if (defaultResponseConverter != null) { + // 如果未设置方法级别的回复转换器,且设置了服务级别的默认回复转换器,则应用服务级别的默认回复转换器; + return defaultResponseConverter; + } + if (responseConverterFactory != null) { + return responseConverterFactory.createResponseConverter(actionDef, mth); + } + } + if (respConverterClass != null && respConverterClass != ResponseConverter.class) { + if (ResponseConverter.class.isAssignableFrom(respConverterClass)) { + return (ResponseConverter) BeanUtils.instantiate(respConverterClass); + } else { + throw new IllegalHttpServiceDefinitionException( + "The specified response converter doesn't implement the interface " + + ResponseConverter.class.getName() + "!"); + } + } + // create default response converter; + return DefaultResponseConverterFactory.INSTANCE.createResponseConverter(actionDef, mth); + + // if (byte[].class == retnClazz) { + // return ByteArrayResponseConverter.INSTANCE; + // } + // if (String.class == retnClazz) { + // return StringResponseConverter.INSTANCE; + // } + // // TODO:未处理 基本类型、输入输出流; + // return new JsonResponseConverter(retnClazz); + } + + /** + * 创建路径参数解析器; + * + * @param pathParamAnnos + * @return + */ + private PathParamResolver createPathParamResolver(List> pathParamAnnos) { + if (pathParamAnnos.size() == 0) { + return PathParamResolvers.NONE_PATH_PARAM_RESOLVER; + } + List> pathParamDefs = new LinkedList>(); + for (ArgDefEntry entry : pathParamAnnos) { + if (StringUtils.isEmpty(entry.getDefinition().name())) { + throw new IllegalHttpServiceDefinitionException("The name of path parameter is empty!"); + } + + Class converterClazz = entry.getDefinition().converter(); + StringConverter converter = StringConverterFactory.instantiateStringConverter(converterClazz); + ArgDefEntry argDefEntry = new ArgDefEntry(entry.getIndex(), + entry.getArgType(), new PathParamDefinition(entry.getDefinition().name(), converter)); + pathParamDefs.add(argDefEntry); + } + + return PathParamResolvers.createResolver(pathParamDefs); + } + + /** + * 创建请求参数解析器; + * + * @param reqParamAnnos + * @return + */ + private RequestParamResolver createRequestParamResolver(List> reqParamAnnos, + List> reqParamsAnnos) { + List> reqDefs = RequestParamDefinition + .resolveSingleParamDefinitions(reqParamAnnos); + List> reqMapDefs = RequestParamMapDefinition + .resolveParamMapDefinitions(reqParamsAnnos); + + return RequestParamResolvers.createParamMapResolver(reqDefs, reqMapDefs); + + } + + /** + * @param paramIndex + * @param parameter + * @param reqBodyAnnoEntry + * @return + */ + private RequestBodyResolver createBodyResolver(ArgDefEntry reqBodyAnnoEntry) { + Class converterClazz = reqBodyAnnoEntry.getDefinition().converter(); + RequestBodyConverter converter = null; + if (converterClazz == RequestBodyConverter.class || converterClazz == null) { + converter = defaultRequestBodyConverter; + if (converter == null) { + // create default body converter; + converter = new TypeAutoAdaptingRequestBodyConverter(reqBodyAnnoEntry.getArgType()); + } + } else { + if (!ClassUtils.isAssignable(RequestBodyConverter.class, converterClazz)) { + throw new IllegalHttpServiceDefinitionException( + "The specified body converter doesn't implement the interface " + + RequestBodyConverter.class.getName() + "!"); + } + converter = (RequestBodyConverter) BeanUtils.instantiate(converterClazz); + } + + RequestBodyDefinition reqBodyDef = new RequestBodyDefinition(reqBodyAnnoEntry.getDefinition().required(), + converter); + ArgDefEntry reqBodyDefEntry = new ArgDefEntry( + reqBodyAnnoEntry.getIndex(), reqBodyAnnoEntry.getArgType(), reqBodyDef); + return RequestBodyResolvers.createArgumentResolver(reqBodyDefEntry); + } + + /** + * 检查传入的三个参数中是否有两个或两个以上为非空; + * + * @param reqBodyAnno + * @param reqParamAnno + * @param pathParamAnno + * @return 有两个或两个以上为非空时返回 true; + * + * 全部为 null 或只有一个为 null 时,返回 false; + */ + private static boolean hasConflictiveAnnotation(RequestBody reqBodyAnno, RequestParam reqParamAnno, + RequestParamMap reqParamsAnno, PathParam pathParamAnno) { + return 1 < (reqBodyAnno == null ? 0 : 1) + (reqParamAnno == null ? 0 : 1) + (reqParamsAnno == null ? 0 : 1) + + (pathParamAnno == null ? 0 : 1); + } + + /** + * 解析被调用的方法,映射为 http 请求; + */ + private Object invoke(ServiceEndpoint serviceEndpoint, HttpServiceContext serviceContext, CloseableHttpClient httpClient, RequestHeader[] headers, + Method method, Object[] args) throws Throwable { + ServiceActionContext actionContext = actions.get(method); + if (actionContext == null) { + throw new UnsupportedOperationException("The invoked method was not a service action!"); + } + try { + HttpServiceRequest request = resolveRequest(serviceEndpoint, actionContext, args); + + HttpUriRequest httpRequest = buildRequest(request); + + // 设置预定义的头部; + setHeaders(httpRequest, headers); + // 设置解析请求生成的头部; + setHeaders(httpRequest, request.getHeaders()); + if (authorizationHeaderResolver != null) { + AuthorizationHeader auth = authorizationHeaderResolver.generateHeader(request); + // 设置认证属性; + buildAuthorization(httpRequest, auth); + } + + // 设置默认的 Content-Type; + Header[] contentTypeHeaders = httpRequest.getHeaders(HttpHeaders.CONTENT_TYPE); + if (contentTypeHeaders == null || contentTypeHeaders.length == 0) { + httpRequest.setHeader(HttpHeaders.CONTENT_TYPE, "application/json"); + } + CloseableHttpClient closeableHttpClient = HttpClientPool.getHttpClient(serviceEndpoint.getHost(), serviceEndpoint.getPort()); + + CloseableHttpResponse response = closeableHttpClient.execute(httpRequest); +// CloseableHttpResponse response = httpClient.execute(httpRequest); + try { + // 引发 http 异常; + if (response.getStatusLine().getStatusCode() >= 400) { + processAndThrowHttpException(actionContext, request, response); + // 注:上一步已抛出异常; + return null; + } + InputStream respStream = response.getEntity().getContent(); + Object respObject = actionContext.getResponseConverter().getResponse(request, respStream, + serviceContext); + return respObject; + } finally { + response.close(); + } + } catch (Exception e) { + if (isCustomThownException(e, actionContext)) { + throw e; + } + if (e instanceof HttpServiceException) { + throw (HttpServiceException) e; + } + throw new HttpServiceException(e.getMessage(), e); + } + } + + private void setHeaders(HttpUriRequest httpRequest, RequestHeader[] headers) { + if (headers == null) { + return; + } + for (RequestHeader header : headers) { + httpRequest.setHeader(header.getName(), header.getValue()); + } + } + + private void setHeaders(HttpUriRequest httpRequest, Properties customHeaders) { + Set names = customHeaders.stringPropertyNames(); + for (String name : names) { + httpRequest.setHeader(name, customHeaders.getProperty(name)); + } + } + + /** + * 判断指定的异常是否属于指定服务操作的接口方法通过 throws 声明的异常; + * + * @param e + * @param actionContext + * @return + */ + private boolean isCustomThownException(Exception e, ServiceActionContext actionContext) { + Class exType = e.getClass(); + Class[] thrownExTypes = actionContext.getThrownExceptionTypes(); + for (Class thrExType : thrownExTypes) { + if (thrExType.isAssignableFrom(exType)) { + return true; + } + } + return false; + } + + /** + * 处理 HTTP 错误,并抛出 HttpStatusException 异常; + * + * @param actionContext + * @param response + */ + private void processAndThrowHttpException(ServiceActionContext actionContext, ServiceRequest request, + CloseableHttpResponse response) throws HttpStatusException { + String content = null; + if (actionContext.isResolveContentOnHttpError()) { + try { + InputStream respStream = response.getEntity().getContent(); + content = (String) StringResponseConverter.INSTANCE.getResponse(request, respStream, null); + } catch (UnsupportedOperationException e) { + throw new HttpServiceException(e.getMessage(), e); + } catch (IOException e) { + throw new HttpServiceException(e.getMessage(), e); + } catch (Exception e) { + if (e instanceof HttpServiceException) { + throw (HttpServiceException) e; + } + throw new HttpServiceException(e.getMessage(), e); + } + } + String errMsg = String.format("[status=%s] %s", response.getStatusLine().getStatusCode(), content); + throw new HttpStatusException(response.getStatusLine().getStatusCode(), errMsg); + } + + private HttpServiceRequest resolveRequest(ServiceEndpoint serviceEndpoint, ServiceActionContext actionContext, Object[] args) throws IOException { + HttpServiceRequest request; + switch (actionContext.getRequestMethod()) { + case GET: + request = resolveGetRequest(serviceEndpoint, actionContext, args); + break; + case POST: + case PUT: + request = resolvePostOrPutRequest(serviceEndpoint, actionContext, args); + break; + case DELETE: + request = resolveDeleteRequest(serviceEndpoint, actionContext, args); + break; + default: + throw new UnsupportedOperationException( + "Unsupported http method '" + actionContext.getRequestMethod() + "'!"); + } + if (actionContext.getContentType() != null) { + request.setHeader(HttpHeaders.CONTENT_TYPE, actionContext.getContentType()); + } + return request; + } + + /** + * 创建请求; + * + * @param actionContext + * @param args + * @return + */ + private HttpUriRequest buildRequest(ServiceRequest request) { + ByteBuffer bodyBytes = null; + if (request.getBody() != null) { + // bodyStream = new ByteArrayInputStream(request.getBody().array()); + + bodyBytes = request.getBody(); + } + NamedParamMap reqParams = request.getRequestParams(); + switch (request.getHttpMethod()) { + case GET: + return new HttpGet(request.getUri()); + case POST: + HttpPost httppost = new HttpPost(request.getUri()); + + if (reqParams != null) { + // 以 form 表单提交; + NamedParam[] propNames = reqParams.getParams(); + List formParams = new ArrayList(); + for (NamedParam param : propNames) { + formParams.add(new BasicNameValuePair(param.getName(), param.getValue())); + } + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(formParams, Consts.UTF_8); + httppost.setEntity(formEntity); + // 设置默认的 Content-Type; + httppost.setHeader(formEntity.getContentType()); + } + if (bodyBytes != null) { + // 查询参数以 Stream body 方式提交; + ByteArrayEntity entity = new ByteArrayEntity(bodyBytes.array()); + // HttpEntity streamEntity = new InputStreamEntity(bodyStream); + httppost.setEntity(entity); + // 设置默认的 Content-Type; + httppost.setHeader(entity.getContentType()); + } + return httppost; + case PUT: + HttpPut httpput = new HttpPut(request.getUri()); + if (reqParams != null) { + // 以 form 表单提交; + NamedParam[] paramValues = reqParams.getParams(); + List formParams = new ArrayList(); + for (NamedParam param : paramValues) { + formParams.add(new BasicNameValuePair(param.getName(), param.getValue())); + } + UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(formParams, Consts.UTF_8); + httpput.setEntity(formEntity); + } + if (bodyBytes != null) { + // 查询参数以 Stream body 方式提交; + ByteArrayEntity entity = new ByteArrayEntity(bodyBytes.array()); + // HttpEntity streamEntity = new InputStreamEntity(bodyStream); + httpput.setEntity(entity); + } + return httpput; + case DELETE: + // HttpDelete httpDelete = new HttpDelete(uri); + LocalHttpDelete httpDelete = new LocalHttpDelete(request.getUri()); + // 查询参数以 delete body 方式提交 + if (bodyBytes != null) { + ByteArrayEntity entity = new ByteArrayEntity(bodyBytes.array()); + // HttpEntity entity = new InputStreamEntity(bodyStream); + httpDelete.setEntity(entity); + } + // HttpEntity entity = new InputStreamEntity(bodyStream); + // httpDelete.setEntity(entity); + return httpDelete; + default: + throw new UnsupportedOperationException("Unsupported http method '" + request.getHttpMethod() + "'!"); + } + } + + /** + * 设置http请求头的Authorization属性 + * + * @param request + * @param setting + */ + private void buildAuthorization(HttpUriRequest request, RequestHeader setting) { + request.addHeader(setting.getName(), setting.getValue()); + } + + /** + * 创建 http post 请求; + * + * @param actionContext + * @param args + * @return + * @throws IOException + */ + // private HttpServiceRequest resolvePostRequest(ServiceActionContext + // actionContext, Object[] args) + // throws IOException { + // Map pathParams = + // actionContext.getPathParamResolver().resolve(args); + // Properties reqParams = + // actionContext.getRequestParamResolver().resolve(args); + // InputStream inputStream = + // actionContext.getRequestBodyResolver().resolve(args); + // URI uri = actionContext.getPathTemplate().generateRequestURI(pathParams, + // reqParams, + // ServiceActionContext.DEFAULT_CHARSET); + // byte[] bytes = BytesUtils.copyToBytes(inputStream); + // return new HttpServiceRequest(HttpMethod.POST, uri, + // ByteBuffer.wrap(bytes)); + // } + + /** + * 创建http put请求 + * + * @param actionContext + * @param args + * @return + * @throws IOException + */ + // private HttpServiceRequest resolvePutRequest(ServiceActionContext + // actionContext, Object[] args) throws IOException { + // Map pathParams = + // actionContext.getPathParamResolver().resolve(args); + // Properties reqParams = + // actionContext.getRequestParamResolver().resolve(args); + // InputStream inputStream = + // actionContext.getRequestBodyResolver().resolve(args); + // URI uri = actionContext.getPathTemplate().generateRequestURI(pathParams, + // reqParams, + // ServiceActionContext.DEFAULT_CHARSET); + // byte[] bytes = BytesUtils.copyToBytes(inputStream); + // return new HttpServiceRequest(HttpMethod.PUT, uri, + // ByteBuffer.wrap(bytes)); + // } + + private HttpServiceRequest resolvePostOrPutRequest(ServiceEndpoint serviceEndpoint, ServiceActionContext actionContext, Object[] args) + throws IOException { + // 解析路径参数; + Map pathParams = actionContext.getPathParamResolver().resolve(args); + HttpMethod httpMethod = actionContext.getRequestMethod(); + + // 解析 RequestBody; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + actionContext.getRequestBodyResolver().resolve(args, out); + byte[] bodyBytes = out.toByteArray(); + + boolean noBody = bodyBytes.length == 0; + + // 解析 RequestParam; + NamedParamMap reqParams = actionContext.getRequestParamResolver().resolve(args); + boolean noReqParams = reqParams.isEmpty(); + actionContext.getRequestParamFilter().filter(httpMethod, reqParams); + + // 如果只有 RequestBody 标注的参数,则以 RequestBody 参数的序列化输出作为请求体; + if ((!noBody) && noReqParams) { + URI uri = actionContext.getPathTemplate().generateRequestURI(serviceEndpoint, pathParams, new NamedParamMap(), + ServiceActionContext.DEFAULT_CHARSET); + ByteBuffer body = ByteBuffer.wrap(bodyBytes); + return new HttpServiceRequest(httpMethod, uri, null, body, args); + } + // 如果没有 RequestBody 标注的参数,只有 RequestParam ,则 RequestParam 通过请求体以表单格式提交; + if (noBody && (!noReqParams)) { + URI uri = actionContext.getPathTemplate().generateRequestURI(serviceEndpoint, pathParams, new NamedParamMap(), + ServiceActionContext.DEFAULT_CHARSET); + return new HttpServiceRequest(httpMethod, uri, reqParams, null, args); + } + + // 如果同时有 RequestBody 标注的参数和 RequestParam 标注的参数,则以 RequestBody + // 参数的序列化输出作为请求体,RequestParam 作为 URL 参数; + if ((!noBody) && (!noReqParams)) { + URI uri = actionContext.getPathTemplate().generateRequestURI(serviceEndpoint, pathParams, reqParams, + ServiceActionContext.DEFAULT_CHARSET); + ByteBuffer body = ByteBuffer.wrap(bodyBytes); + return new HttpServiceRequest(httpMethod, uri, null, body, args); + } + + // 既没有 RequestBody,也没有 RequestParam; + URI uri = actionContext.getPathTemplate().generateRequestURI(serviceEndpoint, pathParams, new NamedParamMap(), + ServiceActionContext.DEFAULT_CHARSET); + return new HttpServiceRequest(httpMethod, uri, null, null, args); + } + + /** + * 创建http get请求 + * + * @param actionContext + * @param args + * @return + */ + private HttpServiceRequest resolveGetRequest(ServiceEndpoint serviceEndpoint, ServiceActionContext actionContext, Object[] args) { + Map pathParams = actionContext.getPathParamResolver().resolve(args); + NamedParamMap reqParams = actionContext.getRequestParamResolver().resolve(args); + URI uri = actionContext.getPathTemplate().generateRequestURI(serviceEndpoint, pathParams, reqParams, + ServiceActionContext.DEFAULT_CHARSET); + // 对于 get 请求,请求参数已经编码到 URI 中,所以不必再传递出去进行处理; + return new HttpServiceRequest(HttpMethod.GET, uri, null, null, args); + } + + /** + * 创建http delete请求 + * + * @param actionContext + * @param args + * @return + * @throws IOException + */ + private HttpServiceRequest resolveDeleteRequest(ServiceEndpoint serviceEndpoint, ServiceActionContext actionContext, Object[] args) + throws IOException { + Map pathParams = actionContext.getPathParamResolver().resolve(args); + + NamedParamMap reqParams = actionContext.getRequestParamResolver().resolve(args); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + actionContext.getRequestBodyResolver().resolve(args, out); + byte[] bodyBytes = out.toByteArray(); + + URI uri = actionContext.getPathTemplate().generateRequestURI(serviceEndpoint, pathParams, reqParams, + ServiceActionContext.DEFAULT_CHARSET); + + ByteBuffer body = bodyBytes.length == 0 ? null : ByteBuffer.wrap(bodyBytes); + return new HttpServiceRequest(HttpMethod.DELETE, uri, null, body, args); + } + + private static class ServiceInvocationHandler implements InvocationHandler, HttpServiceContext { + + private HttpServiceAgent serviceAgent; + + private HttpServiceConnection connection; + + private RequestHeader[] headers; + + private Object bindingData; + + public ServiceInvocationHandler(HttpServiceAgent serviceAgent, HttpServiceConnection connection, + RequestHeader[] headers, Object bindingData) { + this.serviceAgent = serviceAgent; + this.connection = connection; + this.headers = headers; + this.bindingData = bindingData; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + return serviceAgent.invoke(connection.getEndpoint(), this, connection.getHttpClient(), headers, method, args); + } + + @Override + public Class getServiceClasss() { + return serviceAgent.serviceClass; + } + + @Override + public Object getProxyBindingData() { + return bindingData; + } + + } + + private static class HttpServiceContextImpl implements HttpServiceContext { + + private Class serviceClass; + + private Object proxyBindingData; + + public HttpServiceContextImpl(Class serviceClass, Object proxyBindingData) { + this.serviceClass = serviceClass; + this.proxyBindingData = proxyBindingData; + } + + @Override + public Class getServiceClasss() { + return serviceClass; + } + + @Override + public Object getProxyBindingData() { + return proxyBindingData; + } + + } + + /** + * HttpServiceRequest 是对一次实际的服务调用转换生成的HTTP请求的模型; + * + * @author haiq + * + */ + private static class HttpServiceRequest implements ServiceRequest { + + private HttpMethod method; + + private URI uri; + + private ByteBuffer body; + + private Properties headers = new Properties(); + + private NamedParamMap requestParams; + + private Object[] args; + + public HttpServiceRequest(HttpMethod method, URI uri, NamedParamMap requestParams, ByteBuffer body, + Object[] args) { + this.method = method; + this.uri = uri; + this.requestParams = requestParams; + this.body = body; + this.args = args; + } + + /* + * (non-Javadoc) + * + * @see my.utils.http.agent.Request#getMethod() + */ + @Override + public HttpMethod getHttpMethod() { + return method; + } + + @Override + public NamedParamMap getRequestParams() { + return requestParams; + } + + /* + * (non-Javadoc) + * + * @see my.utils.http.agent.Request#getUri() + */ + @Override + public URI getUri() { + return uri; + } + + /* + * (non-Javadoc) + * + * @see my.utils.http.agent.Request#getBody() + */ + @Override + public ByteBuffer getBody() { + return body; + } + + @SuppressWarnings("unused") + public void setHeader(String name, String value) { + headers.setProperty(name, value); + } + + public Properties getHeaders() { + return headers; + } + + @Override + public Object[] getArgs() { + return args; + } + + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpServiceConnection.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpServiceConnection.java new file mode 100644 index 00000000..1fb2a219 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/HttpServiceConnection.java @@ -0,0 +1,43 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.IOException; + +import org.apache.http.impl.client.CloseableHttpClient; + +class HttpServiceConnection implements ServiceConnection{ + + private ServiceEndpoint endpoint; + + private CloseableHttpClient httpClient; + + HttpServiceConnection(ServiceEndpoint endpoint, CloseableHttpClient httpClient) { + this.endpoint = endpoint; + this.httpClient = httpClient; + } + + CloseableHttpClient getHttpClient() { + CloseableHttpClient cli = httpClient; + if (cli == null) { + throw new IllegalArgumentException("HttpServiceConnection has been closed!"); + } + return cli; + } + + @Override + public void close() { + CloseableHttpClient cli = httpClient; + if (cli != null) { + httpClient = null; + try { + cli.close(); + } catch (IOException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + } + + @Override + public ServiceEndpoint getEndpoint() { + return endpoint; + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/IllegalHttpServiceDefinitionException.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/IllegalHttpServiceDefinitionException.java new file mode 100644 index 00000000..4ea58bc6 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/IllegalHttpServiceDefinitionException.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.http.agent; + +import com.jd.blockchain.utils.http.HttpServiceException; + +public class IllegalHttpServiceDefinitionException extends HttpServiceException{ + + private static final long serialVersionUID = -6866487415383367958L; + + public IllegalHttpServiceDefinitionException() { + } + + public IllegalHttpServiceDefinitionException(String message) { + super(message); + } + + public IllegalHttpServiceDefinitionException(String message, Throwable cause) { + super(message, cause); + } + + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/LocalHttpDelete.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/LocalHttpDelete.java new file mode 100644 index 00000000..1240123e --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/LocalHttpDelete.java @@ -0,0 +1,33 @@ +package com.jd.blockchain.utils.http.agent; + +import java.net.URI; + +import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; + +public class LocalHttpDelete extends HttpEntityEnclosingRequestBase { + + public final static String METHOD_NAME = "DELETE"; + + public LocalHttpDelete() { + super(); + } + + public LocalHttpDelete(final URI uri) { + super(); + setURI(uri); + } + + /** + * @throws IllegalArgumentException if the uri is invalid. + */ + public LocalHttpDelete(final String uri) { + super(); + setURI(URI.create(uri)); + } + + @Override + public String getMethod() { + return METHOD_NAME; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/NullRequestParamFilter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/NullRequestParamFilter.java new file mode 100644 index 00000000..b46b089b --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/NullRequestParamFilter.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.utils.http.agent; + +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.NamedParamMap; +import com.jd.blockchain.utils.http.RequestParamFilter; + +public class NullRequestParamFilter implements RequestParamFilter{ + + public static RequestParamFilter INSTANCE = new NullRequestParamFilter(); + + private NullRequestParamFilter() { + } + + @Override + public void filter(HttpMethod requestMethod, NamedParamMap requestParams) { + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamDefinition.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamDefinition.java new file mode 100644 index 00000000..6f6adfef --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamDefinition.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.http.agent; + +import com.jd.blockchain.utils.http.StringConverter; + +class PathParamDefinition { + + private String name; + + private StringConverter converter; + + public String getName() { + return name; + } + + public StringConverter getConverter() { + return converter; + } + + public PathParamDefinition(String name, StringConverter converter) { + this.name = name; + this.converter = converter; + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamResolver.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamResolver.java new file mode 100644 index 00000000..3f9ae4be --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamResolver.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.http.agent; + +import java.util.Map; + +/** + * 路径参数解析器; + * + * @author haiq + * + */ +interface PathParamResolver { + + /** + * 将方法参数列表解析为路径参数的变量表; + * + * @param args 方法参数列表; + * @return + */ + Map resolve(Object[] args); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamResolvers.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamResolvers.java new file mode 100644 index 00000000..c5148bdd --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PathParamResolvers.java @@ -0,0 +1,56 @@ +package com.jd.blockchain.utils.http.agent; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +class PathParamResolvers { + + /** + * 空路径参数解析器; + */ + public static final PathParamResolver NONE_PATH_PARAM_RESOLVER = new NonePathParamResolver(); + + /** + * 根据指定的路径参数定义创建路径参数解析器; + * + * @param paramDefinitions + * @return + */ + public static PathParamResolver createResolver(List> paramDefinitions) { + return new ArgArrayPathParamResolver(paramDefinitions); + } + + private static class ArgArrayPathParamResolver implements PathParamResolver { + + private List> paramDefinitions; + + public ArgArrayPathParamResolver(List> paramDefinitions) { + this.paramDefinitions = paramDefinitions; + } + + @Override + public Map resolve(Object[] args) { + Map pathParams = new HashMap(); + String name; + String value; + for (ArgDefEntry paramDef : paramDefinitions) { + name = paramDef.getDefinition().getName(); + value = paramDef.getDefinition().getConverter().toString(args[paramDef.getIndex()]); + pathParams.put(name, value); + } + return pathParams; + } + } + + private static class NonePathParamResolver implements PathParamResolver { + + @SuppressWarnings("unchecked") + @Override + public Map resolve(Object[] args) { + return Collections.EMPTY_MAP; + } + + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PojoPropertiesConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PojoPropertiesConverter.java new file mode 100644 index 00000000..04e35415 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PojoPropertiesConverter.java @@ -0,0 +1,72 @@ +package com.jd.blockchain.utils.http.agent; + +import java.beans.PropertyDescriptor; +import java.util.LinkedList; +import java.util.List; + +import org.springframework.beans.BeanWrapper; +import org.springframework.beans.BeanWrapperImpl; +import org.springframework.core.convert.TypeDescriptor; + +import com.jd.blockchain.utils.http.NamedParamMap; +import com.jd.blockchain.utils.http.PropertiesConverter; +import com.jd.blockchain.utils.http.RequestParam; + +/** + * 对 POJO 属性的转换器; + * + * @author haiq + * + */ +public class PojoPropertiesConverter implements PropertiesConverter { + + private List propNames = new LinkedList(); + + private RequestParamResolver paramResolver; + + private Class argType; + + public PojoPropertiesConverter(Class argType) { + this.argType = argType; + resolveParamProperties(); + } + + private void resolveParamProperties() { + BeanWrapperImpl beanWrapper = new BeanWrapperImpl(argType); + List> reqParamDefs = new LinkedList>(); + PropertyDescriptor[] propDescs = beanWrapper.getPropertyDescriptors(); + TypeDescriptor propTypeDesc; + for (PropertyDescriptor propDesc : propDescs) { + propTypeDesc = beanWrapper.getPropertyTypeDescriptor(propDesc.getName()); + + RequestParam reqParamAnno = propTypeDesc.getAnnotation(RequestParam.class); + if (reqParamAnno == null) { + // 忽略未标注 RequestParam 的属性; + continue; + } + RequestParamDefinition reqParamDef = RequestParamDefinition.resolveDefinition(reqParamAnno); + ArgDefEntry defEntry = new ArgDefEntry(reqParamDefs.size(), propTypeDesc.getType(), + reqParamDef); + reqParamDefs.add(defEntry); + propNames.add(propDesc.getName()); + } + paramResolver = RequestParamResolvers.createParamResolver(reqParamDefs); + } + + @Override + public NamedParamMap toProperties(Object arg) { + if (propNames.size() == 0) { + return new NamedParamMap(); + } + BeanWrapper beanWrapper = new BeanWrapperImpl(arg); + Object[] propValues = new Object[propNames.size()]; + int i = 0; + for (String propName : propNames) { + propValues[i] = beanWrapper.getPropertyValue(propName); + i++; + } + NamedParamMap params = paramResolver.resolve(propValues); + return params; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PropertiesConverterFactory.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PropertiesConverterFactory.java new file mode 100644 index 00000000..00e8e474 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/PropertiesConverterFactory.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.http.agent; + +import org.springframework.beans.BeanUtils; + +import com.jd.blockchain.utils.http.PropertiesConverter; +import com.jd.blockchain.utils.http.StringConverter; + +public class PropertiesConverterFactory { + + public static PropertiesConverter instantiatePropertiesConverter(Class converterClazz, Class argType){ + if (converterClazz == null || PropertiesConverter.class == converterClazz || PojoPropertiesConverter.class == converterClazz) { + return new PojoPropertiesConverter(argType); + } + if (!PropertiesConverter.class.isAssignableFrom(converterClazz)) { + throw new IllegalHttpServiceDefinitionException( + "The specified converter of path param doesn't implement the interface " + + StringConverter.class.getName() + "!"); + } + + PropertiesConverter converter = (PropertiesConverter) BeanUtils.instantiate(converterClazz); + return converter; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyDefinition.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyDefinition.java new file mode 100644 index 00000000..e30938e1 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyDefinition.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.http.agent; + +import com.jd.blockchain.utils.http.RequestBodyConverter; + +class RequestBodyDefinition { + + private boolean required; + + private RequestBodyConverter converter; + + public RequestBodyDefinition(boolean required, RequestBodyConverter converter) { + this.required = required; + this.converter = converter; + } + + public RequestBodyConverter getConverter() { + return converter; + } + + public boolean isRequired() { + return required; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolver.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolver.java new file mode 100644 index 00000000..c7dead00 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolver.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.OutputStream; + +interface RequestBodyResolver { + + void resolve(Object[] args, OutputStream out); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolverComposite.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolverComposite.java new file mode 100644 index 00000000..9b5ec9bb --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolverComposite.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.OutputStream; +import java.util.LinkedList; +import java.util.List; + +class RequestBodyResolverComposite implements RequestBodyResolver { + + private List resolverList = new LinkedList(); + + @Override + public void resolve(Object[] args, OutputStream out) { + for (RequestBodyResolver resolver : resolverList) { + resolver.resolve(args, out); + } + } + + public void addRequestBodyResolver(RequestBodyResolver resolver) { + resolverList.add(resolver); + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolvers.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolvers.java new file mode 100644 index 00000000..e9ee9b8b --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestBodyResolvers.java @@ -0,0 +1,63 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.utils.http.HttpServiceException; +import com.jd.blockchain.utils.io.RuntimeIOException; + +class RequestBodyResolvers { + + public static final RequestBodyResolver NULL_BODY_RESOLVER = new NullBodyResolver(); + + /** + * 创建基于参数列表的解析器; + * + * @param argIndex + * 要作为 body 输出的参数的位置; + * @param converter + * 参数值转换器; + * @return + */ + public static RequestBodyResolver createArgumentResolver(ArgDefEntry defEntry) { + return new ArgurmentResolver(defEntry); + } + + private static final class ArgurmentResolver implements RequestBodyResolver { + + private ArgDefEntry defEntry; + + public ArgurmentResolver(ArgDefEntry defEntry) { + this.defEntry = defEntry; + } + + @Override + public void resolve(Object[] args, OutputStream out) { + Object arg = args[defEntry.getIndex()]; + if (arg == null && defEntry.getDefinition().isRequired()) { + throw new HttpServiceException("The required body argument is null!"); + } + try { + defEntry.getDefinition().getConverter().write(arg, out); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + } + + /** + * 空的 body 解析器; + * + * @author haiq + * + */ + private static final class NullBodyResolver implements RequestBodyResolver { + + @Override + public void resolve(Object[] args, OutputStream out) { + //Do nothing; + } + + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestHeader.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestHeader.java new file mode 100644 index 00000000..4512da70 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestHeader.java @@ -0,0 +1,9 @@ +package com.jd.blockchain.utils.http.agent; + +public interface RequestHeader { + + String getName(); + + String getValue(); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamDefinition.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamDefinition.java new file mode 100644 index 00000000..9a7d3033 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamDefinition.java @@ -0,0 +1,74 @@ +package com.jd.blockchain.utils.http.agent; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.util.StringUtils; + +import com.jd.blockchain.utils.http.RequestParam; +import com.jd.blockchain.utils.http.StringConverter; + +class RequestParamDefinition { + + private String name; + + private boolean required; + + private boolean array; + + private String ignoreValue; + + private StringConverter converter; + + public RequestParamDefinition(String name, boolean required, boolean array, String ignoreValue, + StringConverter converter) { + this.name = name; + this.required = required; + this.array = array; + this.ignoreValue = ignoreValue; + this.converter = converter; + } + + public String getName() { + return name; + } + + public boolean isRequired() { + return required; + } + + public boolean isArray() { + return array; + } + + public String getIgnoreValue() { + return ignoreValue; + } + + public StringConverter getConverter() { + return converter; + } + + public static List> resolveSingleParamDefinitions( + List> reqParamAnnos) { + List> reqDefs = new LinkedList>(); + for (ArgDefEntry entry : reqParamAnnos) { + RequestParam reqParamAnno = entry.getDefinition(); + RequestParamDefinition reqDef = resolveDefinition(reqParamAnno); + reqDefs.add(new ArgDefEntry(entry.getIndex(), entry.getArgType(), reqDef)); + } + return reqDefs; + } + + public static RequestParamDefinition resolveDefinition(RequestParam reqParamAnno) { + if (StringUtils.isEmpty(reqParamAnno.name())) { + throw new IllegalHttpServiceDefinitionException("The name of request parameter is empty!"); + } + + Class converterClazz = reqParamAnno.converter(); + StringConverter converter = StringConverterFactory.instantiateStringConverter(converterClazz); + RequestParamDefinition reqDef = new RequestParamDefinition(reqParamAnno.name(), reqParamAnno.required(), + reqParamAnno.array(), reqParamAnno.ignoreValue(), converter); + return reqDef; + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamMapDefinition.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamMapDefinition.java new file mode 100644 index 00000000..6ddfaf9e --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamMapDefinition.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.utils.http.agent; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.util.StringUtils; + +import com.jd.blockchain.utils.http.PropertiesConverter; +import com.jd.blockchain.utils.http.RequestParamMap; + +class RequestParamMapDefinition { + + private String prefix; + + private boolean required; + + private PropertiesConverter converter; + + public RequestParamMapDefinition(String prefix, String seperator, boolean required, PropertiesConverter converter) { + if (prefix == null || prefix.length() ==0) { + this.prefix = ""; + }else{ + this.prefix = prefix + seperator; + } + this.required = required; + this.converter = converter; + } + + public boolean isRequired() { + return required; + } + + public PropertiesConverter getConverter() { + return converter; + } + + public String getPrefix() { + return prefix; + } + + + public static List> resolveParamMapDefinitions(List> reqParamAnnos){ + List> reqDefs = new LinkedList>(); + for (ArgDefEntry entry : reqParamAnnos) { + RequestParamMap reqParamAnno = entry.getDefinition(); + String prefix = StringUtils.trimWhitespace(reqParamAnno.prefix()); + String seperator = StringUtils.trimWhitespace(reqParamAnno.seperator()); + + Class converterClazz = reqParamAnno.converter(); + PropertiesConverter converter = PropertiesConverterFactory.instantiatePropertiesConverter(converterClazz, entry.getArgType()); + RequestParamMapDefinition reqDef = new RequestParamMapDefinition(prefix, seperator, reqParamAnno.required(),converter); + reqDefs.add(new ArgDefEntry(entry.getIndex(), entry.getArgType(), reqDef)); + } + return reqDefs; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamResolver.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamResolver.java new file mode 100644 index 00000000..3ac501bb --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamResolver.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.http.agent; + +import com.jd.blockchain.utils.http.NamedParamMap; + +/** + * 请求参数解析器; + * + * @author haiq + * + */ +interface RequestParamResolver { + + /** + * 将方法参数列表解析为请求参数的变量表; + * + * @param args 方法参数列表; + * @return + */ + NamedParamMap resolve(Object[] args); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamResolvers.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamResolvers.java new file mode 100644 index 00000000..be76a92d --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestParamResolvers.java @@ -0,0 +1,220 @@ +package com.jd.blockchain.utils.http.agent; + +import java.util.LinkedList; +import java.util.List; + +import org.springframework.util.CollectionUtils; + +import com.jd.blockchain.utils.http.HttpServiceException; +import com.jd.blockchain.utils.http.NamedParamMap; +import com.jd.blockchain.utils.http.StringConverter; + +/** + * 请求参数解析器; + * + * @author haiq + * + */ +abstract class RequestParamResolvers { + + public static final RequestParamResolver NONE_REQUEST_PARAM_RESOLVER = new NoneRequestParamResolver(); + + /** + * 创建解析器; + * + * @param definitions + * 方法参数定义; + * @return + */ + public static RequestParamResolver createParamMapResolver( + List> reqParamDefinitions, + List> reqParamMapDefinitions) { + if ((!CollectionUtils.isEmpty(reqParamDefinitions)) && (!CollectionUtils.isEmpty(reqParamMapDefinitions))) { + RequestParamResolver resolver1 = createParamResolver(reqParamDefinitions); + RequestParamResolver resolver2 = createParamMapResolver(reqParamMapDefinitions); + return new MultiRequestParamResolverWrapper(resolver1, resolver2); + } + if (!CollectionUtils.isEmpty(reqParamDefinitions)) { + return createParamResolver(reqParamDefinitions); + } + if (!CollectionUtils.isEmpty(reqParamMapDefinitions)) { + return createParamMapResolver(reqParamMapDefinitions); + } + return NONE_REQUEST_PARAM_RESOLVER; + } + + /** + * 创建解析器; + * + * @param definitions + * 方法参数定义; + * @return + */ + public static RequestParamResolver createParamMapResolver( + List> definitions) { + return new ArgArrayRequestParamMapResolver(definitions); + } + + /** + * 创建解析器; + * + * @param definitions + * 方法参数定义; + * @return + */ + public static RequestParamResolver createParamResolver(List> definitions) { + return new ArgArrayRequestParamResolver(definitions); + } + + /** + * 方法参数表解析器; + * + * @author haiq + * + */ + private static class ArgArrayRequestParamMapResolver implements RequestParamResolver { + + private List> definitions; + + /** + * @param definitions + */ + public ArgArrayRequestParamMapResolver(List> definitions) { + this.definitions = new LinkedList>(definitions); + } + + @Override + public NamedParamMap resolve(Object[] args) { + NamedParamMap params = new NamedParamMap(); + for (ArgDefEntry defEntry : definitions) { + RequestParamMapDefinition def = defEntry.getDefinition(); + Object argValue = args[defEntry.getIndex()]; + if (argValue == null && def.isRequired()) { + throw new HttpServiceException("The required argument object is null!"); + } + + NamedParamMap extParams = def.getConverter().toProperties(argValue); + if (extParams.isEmpty()) { + if (def.isRequired()) { + throw new HttpServiceException("The required request parameter map is empty!"); + } + // 非必需参数,忽略空值; + continue; + } + if (extParams != null) { + // 合并参数; + params.merge(extParams, def.getPrefix()); + } + } // End of for; + return params; + } + + } + + /** + * 方法参数解析器; + * + * @author haiq + * + */ + private static class ArgArrayRequestParamResolver implements RequestParamResolver { + + private List> paramDefinitions; + + /** + * @param paramDefinitions + */ + public ArgArrayRequestParamResolver(List> paramDefinitions) { + this.paramDefinitions = new LinkedList>(paramDefinitions); + } + + @Override + public NamedParamMap resolve(Object[] args) { + NamedParamMap params = new NamedParamMap(); + for (ArgDefEntry defEntry : paramDefinitions) { + RequestParamDefinition def = defEntry.getDefinition(); + Object arg = args[defEntry.getIndex()]; + if (arg == null && def.isRequired()) { + throw new HttpServiceException("The required argument object is null!"); + } + + resovleParams(params, def, arg); + } // End of for; + return params; + } + + private void resovleParams(NamedParamMap params, RequestParamDefinition def, Object arg) { + if (def.isArray() && arg != null) { + if (arg.getClass().isArray()) { + Object[] valObjs = (Object[]) arg; + for (Object val : valObjs) { + resovleParamValue(params, def, val); + } + } + }else { + resovleParamValue(params, def, arg); + } + } + + private void resovleParamValue(NamedParamMap params, RequestParamDefinition def, Object arg) { + String value = def.getConverter().toString(arg); + if (value == null) { + if (def.isRequired()) { + throw new HttpServiceException("The required argument value is null!"); + } + // not required, and ignore null value; + return; + } + if (value.equals(def.getIgnoreValue())) { + // ignore ; + return; + } + params.addParam(def.getName(), value); + } + + } + + /** + * 将多个请求参数解析器的解析结果组合在一起的包装器; + * + * @author haiq + * + */ + private static class MultiRequestParamResolverWrapper implements RequestParamResolver { + + private RequestParamResolver[] resolvers; + + public MultiRequestParamResolverWrapper(RequestParamResolver... resolvers) { + this.resolvers = resolvers; + } + + @Override + public NamedParamMap resolve(Object[] args) { + NamedParamMap params = new NamedParamMap(); + for (RequestParamResolver resolver : resolvers) { + NamedParamMap extParams = resolver.resolve(args); + params.merge(extParams); + } + return params; + } + + } + + /** + * 空的请求参数解析器; + * + * 总是返回空的请求参数; + * + * @author haiq + * + */ + private static class NoneRequestParamResolver implements RequestParamResolver { + + @Override + public NamedParamMap resolve(Object[] args) { + return new NamedParamMap(); + } + + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestPathTemplate.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestPathTemplate.java new file mode 100644 index 00000000..3d021af7 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestPathTemplate.java @@ -0,0 +1,87 @@ +package com.jd.blockchain.utils.http.agent; + +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.Charset; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.apache.http.NameValuePair; +import org.apache.http.client.utils.URIBuilder; + +import com.jd.blockchain.utils.PathUtils; +import com.jd.blockchain.utils.http.NamedParamMap; + +class RequestPathTemplate { +// private ServiceEndpoint serviceEndpoint; + private String servicePath; + private String actionPath; + + public RequestPathTemplate(String servicePath, String actionPath) { +// this.serviceEndpoint = serviceEndpoint; + this.servicePath = PathUtils.standardize(servicePath); + this.actionPath = PathUtils.standardize(actionPath); + } + + + /** + * 更新请求路径; + * + * @param pathVariableName + * @param value + */ + private static String updateActionPath(String actionPath, String pathVariableName, String value) { + String pathVarName = String.format("{%s}", pathVariableName); + actionPath = actionPath.replace(pathVarName, value); + return actionPath; + } + + /** + * 返回完整的请求URL; + * + * @param pathParams + * 路径参数; + * @param queryParams + * 查询参数; + * @return + */ + public URI generateRequestURI(ServiceEndpoint serviceEndpoint, Map pathParams, NamedParamMap queryParams, + Charset encodingCharset) { + // 生成路径; + String reallyActionPath = createActionPath(pathParams); + String path = PathUtils.concatPaths(serviceEndpoint.getContextPath(), servicePath, reallyActionPath); + path = PathUtils.absolute(path); + + // 生成查询字符串; + URIBuilder uriBuilder = new URIBuilder(); + uriBuilder.setCharset(encodingCharset); + if (serviceEndpoint.isSecure()) { + uriBuilder.setScheme("https"); + }else{ + uriBuilder.setScheme("http"); + } + + uriBuilder.setHost(serviceEndpoint.getHost()); + uriBuilder.setPort(serviceEndpoint.getPort()); + uriBuilder.setPath(path.toString()); + List queryParameters = RequestUtils.createQueryParameters(queryParams); + uriBuilder.setParameters(queryParameters); + try { + return uriBuilder.build(); + } catch (URISyntaxException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } + } + + private String createActionPath(Map pathParams) { + String reallyActionPath = actionPath; + if (pathParams != null) { + for (Entry pathParam : pathParams.entrySet()) { + reallyActionPath = updateActionPath(reallyActionPath, pathParam.getKey(), pathParam.getValue()); + } + } + return reallyActionPath; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestUtils.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestUtils.java new file mode 100644 index 00000000..c47e0a81 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/RequestUtils.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.utils.http.agent; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.http.NameValuePair; +import org.apache.http.message.BasicNameValuePair; + +import com.jd.blockchain.utils.http.NamedParam; +import com.jd.blockchain.utils.http.NamedParamMap; + +public abstract class RequestUtils { + + @SuppressWarnings("unchecked") + public static List createQueryParameters(NamedParamMap queryParams) { + if (queryParams == null || queryParams.isEmpty()) { + return Collections.EMPTY_LIST; + } + List params = new ArrayList(); + NamedParam[] paramValues = queryParams.getParams(); + for (NamedParam param : paramValues) { + params.add(new BasicNameValuePair(param.getName(), param.getValue())); + } + return params; + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceActionContext.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceActionContext.java new file mode 100644 index 00000000..8635557d --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceActionContext.java @@ -0,0 +1,148 @@ +package com.jd.blockchain.utils.http.agent; + +import java.lang.reflect.Method; +import java.nio.charset.Charset; + +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.RequestParamFilter; +import com.jd.blockchain.utils.http.ResponseConverter; + +/** + * 服务操作上下文; + * + * 维持了一个特定的服务操作相关的参数定义; + * + * @author haiq + * + */ +class ServiceActionContext { + + public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); + + private Method serviceMethod; + + private HttpMethod requestMethod; + + private RequestPathTemplate pathTemplate; + + private PathParamResolver pathParamResolver; + + private RequestParamFilter requestParamFilter; + + private RequestParamResolver requestParamResolver; + + private RequestBodyResolver requestBodyResolver; + + private ResponseConverter responseConverter; + + private Class[] thrownExceptionTypes; + + private String contentType; + + private boolean resolveContentOnHttpError; + + /** + * 创建服务操作上下文; + * + * @param serviceMethod + * 服务接口的方法; + * @param requestMethod + * 服务操作调用所采用的 HTTP 方法; + * @param pathTemplate + * 服务操作的 HTTP 路径模板; + * @param pathParamResolver + * 服务操作的 HTTP URL 路径参数解析器; + * @param requestParamResolver + * 服务操作的 HTTP URL 查询参数解析器; + * @param requestBodyResolver + * 服务操作的 HTTP 请求体解析器; + * @param responseConverter + * 服务操作的 HTTP 成功回复结果转换器; + * @param thrownExceptionTypes + * 服务接口的方法通过 throws 关键字声明的异常的类型列表; + * @param resolveContentOnHttpError + * 是否在 HTTP 错误中包含回复的 HTTP 内容; + */ + public ServiceActionContext(Method serviceMethod, HttpMethod requestMethod, String contentType, RequestPathTemplate pathTemplate, + PathParamResolver pathParamResolver, RequestParamFilter requestParamFilter, + RequestParamResolver requestParamResolver, RequestBodyResolver requestBodyResolver, + ResponseConverter responseConverter, Class[] thrownExceptionTypes, + boolean resolveContentOnHttpError) { + this.serviceMethod = serviceMethod; + this.requestMethod = requestMethod; + this.contentType = contentType; + this.pathTemplate = pathTemplate; + this.pathParamResolver = pathParamResolver; + this.requestParamFilter = requestParamFilter; + this.requestParamResolver = requestParamResolver; + this.requestBodyResolver = requestBodyResolver; + this.responseConverter = responseConverter; + this.thrownExceptionTypes = thrownExceptionTypes; + this.resolveContentOnHttpError = resolveContentOnHttpError; + } + + /** + * 请求路径模板; + * + * @return + */ + public RequestPathTemplate getPathTemplate() { + return pathTemplate; + } + + /** + * 路径参数解析器; + * + * @return + */ + public PathParamResolver getPathParamResolver() { + return pathParamResolver; + } + + /** + * 请求参数解析器; + * + * @return + */ + public RequestParamResolver getRequestParamResolver() { + return requestParamResolver; + } + + /** + * 回复结果转换器; + * + * @return + */ + public ResponseConverter getResponseConverter() { + return responseConverter; + } + + public Method getServiceMethod() { + return serviceMethod; + } + + public HttpMethod getRequestMethod() { + return requestMethod; + } + + public RequestBodyResolver getRequestBodyResolver() { + return requestBodyResolver; + } + + public boolean isResolveContentOnHttpError() { + return resolveContentOnHttpError; + } + + public Class[] getThrownExceptionTypes() { + return thrownExceptionTypes; + } + + public RequestParamFilter getRequestParamFilter() { + return requestParamFilter; + } + + public String getContentType() { + return contentType; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceConnection.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceConnection.java new file mode 100644 index 00000000..04f9d611 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceConnection.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.Closeable; + +public interface ServiceConnection extends Closeable { + + ServiceEndpoint getEndpoint(); + + @Override + void close(); + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceConnectionManager.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceConnectionManager.java new file mode 100644 index 00000000..f676fdc6 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceConnectionManager.java @@ -0,0 +1,137 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.Closeable; +import java.security.KeyManagementException; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.X509TrustManager; + +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.ssl.NoopHostnameVerifier; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.client.HttpClients; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; + +public class ServiceConnectionManager implements Closeable { + + private PoolingHttpClientConnectionManager connectionMananeger; + + public ServiceConnectionManager() { + this.connectionMananeger = new PoolingHttpClientConnectionManager(); + setMaxTotal(100).setDefaultMaxPerRoute(20); + } + + public ServiceConnectionManager setMaxTotal(int maxConn) { + connectionMananeger.setMaxTotal(maxConn); + return this; + } + + public ServiceConnectionManager setDefaultMaxPerRoute(int maxConnPerRoute) { + connectionMananeger.setDefaultMaxPerRoute(maxConnPerRoute); + return this; + } + + HttpClientConnectionManager getHttpConnectionManager() { + return connectionMananeger; + } + + /** + * 创建一个受此连接管理器管理的连接; + * + * @param endpoint + * @return + */ + public ServiceConnection create(ServiceEndpoint serviceEndpoint) { + CloseableHttpClient httpClient = createHttpClient(serviceEndpoint, this); + return new HttpServiceConnection(serviceEndpoint, httpClient); + } + + /** + * 创建一个不受管理的连接; + * + * @param serviceEndpoint + * @return + */ + public static ServiceConnection connect(ServiceEndpoint serviceEndpoint) { + CloseableHttpClient httpClient = createHttpClient(serviceEndpoint, null); + return new HttpServiceConnection(serviceEndpoint, httpClient); + } + + @Override + public void close() { + PoolingHttpClientConnectionManager cm = connectionMananeger; + if (cm != null) { + connectionMananeger = null; + cm.close(); + } + } + + private static CloseableHttpClient createHttpClient(ServiceEndpoint serviceEndpoint, + ServiceConnectionManager connectionManager) { + try { + HttpClientBuilder httpClientBuilder = HttpClients.custom(); + + if (connectionManager != null) { + HttpClientConnectionManager httpConnMng = connectionManager.getHttpConnectionManager(); + httpClientBuilder.setConnectionManager(httpConnMng).setConnectionManagerShared(true); + } + + if (serviceEndpoint.isSecure()) { + httpClientBuilder.setSSLSocketFactory(createSSLConnectionSocketFactory()); + } + + return httpClientBuilder.build(); + } catch (KeyManagementException e) { + throw new IllegalStateException(e.getMessage(), e); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + /** + * 创建SSL安全连接 + * + * @return + * @throws NoSuchAlgorithmException + * @throws KeyManagementException + */ + private static SSLConnectionSocketFactory createSSLConnectionSocketFactory() + throws NoSuchAlgorithmException, KeyManagementException { + SSLConnectionSocketFactory sslsf = null; + SSLContext context = SSLContext.getInstance("TLS"); + context.init(null, new TrustManager[] { trustManager }, null); + sslsf = new SSLConnectionSocketFactory(context, NoopHostnameVerifier.INSTANCE); + return sslsf; + } + + /** + * 重新验证方法,取消SSL验证(信任所有证书) + */ + private static TrustManager trustManager = new X509TrustManager() { + + @Override + public void checkClientTrusted(X509Certificate[] ax509certificate, String s) throws CertificateException { + // TODO Auto-generated method stub + + } + + @Override + public void checkServerTrusted(X509Certificate[] ax509certificate, String s) throws CertificateException { + // TODO Auto-generated method stub + + } + + @Override + public X509Certificate[] getAcceptedIssuers() { + return null; + } + + }; + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceEndpoint.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceEndpoint.java new file mode 100644 index 00000000..5baf60bf --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceEndpoint.java @@ -0,0 +1,50 @@ +package com.jd.blockchain.utils.http.agent; + +import org.springframework.util.StringUtils; + +import com.jd.blockchain.utils.net.NetworkAddress; + +/** + * 服务器设置; + * + * @author haiq + * + */ +public class ServiceEndpoint extends NetworkAddress implements Cloneable { + + private static final long serialVersionUID = 128018335830143965L; + + private String contextPath; + + public ServiceEndpoint(String host, int port, boolean secure) { + this(host, port, secure, null); + } + + public ServiceEndpoint(NetworkAddress networkAddress) { + this(networkAddress.getHost(), networkAddress.getPort(), networkAddress.isSecure(), null); + } + + public ServiceEndpoint(String host, int port, boolean secure, String contextPath) { + super(host, port, secure); + contextPath = StringUtils.cleanPath(contextPath); + if (StringUtils.isEmpty(contextPath)) { + this.contextPath = "/"; + }else{ + this.contextPath = contextPath; + } + } + + public String getContextPath() { + return contextPath; + } + + @Override + public ServiceEndpoint clone() { + try { + return (ServiceEndpoint) super.clone(); + } catch (CloneNotSupportedException e) { + throw new UnsupportedOperationException(e.getMessage(), e); + } + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceRequest.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceRequest.java new file mode 100644 index 00000000..16a00aab --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/ServiceRequest.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.utils.http.agent; + +import java.net.URI; +import java.nio.ByteBuffer; + +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.NamedParamMap; + +public interface ServiceRequest { + + HttpMethod getHttpMethod(); + + URI getUri(); + + ByteBuffer getBody(); + + NamedParamMap getRequestParams(); + + /** + * 返回服务方法的参数值列表; + * + * @return + */ + Object[] getArgs(); + +} \ No newline at end of file diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/StringConverterFactory.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/StringConverterFactory.java new file mode 100644 index 00000000..3d209d61 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/StringConverterFactory.java @@ -0,0 +1,28 @@ +package com.jd.blockchain.utils.http.agent; + +import org.springframework.beans.BeanUtils; + +import com.jd.blockchain.utils.http.StringConverter; +import com.jd.blockchain.utils.http.converters.ObjectToStringConverter; + +abstract class StringConverterFactory { + + public static final StringConverter DEFAULT_PARAM_CONVERTER = new ObjectToStringConverter(); + + public static StringConverter instantiateStringConverter(Class converterClazz) { + StringConverter converter = null; + if (converterClazz != null) { + if (!StringConverter.class.isAssignableFrom(converterClazz)) { + throw new IllegalHttpServiceDefinitionException( + "The specified converter of path param doesn't implement the interface " + + StringConverter.class.getName() + "!"); + } + + converter = (StringConverter) BeanUtils.instantiate(converterClazz); + } else { + // create default converter; + converter = DEFAULT_PARAM_CONVERTER; + } + return converter; + } +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/TypeAutoAdaptingRequestBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/TypeAutoAdaptingRequestBodyConverter.java new file mode 100644 index 00000000..c847cb88 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/agent/TypeAutoAdaptingRequestBodyConverter.java @@ -0,0 +1,62 @@ +package com.jd.blockchain.utils.http.agent; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.springframework.util.ClassUtils; + +import com.jd.blockchain.utils.http.RequestBodyConverter; +import com.jd.blockchain.utils.http.converters.ByteArrayBodyConverter; +import com.jd.blockchain.utils.http.converters.InputStreamBodyConverter; +import com.jd.blockchain.utils.http.converters.JsonBodyConverter; +import com.jd.blockchain.utils.http.converters.ObjectToStringBodyConverter; + +/** + * 类型自动匹配的 RequestBody 转换器; + * @author haiq + * + */ +class TypeAutoAdaptingRequestBodyConverter implements RequestBodyConverter{ + + private static final RequestBodyConverter OBJECT_TO_STRING_CONVERTER = new ObjectToStringBodyConverter(); + private static final RequestBodyConverter INPUT_STREAM_CONVERTER = new InputStreamBodyConverter(); + private static final RequestBodyConverter BYTES_CONVERTER = new ByteArrayBodyConverter(); +// private static final RequestBodyConverter JSON_CONVERTER = new JsonBodyConverter(); + + private RequestBodyConverter converter; + + public TypeAutoAdaptingRequestBodyConverter(Class argType) { + converter = createConverter(argType); + } + + private RequestBodyConverter createConverter(Class argType){ + if (ClassUtils.isAssignable(InputStream.class, argType)) { + return INPUT_STREAM_CONVERTER; + } + if (ClassUtils.isAssignable(String.class, argType)) { + return OBJECT_TO_STRING_CONVERTER; + } + if (ClassUtils.isAssignable(byte[].class, argType)) { + return BYTES_CONVERTER; + } + if (ClassUtils.isPrimitiveOrWrapper(argType)) { + return OBJECT_TO_STRING_CONVERTER; + } + if (ClassUtils.isAssignable(OutputStream.class, argType)) { + throw new IllegalHttpServiceDefinitionException("Unsupported type for the request body argument!"); + } + //默认按照 JSON 方式返回; + return new JsonBodyConverter(argType); +// return JSON_CONVERTER; + } + + @Override + public void write(Object param, OutputStream out) throws IOException{ + if (param == null) { + return; + } + converter.write(param, out); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/auth/Securities.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/auth/Securities.java new file mode 100644 index 00000000..0bf8c444 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/auth/Securities.java @@ -0,0 +1,8 @@ +package com.jd.blockchain.utils.http.auth; + +import com.jd.blockchain.utils.security.ShaUtils; + +public class Securities { + + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/BinarySerializeRequestBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/BinarySerializeRequestBodyConverter.java new file mode 100644 index 00000000..402e91f4 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/BinarySerializeRequestBodyConverter.java @@ -0,0 +1,23 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.converters.BinarySerializeRequestBodyConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/5 下午5:09 + * Description: 序列化请求体 + */ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.utils.http.RequestBodyConverter; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +public class BinarySerializeRequestBodyConverter implements RequestBodyConverter { + + @Override + public void write(Object param, OutputStream out) throws IOException { + BinarySerializeUtils.serialize(param, out); + } +} \ No newline at end of file diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/BinarySerializeResponseConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/BinarySerializeResponseConverter.java new file mode 100644 index 00000000..a9da4670 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/BinarySerializeResponseConverter.java @@ -0,0 +1,26 @@ +/** + * Copyright: Copyright 2016-2020 JD.COM All Right Reserved + * FileName: com.jd.blockchain.sdk.converters.BinarySerializeResponseConverter + * Author: shaozhuguang + * Department: 区块链研发部 + * Date: 2018/9/5 下午5:22 + * Description: + */ +package com.jd.blockchain.utils.http.converters; + +import java.io.InputStream; + +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +public class BinarySerializeResponseConverter implements ResponseConverter { + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) + throws Exception { + return BinarySerializeUtils.deserialize(responseStream); + } + +} \ No newline at end of file diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ByteArrayBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ByteArrayBodyConverter.java new file mode 100644 index 00000000..c0daae22 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ByteArrayBodyConverter.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.utils.http.RequestBodyConverter; + +public class ByteArrayBodyConverter implements RequestBodyConverter{ + + @Override + public void write(Object param, OutputStream out) throws IOException{ + out.write((byte[])param); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ByteArrayResponseConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ByteArrayResponseConverter.java new file mode 100644 index 00000000..dfa25ad3 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ByteArrayResponseConverter.java @@ -0,0 +1,31 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.springframework.util.StreamUtils; + +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; + +public class ByteArrayResponseConverter implements ResponseConverter { + + public static final ByteArrayResponseConverter INSTANCE = new ByteArrayResponseConverter(); + + private ByteArrayResponseConverter() { + } + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) { + try { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + StreamUtils.copy(responseStream, out); + return out.toByteArray(); + } catch (IOException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/EmptyBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/EmptyBodyConverter.java new file mode 100644 index 00000000..acbb91e9 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/EmptyBodyConverter.java @@ -0,0 +1,15 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.OutputStream; + +import com.jd.blockchain.utils.http.RequestBodyConverter; + +public class EmptyBodyConverter implements RequestBodyConverter{ + + @Override + public void write(Object param, OutputStream out) throws IOException{ + //Do nothing; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/InputStreamBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/InputStreamBodyConverter.java new file mode 100644 index 00000000..fe6928d6 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/InputStreamBodyConverter.java @@ -0,0 +1,17 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.jd.blockchain.utils.http.RequestBodyConverter; +import com.jd.blockchain.utils.io.BytesUtils; + +public class InputStreamBodyConverter implements RequestBodyConverter{ + + @Override + public void write(Object param, OutputStream out) throws IOException{ + BytesUtils.copy((InputStream)param, out); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonBodyConverter.java new file mode 100644 index 00000000..c6416935 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonBodyConverter.java @@ -0,0 +1,29 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import com.jd.blockchain.utils.http.HttpServiceConsts; +import com.jd.blockchain.utils.http.RequestBodyConverter; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +public class JsonBodyConverter implements RequestBodyConverter { + + private Class dataType; + + public JsonBodyConverter(Class dataType) { + this.dataType = dataType; + } + + @Override + public void write(Object param, OutputStream out) throws IOException{ + String jsonString = JSONSerializeUtils.serializeToJSON(param, dataType); + try { + out.write(jsonString.getBytes(HttpServiceConsts.CHARSET)); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonConverter.java new file mode 100644 index 00000000..217ee4a1 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonConverter.java @@ -0,0 +1,27 @@ +package com.jd.blockchain.utils.http.converters; + +import com.jd.blockchain.utils.http.StringConverter; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +/** + * JSON 格式的参数转换器; + * + * @author haiq + * + */ +public class JsonConverter implements StringConverter { + + private Class dataType; + + public JsonConverter(Class dataType) { + this.dataType = dataType; + } + + @Override + public String toString(Object obj) { + // TODO:未定义“日期时间”的输出格式 ; + return JSONSerializeUtils.serializeToJSON(obj, dataType); + // return JSON.toJSONString(obj); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonResponseConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonResponseConverter.java new file mode 100644 index 00000000..04195133 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/JsonResponseConverter.java @@ -0,0 +1,30 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.InputStream; + +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +public class JsonResponseConverter implements ResponseConverter { + + private Class clazz; + + public JsonResponseConverter(Class clazz) { + this.clazz = clazz; + } + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) throws Exception { + String jsonResponse = (String) StringResponseConverter.INSTANCE.getResponse(request, responseStream, null); + if (jsonResponse == null) { + return null; + } + jsonResponse = jsonResponse.trim(); + // TODO: 未指定“日期时间”格式的策略; + return JSONSerializeUtils.deserializeAs(jsonResponse, clazz); +// return JSON.toJavaObject(JSONObject.parseObject(jsonResponse), clazz); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/NullResponseConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/NullResponseConverter.java new file mode 100644 index 00000000..71b06042 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/NullResponseConverter.java @@ -0,0 +1,21 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.InputStream; + +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; + +public class NullResponseConverter implements ResponseConverter { + + public static final ResponseConverter INSTANCE = new NullResponseConverter(); + + private NullResponseConverter() { + } + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) { + return null; + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ObjectToStringBodyConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ObjectToStringBodyConverter.java new file mode 100644 index 00000000..cdea2d27 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ObjectToStringBodyConverter.java @@ -0,0 +1,23 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +import com.jd.blockchain.utils.http.HttpServiceConsts; +import com.jd.blockchain.utils.http.RequestBodyConverter; + +public class ObjectToStringBodyConverter implements RequestBodyConverter { + + @Override + public void write(Object param, OutputStream out) throws IOException{ + try { + String text = param.toString(); + byte[] bytes = text.getBytes(HttpServiceConsts.CHARSET); + out.write(bytes); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e.getMessage(), e); + } + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ObjectToStringConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ObjectToStringConverter.java new file mode 100644 index 00000000..21e12d4e --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/ObjectToStringConverter.java @@ -0,0 +1,12 @@ +package com.jd.blockchain.utils.http.converters; + +import com.jd.blockchain.utils.http.StringConverter; + +public class ObjectToStringConverter implements StringConverter { + + @Override + public String toString(Object param) { + return param == null ? null : param.toString(); + } + +} diff --git a/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/StringResponseConverter.java b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/StringResponseConverter.java new file mode 100644 index 00000000..a5858295 --- /dev/null +++ b/source/utils/utils-http/src/main/java/com/jd/blockchain/utils/http/converters/StringResponseConverter.java @@ -0,0 +1,60 @@ +package com.jd.blockchain.utils.http.converters; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; + +import com.jd.blockchain.utils.http.HttpServiceConsts; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.HttpServiceException; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; + +/** + * 返回字符的转换器; + * + * @author haiq + * + */ +public class StringResponseConverter implements ResponseConverter { + + public static final ResponseConverter INSTANCE = new StringResponseConverter(); + + private StringResponseConverter() { + } + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) { + String responseText = readString(responseStream); + return responseText; + } + + private String readString(InputStream in) { + InputStreamReader reader = null; + try { + reader = new InputStreamReader(in, HttpServiceConsts.CHARSET); + + StringBuilder text = new StringBuilder(); + char[] buffer = new char[256]; + int len = 0; + while((len = reader.read(buffer)) > 0){ + text.append(buffer, 0, len); + } + return text.toString(); + } catch (UnsupportedEncodingException e) { + throw new IllegalArgumentException(e.getMessage(), e); + } catch (IOException e) { + throw new HttpServiceException(e.getMessage(), e); + } finally { + if (reader != null) { + try { + reader.close(); + } catch (IOException e) { + // ignore exception thrown again; + } + } + } + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/BaseRequestSetting.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/BaseRequestSetting.java new file mode 100644 index 00000000..07a343ab --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/BaseRequestSetting.java @@ -0,0 +1,31 @@ +package test.my.utils.http.agent; + +import com.jd.blockchain.utils.http.RequestParam; + +public abstract class BaseRequestSetting { + + @RequestParam(name = "id") + private String id; + + @RequestParam(name = "type") + public final OpType type = OpType.TYPE2; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public abstract int getValue(); + + /** + * 只读属性; + * + * @return + */ + public OpType getType() { + return type; + } +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/ContentRequestSetting.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/ContentRequestSetting.java new file mode 100644 index 00000000..c0ff6cbe --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/ContentRequestSetting.java @@ -0,0 +1,48 @@ +package test.my.utils.http.agent; + +import com.jd.blockchain.utils.http.RequestParam; + +/** + * ContentRequestSetting 用于测试以 POJO 方式定义请求参数; + * + * 并且通过继承 BaseRequestSetting 用于测试通过继承 POJO 的方式继承请求参数的定义; + * + * @author haiq + * + */ +public class ContentRequestSetting extends BaseRequestSetting { + + @RequestParam(name = "permited") + private boolean permited; + @RequestParam(name = "male", converter = CustomBooleanConverter.class) + private boolean male; + + + @RequestParam(name = "value", required = false, ignoreValue = "-1") + private int value; + + public boolean isPermited() { + return permited; + } + + public void setPermited(boolean permited) { + this.permited = permited; + } + + public boolean isMale() { + return male; + } + + public void setMale(boolean male) { + this.male = male; + } + + @Override + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/CustomBooleanConverter.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/CustomBooleanConverter.java new file mode 100644 index 00000000..44593cfe --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/CustomBooleanConverter.java @@ -0,0 +1,13 @@ +package test.my.utils.http.agent; + +import com.jd.blockchain.utils.http.StringConverter; + +public class CustomBooleanConverter implements StringConverter { + + @Override + public String toString(Object obj) { + Boolean value = (Boolean) obj; + return value.booleanValue() ? "1" : "0"; + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/DataResponse.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/DataResponse.java new file mode 100644 index 00000000..9c31b93a --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/DataResponse.java @@ -0,0 +1,44 @@ +package test.my.utils.http.agent; + +public class DataResponse { + + private boolean success; + + private String error; + + private String content; + + public void setSuccess(boolean success) { + this.success = success; + } + + public void setError(String error) { + this.error = error; + } + + public void setContent(String content) { + this.content = content; + } + + public DataResponse() { + } + + public DataResponse(boolean success, String error, String content) { + this.success = success; + this.error = error; + this.content = content; + } + + public boolean isSuccess() { + return success; + } + + public String getError() { + return error; + } + + public String getContent() { + return content; + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentException.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentException.java new file mode 100644 index 00000000..bd133402 --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentException.java @@ -0,0 +1,15 @@ +package test.my.utils.http.agent; + +public class GetContentException extends Exception{ + + private static final long serialVersionUID = 8896491616375068614L; + + public GetContentException() { + } + + + public GetContentException(String message) { + super(message); + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentResponseConverter.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentResponseConverter.java new file mode 100644 index 00000000..77ed4fbe --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentResponseConverter.java @@ -0,0 +1,20 @@ +package test.my.utils.http.agent; + +import java.io.InputStream; + +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.http.converters.JsonResponseConverter; + +public class GetContentResponseConverter implements ResponseConverter { + + private JsonResponseConverter jsonResponseConverter = new JsonResponseConverter(DataResponse.class); + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) throws Exception { + DataResponse data = (DataResponse) jsonResponseConverter.getResponse(request, responseStream, null); + return data.getContent(); + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentResponseConverterWithException.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentResponseConverterWithException.java new file mode 100644 index 00000000..9d05f92f --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/GetContentResponseConverterWithException.java @@ -0,0 +1,23 @@ +package test.my.utils.http.agent; + +import java.io.InputStream; + +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.http.converters.JsonResponseConverter; + +public class GetContentResponseConverterWithException implements ResponseConverter { + + private JsonResponseConverter jsonResponseConverter = new JsonResponseConverter(DataResponse.class); + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) throws Exception { + DataResponse data = (DataResponse) jsonResponseConverter.getResponse(request, responseStream, null); + if (data.isSuccess()) { + return data.getContent(); + } + throw new GetContentException(data.getError()); + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpRequestCollector.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpRequestCollector.java new file mode 100644 index 00000000..e16b5a80 --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpRequestCollector.java @@ -0,0 +1,149 @@ +package test.my.utils.http.agent; + +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.io.ByteArray; +import com.jd.blockchain.utils.io.BytesUtils; + +public class HttpRequestCollector extends HttpServlet{ + + private static final long serialVersionUID = -8014615357825392276L; + + private List reqRecords = new LinkedList(); + + private String requestPath = null; + + private String authorization = null; + + private String responseText = null; + + private byte[] responseBytes = null; + + private String requestMethod = null; + + private ByteArray requestBody = null; + + public HttpRequestCollector(String responseText) { + this.responseText = responseText; + } + + public HttpRequestCollector(byte[] responseBytes) { + this.responseBytes = responseBytes; + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + reqRecords.add(new HttpRequestInfo(HttpMethod.GET, req.getParameterMap())); + requestPath = req.getRequestURI(); + authorization = req.getHeader("Authorization"); + requestMethod = req.getMethod(); + + if (responseText != null) { + resp.getWriter().print(responseText); + resp.getWriter().flush(); + }else if(responseBytes != null) { + resp.getOutputStream().write(responseBytes, 0, responseBytes.length); + resp.getOutputStream().flush(); + } + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + reqRecords.add(new HttpRequestInfo(HttpMethod.POST, req.getParameterMap())); + requestPath = req.getRequestURI(); + authorization = req.getHeader("Authorization"); + requestMethod = req.getMethod(); + requestBody = readRequestBody(req); + + if (responseText != null) { + resp.getWriter().print(responseText); + resp.getWriter().flush(); + }else if(responseBytes != null) { + resp.getOutputStream().write(responseBytes, 0, responseBytes.length); + resp.getOutputStream().flush(); + } + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ + reqRecords.add(new HttpRequestInfo(HttpMethod.PUT, req.getParameterMap())); + requestPath = req.getRequestURI(); + authorization = req.getHeader("Authorization"); + requestMethod = req.getMethod(); + requestBody = readRequestBody(req); + + if (responseText != null) { + resp.getWriter().print(responseText); + resp.getWriter().flush(); + }else if(responseBytes != null) { + resp.getOutputStream().write(responseBytes, 0, responseBytes.length); + resp.getOutputStream().flush(); + } + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{ + reqRecords.add(new HttpRequestInfo(HttpMethod.DELETE, req.getParameterMap())); + requestPath = req.getRequestURI(); + authorization = req.getHeader("Authorization"); + requestMethod = req.getMethod(); + + if (responseText != null) { + resp.getWriter().print(responseText); + resp.getWriter().flush(); + }else if(responseBytes != null) { + resp.getOutputStream().write(responseBytes, 0, responseBytes.length); + resp.getOutputStream().flush(); + } + } + + private ByteArray readRequestBody(HttpServletRequest request) throws UnsupportedEncodingException, IOException{ + byte[] bodyBytes = BytesUtils.copyToBytes(request.getInputStream()); + return ByteArray.wrap(bodyBytes); + +// BufferedReader reader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8")); +// String line = null; +// StringBuilder builder = new StringBuilder(); +// while((line = reader.readLine()) != null){ +// builder.append(line); +// } +// return builder.toString(); + } + + + + public Iterator getRequestRecords(){ + return reqRecords.iterator(); + } + + public String getRequestPath(){ + return requestPath; + } + + public String getAuthorization(){ + return authorization; + } + + public String getRequestMethod(){ + return requestMethod; + } + + public ByteArray getRequestBody(){ + return requestBody; + } + + public void clearRequestRecords(){ + reqRecords.clear(); + requestPath = null; + } +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpRequestInfo.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpRequestInfo.java new file mode 100644 index 00000000..bcada3cf --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpRequestInfo.java @@ -0,0 +1,28 @@ +package test.my.utils.http.agent; + +import java.util.HashMap; +import java.util.Map; + +import com.jd.blockchain.utils.http.HttpMethod; + +public class HttpRequestInfo { + + private HttpMethod method; + + private Map parameters; + + + public HttpMethod getMethod() { + return method; + } + + public Map getParameters() { + return parameters; + } + + public HttpRequestInfo(HttpMethod method, Map parameters) { + this.method = method; + this.parameters = new HashMap(parameters); + } + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpServiceAgentTest.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpServiceAgentTest.java new file mode 100644 index 00000000..2a1cf757 --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpServiceAgentTest.java @@ -0,0 +1,565 @@ +package test.my.utils.http.agent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.io.InputStream; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; + +import javax.servlet.http.HttpServlet; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.agent.AuthorizationAlgs; +import com.jd.blockchain.utils.http.agent.AuthorizationHeader; +import com.jd.blockchain.utils.http.agent.HttpServiceAgent; +import com.jd.blockchain.utils.http.agent.ServiceEndpoint; +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; +import com.jd.blockchain.utils.web.server.WebServer; + +public class HttpServiceAgentTest { + + private static final String host = "127.0.0.1"; + + private static final int port = 10809; + + private static final String SENDER_NAME = "upush_test"; + + private static final String SECRET_KEY = "123abc"; + + private WebServer server; + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + if (server != null) { + server.stop(); + } + } + + private void prepareEnvironment(String contextPath, HttpServlet servlet, String servletMapping) { + int port = 10809; + server = new WebServer(host, port); + server.registServlet("test-servlet", servlet, servletMapping); + server.setContextPath(contextPath); + server.start(); + } + + @Test + public void testRequestParam() { + String contextPath = "/testserver"; + String servicePath = "/test/content"; + DataResponse expectedResponse = new DataResponse(true, null, "TestContent\r\nabckkk\r\n1"); + String expectedResponseText = JSON.toJSONString(expectedResponse); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint endpoint = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authorization = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + // setting.setContextPath(contextPath); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, endpoint, authorization); + + // 主要路径; + String expectedId = "001"; + boolean expectedPermited = true; + boolean origMale = false; + String expectedMaleStr = (new CustomBooleanConverter()).toString(origMale); + int expectedValue = 10; + // 发起请求; + String actualResponseText = testService.content(expectedId, expectedPermited, origMale, expectedValue); + // 验证结果; + assertEquals(expectedResponseText, actualResponseText); + Iterator reqRecords = servlet.getRequestRecords(); + assertTrue(reqRecords.hasNext()); + HttpRequestInfo reqRecord = reqRecords.next(); + assertEquals(HttpMethod.GET, reqRecord.getMethod()); + Map params = reqRecord.getParameters(); + assertEquals(4, params.size()); + assertTrue(params.containsKey("id")); + assertTrue(params.containsKey("permited")); + assertTrue(params.containsKey("male")); + assertTrue(params.containsKey("value")); + assertEquals(expectedId, params.get("id")[0]); + assertEquals(Boolean.toString(expectedPermited), params.get("permited")[0]); + assertEquals(expectedMaleStr, params.get("male")[0]); + assertEquals(expectedValue + "", params.get("value")[0]); + // 清理数据; + servlet.clearRequestRecords(); + + // 在主要路径的基础上验证标注的请求路径名,忽略特定值的参数; + expectedId = "001"; + expectedPermited = true; + origMale = false; + expectedMaleStr = (new CustomBooleanConverter()).toString(origMale); + int expectedIgnoreValue = -1; + // 发起请求; + DataResponse actualResponse = testService.getContent(expectedId, expectedPermited, origMale, + expectedIgnoreValue, "other values"); + // 验证结果; + assertEquals(expectedResponse.getContent(), actualResponse.getContent()); + assertEquals(expectedResponse.getError(), actualResponse.getError()); + assertEquals(expectedResponse.isSuccess(), actualResponse.isSuccess()); + + reqRecords = servlet.getRequestRecords(); + assertTrue(reqRecords.hasNext()); + reqRecord = reqRecords.next(); + assertEquals(HttpMethod.GET, reqRecord.getMethod()); + params = reqRecord.getParameters(); + assertEquals(3, params.size()); + assertTrue(params.containsKey("id")); + assertTrue(params.containsKey("permited")); + assertTrue(params.containsKey("male")); + assertTrue(!params.containsKey("value"));// 不包含应被忽略的值; + assertEquals(expectedId, params.get("id")[0]); + assertEquals(Boolean.toString(expectedPermited), params.get("permited")[0]); + assertEquals(expectedMaleStr, params.get("male")[0]); + servlet.clearRequestRecords(); + + // 在前面的测试路径的基础上验证 http post 方法发送请求,以及自定义的回复转换器; + expectedId = "001"; + expectedPermited = true; + boolean expectedMale = false; + expectedValue = 188; + // 发起请求; + String actualResponseText2 = testService.fetchContent(expectedId, expectedPermited, expectedMale, + expectedValue); + // 验证结果; + assertEquals(expectedResponse.getContent(), actualResponseText2); + reqRecords = servlet.getRequestRecords(); + assertTrue(reqRecords.hasNext()); + reqRecord = reqRecords.next(); + assertEquals(HttpMethod.POST, reqRecord.getMethod()); + params = reqRecord.getParameters(); + assertEquals(4, params.size()); + assertTrue(params.containsKey("id")); + assertTrue(params.containsKey("permited")); + assertTrue(params.containsKey("male")); + assertTrue(params.containsKey("value")); + assertEquals(expectedId, params.get("id")[0]); + assertEquals(Boolean.toString(expectedPermited), params.get("permited")[0]); + assertEquals(expectedMaleStr, params.get("male")[0]); + assertEquals(expectedValue + "", params.get("value")[0]); + + } + + /** + * 测试自定义的 ResponseConverter 以及服务方法声明异常的处理机制; + */ + public void testResponseConverterWithException_Success() { + String contextPath = "/testserver"; + String servicePath = "/test/content"; + boolean success = true; + String expectedError = ""; + DataResponse expectedResponse = new DataResponse(success, expectedError, "TestContent\r\nabckkk\r\n1"); + String expectedResponseText = JSON.toJSONString(expectedResponse); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 准备数据; + String expectedId = "001"; + boolean expectedPermited = true; + boolean origMale = false; + int expectedValue = 10; + + // 主要路径:测试自定义的回复转换器返回正常时的回复结果; + expectedId = "001"; + expectedPermited = true; + boolean expectedMale = false; + String expectedMaleStr = (new CustomBooleanConverter()).toString(origMale); + expectedValue = 188; + // 发起请求; + GetContentException expectedException = null; + String actualResponseText2 = null; + try { + actualResponseText2 = testService.fetchContentWithError(expectedId, expectedPermited, expectedMale, + expectedValue); + } catch (GetContentException e) { + expectedException = e; + } + // 验证结果; + assertTrue(expectedException == null);// 预期没有异常; + + assertEquals(expectedResponse.getContent(), actualResponseText2); + Iterator reqRecords = servlet.getRequestRecords(); + assertTrue(reqRecords.hasNext()); + HttpRequestInfo reqRecord = reqRecords.next(); + assertEquals(HttpMethod.POST, reqRecord.getMethod()); + Map params = reqRecord.getParameters(); + assertEquals(4, params.size()); + assertTrue(params.containsKey("id")); + assertTrue(params.containsKey("permited")); + assertTrue(params.containsKey("male")); + assertTrue(params.containsKey("value")); + assertEquals(expectedId, params.get("id")[0]); + assertEquals(Boolean.toString(expectedPermited), params.get("permited")[0]); + assertEquals(expectedMaleStr, params.get("male")[0]); + assertEquals(expectedValue + "", params.get("value")[0]); + + } + + /** + * 测试自定义的 ResponseConverter 以及服务方法声明异常的处理机制; + */ + public void testResponseConverterWithException_Error() { + String contextPath = "/testserver"; + String servicePath = "/test/content"; + boolean success = false; + String expectedError = "test error message"; + DataResponse expectedResponse = new DataResponse(success, expectedError, "TestContent\r\nabckkk\r\n1"); + String expectedResponseText = JSON.toJSONString(expectedResponse); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 准备数据; + String expectedId = "001"; + boolean expectedPermited = true; + int expectedValue = 10; + + // 主要路径:测试自定义的回复转换器返回正常时的回复结果; + expectedId = "001"; + expectedPermited = true; + boolean expectedMale = false; + expectedValue = 188; + // 发起请求; + GetContentException expectedException = null; + try { + String actualResponseText2 = testService.fetchContentWithError(expectedId, expectedPermited, expectedMale, + expectedValue); + } catch (GetContentException e) { + expectedException = e; + } + // 验证结果; + assertTrue(expectedException != null);// 预期没有异常; + assertEquals(expectedError, expectedException.getMessage()); + } + + @Test + public void testRequestParamMap() { + String contextPath = "/testserver"; + String servicePath = "/test/content"; + DataResponse expectedResponse = new DataResponse(true, null, "TestContent\r\nabckkk\r\n1"); + String expectedResponseText = JSON.toJSONString(expectedResponse); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 主要路径; + String expectedId = "001"; + boolean expectedPermited = true; + boolean origMale = false; + String expectedMaleStr = (new CustomBooleanConverter()).toString(origMale); + int expectedValue = 10; + + ContentRequestSetting expected = new ContentRequestSetting(); + expected.setId(expectedId); + expected.setMale(origMale); + expected.setPermited(expectedPermited); + expected.setValue(expectedValue); + + // 发起请求; + String actualResponseText = testService.content(expected); + // 验证结果; + assertEquals(expectedResponseText, actualResponseText); + Iterator reqRecords = servlet.getRequestRecords(); + assertTrue(reqRecords.hasNext()); + HttpRequestInfo reqRecord = reqRecords.next(); + assertEquals(HttpMethod.GET, reqRecord.getMethod()); + Map params = reqRecord.getParameters(); + assertEquals(5, params.size()); + assertTrue(params.containsKey("id")); + assertTrue(params.containsKey("permited")); + assertTrue(params.containsKey("male")); + assertTrue(params.containsKey("value")); + assertEquals(expectedId, params.get("id")[0]); + assertEquals(Boolean.toString(expectedPermited), params.get("permited")[0]); + assertEquals(expectedMaleStr, params.get("male")[0]); + assertEquals(expectedValue + "", params.get("value")[0]); + + assertTrue(params.containsKey("type"));// 验证只读常量字段也可以映射为请求参数; + assertEquals(expected.getType().toString(), params.get("type")[0]); + // 清理数据; + servlet.clearRequestRecords(); + } + + @Test + public void testRequestParam_Map() { + // TODO implemented; + } + + @Test + public void testMultiRequestParamMap() { + // TODO implemented; + } + + /** + * 测试多行文本的发送和获取; + */ + @Test + public void testMultilineTextSendAndGet() { + String contextPath = "/testserver"; + String servicePath = "/test/content/update"; + String expectedResponseText = "TestContent\r\nline2:testcontent.......\r\nline3::::"; + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 发起请求; + String actualResponseText = testService.updateAndGetContent(expectedResponseText); + // 验证结果; + assertEquals(expectedResponseText, actualResponseText); + + Iterator reqRecords = servlet.getRequestRecords(); + assertTrue(reqRecords.hasNext()); + HttpRequestInfo reqRecord = reqRecords.next(); + assertEquals(HttpMethod.GET, reqRecord.getMethod()); + Map params = reqRecord.getParameters(); + assertEquals(1, params.size()); + assertTrue(params.containsKey("content")); + assertEquals(expectedResponseText, params.get("content")[0]); + + // 清理数据; + servlet.clearRequestRecords(); + } + + @Test + public void testRequestParam_Map_Body() { + // TODO implemented; + } + + /** + * 测试PathParam + */ + @Test + public void testPathParam() { + String contextPath = "/testserver"; + String servicePath = "/test/content/*"; + DataResponse expectedResponse = new DataResponse(true, null, "TestContent\r\nabckkk\r\n001"); + String expectedResponseText = JSON.toJSONString(expectedResponse); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 主要路径; + String expectedId = "001"; + // 发起请求; + String actualResponseText = testService.contentById(expectedId); + // 验证结果; + assertEquals(expectedResponseText, actualResponseText); + assertEquals("/testserver/test/content/001", servlet.getRequestPath()); + assertEquals(AuthorizationAlgs.DEFAULT + " upush_test:123abc", servlet.getAuthorization()); + // 清理数据; + servlet.clearRequestRecords(); + + /** + * + * 测试put请求 + * + */ + String expectedContent = "Hello World!"; + // 发起请求; + actualResponseText = testService.contentByPut(expectedId, expectedContent); + // 验证结果; + assertEquals(expectedResponseText, actualResponseText); + assertEquals("PUT", servlet.getRequestMethod()); + // 清理数据; + servlet.clearRequestRecords(); + } + + @Test + public void testRequestBody() { + String contextPath = "/testserver"; + String servicePath = "/test/content/*"; + DataResponse expectedResponse = new DataResponse(true, null, "TestContent\r\nabckkk\r\n001"); + String expectedResponseText = JSON.toJSONString(expectedResponse); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseText); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 主要路径; + String expectedId = "001"; + String expectedName = "liuxrb"; + RequestContent expectedRequestContent = new RequestContent(100, "Hello, World!"); + // 发起请求; + String actualResponseText = testService.contentBody(expectedId, expectedName, expectedRequestContent); + // 验证结果; + assertEquals(expectedResponseText, actualResponseText); + assertEquals("/testserver/test/content/001", servlet.getRequestPath()); + assertEquals(AuthorizationAlgs.DEFAULT + " upush_test:123abc", servlet.getAuthorization()); + + JSONObject jsonReqBody = JSONObject.parseObject(servlet.getRequestBody().toString("utf-8")); + JSONObject expectedJsonReqBody = (JSONObject) JSONObject.toJSON(expectedRequestContent); + assertJSONEqual(jsonReqBody, expectedJsonReqBody); + // assertEquals(JSONObject.toJSONString(expectedRequestContent), + // servlet.getRequestBody()); + // 清理数据; + servlet.clearRequestRecords(); + + } + + @Test + public void testMultiRequestBodies() { + String contextPath = "/testserver"; + String servicePath = "/test/content/*"; + + TestData expectedResponseData = new TestData(); + expectedResponseData.setId(99); + expectedResponseData.setValue(UUID.randomUUID().toString()); + byte[] expectedResponseBytes = BinarySerializeUtils.serialize(expectedResponseData); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseBytes); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + HttpTestService testService = HttpServiceAgent.createService(HttpTestService.class, setting, authSetting); + + // 主要路径; + String expectedId = "001"; + String expectedName = "liuxrb"; + + TestData data1 = new TestData(); + data1.setId(1); + data1.setValue(UUID.randomUUID().toString()); + TestData data2 = new TestData(); + data2.setId(1); + data2.setValue(UUID.randomUUID().toString()); + // 发起请求; + TestData actualResponseData = testService.multiContentBodies(expectedId, expectedName, data1, data2); + // 验证结果; + assertEquals(expectedResponseData.getId(), actualResponseData.getId()); + assertEquals(expectedResponseData.getValue(), actualResponseData.getValue()); + + assertEquals("/testserver/test/content/001/multi", servlet.getRequestPath()); + assertEquals(AuthorizationAlgs.DEFAULT + " upush_test:123abc", servlet.getAuthorization()); + + InputStream in = servlet.getRequestBody().asInputStream(); + TestData actualData1 = BinarySerializeUtils.deserialize(in); + TestData actualData2 = BinarySerializeUtils.deserialize(in); + + assertEquals(data1.getId(), actualData1.getId()); + assertEquals(data1.getValue(), actualData1.getValue()); + assertEquals(data2.getId(), actualData2.getId()); + assertEquals(data2.getValue(), actualData2.getValue()); + // assertEquals(JSONObject.toJSONString(expectedRequestContent), + // servlet.getRequestBody()); + // 清理数据; + servlet.clearRequestRecords(); + + } + + + @Test + public void testMultiRequestBodiesWithDefaultConverter() { + String contextPath = "/testserver"; + String servicePath = "/test/content/*"; + + TestData expectedResponseData = new TestData(); + expectedResponseData.setId(99); + expectedResponseData.setValue(UUID.randomUUID().toString()); + byte[] expectedResponseBytes = BinarySerializeUtils.serialize(expectedResponseData); + HttpRequestCollector servlet = new HttpRequestCollector(expectedResponseBytes); + + // 准备环境; + prepareEnvironment(contextPath, servlet, servicePath); + + ServiceEndpoint setting = new ServiceEndpoint(host, port, false, contextPath); + AuthorizationHeader authSetting = new AuthorizationHeader(AuthorizationAlgs.DEFAULT, SENDER_NAME, SECRET_KEY); + + // 创建测试对象; + MultiRequestBodiesWithDefaultConverterTestService testService = HttpServiceAgent.createService(MultiRequestBodiesWithDefaultConverterTestService.class, setting, authSetting); + + // 主要路径; + String expectedId = "001"; + String expectedName = "liuxrb"; + + TestData data1 = new TestData(); + data1.setId(1); + data1.setValue(UUID.randomUUID().toString()); + TestData data2 = new TestData(); + data2.setId(1); + data2.setValue(UUID.randomUUID().toString()); + // 发起请求; + TestData actualResponseData = testService.multiContentBodies(expectedId, expectedName, data1, data2); + // 验证结果; + assertEquals(expectedResponseData.getId(), actualResponseData.getId()); + assertEquals(expectedResponseData.getValue(), actualResponseData.getValue()); + + assertEquals("/testserver/test/content/001/multi", servlet.getRequestPath()); + assertEquals(AuthorizationAlgs.DEFAULT + " upush_test:123abc", servlet.getAuthorization()); + + InputStream in = servlet.getRequestBody().asInputStream(); + TestData actualData1 = BinarySerializeUtils.deserialize(in); + TestData actualData2 = BinarySerializeUtils.deserialize(in); + + assertEquals(data1.getId(), actualData1.getId()); + assertEquals(data1.getValue(), actualData1.getValue()); + assertEquals(data2.getId(), actualData2.getId()); + assertEquals(data2.getValue(), actualData2.getValue()); + // assertEquals(JSONObject.toJSONString(expectedRequestContent), + // servlet.getRequestBody()); + // 清理数据; + servlet.clearRequestRecords(); + + } + + private void assertJSONEqual(JSONObject jsonObj1, JSONObject jsonObj2) { + Set> fields = jsonObj1.entrySet(); + for (Entry field : fields) { + assertTrue(jsonObj2.containsKey(field.getKey())); + Object value1 = field.getValue(); + Object value2 = jsonObj2.get(field.getKey()); + if (value1 != null && value2 != null && value1 instanceof JSONObject && value2 instanceof JSONObject) { + assertJSONEqual((JSONObject) value1, (JSONObject) value2); + } else { + assertEquals(value1, value2); + } + } + } +} + diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpTestService.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpTestService.java new file mode 100644 index 00000000..a8414ace --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/HttpTestService.java @@ -0,0 +1,131 @@ +package test.my.utils.http.agent; + +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.HttpService; +import com.jd.blockchain.utils.http.PathParam; +import com.jd.blockchain.utils.http.RequestBody; +import com.jd.blockchain.utils.http.RequestParam; +import com.jd.blockchain.utils.http.RequestParamMap; +import com.jd.blockchain.utils.http.converters.BinarySerializeRequestBodyConverter; +import com.jd.blockchain.utils.http.converters.BinarySerializeResponseConverter; + +@HttpService(path = "/test") +public interface HttpTestService { + + /** + * 通过 HTTP GET 方法获取文本内容; + * + * @param id + * @param permited + * @param male + * 性别;作为请求参数,设置了自定义的转换器将布尔值转换为 0/1 ; + * @param value + * 值;定义为非必须的请求参数,如果值为 -1 时被忽略; + * @return 返回结果是包含了请求处理状态、错误信息、成功的数据内容在内的 JSON 字符串; + */ + @HttpAction(method = HttpMethod.GET) + public String content(@RequestParam(name = "id") String id, @RequestParam(name = "permited") boolean permited, + @RequestParam(name = "male", converter = CustomBooleanConverter.class) boolean male, + @RequestParam(name = "value", required = false, ignoreValue = "-1") int value); + + /** + * 通过 HTTP GET 方法获取文本内容; + * + * @param setting + * 请求参数 + * @return + */ + @HttpAction(method = HttpMethod.GET) + public String content(@RequestParamMap ContentRequestSetting setting); + + /** + * 通过 HTTP GET 方法获取文本内容; + * + * @param id + * @param permited + * @param male + * @param value + * @param otherArg + * 定义一个额外的方法参数,但该参数的内容不会包含在 http 请求中; + * @return + */ + @HttpAction(path = "/content", method = HttpMethod.GET) + public DataResponse getContent(@RequestParam(name = "id") String id, + @RequestParam(name = "permited") boolean permited, + @RequestParam(name = "male", converter = CustomBooleanConverter.class) boolean male, + @RequestParam(name = "value", required = false, ignoreValue = "-1") int value, String otherArg); + + /** + * 以 HTTP POST 方式获取内容; + * + * @param id + * @param permited + * @param male + * @param value + * @return 指定了自定义的返回结果转换器,使返回结果仅包含数据内容,而不包含成功状态及错误信息; + */ + @HttpAction(path = "/content", method = HttpMethod.POST, responseConverter = GetContentResponseConverter.class) + public String fetchContent(@RequestParam(name = "id") String id, @RequestParam(name = "permited") boolean permited, + @RequestParam(name = "male", converter = CustomBooleanConverter.class) boolean male, + @RequestParam(name = "value", required = false, ignoreValue = "-1") int value); + + /** + * 以 HTTP POST 方式获取内容; + * + * @param id + * @param permited + * @param male + * @param value + * @return 指定了自定义的返回结果转换器,使返回结果仅包含成功时数据内容,而不包含成功状态及错误信息,并且失败时抛出声明的异常; + * @throws GetContentException + */ + @HttpAction(path = "/content", method = HttpMethod.POST, responseConverter = GetContentResponseConverterWithException.class) + public String fetchContentWithError(@RequestParam(name = "id") String id, + @RequestParam(name = "permited") boolean permited, + @RequestParam(name = "male", converter = CustomBooleanConverter.class) boolean male, + @RequestParam(name = "value", required = false, ignoreValue = "-1") int value) throws GetContentException; + + @HttpAction(path = "/content/update", method = HttpMethod.GET) + public String updateAndGetContent(@RequestParam(name = "content") String content); + + @HttpAction(path = "/content/{id}", method = HttpMethod.POST) + public String contentById(@PathParam(name = "id") String id); + + @HttpAction(path = "/content/ids", method = HttpMethod.POST) + public String contentByIds(@RequestParam(name = "ids", array=true) String[] ids); + + /** + * 以Http put 请求 + * + * @param id + * @return + */ + @HttpAction(path = "/content/{id}", method = HttpMethod.PUT) + public String contentByPut(@PathParam(name = "id") String id, @RequestParam(name = "content") String content); + + /** + * http delete 请求 + * + * @param id + * @return + */ + @HttpAction(path = "/content/{id}", method = HttpMethod.DELETE) + public String contentByDelete(@PathParam(name = "id") String id); + + @HttpAction(path = "/content/{id}", method = HttpMethod.POST) + public String contentBody(@PathParam(name = "id") String id, @RequestParam(name = "name") String name, + @RequestBody RequestContent requestContent); + + @HttpAction(path = "/content/{id}/multi", method = HttpMethod.POST, responseConverter = BinarySerializeResponseConverter.class) + public TestData multiContentBodies(@PathParam(name = "id") String id, @RequestParam(name = "name") String name, + @RequestBody(converter = BinarySerializeRequestBodyConverter.class) TestData data1, + @RequestBody(converter = BinarySerializeRequestBodyConverter.class) TestData data2); + + /** + * 测试https请求 + */ + @HttpAction(path = "/", method = HttpMethod.GET) + public String testHttps(); + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/MultiRequestBodiesWithDefaultConverterTestService.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/MultiRequestBodiesWithDefaultConverterTestService.java new file mode 100644 index 00000000..618b7dcf --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/MultiRequestBodiesWithDefaultConverterTestService.java @@ -0,0 +1,18 @@ +package test.my.utils.http.agent; + +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.HttpMethod; +import com.jd.blockchain.utils.http.HttpService; +import com.jd.blockchain.utils.http.PathParam; +import com.jd.blockchain.utils.http.RequestBody; +import com.jd.blockchain.utils.http.RequestParam; +import com.jd.blockchain.utils.http.converters.BinarySerializeRequestBodyConverter; +import com.jd.blockchain.utils.http.converters.BinarySerializeResponseConverter; + +@HttpService(path = "/test", defaultRequestBodyConverter = BinarySerializeRequestBodyConverter.class, defaultResponseConverter = BinarySerializeResponseConverter.class) +public interface MultiRequestBodiesWithDefaultConverterTestService { + @HttpAction(path = "/content/{id}/multi", method = HttpMethod.POST) + public TestData multiContentBodies(@PathParam(name = "id") String id, @RequestParam(name = "name") String name, + @RequestBody TestData data1, @RequestBody TestData data2); + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/OpType.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/OpType.java new file mode 100644 index 00000000..6932e798 --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/OpType.java @@ -0,0 +1,25 @@ +package test.my.utils.http.agent; + +/** + * 用于验证将枚举类型的 POJO 字段映射为请求参数时,默认情况下通过 toString 方法取得参数值; + * + * @author haiq + * + */ +public enum OpType { + + TYPE1(1), + + TYPE2(2); + + private int value; + + private OpType(int value) { + this.value = value; + } + + @Override + public String toString() { + return String.valueOf(this.value); + } +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/RequestContent.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/RequestContent.java new file mode 100644 index 00000000..e35a223e --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/RequestContent.java @@ -0,0 +1,37 @@ +package test.my.utils.http.agent; + +public class RequestContent { + + private int requestId; + + private String requestContent; + + public RequestContent(){ + + } + + public RequestContent(int requestId, String requestContent){ + this.requestId = requestId; + this.requestContent = requestContent; + } + + + + public int getRequestId() { + return requestId; + } + + public void setRequestId(int requestId) { + this.requestId = requestId; + } + + public String getRequestContent() { + return requestContent; + } + + public void setRequestContent(String requestContent) { + this.requestContent = requestContent; + } + + +} diff --git a/source/utils/utils-http/src/test/java/test/my/utils/http/agent/TestData.java b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/TestData.java new file mode 100644 index 00000000..585a8a0f --- /dev/null +++ b/source/utils/utils-http/src/test/java/test/my/utils/http/agent/TestData.java @@ -0,0 +1,29 @@ +package test.my.utils.http.agent; + +import java.io.Serializable; + +public class TestData implements Serializable { + + private static final long serialVersionUID = -4339820200060895807L; + + private int id; + + private String value; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + +} diff --git a/source/utils/utils-serialize/pom.xml b/source/utils/utils-serialize/pom.xml new file mode 100644 index 00000000..c8349fb3 --- /dev/null +++ b/source/utils/utils-serialize/pom.xml @@ -0,0 +1,22 @@ + + 4.0.0 + + com.jd.blockchain + utils + 0.8.2.RELEASE + + utils-serialize + + + + com.jd.blockchain + utils-common + ${project.version} + + + com.alibaba + fastjson + + + \ No newline at end of file diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/binary/BinarySerializeUtils.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/binary/BinarySerializeUtils.java new file mode 100644 index 00000000..f0e2edf2 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/binary/BinarySerializeUtils.java @@ -0,0 +1,63 @@ +package com.jd.blockchain.utils.serialize.binary; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; + +import com.jd.blockchain.utils.io.BytesUtils; +import com.jd.blockchain.utils.io.RuntimeIOException; + +public class BinarySerializeUtils { + + public static byte[] serialize(Object object) { + if (object == null) { + return BytesUtils.EMPTY_BYTES; + } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + serialize(object, out); + return out.toByteArray(); + } + + public static void serialize(Object object, OutputStream out) { + if (object == null) { + return; + } + try { + ObjectOutputStream objOut = new ObjectOutputStream(out); + objOut.writeObject(object); + objOut.flush(); + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } + } + + public static T deserialize(byte[] bytes) { + if (bytes == null || bytes.length == 0) { + return null; + } + ByteArrayInputStream in = new ByteArrayInputStream(bytes); + return deserialize(in); + } + + @SuppressWarnings("unchecked") + public static T deserialize(InputStream in) { + try { + ObjectInputStream objIn = new ObjectInputStream(in); + Object obj = objIn.readObject(); + return (T) obj; + } catch (IOException e) { + throw new RuntimeIOException(e.getMessage(), e); + } catch (ClassNotFoundException e) { + throw new IllegalStateException(e.getMessage(), e); + } + } + + public static T copyOf(T obj) { + byte[] bts = serialize(obj); + return deserialize(bts); + } +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/ArrayMergeStrategy.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/ArrayMergeStrategy.java new file mode 100644 index 00000000..c631c55a --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/ArrayMergeStrategy.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.utils.serialize.json; + +/** + * 数组的合并策略; + * + * @author haiq + * + */ +public enum ArrayMergeStrategy { + + /** + * 替换策略:用新的数组替换掉原来的数组; + */ + REPLACE, + + /** + * 追加策略:把新数组的元素追加到原来的数组元素之后; + */ + APPEND, + + /** + * 深度合并策略:对于相同位置的元素,如果是原生值则进行替换,如果是JSON对象,则进行递归合并;新数组多出的元素则追加到原数组后面; + */ + DEEP_MERGE + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/GenericType.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/GenericType.java new file mode 100644 index 00000000..0220cad8 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/GenericType.java @@ -0,0 +1,20 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; + +public class GenericType { + + private final Type firstTypeArgument; + + protected GenericType(){ + Type superClass = getClass().getGenericSuperclass(); + + firstTypeArgument = ((ParameterizedType) superClass).getActualTypeArguments()[0]; + } + + public Type getTypeArgument() { + return firstTypeArgument; + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBean.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBean.java new file mode 100644 index 00000000..995070be --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBean.java @@ -0,0 +1,552 @@ +package com.jd.blockchain.utils.serialize.json; + +import static com.alibaba.fastjson.util.TypeUtils.castToBigDecimal; +import static com.alibaba.fastjson.util.TypeUtils.castToBigInteger; +import static com.alibaba.fastjson.util.TypeUtils.castToBoolean; +import static com.alibaba.fastjson.util.TypeUtils.castToByte; +import static com.alibaba.fastjson.util.TypeUtils.castToBytes; +import static com.alibaba.fastjson.util.TypeUtils.castToDate; +import static com.alibaba.fastjson.util.TypeUtils.castToDouble; +import static com.alibaba.fastjson.util.TypeUtils.castToFloat; +import static com.alibaba.fastjson.util.TypeUtils.castToInt; +import static com.alibaba.fastjson.util.TypeUtils.castToLong; +import static com.alibaba.fastjson.util.TypeUtils.castToShort; +import static com.alibaba.fastjson.util.TypeUtils.castToSqlDate; +import static com.alibaba.fastjson.util.TypeUtils.castToTimestamp; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Collection; +import java.util.Date; +import java.util.Map; +import java.util.regex.Pattern; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.annotation.JSONType; +import com.alibaba.fastjson.util.TypeUtils; + +@JSONType(serializer=JSONBeanSerializer.class,deserializer =JSONBeanDeserializer.class) +public class JSONBean { + + /** + * JSON Bean 的属性路径的正则表达式;
+ * + * 属性路径由“字段名”,加上可选的数组下标索引“[位置]”,加上句点“.”构成;
+ * + * 如: contract.addresses[1].telephone ; + */ + private static final String KEY_PATH_REGEX = "\\A([\\w\\$]+(\\[\\d+\\])?\\.)*[\\w\\$]+\\z"; + + private static final Pattern KEY_PATH_PATTERN = Pattern.compile(KEY_PATH_REGEX); + + private JSONObject jsonObject; + + public JSONBean() { + this(new JSONObject()); + } + + protected JSONBean(JSONObject jsonObj) { + this.jsonObject = jsonObj; + } + + + public JSONObject getJsonObject() { + return jsonObject; + } + + public void setJsonObject(JSONObject jsonObject) { + this.jsonObject = jsonObject; + } + + public boolean isEmpty(){ + return jsonObject.isEmpty(); + } + + /** + * 合并指定的 JSON Bean 的全部属性到当前对象;
+ * + * 对数组类型的属性采取“替换”策略 {@link ArrayMergeStrategy.REPLACE}进行合并; + * + * @param jsonBean + * 要合并的 JSON 对象; + * @return + */ + public JSONBean merge(JSONBean jsonBean) { + if (jsonBean == this) { + return this; + } + return merge(jsonBean, ArrayMergeStrategy.REPLACE); + } + + protected Object get(String keyPath) { + if (!KEY_PATH_PATTERN.matcher(keyPath).matches()) { + throw new IllegalArgumentException("Illegal key path! --keyPath=[" + keyPath + "]"); + } + int startInx = 0; + int endIdx = -1; + Object value = null; + do { + // 逐段分解属性; + endIdx = keyPath.indexOf('.', startInx); + String key; + if (endIdx > startInx) { + key = keyPath.substring(startInx, endIdx); + + startInx = endIdx + 1;// 更新下一次搜索的起始索引; + } else { + //最后一项; + key = keyPath.substring(startInx); + } + String field = key; + int leftIdx = key.indexOf('['); + int position = -1; + if (leftIdx > 0) { + field = key.substring(0, leftIdx); + position = Integer.parseInt(key.substring(leftIdx + 1, key.length() - 1)); + // 注:KeyPath 的正则表达式限制了数组下标的数字不可能为负数,否则不能通过校验; + } + + // 解析对象属性; + if (value == null) { + value = jsonObject.get(field); + } else { + if (value instanceof JSONObject) { + value = ((JSONObject) value).get(field); + } else if (value instanceof JSONArray) { + throw new IllegalArgumentException("Array cann't be accessed by field[" + field + "]!"); + } else { + throw new IllegalArgumentException( + "JSON primitive value cann't be accessed by field[" + field + "]!"); + } + } + if (value == null) { + return null; +// throw new IllegalArgumentException("The field[" + field + "] not exist!"); + } + // 解析数组元素; + if (position > -1) { + if (value instanceof JSONArray) { + JSONArray arrayValue = (JSONArray) value; + if (position < arrayValue.size()) { + value = arrayValue.get(position); + } else { + throw new IndexOutOfBoundsException( + "Index[" + position + "] out of the size of array field[" + field + "]!"); + } + } else if (value instanceof JSONObject) { + throw new IllegalArgumentException( + "JSON object field[" + field + "] cann't be accessed by array index!"); + } else { + throw new IllegalArgumentException( + "JSON primitive value field[" + field + "] cann't be accessed by array index!"); + } + } + } while (endIdx > -1); + + return value; + } + + + /** + * 设置属性; + * + * 注:未实现对 key path 进行解析; + * + * @param key + * @param value + */ + protected void set(String key, Object value){ + //TODO: 未实现对 key path 进行解析; + jsonObject.put(key, value); + } + + + public T getObject(String key, Class clazz) { + Object obj = get(key); + return TypeUtils.castToJavaBean(obj, clazz); + } + + public Boolean getBoolean(String key) { + Object value = get(key); + + if (value == null) { + return null; + } + + return castToBoolean(value); + } + + public byte[] getBytes(String key) { + Object value = get(key); + + if (value == null) { + return null; + } + + return castToBytes(value); + } + + public boolean getBooleanValue(String key) { + Object value = get(key); + + if (value == null) { + return false; + } + + return castToBoolean(value).booleanValue(); + } + + public Byte getByte(String key) { + Object value = get(key); + + return castToByte(value); + } + + public byte getByteValue(String key) { + Object value = get(key); + + if (value == null) { + return 0; + } + + return castToByte(value).byteValue(); // TODO 如果 value + // 是""、"null"或"NULL",可能会存在报空指针的情况,是否需要加以处理? + // 其他转换也存在类似情况 + } + + public Short getShort(String key) { + Object value = get(key); + + return castToShort(value); + } + + public short getShortValue(String key) { + Object value = get(key); + + if (value == null) { + return 0; + } + + return castToShort(value).shortValue(); + } + + public Integer getInteger(String key) { + Object value = get(key); + + return castToInt(value); + } + + public int getIntValue(String key) { + Object value = get(key); + + if (value == null) { + return 0; + } + + return castToInt(value).intValue(); + } + + public Long getLong(String key) { + Object value = get(key); + + return castToLong(value); + } + + public long getLongValue(String key) { + Object value = get(key); + + if (value == null) { + return 0L; + } + + return castToLong(value).longValue(); + } + + public Float getFloat(String key) { + Object value = get(key); + + return castToFloat(value); + } + + public float getFloatValue(String key) { + Object value = get(key); + + if (value == null) { + return 0F; + } + + return castToFloat(value).floatValue(); + } + + public Double getDouble(String key) { + Object value = get(key); + + return castToDouble(value); + } + + public double getDoubleValue(String key) { + Object value = get(key); + + if (value == null) { + return 0D; + } + + return castToDouble(value); + } + + public BigDecimal getBigDecimal(String key) { + Object value = get(key); + + return castToBigDecimal(value); + } + + public BigInteger getBigInteger(String key) { + Object value = get(key); + + return castToBigInteger(value); + } + + public String getString(String key) { + Object value = get(key); + + if (value == null) { + return null; + } + + return value.toString(); + } + + public Date getDate(String key) { + Object value = get(key); + + return castToDate(value); + } + + public java.sql.Date getSqlDate(String key) { + Object value = get(key); + + return castToSqlDate(value); + } + + public java.sql.Timestamp getTimestamp(String key) { + Object value = get(key); + + return castToTimestamp(value); + } + + @Override + public String toString() { + return jsonObject.toJSONString(); + } + + public T toJavaBean(Class beanClass){ + return jsonObject.toJavaObject(beanClass); + } + + /** + * 合并指定的 JSON Bean 的全部属性到当前对象; + * + * @param jsonBean + * 要合并的 JSON 对象; + * @param arrayMergeStrategy + * 对数组类型的属性采取的合并策略; + * @return + */ + public JSONBean merge(JSONBean jsonBean, ArrayMergeStrategy arrayMergeStrategy) { + mergeJSONObject(this.jsonObject, jsonBean.jsonObject, arrayMergeStrategy); + return this; + } + + private static void mergeJSONObject(JSONObject target, JSONObject from, ArrayMergeStrategy arrayMergeStrategy) { + for (Map.Entry field : from.entrySet()) { + if (target.containsKey(field.getKey())) { + Object targetFieldValue = target.get(field.getKey()); + Object fromFieldValue = field.getValue(); + if ((targetFieldValue instanceof JSONObject) && (fromFieldValue instanceof JSONObject)) { + // 都是 JSON 对象,进行深度合并; + mergeJSONObject((JSONObject) targetFieldValue, (JSONObject) fromFieldValue, arrayMergeStrategy); + } else if ((targetFieldValue instanceof JSONArray) && (fromFieldValue instanceof JSONArray)) { + // 都是 JSON 数组,进行数组合并; + mergeJSONArray((JSONArray) targetFieldValue, (JSONArray) fromFieldValue, arrayMergeStrategy); + } else { + // 类型不同,直接替换; + target.put(field.getKey(), field.getValue()); + } + } else { + target.put(field.getKey(), field.getValue()); + } + } + } + + private static void mergeJSONArray(JSONArray target, JSONArray from, ArrayMergeStrategy strategy) { + switch (strategy) { + case REPLACE: + if (target.size() > from.size()) { + for (int i = 0; i < from.size(); i++) { + target.set(i, from.get(i)); + } + } else { + target.clear(); + target.addAll(from); + } + break; + case APPEND: + target.addAll(from); + break; + case DEEP_MERGE: + // 对位置相同的元素进行合并; + int mergeCount = Math.min(target.size(), from.size()); + Object targetFieldValue; + Object fromFieldValue; + for (int i = 0; i < mergeCount; i++) { + targetFieldValue = target.get(i); + fromFieldValue = from.get(i); + if ((targetFieldValue instanceof JSONObject) && (fromFieldValue instanceof JSONObject)) { + // 都是 JSON 对象,进行深度合并; + mergeJSONObject((JSONObject) targetFieldValue, (JSONObject) fromFieldValue, strategy); + } else if ((targetFieldValue instanceof JSONArray) && (fromFieldValue instanceof JSONArray)) { + // 都是 JSON 数组,进行数组合并; + mergeJSONArray((JSONArray) targetFieldValue, (JSONArray) fromFieldValue, strategy); + } else { + // 类型不同,直接替换; + target.set(i, fromFieldValue); + } + } + // 对多出来的元素进行追加; + if (from.size() > target.size()) { + for (int i = target.size(); i < from.size(); i++) { + target.add(from.get(i)); + } + } + break; + default: + throw new IllegalStateException("Unsupported strategy[" + strategy + "] for merging json array!"); + } + } + + public static JSONBean parse(String json){ + JSONObject jsonObj = parseJSONObject(json); + return new JSONBean(jsonObj); + } + + public static JSONBean wrap(Object object) { + if (object == null) { + throw new IllegalArgumentException("Null object!"); + } + if (object instanceof JSONBean) { + return (JSONBean) object; + } + JSONObject jsonObj = toJSONObject(object); + return new JSONBean(jsonObj); + } + + public static JSONBean tryWrap(Object object) { + if (object == null) { + return null; + } + if (object instanceof JSONBean) { + return (JSONBean) object; + } + JSONObject jsonObj = tryToJSONObject(object); + if (jsonObj == null) { + return null; + } + return new JSONBean(jsonObj); + } + + public static JSONObject tryToJSONObject(Object javaObject){ + if (javaObject == null) { + return null; + } + + if (javaObject instanceof JSONObject) { + return (JSONObject) javaObject; + } + + if (javaObject instanceof Collection) { + return null; + } + Class cls = javaObject.getClass(); + if (cls.isArray()) { + return null; + } + if (cls.isEnum()) { + return null; + } + if (isPrimitive(cls)) { + return null; + } + + Object obj = JSONObject.toJSON(javaObject); + if (obj instanceof JSONObject) { + return (JSONObject) obj; + } + + return null; + } + + private static JSONObject parseJSONObject(String json){ + Object obj = JSON.parse(json); + if (obj instanceof JSONObject) { + return (JSONObject) obj; + } + throw new IllegalArgumentException("The specified json string is not a JSON Object!"); + } + + private static JSONObject toJSONObject(Object javaObject) { + if (javaObject == null) { + throw new IllegalArgumentException("The wrapped java object is null!"); + } + + if (javaObject instanceof JSONObject) { + return (JSONObject) javaObject; + } + + if (javaObject instanceof Collection) { + throw new IllegalArgumentException("The wrapped java object is a collection!"); + } + Class cls = javaObject.getClass(); + if (cls.isArray()) { + throw new IllegalArgumentException("The wrapped java object is a array!"); + } + if (cls.isEnum()) { + throw new IllegalArgumentException("The wrapped java object is a enum!"); + } + if (isPrimitive(cls)) { + throw new IllegalArgumentException("The wrapped java object is a primitive value!"); + } + + Object obj = JSONObject.toJSON(javaObject); + if (obj instanceof JSONObject) { + return (JSONObject) obj; + } + + throw new IllegalArgumentException("The wrapped java object cann't be converted to a JSON Object!"); + } + + + + private static boolean isPrimitive(Class clazz) { + return clazz.isPrimitive() // + || clazz == Boolean.class // + || clazz == Character.class // + || clazz == Byte.class // + || clazz == Short.class // + || clazz == Integer.class // + || clazz == Long.class // + || clazz == Float.class // + || clazz == Double.class // + || clazz == BigInteger.class // + || clazz == BigDecimal.class // + || clazz == String.class // + || clazz == java.util.Date.class // + || clazz == java.sql.Date.class // + || clazz == java.sql.Time.class // + || clazz == java.sql.Timestamp.class // + || clazz.isEnum() // + ; + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBeanDeserializer.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBeanDeserializer.java new file mode 100644 index 00000000..834480ce --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBeanDeserializer.java @@ -0,0 +1,38 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.lang.reflect.Type; + +import com.alibaba.fastjson.JSONException; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.DefaultJSONParser; +import com.alibaba.fastjson.parser.JSONToken; +import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; + +public class JSONBeanDeserializer implements ObjectDeserializer { + + @SuppressWarnings("unchecked") + @Override + public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + if (type instanceof Class && JSONBean.class.isAssignableFrom((Class) type)) { + Class clazz = (Class) type; + try { + JSONBean jsonBean = (JSONBean) clazz.newInstance(); + JSONObject jsonObj = (JSONObject) parser.parseObject(jsonBean.getJsonObject()); + jsonBean.setJsonObject(jsonObj); + + return (T) jsonBean; + } catch (InstantiationException e) { + throw new JSONException(e.getMessage(), e); + } catch (IllegalAccessException e) { + throw new JSONException(e.getMessage(), e); + } + } + return (T) parser.parse(fieldName); + } + + @Override + public int getFastMatchToken() { + return JSONToken.LBRACE; + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBeanSerializer.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBeanSerializer.java new file mode 100644 index 00000000..06afeeb5 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONBeanSerializer.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeWriter; + +public class JSONBeanSerializer implements ObjectSerializer { + + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) + throws IOException { + SerializeWriter out = serializer.out; + if (object == null) { + out.writeNull(); + return; + } + JSONBean jsonBean = (JSONBean) object; + out.write(jsonBean.toString()); + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONSerializeUtils.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONSerializeUtils.java new file mode 100644 index 00000000..1d88796f --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONSerializeUtils.java @@ -0,0 +1,359 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.io.IOException; +import java.io.Writer; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Proxy; +import java.lang.reflect.Type; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.ParserConfig; +import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeConfig; +import com.alibaba.fastjson.serializer.SerializeWriter; +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.jd.blockchain.utils.PrimitiveUtils; + +/** + * SerializeUtils 提供统一的序列化反序列化操作实现; + * + * @author haiq + * + */ +public abstract class JSONSerializeUtils { + + private static final ToStringSerializer TO_STRING_SERIALIZER = new ToStringSerializer(); + + private static final RuntimeDeserializer RUNTIME_DESERIALIZER = new RuntimeDeserializer(); + + public static void addTypeMap(Class fromClazz, Class toClazz) { + RUNTIME_DESERIALIZER.addTypeMap(fromClazz, toClazz); + ParserConfig.getGlobalInstance().putDeserializer(fromClazz, RUNTIME_DESERIALIZER); + } + + public static void configSerialization(Class clazz, ObjectSerializer serializer, ObjectDeserializer deserializer) { + SerializeConfig.globalInstance.put(clazz, serializer); + ParserConfig.getGlobalInstance().putDeserializer(clazz, deserializer); + } + + /** + * 配置指定的类型在序列化时总是输出 {@link Object#toString()} 方法的结果 ; + * + * @param type + */ + public static void configStringSerializer(Class type) { + SerializeConfig.globalInstance.put(type, TO_STRING_SERIALIZER); + } + + /** + * 禁用循环引用检测; + * + *
+ * 默认是开启的; + * + * @param type + */ + public static void disableCircularReferenceDetect() { + JSON.DEFAULT_GENERATE_FEATURE |= SerializerFeature.DisableCircularReferenceDetect.getMask(); + } + + // /** + // * 禁用循环引用检测; + // * + // *
+ // * 默认是开启的; + // * + // * @param type + // */ + // public static void disableCircularReferenceDetect(Class type) { + // SerializeConfig.globalInstance.config(type, + // SerializerFeature.DisableCircularReferenceDetect, true); + // } + + // public static void addSerialzedType(Class objectType, Class + // serialzedType) { + // if (!serialzedType.isAssignableFrom(objectType)) { + // throw new IllegalArgumentException("The filteredType[" + + // serialzedType.getName() + // + "] isn't assignable from the objectType[" + objectType.getName() + + // "]!"); + // } + // JavaBeanSerializer serializer = new JavaBeanSerializer(serialzedType); + // SerializeConfig.globalInstance.put(objectType, serializer); + // } + + private JSONSerializeUtils() { + } + + public static Type getGenericType(Object obj) { + return getGenericTypes(obj)[0]; + } + + public static Type[] getGenericTypes(Object obj) { + Type superClass = obj.getClass().getGenericSuperclass(); + + Type[] types = ((ParameterizedType) superClass).getActualTypeArguments(); + return types; + } + + /** + * 判断是否是 JSON 字符; + *

+ * + * 此方法判断的JSON 字符的方法是检查指定的字符是否由花括号 { } 或者方括号 [ ] 或者双引号 ""包围; + *

+ * + * 这只是一种简化但不严谨的方法,检查通过返回 true 也不代表在花括号 { } 或者方括号 [ ] 或者双引号 "" 之间的内容符合 JSON 语法; + * + * @param str + * @return + */ + public static boolean isJSON(String str) { + return isJSONObject(str) || isJSONOArray(str) || isJSONOValue(str); + } + + /** + * 判断是否是 JSON 对象; + *

+ * + * 此方法判断的JSON 对象的方法是检查指定的字符是否由花括号 { } 包围; + *

+ * + * 这只是一种简化但不严谨的方法,检查通过返回 true 也不代表在花括号 { } 之间的内容符合 JSON 语法; + * + * @param str + * @return + */ + public static boolean isJSONObject(String str) { + return str.startsWith("{") && str.endsWith("}"); + } + + /** + * 判断是否是 JSON 数组; + *

+ * + * 此方法判断的JSON 数组的方法是检查指定的字符是否由方括号 [ ] 包围; + *

+ * + * 这只是一种简化但不严谨的方法,检查通过返回 true 也不代表在方括号 [ ] 之间的内容符合 JSON 语法; + * + * @param str + * @return + */ + public static boolean isJSONOArray(String str) { + return str.startsWith("[") && str.endsWith("]"); + } + + /** + * 判断是否是 JSON 值; + *

+ * + * 此方法判断的JSON 字符的方法是检查指定的字符是否由双引号 "" 包围; + *

+ * + * 这只是一种简化但不严谨的方法,检查通过返回 true 也不代表在双引号 "" 之间的内容符合 JSON 语法; + * + * @param str + * @return + */ + public static boolean isJSONOValue(String str) { + return str.startsWith("\"") && str.endsWith("\""); + } + + /** + * 将对象序列化为 JSON 字符串;(紧凑格式) + * + * @param data + * @return + */ + public static String serializeToJSON(Object data) { + // if (data instanceof JSONObject) { + // return ((JSONObject) data).toJSONString(); + // } + return serializeToJSON(data, null, false); + } + + /** + * 将对象序列化为 JSON 字符串;(紧凑格式) + * + * @param data + * @return + */ + public static String serializeToJSON(Object data, Class serializedType) { + return serializeToJSON(data, serializedType, false); + } + + /** + * 将对象序列化为 JSON 字符串; + * + * @param data + * 要序列化的对象; + * @param prettyFormat + * 是否以包含换行和缩进的良好格式输出 JSON; + * @return + */ + public static String serializeToJSON(Object data, boolean prettyFormat) { + return serializeToJSON(data, null, prettyFormat); + } + + /** + * 将对象序列化为 JSON 字符串; + * + * @param data + * 要序列化的对象; + * @param serializedType + * 要序列化的对象的输出的类型;
+ * 指定该对象的父类或者某一个实现的接口类型,序列化输出的 JSON 将只包含该类型的属性;
+ * 如果指定为 null, 则按对象本身的类型进行序列化; + * @param prettyFormat + * 是否以包含换行和缩进的良好格式输出 JSON; + * @return + */ + public static String serializeToJSON(Object data, Class serializedType, boolean prettyFormat) { + return serializeToJSON(data, serializedType, null, prettyFormat); + } + + /** + * 将对象序列化为 JSON 字符串; + * + * @param data + * 要序列化的对象; + * @param serializedType + * 要序列化的对象的输出的类型;
+ * 指定该对象的父类或者某一个实现的接口类型,序列化输出的 JSON 将只包含该类型的属性;
+ * 如果指定为 null, 则按对象本身的类型进行序列化; + * @param dateFormat + * 日期格式; + * @param prettyFormat + * 是否以包含换行和缩进的良好格式输出 JSON; + * @return + */ + public static String serializeToJSON(Object data, Class serializedType, String dateFormat, + boolean prettyFormat) { + SerializeWriter out; + + if (prettyFormat) { + out = new SerializeWriter((Writer) null, JSON.DEFAULT_GENERATE_FEATURE, SerializerFeature.PrettyFormat); + } else { + out = new SerializeWriter((Writer) null, JSON.DEFAULT_GENERATE_FEATURE, SerializerFeature.EMPTY); + } + + try { + if (data == null) { + return null; + // out.writeNull(); + } else { + // 确定要序列化的类型; + if (serializedType == null) { + serializedType = data.getClass(); + } else if ((!PrimitiveUtils.isPrimitiveType(serializedType)) + && (!PrimitiveUtils.isPrimitiveType(data.getClass())) + && (!serializedType.isAssignableFrom(data.getClass()))) { + throw new IllegalArgumentException("The serialized type[" + serializedType.getName() + + "] isn't assignable from the data type[" + data.getClass().getName() + "]!"); + } + + if (PrimitiveUtils.isWrapping(data.getClass(), serializedType)) { + // 避免 serializedType 原生的值类型时引发 fastjson 的序列化异常; + serializedType = data.getClass(); + } + + JSONSerializer serializer = new JSONSerializer(out, SerializeConfig.globalInstance); + + // 配置日期格式; + if (dateFormat != null && dateFormat.length() != 0) { + serializer.setDateFormat(dateFormat); + serializer.config(SerializerFeature.WriteDateUseDateFormat, true); + } + + // 序列化; + ObjectSerializer writer = serializer.getObjectWriter(serializedType); + + writer.write(serializer, data, null, null, JSON.DEFAULT_GENERATE_FEATURE); + } + return out.toString(); + } catch (IOException e) { + throw new IllegalStateException( + "Error occurred on serializing type[" + serializedType.getName() + "]! --" + e.getMessage(), e); + } finally { + out.close(); + } + } + + /** + * + * @param json + * @param dataClazz + * @return + */ + public static T deserializeFromJSON(String json, Class dataClazz) { + return JSON.parseObject(json, dataClazz); + } + + /** + * + * + * @param jsonObj + * @param dataClazz + * @param + * @return + */ + public static T deserializeFromJSONObject(JSONObject jsonObj, Class dataClazz) { + return (T)Proxy.newProxyInstance(dataClazz.getClassLoader(), new Class[] {dataClazz}, jsonObj); + } + + /** + * + * @param json + * @param dataClazz + * @return + */ + public static T deserializeFromJSON(String json, GenericType type) { + return JSON.parseObject(json, type.getTypeArgument()); + } + + @SuppressWarnings("unchecked") + public static T deserializeAs(Object data, Class clazz) { + if (data == null) { + return null; + } + if (data instanceof JSON) { + return ((JSON) data).toJavaObject(clazz); + } + if (data instanceof JSONBean) { + return ((JSONBean) data).toJavaBean(clazz); + } + if (data instanceof String) { + if (clazz.isInterface()) { + JSONObject jsonObj = JSONSerializeUtils.deserializeAs(data, JSONObject.class); + return deserializeFromJSONObject(jsonObj, clazz); + } + if (isJSON((String) data)) { + return deserializeFromJSON((String) data, clazz); + } + } + if (data instanceof JSONString) { + String jsonStr = ((JSONString) data).toString(); + if (isJSON(jsonStr)) { + return deserializeFromJSON(jsonStr, clazz); + } else { + data = jsonStr; + } + } + if (PrimitiveUtils.isPrimitiveType(clazz)) { + return PrimitiveUtils.castTo(data, clazz); + } + if (clazz.isAssignableFrom(data.getClass())) { + return (T) data; + } + if (clazz.isAssignableFrom(String.class)) { + return (T) data.toString(); + } + throw new IllegalArgumentException("Unsupported deserialization from type[" + data.getClass().toString() + + "] to type[" + clazz.toString() + "]!"); + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONString.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONString.java new file mode 100644 index 00000000..873e09f3 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONString.java @@ -0,0 +1,42 @@ +package com.jd.blockchain.utils.serialize.json; + +import com.alibaba.fastjson.annotation.JSONType; + +/** + * JSONString 用于包装不需要执行 JSON 序列化而直接输出的字符串; + * @author haiq + * + */ +@JSONType(serializer=JSONStringSerializer.class,deserializer =JSONStringDeserializer.class) +public final class JSONString { + + private String jsonString; + + public JSONString(String jsonString) { + if (jsonString == null) { + throw new IllegalArgumentException("Null json string!"); + } +// if (!SerializeUtils.isJSON(jsonString)) { +// throw new IllegalArgumentException("The arg is not a JSON string!"); +// } + this.jsonString = jsonString; + } + + private JSONString() { + } + + @Override + public String toString() { + return jsonString; + } + + public static JSONString toJSONString(Object data){ + if (data == null) { + return null; + } + String jsonString = JSONSerializeUtils.serializeToJSON(data); + JSONString retn = new JSONString(); + retn.jsonString = jsonString; + return retn; + } +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONStringDeserializer.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONStringDeserializer.java new file mode 100644 index 00000000..671b61c1 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONStringDeserializer.java @@ -0,0 +1,26 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.lang.reflect.Type; + +import com.alibaba.fastjson.parser.DefaultJSONParser; +import com.alibaba.fastjson.parser.JSONToken; +import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; + +public class JSONStringDeserializer implements ObjectDeserializer{ + + @SuppressWarnings("unchecked") + @Override + public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + if (type instanceof Class && JSONString.class.isAssignableFrom((Class) type)) { + String jsonString = parser.parseObject(String.class); + return (T) new JSONString(jsonString); + } + return (T) parser.parse(fieldName); + } + + @Override + public int getFastMatchToken() { + return JSONToken.LBRACE; + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONStringSerializer.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONStringSerializer.java new file mode 100644 index 00000000..6752e072 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/JSONStringSerializer.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeWriter; + +public class JSONStringSerializer implements ObjectSerializer { + + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) + throws IOException { + SerializeWriter out = serializer.out; + if (object == null) { + out.writeNull(); + return; + } + JSONString jsonString = (JSONString) object; + out.write(jsonString.toString()); + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/RuntimeDeserializer.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/RuntimeDeserializer.java new file mode 100644 index 00000000..2ebc7543 --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/RuntimeDeserializer.java @@ -0,0 +1,36 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.lang.reflect.Type; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.fastjson.parser.DefaultJSONParser; +import com.alibaba.fastjson.parser.JSONToken; +import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; + +public class RuntimeDeserializer implements ObjectDeserializer { + + private Map, Class> typeMap = new ConcurrentHashMap, Class>(); + + synchronized void addTypeMap(Class fromClazz, Class toClazz){ + typeMap.put(fromClazz, toClazz); + } + + @SuppressWarnings("unchecked") + @Override + public T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { + if (type instanceof Class) { + Class toClazz = typeMap.get((Class) type); + if (toClazz != null) { + return (T) parser.parseObject(toClazz); + } + } + return (T) parser.parse(fieldName); + } + + @Override + public int getFastMatchToken() { + return JSONToken.LBRACE; + } + +} diff --git a/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/ToStringSerializer.java b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/ToStringSerializer.java new file mode 100644 index 00000000..b66b160a --- /dev/null +++ b/source/utils/utils-serialize/src/main/java/com/jd/blockchain/utils/serialize/json/ToStringSerializer.java @@ -0,0 +1,24 @@ +package com.jd.blockchain.utils.serialize.json; + +import java.io.IOException; +import java.lang.reflect.Type; + +import com.alibaba.fastjson.serializer.JSONSerializer; +import com.alibaba.fastjson.serializer.ObjectSerializer; +import com.alibaba.fastjson.serializer.SerializeWriter; + +public class ToStringSerializer implements ObjectSerializer { + + @Override + public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) + throws IOException { + SerializeWriter out = serializer.out; + if (object == null) { + out.writeNull(); + return; + } + + out.writeString(object.toString()); + } + +} diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/BinarySerializeUtilsTest.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/BinarySerializeUtilsTest.java new file mode 100644 index 00000000..e10ff2c0 --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/BinarySerializeUtilsTest.java @@ -0,0 +1,97 @@ +package test.my.utils.serialize; + +import static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.Serializable; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +public class BinarySerializeUtilsTest { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Test + public void testSerializePrimitive() { + int v = 5; + byte[] bytes = BinarySerializeUtils.serialize(v); + Object desV = BinarySerializeUtils.deserialize(bytes); + + int v2 = (int) desV; + + assertEquals(v, v2); + } + + @Test + public void testSerializeMultiValues() { + String str = "kkkjijfekwejwe"; + int v = 5; + Data data = new Data(); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + BinarySerializeUtils.serialize(str, out); + BinarySerializeUtils.serialize(v, out); + BinarySerializeUtils.serialize(data, out); + + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + + String deStr = BinarySerializeUtils.deserialize(in); + int deV = BinarySerializeUtils.deserialize(in); + Data deData = BinarySerializeUtils.deserialize(in); + + assertEquals(str, deStr); + assertEquals(v, deV); + assertEquals(data.getId(), deData.getId()); + assertEquals(data.getName(), deData.getName()); + assertEquals(data.isMale(), deData.isMale()); + } + + + public static class Data implements Serializable{ + + private static final long serialVersionUID = -3168475060300920369L; + + private int id = 12; + private boolean male = false; + + private String name = "John"; + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public boolean isMale() { + return male; + } + + public void setMale(boolean male) { + this.male = male; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + + + } +} diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Car.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Car.java new file mode 100644 index 00000000..99dfe2c7 --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Car.java @@ -0,0 +1,46 @@ +package test.my.utils.serialize; + +public class Car implements ICar{ + private int weight; + + private int cost; + + private Wheel wheel; + + /* (non-Javadoc) + * @see test.my.utils.serialize.ICar#getWeight() + */ + @Override + public int getWeight() { + return weight; + } + + public void setWeight(int weight) { + this.weight = weight; + } + + /* (non-Javadoc) + * @see test.my.utils.serialize.ICar#getCost() + */ + @Override + public int getCost() { + return cost; + } + + public void setCost(int cost) { + this.cost = cost; + } + + public Wheel getWheel() { + return wheel; + } + + public void setWheel(Wheel wheel) { + this.wheel = wheel; + } + + public boolean test(Car car) { + return this.weight == car.weight; + } + +} \ No newline at end of file diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Entity.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Entity.java new file mode 100644 index 00000000..3f142549 --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Entity.java @@ -0,0 +1,65 @@ +package test.my.utils.serialize; + +import java.lang.reflect.Type; + +public class Entity { + private String name; + + private int value; + + private Level level; + + private String data; + + private T header; + + private transient Type type; + + public Entity() { + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + public String getData() { + return data; + } + + public void setData(String data) { + this.data = data; + } + + public T getHeader() { + return header; + } + + public void setHeader(T header) { + this.header = header; + } + + public Type getType() { + return type; + } + + public Level getLevel() { + return level; + } + + public void setLevel(Level level) { + this.level = level; + } + +} \ No newline at end of file diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/ICar.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/ICar.java new file mode 100644 index 00000000..81c0a538 --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/ICar.java @@ -0,0 +1,10 @@ +package test.my.utils.serialize; + +public interface ICar { + + int getWeight(); + + int getCost(); + + Wheel getWheel(); +} \ No newline at end of file diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/JSONBeanTest.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/JSONBeanTest.java new file mode 100644 index 00000000..e32e7bed --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/JSONBeanTest.java @@ -0,0 +1,327 @@ +package test.my.utils.serialize; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.Date; +import java.util.UUID; + +import org.junit.Test; + +import com.jd.blockchain.utils.serialize.json.JSONBean; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +public class JSONBeanTest { + + @Test + public void testGet() { + CompositData compositeData = new CompositData(); + compositeData.setTelNo("18800101234"); + compositeData.setTime(new Date()); + + Score[] scores = new Score[2]; + scores[0] = new Score(); + scores[0].setId("A"); + scores[0].setValue(10); + scores[1] = new Score(); + scores[1].setId("B"); + scores[1].setValue(20); + + ExtData extdata = new ExtData(); + extdata.setCode("ext-00012"); + extdata.setAmount(1000); + extdata.setAddress("mvsekkjfdsafie932kjkasfdas"); + extdata.setEnable(true); + extdata.setCompositeData(compositeData); + extdata.setScores(scores); + + JSONBean jsonBean = JSONBean.wrap(extdata); + + String telNo = jsonBean.getString("compositeData.telNo"); + assertEquals(compositeData.getTelNo(), telNo); + + Date time = jsonBean.getDate("compositeData.time"); + assertEquals(compositeData.getTime(), time); + + int score2 = jsonBean.getIntValue("scores[1].value"); + assertEquals(scores[1].getValue(), score2); + } + + @Test + public void testMergeJSONBeanArrayMergeStrategy() { + // 准备初始数据; + CompositData compositeData = new CompositData(); + compositeData.setTelNo("18800101234"); + compositeData.setTime(new Date()); + + Score[] scores = new Score[2]; + scores[0] = new Score(); + scores[0].setId("A"); + scores[0].setValue(10); + scores[1] = new Score(); + scores[1].setId("B"); + scores[1].setValue(20); + + ExtData extdata = new ExtData(); + extdata.setCode("ext-00012"); + extdata.setAmount(1000); + extdata.setAddress("mvsekkjfdsafie932kjkasfdas"); + extdata.setEnable(true); + extdata.setCompositeData(compositeData); + extdata.setScores(scores); + + JSONBean jsonBean = JSONBean.wrap(extdata); + + String telNo = jsonBean.getString("compositeData.telNo"); + assertEquals(compositeData.getTelNo(), telNo); + + int score2 = jsonBean.getIntValue("scores[1].value"); + assertEquals(scores[1].getValue(), score2); + + // 准备合并数据; + Score[] scores2 = new Score[3]; + scores2[0] = new Score(); + scores2[0].setId("c"); + scores2[0].setValue(28); + scores2[1] = new Score(); + scores2[1].setId("d"); + scores2[1].setValue(50); + scores2[2] = new Score(); + scores2[2].setId("e"); + scores2[2].setValue(66); + + SubData subdata2 = new SubData(); + subdata2.setCode("sub-00018"); + subdata2.setAmount(180); + subdata2.setScores(scores2); + + JSONBean jsonBean2 = JSONBean.wrap(subdata2); + + jsonBean.merge(jsonBean2); + String code = jsonBean.getString("code"); + assertEquals(subdata2.getCode(), code); + telNo = jsonBean.getString("compositeData.telNo"); + assertEquals(compositeData.getTelNo(), telNo); + + score2 = jsonBean.getIntValue("scores[1].value"); + assertEquals(scores2[1].getValue(), score2); + + int score3 = jsonBean.getIntValue("scores[2].value"); + assertEquals(scores2[2].getValue(), score3); + } + + @Test + public void testSerializeAndDeserialize1(){ + CompositData compositeData = new CompositData(); + compositeData.setTelNo("18800101234"); + compositeData.setTime(new Date()); + + Score[] scores = new Score[2]; + scores[0] = new Score(); + scores[0].setId("A"); + scores[0].setValue(10); + scores[1] = new Score(); + scores[1].setId("B"); + scores[1].setValue(20); + + ExtData extdata = new ExtData(); + extdata.setCode("ext-00012"); + extdata.setAmount(1000); + extdata.setAddress("mvsekkjfdsafie932kjkasfdas"); + extdata.setEnable(true); + extdata.setCompositeData(compositeData); + extdata.setScores(scores); + + JSONBean jsonBean = JSONBean.wrap(extdata); + + String json = JSONSerializeUtils.serializeToJSON(jsonBean); + System.out.println("--------- serialize ----------"); + System.out.println(json); + + JSONBean jsonBean2 = JSONSerializeUtils.deserializeFromJSON(json, JSONBean.class); + System.out.println("--------- deserialize ----------"); + assertNotNull(jsonBean2); + assertEquals(extdata.getCode(), jsonBean2.getString("code")); + assertEquals(extdata.getAmount(), jsonBean2.getIntValue("amount")); + assertEquals(extdata.isEnable(), jsonBean2.getBooleanValue("enable")); + } + @Test + public void testSerializeAndDeserialize2(){ + CompositData compositeData = new CompositData(); + compositeData.setTelNo("18800101234"); + compositeData.setTime(new Date()); + + Score[] scores = new Score[2]; + scores[0] = new Score(); + scores[0].setId("A"); + scores[0].setValue(10); + scores[1] = new Score(); + scores[1].setId("B"); + scores[1].setValue(20); + + ExtData extdata = new ExtData(); + extdata.setCode("ext-00012"); + extdata.setAmount(1000); + extdata.setAddress("mvsekkjfdsafie932kjkasfdas"); + extdata.setEnable(true); + extdata.setCompositeData(compositeData); + extdata.setScores(scores); + + JSONBean jsonBean = JSONBean.wrap(extdata); + + TopMetadata meta = new TopMetadata(); + meta.setId(UUID.randomUUID().toString()); + meta.setHeader(jsonBean); + + String json = JSONSerializeUtils.serializeToJSON(meta); + System.out.println("--------- serialize ----------"); + System.out.println(json); + + TopMetadata meta2 = JSONSerializeUtils.deserializeFromJSON(json, TopMetadata.class); + System.out.println("--------- deserialize ----------"); + System.out.println("id="+ meta2.getId()); + assertNotNull(meta2.getHeader()); + assertEquals(extdata.getCode(), meta2.getHeader().getString("code")); + assertEquals(extdata.getAmount(), meta2.getHeader().getIntValue("amount")); + assertEquals(extdata.isEnable(), meta2.getHeader().getBooleanValue("enable")); + } + + public static class TopMetadata{ + + private String id; + + private JSONBean header; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public JSONBean getHeader() { + return header; + } + + public void setHeader(JSONBean header) { + this.header = header; + } + + } + + public static class SubData { + + private String code; + + private int amount; + + private Score[] scores; + + public Score[] getScores() { + return scores; + } + + public void setScores(Score[] scores) { + this.scores = scores; + } + + public String getCode() { + return code; + } + + public int getAmount() { + return amount; + } + + public void setCode(String code) { + this.code = code; + } + + public void setAmount(int amount) { + this.amount = amount; + } + + } + + public static class Score { + private String id; + + private int value; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public int getValue() { + return value; + } + + public void setValue(int value) { + this.value = value; + } + + } + + public static class ExtData extends SubData { + + private String address; + + private boolean enable; + + private CompositData compositeData; + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public boolean isEnable() { + return enable; + } + + public void setEnable(boolean enable) { + this.enable = enable; + } + + public CompositData getCompositeData() { + return compositeData; + } + + public void setCompositeData(CompositData compositeData) { + this.compositeData = compositeData; + } + } + + public static class CompositData { + + private String telNo; + + private Date time; + + public String getTelNo() { + return telNo; + } + + public void setTelNo(String telNo) { + this.telNo = telNo; + } + + public Date getTime() { + return time; + } + + public void setTime(Date time) { + this.time = time; + } + + } + +} diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/JSONSerializeUtilsTest.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/JSONSerializeUtilsTest.java new file mode 100644 index 00000000..3550f353 --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/JSONSerializeUtilsTest.java @@ -0,0 +1,262 @@ +package test.my.utils.serialize; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Proxy; +import java.util.Date; +import java.util.UUID; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.alibaba.fastjson.parser.ParserConfig; +import com.jd.blockchain.utils.serialize.json.GenericType; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import com.jd.blockchain.utils.serialize.json.JSONString; + +public class JSONSerializeUtilsTest { + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + } + + @Before + public void setUp() throws Exception { + } + + @After + public void tearDown() throws Exception { + } + + + @Test + public void testInterface() { + Car car = new Car(); + car.setCost(10); + car.setWeight(1000); + Wheel wheel = new Wheel(); + wheel.setBlack("Black"); + car.setWheel(wheel); + + String json = JSONSerializeUtils.serializeToJSON(car); + + ParserConfig.global.setAutoTypeSupport(true); + + JSONObject jsonObj = JSONSerializeUtils.deserializeAs(json, JSONObject.class); + ICar decar = (ICar) Proxy.newProxyInstance(ICar.class.getClassLoader(), new Class[] {ICar.class}, jsonObj); + System.out.println("Class of decar :" + decar.getClass().getName()); + assertNotNull(decar); + assertEquals(car.getCost(), decar.getCost()); + assertEquals(car.getWeight(), decar.getWeight()); + Wheel deWheel = decar.getWheel(); + assertEquals(wheel.getBlack(), deWheel.getBlack()); + } + + @Test + public void tempTest() { + Car car = new Car(); + car.setCost(10); + car.setWeight(1000); + + String name = "john"; + boolean enable = true; + + Object[] dataArrays = new Object[] { name, car, enable, null, new Date() }; + String json = JSON.toJSONString(dataArrays); + System.out.println("--------- json ---------"); + System.out.println(json); + + System.out.println("--------- deserialize ---------"); + Object deObj = JSON.parse(json); + assertTrue(deObj instanceof JSONArray); + System.out.println("deObj.type=" + deObj.getClass().getName()); + JSONArray jsonArray = (JSONArray) deObj; + assertEquals(5, jsonArray.size()); + + assertTrue(jsonArray.get(0) instanceof String); + assertTrue(jsonArray.get(1) instanceof JSONObject); + assertTrue(jsonArray.get(2) instanceof Boolean); + assertNull(jsonArray.get(3)); + assertTrue(jsonArray.get(4) instanceof Long); + System.out.println("[0]--" + jsonArray.get(0).getClass().getName()); + System.out.println("[1]--" + jsonArray.get(1).getClass().getName()); + System.out.println("[2]--" + jsonArray.get(2).getClass().getName()); + System.out.println("[3]--" + jsonArray.get(3)); + System.out.println("[4]--" + jsonArray.get(4).getClass().getName()); + + System.out.println("--------- get specified type --------"); + assertTrue(jsonArray.getObject(1, Car.class) instanceof Car); + assertTrue(jsonArray.getDate(4) instanceof Date); + assertTrue(jsonArray.getObject(4, Date.class) instanceof Date); + System.out.println("[1]--" + jsonArray.getObject(1, Car.class).getClass().getName()); + System.out.println("[4]--" + jsonArray.getObject(4, Date.class).getClass().getName()); + } + + @Test + public void testSerialize() { + Car car = new Car(); + car.setCost(10000); + car.setWeight(600); + + String jsonCar = JSONSerializeUtils.serializeToJSON(car); + + Entity entity = new Entity(); + entity.setName("test-entity"); + entity.setValue(80); + entity.setData(jsonCar); + entity.setHeader(car); + entity.setLevel(Level.LOW); + + String jsonEntity = JSONSerializeUtils.serializeToJSON(entity); + GenericType> type = new GenericType>() { + }; + Entity deEntity = JSONSerializeUtils.deserializeFromJSON(jsonEntity, type); + assertEquals(entity.getName(), deEntity.getName()); + assertEquals(entity.getValue(), deEntity.getValue()); + assertEquals(entity.getData(), deEntity.getData()); + assertEquals(entity.getLevel(), deEntity.getLevel()); + assertSame(entity.getLevel(), deEntity.getLevel()); + assertNotNull(deEntity.getHeader()); + assertTrue(deEntity.getHeader() instanceof Car); + assertEquals(car.getCost(), deEntity.getHeader().getCost()); + + testGereric(entity, jsonEntity); + + Entity deEntity2 = JSONSerializeUtils.deserializeFromJSON(jsonEntity, Entity.class); + assertNotNull(deEntity2); + assertTrue(deEntity2.getHeader() instanceof JSONObject); + test(deEntity2, JSONObject.class); + + Entity entityStr = new Entity(); + entityStr.setName("test-entity"); + entityStr.setValue(80); + entityStr.setData("StringCarData"); + entityStr.setHeader("StringCar"); + entityStr.setLevel(Level.LOW); + String jsonEntityStr = JSONSerializeUtils.serializeToJSON(entityStr); + + Entity deEntity3 = JSONSerializeUtils.deserializeFromJSON(jsonEntityStr, Entity.class); + assertNotNull(deEntity3); + assertTrue(deEntity3.getHeader() instanceof String); + + test(deEntity3, String.class); + } + + private void test(Entity entity, Class type) { + assertNotNull(entity); + assertNotNull(entity.getHeader()); + assertTrue(type.isAssignableFrom(entity.getHeader().getClass())); + } + + @SuppressWarnings("unchecked") + private void testGereric(Entity expectedEntity, String jsonEntity) { + Entity deEntity = JSONSerializeUtils.deserializeFromJSON(jsonEntity, Entity.class); + assertEquals(expectedEntity.getName(), deEntity.getName()); + assertEquals(expectedEntity.getValue(), deEntity.getValue()); + assertEquals(expectedEntity.getData(), deEntity.getData()); + assertEquals(expectedEntity.getLevel(), deEntity.getLevel()); + assertSame(expectedEntity.getLevel(), deEntity.getLevel()); + assertNotNull(deEntity.getHeader()); + assertTrue(deEntity.getHeader() instanceof JSONObject); + } + + @Test + public void testJSONArray() { + Car[] cars = new Car[2]; + + cars[0] = new Car(); + cars[0].setCost(10); + cars[0].setWeight(1000); + + cars[1] = new Car(); + cars[1].setCost(10); + cars[1].setWeight(1000); + + String jsonString = JSONSerializeUtils.serializeToJSON(cars); + Object json = JSON.parse(jsonString); + assertTrue(json instanceof JSONArray); + + Car[] deCars = JSONSerializeUtils.deserializeAs(json, Car[].class); + assertNotNull(deCars); + assertEquals(cars.length, deCars.length); + for (int i = 0; i < deCars.length; i++) { + assertNotNull(cars[i]); + assertEquals(cars[i].getCost(), deCars[i].getCost()); + assertEquals(cars[i].getWeight(), deCars[i].getWeight()); + } + } + + @Test + public void testSerializeString() { + String origString = "test string"; + String json = JSONSerializeUtils.serializeToJSON(origString, String.class); + + String desString = JSON.parseObject(json, String.class); + assertEquals(origString, desString); + + String desString1 = JSONSerializeUtils.deserializeAs(json, String.class); + assertEquals(origString, desString1); + + String desString2 = JSONSerializeUtils.deserializeAs(origString, String.class); + assertEquals(origString, desString2); + + Car car = new Car(); + car.setCost(10); + car.setWeight(1000); + + String carJSON = JSONSerializeUtils.serializeToJSON(car); + String carJSON1 = JSON.toJSONString(car, false); + assertEquals(carJSON, carJSON1); + + String carString = JSON.parseObject(carJSON, String.class); + assertEquals(carJSON, carString); + String carString2 = JSONSerializeUtils.deserializeAs(carJSON, String.class); + assertEquals(carJSON, carString2); + } + + @Test + public void testJSONString(){ + Car car = new Car(); + car.setCost(10); + car.setWeight(1000); + + String carJSON = JSONSerializeUtils.serializeToJSON(car); + JSONString jsonString = new JSONString(carJSON); + System.out.println("1:--\r\n" + carJSON ); + + String newJSONString = JSONSerializeUtils.serializeToJSON(jsonString); + assertEquals(carJSON, newJSONString); + System.out.println("2:--\r\n" + newJSONString ); + + JSONString newJSONString2 = JSONSerializeUtils.deserializeAs(newJSONString, JSONString.class); + assertEquals(carJSON, newJSONString2.toString()); + + String address = UUID.randomUUID().toString(); + JSONString jsonAddress = new JSONString(JSONSerializeUtils.serializeToJSON(address)); + String desAddress = JSONSerializeUtils.deserializeAs(jsonAddress, String.class); + assertEquals(address, desAddress); + + String emptyStr = ""; + JSONString emptyJsonStr = new JSONString(JSONSerializeUtils.serializeToJSON(emptyStr)); + String desEmptyStr = JSONSerializeUtils.deserializeAs(emptyJsonStr, String.class); + assertEquals(emptyStr, desEmptyStr); + + String nullStr = null; + String nullJson = JSONSerializeUtils.serializeToJSON(nullStr); + assertNull(nullJson); + } +} diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Level.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Level.java new file mode 100644 index 00000000..d960b4ed --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Level.java @@ -0,0 +1,18 @@ +package test.my.utils.serialize; + +public enum Level { + + HIGH(10), + + LOW(1); + + private final int value; + + private Level(int value) { + this.value = value; + } + + public int getValue() { + return value; + } +} diff --git a/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Wheel.java b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Wheel.java new file mode 100644 index 00000000..56cb8186 --- /dev/null +++ b/source/utils/utils-serialize/src/test/java/test/my/utils/serialize/Wheel.java @@ -0,0 +1,15 @@ +package test.my.utils.serialize; + +public class Wheel { + + private String black; + + public String getBlack() { + return black; + } + + public void setBlack(String black) { + this.black = black; + } + +} diff --git a/source/utils/utils-test/.gitignore b/source/utils/utils-test/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/utils/utils-test/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/utils/utils-test/pom.xml b/source/utils/utils-test/pom.xml new file mode 100644 index 00000000..5a3f0fa3 --- /dev/null +++ b/source/utils/utils-test/pom.xml @@ -0,0 +1,75 @@ + + 4.0.0 + + com.jd.blockchain + utils + 0.8.2.RELEASE + + utils-test + + + + org.slf4j + slf4j-api + + + com.jd.blockchain + utils-serialize + ${project.version} + + + + javax.servlet + javax.servlet-api + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-servlet + + + + + org.springframework + spring-web + + + org.springframework + spring-webmvc + + + org.springframework + spring-beans + + + org.springframework + spring-test + + + junit + junit + + + org.mockito + mockito-core + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/JunitAssertMatcher.java b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/JunitAssertMatcher.java new file mode 100644 index 00000000..7febfc1d --- /dev/null +++ b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/JunitAssertMatcher.java @@ -0,0 +1,34 @@ +package com.jd.blockchain.utils.test; + +import static org.junit.Assert.*; + +import org.hamcrest.CustomMatcher; + +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; + +public class JunitAssertMatcher { + + public static CustomMatcher assertEqualsMatch(final T expected){ + return new CustomMatcher("expected arg equals the value [" + expected + "].") { + @Override + public boolean matches(Object item) { + assertEquals(expected, item); + return true; + } + }; + } + + public static CustomMatcher assertJsonEqualMatch(final T expected){ + return new CustomMatcher("expected arg equals the value [" + expected + "].") { + @Override + public boolean matches(Object item) { + assertNotNull(item); + String actualJSON = JSONSerializeUtils.serializeToJSON(item); + String expectedJSON = JSONSerializeUtils.serializeToJSON(expected); + assertEquals(expectedJSON, actualJSON); + return true; + } + }; + } + +} diff --git a/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/ServletRegister.java b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/ServletRegister.java new file mode 100644 index 00000000..09f8f703 --- /dev/null +++ b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/ServletRegister.java @@ -0,0 +1,44 @@ +package com.jd.blockchain.utils.test; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import javax.servlet.Servlet; +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletRegistration; + +public class ServletRegister implements ServletContextListener { + + private Map servlets = new LinkedHashMap(); + + public ServletRegister() { + } + + public void addServlet(String mapping, Servlet servlet){ + if (servlets.containsKey(mapping)) { + throw new IllegalArgumentException("The same mapping already exist! --[mapping="+mapping+"]"); + } + servlets.put(mapping, servlet); + } + + @Override + public void contextInitialized(ServletContextEvent event) { + ServletContext servletContext = event.getServletContext(); + int i=1; + for (Entry entry : servlets.entrySet()) { + ServletRegistration.Dynamic serviceDispatcherServlet = servletContext.addServlet("servlet-" + i, entry.getValue()); + serviceDispatcherServlet.addMapping(entry.getKey()); + serviceDispatcherServlet.setLoadOnStartup(1); + serviceDispatcherServlet.setAsyncSupported(true); + i++; + } + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + } + +} diff --git a/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/WebBoot.java b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/WebBoot.java new file mode 100644 index 00000000..4068e561 --- /dev/null +++ b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/WebBoot.java @@ -0,0 +1,122 @@ +package com.jd.blockchain.utils.test; + +import java.net.InetSocketAddress; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.servlet.ServletContextListener; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class WebBoot { + + private static final Logger LOGGER = LoggerFactory.getLogger(WebBoot.class); + + private static final AtomicInteger PORT_POOL; + + static { + int seed = 10000; + int time = (int) (System.nanoTime() & 0xFFF); + PORT_POOL = new AtomicInteger(seed + time); + } + + private Server server; + + private InetSocketAddress hostAddr; + + public int getPort() { + return hostAddr.getPort(); + } + + public WebBoot(InetSocketAddress hostAddr) { + this(hostAddr, null); + } + + public WebBoot(InetSocketAddress hostAddr, ServletContextListener contextListener) { + this.server = new Server(hostAddr); + this.hostAddr = hostAddr; + init(contextListener); + } + + /** + * 以本地IP和随机端口创建服务器; + */ + public WebBoot() { + this(new InetSocketAddress(PORT_POOL.getAndIncrement()), null); + } + + /** + * 以本地IP和随机端口创建服务器; + */ + public WebBoot(ServletContextListener contextListener) { + this(new InetSocketAddress(PORT_POOL.getAndIncrement()), contextListener); + } + + public static WebBoot startWithRandomPort(ServletContextListener contextListener) { + WebBoot server = null; + RuntimeException error = null; + for (int i = 0; i < 100; i++) { + WebBoot serverTemp = new WebBoot(contextListener); + try { + serverTemp.start(); + server = serverTemp; + break; + } catch (RuntimeException e) { + // retry; + error = e; + LOGGER.warn("Server starting exception! And retry again! --[" + e.getClass().toGenericString() + "] " + + e.getMessage()); + } + } + if (server != null) { + return server; + } + throw error; + } + + public WebBoot(int port) { + this(new InetSocketAddress(port), null); + } + + public WebBoot(int port, ServletContextListener contextListener) { + this(new InetSocketAddress(port), contextListener); + } + + public WebBoot(String host, int port, ServletContextListener contextListener) { + this(host == null ? new InetSocketAddress(port) : new InetSocketAddress(host, port), contextListener); + } + + private void init(ServletContextListener contextListener) { + ServletContextHandler contextHandler = new ServletContextHandler(); + contextHandler.addEventListener(contextListener); + + // Create the SessionHandler (wrapper) to handle the sessions +// HashSessionManager manager = new HashSessionManager(); +// SessionHandler sessions = new SessionHandler(manager); + SessionHandler sessions = new SessionHandler(); + contextHandler.setHandler(sessions); + + server.setHandler(contextHandler); + } + + public void start() { + try { + server.start(); + } catch (Exception e) { + LOGGER.error("Server start error! ---[" + e.getClass().toString() + "] " + e.getMessage()); + throw new RuntimeException(e.getMessage(), e); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + LOGGER.error("Server stop error! ---[" + e.getClass().toString() + "] " + e.getMessage()); + throw new RuntimeException(e.getMessage(), e); + } + } +} diff --git a/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/WebTest.java b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/WebTest.java new file mode 100644 index 00000000..d2a917a8 --- /dev/null +++ b/source/utils/utils-test/src/main/java/com/jd/blockchain/utils/test/WebTest.java @@ -0,0 +1,57 @@ +package com.jd.blockchain.utils.test; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.web.WebAppConfiguration; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.DispatcherServlet; + +@RunWith(SpringJUnit4ClassRunner.class) +@WebAppConfiguration +public class WebTest { + + protected static WebBoot server; + + @Autowired + protected WebApplicationContext wac; + + + @BeforeClass + public static void setUpBeforeClass() throws Exception { + } + + @AfterClass + public static void tearDownAfterClass() throws Exception { + WebBoot booter = server; + server = null; + if (booter != null) { + booter.stop(); + } + } + + @Before + public void setUp() throws Exception { + // 启动 JETTY; + if (server == null) { + ServletRegister servletRegister = new ServletRegister(); + DispatcherServlet dispachterServlet = new DispatcherServlet(wac); + servletRegister.addServlet("/service/*", dispachterServlet); + WebBoot booter = WebBoot.startWithRandomPort(servletRegister); + + server = booter; + afterWebStarted(booter.getPort()); + } + } + + protected void afterWebStarted(int port){ + } + + @After + public void tearDown() throws Exception { + } +} diff --git a/source/utils/utils-web-server/.gitignore b/source/utils/utils-web-server/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/utils/utils-web-server/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/utils/utils-web-server/pom.xml b/source/utils/utils-web-server/pom.xml new file mode 100644 index 00000000..85cb5dfc --- /dev/null +++ b/source/utils/utils-web-server/pom.xml @@ -0,0 +1,34 @@ + + 4.0.0 + + com.jd.blockchain + utils + 0.8.2.RELEASE + + utils-web-server + + + + org.eclipse.jetty + jetty-server + + + org.eclipse.jetty + jetty-servlet + + + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.8.2 + + true + + + + + \ No newline at end of file diff --git a/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/ServletSetting.java b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/ServletSetting.java new file mode 100644 index 00000000..68436162 --- /dev/null +++ b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/ServletSetting.java @@ -0,0 +1,40 @@ +package com.jd.blockchain.utils.web.server; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; + +public class ServletSetting { + + private Set mappings = new LinkedHashSet(); + + private Integer loadOnStartup; + + private Boolean asyncSupported; + + + public Set getMappings() { + return Collections.unmodifiableSet(mappings); + } + + public void addMapping(String mapping) { + this.mappings.add(mapping); + } + + public Integer getLoadOnStartup() { + return loadOnStartup; + } + + public void setLoadOnStartup(Integer loadOnStartup) { + this.loadOnStartup = loadOnStartup; + } + + public Boolean getAsyncSupported() { + return asyncSupported; + } + + public void setAsyncSupported(Boolean asyncSupported) { + this.asyncSupported = asyncSupported; + } + +} diff --git a/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServer.java b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServer.java new file mode 100644 index 00000000..908d2b82 --- /dev/null +++ b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServer.java @@ -0,0 +1,254 @@ +package com.jd.blockchain.utils.web.server; + +import java.io.File; +import java.net.InetSocketAddress; + +import javax.servlet.ServletContextAttributeListener; +import javax.servlet.ServletContextListener; +import javax.servlet.ServletRequestAttributeListener; +import javax.servlet.ServletRequestListener; +import javax.servlet.http.HttpServlet; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.session.SessionHandler; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlet.ServletMapping; +//import org.eclipse.jetty.util.resource.EmptyResource; +import org.eclipse.jetty.util.resource.EmptyResource; + +/** + * WebServer 实现为一个可以通过内嵌方式启动的 web 容器; + * + * @author haiq + * + */ +public class WebServer { + private Server server; + + + protected String resourceRootDir; + + private ServletContextHandler contextHandler; + +// private ServletRegisterManager servletRegisterManager = new ServletRegisterManager(); + + /** + * 创建一个 WebServer 实例; + * + * @param hostAddress + * @param resourceRootDir + * 资源根目录; + */ + public WebServer(InetSocketAddress hostAddress, String resourceRootDir) { + this.resourceRootDir = resourceRootDir; + this.server = new Server(hostAddress); + init(); + } + + /** + * 创建一个 WebServer 实例; + * + * @param host + * 主机地址; + * @param port + * 端口; + * @param resourceRootDir + * 资源根目录; + */ + public WebServer(String host, int port, String resourceRootDir) { + this(new InetSocketAddress(host, port), resourceRootDir); + } + + + /** + * 创建一个 WebServer 实例; + * + * @param port + * 端口 + */ + public WebServer(String host, int port) { + this(new InetSocketAddress(host, port), null); + } + + /** + * 创建一个 WebServer 实例; + * + * @param port + * 端口 + * @param resourceRootDir + * 资源根目录; + */ + public WebServer(int port, String resourceRootDir) { + this(new InetSocketAddress(port), resourceRootDir); + } + + + /** + * 创建一个 WebServer 实例; + * + * @param port + * 端口 + */ + public WebServer(int port) { + this(new InetSocketAddress(port), null); + } + + public void setContextPath(String contextPath){ + this.contextHandler.setContextPath(contextPath); + } + + private void init() { + contextHandler = new ServletContextHandler(); + + if (resourceRootDir != null) { + File resRootDir = new File(resourceRootDir); + if (!resRootDir.isDirectory()) { + throw new IllegalArgumentException( + "The path specified as the resource root directory does not exist or isn't a directory!"); + } + contextHandler.setResourceBase(resourceRootDir); + }else{ + contextHandler.setBaseResource(EmptyResource.INSTANCE); + } + + server.setHandler(contextHandler); + } + + /** + * 增加 ServletContextListener; + * + * @param listener + */ + public void addListener(ServletContextListener listener) { + contextHandler.addEventListener(listener); + } + + /** + * 增加 ServletContextAttributeListener; + * + * @param listener + */ + public void addListener(ServletContextAttributeListener listener) { + contextHandler.addEventListener(listener); + } + + /** + * 增加 ServletRequestListener; + * + * @param listener + */ + public void addListener(ServletRequestListener listener) { + contextHandler.addEventListener(listener); + } + + /** + * 增加 ServletRequestListener; + * + * @param listener + */ + public void addListener(ServletRequestAttributeListener listener) { + contextHandler.addEventListener(listener); + } + + /** + * 注册 Servlet; + * + * @param name + * @param servlet + * @param mapping + */ +// public void registServlet(String name, HttpServlet servlet, String mapping) { +// ServletSetting setting = new ServletSetting(); +// setting.addMapping(mapping); +// registServlet(name, servlet, setting); +// } + + public void registServlet(String name, HttpServlet servlet, String... mappings) { + ServletHolder servletHolder = new ServletHolder(name, servlet); + contextHandler.getServletHandler().addServlet(servletHolder); + ServletMapping servletMapping = new ServletMapping(); + servletMapping.setServletName(name); + servletMapping.setPathSpecs(mappings); + + contextHandler.getServletHandler().addServletMapping(servletMapping); +// for (String mapping : setting.getMappings()) { +// contextHandler.addServlet(servletHolder, mapping); +// } + } + + public void start() { + try { + server.start(); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + + public void stop() { + try { + server.stop(); + } catch (Exception e) { + if (e instanceof RuntimeException) { + throw (RuntimeException) e; + } + throw new RuntimeException(e.getMessage(), e); + } + } + +// private static class ServletRegisterManager implements ServletContextListener { +// private Map servletRegisterMap = new LinkedHashMap(); +// +// public void register(String name, Servlet servlet, ServletSetting setting) { +// servletRegisterMap.put(name, new ServletRegisterInfo(name, servlet, setting)); +// } +// +// @Override +// public void contextInitialized(ServletContextEvent sce) { +// ServletContext context = sce.getServletContext(); +// for (ServletRegisterInfo regInfo : servletRegisterMap.values()) { +// ServletRegistration.Dynamic registration = context.addServlet(regInfo.getName(), regInfo.getServlet()); +// registration.addMapping(regInfo.getSetting().getMappings().toArray(new String[0])); +// if (regInfo.getSetting().getLoadOnStartup() != null) { +// registration.setLoadOnStartup(regInfo.getSetting().getLoadOnStartup()); +// } +// if (regInfo.getSetting().getAsyncSupported() != null) { +// registration.setAsyncSupported(regInfo.getSetting().getAsyncSupported()); +// } +// } +// } +// +// @Override +// public void contextDestroyed(ServletContextEvent sce) { +// } +// +// } + +// private static class ServletRegisterInfo { +// +// private String name; +// +// private Servlet servlet; +// +// private ServletSetting setting; +// +// public ServletRegisterInfo(String name, Servlet servlet, ServletSetting setting) { +// this.name = name; +// this.servlet = servlet; +// this.setting = setting; +// } +// +// public String getName() { +// return name; +// } +// +// public Servlet getServlet() { +// return servlet; +// } +// +// public ServletSetting getSetting() { +// return setting; +// } +// +// } +} diff --git a/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServerDemo.java b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServerDemo.java new file mode 100644 index 00000000..078aae55 --- /dev/null +++ b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServerDemo.java @@ -0,0 +1,61 @@ +package com.jd.blockchain.utils.web.server; + +import java.io.File; +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class WebServerDemo { + + public static void main(String[] args) { + WebServer server = new WebServer("127.0.0.1", 8899, new File("./").getAbsolutePath()); + server.setContextPath("/test"); + + server.registServlet("ts", new TestServlet(), "/a"); + +// server.addListener(new ServletContextListener() { +// @Override +// public void contextInitialized(ServletContextEvent sce) { +// ServletRegistration.Dynamic registration = sce.getServletContext().addServlet("ts", new TestServlet()); +// registration.addMapping("/a"); +// registration.setAsyncSupported(true); +// registration.setLoadOnStartup(1);; +// } +// +// @Override +// public void contextDestroyed(ServletContextEvent sce) { +// } +// }); + + server.start(); + } + + + private static class TestServlet extends HttpServlet{ + + private static final long serialVersionUID = 6723790617689867229L; + + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("service..."); + super.service(req, resp); + } + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("do get..."); + resp.getWriter().write("test response "+ System.currentTimeMillis()); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + System.out.println("do post..."); + resp.getWriter().write("test response "+ System.currentTimeMillis()); + } + + } + +} diff --git a/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServerException.java b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServerException.java new file mode 100644 index 00000000..a0f432f5 --- /dev/null +++ b/source/utils/utils-web-server/src/main/java/com/jd/blockchain/utils/web/server/WebServerException.java @@ -0,0 +1,18 @@ +package com.jd.blockchain.utils.web.server; + +public class WebServerException extends RuntimeException{ + + private static final long serialVersionUID = -9177534295689026560L; + + public WebServerException() { + } + + public WebServerException(String message) { + super(message); + } + + public WebServerException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/source/utils/utils-web/.gitignore b/source/utils/utils-web/.gitignore new file mode 100644 index 00000000..24d64373 --- /dev/null +++ b/source/utils/utils-web/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/source/utils/utils-web/pom.xml b/source/utils/utils-web/pom.xml new file mode 100644 index 00000000..4368e4ed --- /dev/null +++ b/source/utils/utils-web/pom.xml @@ -0,0 +1,25 @@ + + 4.0.0 + + com.jd.blockchain + utils + 0.8.2.RELEASE + + utils-web + + + com.jd.blockchain + utils-http + ${project.version} + + + org.springframework + spring-web + + + com.alibaba + fastjson + + + \ No newline at end of file diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebResponseConverter.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebResponseConverter.java new file mode 100644 index 00000000..3b45fc58 --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebResponseConverter.java @@ -0,0 +1,73 @@ +package com.jd.blockchain.utils.web.client; + +import java.io.InputStream; +import java.lang.reflect.Proxy; + +import com.alibaba.fastjson.JSONObject; +import com.jd.blockchain.utils.PrimitiveUtils; +import com.jd.blockchain.utils.http.HttpServiceContext; +import com.jd.blockchain.utils.http.ResponseConverter; +import com.jd.blockchain.utils.http.agent.ServiceRequest; +import com.jd.blockchain.utils.http.converters.JsonResponseConverter; +import com.jd.blockchain.utils.http.converters.StringResponseConverter; +import com.jd.blockchain.utils.serialize.json.JSONSerializeUtils; +import com.jd.blockchain.utils.web.model.WebResponse; + +public class WebResponseConverter implements ResponseConverter { + + private JsonResponseConverter jsonConverter = new JsonResponseConverter(WebResponse.class); + + private Class dataClazz; + + public WebResponseConverter(Class dataClazz) { + this.dataClazz = dataClazz; + } + + @Override + public Object getResponse(ServiceRequest request, InputStream responseStream, HttpServiceContext serviceContext) throws Exception { +// if (dataClazz.isInterface()) { +// String json = (String) StringResponseConverter.INSTANCE.getResponse(request, responseStream, null); +// if (json == null) { +// return null; +// } +// JSONObject jsonObj = JSONSerializeUtils.deserializeAs(json, JSONObject.class); +// return Proxy.newProxyInstance(dataClazz.getClassLoader(), new Class[] {dataClazz}, jsonObj); +// } + WebResponse response = (WebResponse) jsonConverter.getResponse(request, responseStream, null); + if (response == null) { + return null; + } + if (response.getError() != null) { + throw new WebServiceException(response.getError().getErrorCode(), response.getError().getErrorMessage()); + } + if (response.getData() == null) { + return null; + } + if (dataClazz.isAssignableFrom(response.getData().getClass())) { + return response.getData(); + } + if (dataClazz.isAssignableFrom(String.class)) { + return response.getData().toString(); + } + if (PrimitiveUtils.isPrimitiveType(dataClazz)) { + return PrimitiveUtils.castTo(response.getData(), dataClazz); + } + return JSONSerializeUtils.deserializeAs(response.getData(), dataClazz); +// return response.getData(); + +// JSONString jsonData = response.getData(); +// Object data = SerializeUtils.deserializeAs(jsonData, dataClazz); +// return data; + +// if (response.getData() instanceof JSONObject) { +// JSONObject jsonObj = (JSONObject) response.getData(); +// data = jsonObj.toJavaObject(dataClazz); +// }else if (response.getData() instanceof JSONArray) { +// JSONArray jsonObj = (JSONArray) response.getData(); +// data = jsonObj.toJavaObject(dataClazz); +// }else{ +// data = response.getData(); +// } +// return data; + } +} diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebResponseConverterFactory.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebResponseConverterFactory.java new file mode 100644 index 00000000..932622d3 --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebResponseConverterFactory.java @@ -0,0 +1,16 @@ +package com.jd.blockchain.utils.web.client; + +import java.lang.reflect.Method; + +import com.jd.blockchain.utils.http.HttpAction; +import com.jd.blockchain.utils.http.ResponseBodyConverterFactory; +import com.jd.blockchain.utils.http.ResponseConverter; + +public class WebResponseConverterFactory implements ResponseBodyConverterFactory{ + + @Override + public ResponseConverter createResponseConverter(HttpAction actionDef, Method mth) { + return new WebResponseConverter(mth.getReturnType()); + } + +} diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebServiceException.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebServiceException.java new file mode 100644 index 00000000..9a5d83b0 --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/client/WebServiceException.java @@ -0,0 +1,33 @@ +package com.jd.blockchain.utils.web.client; + +import com.jd.blockchain.utils.http.HttpServiceException; + +public class WebServiceException extends HttpServiceException { + + private static final long serialVersionUID = -4869903115201215122L; + + private int errorCode; + + public int getErrorCode() { + return errorCode; + } + + public WebServiceException() { + } + + public WebServiceException(int errorCode) { + super("Error code[" + errorCode + "]!"); + this.errorCode = errorCode; + } + + public WebServiceException(int errorCode, String message) { + super(message); + this.errorCode = errorCode; + } + + public WebServiceException(int errorCode, String message, Throwable cause) { + super(message, cause); + this.errorCode = errorCode; + } + +} diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/BinaryMessageConverter.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/BinaryMessageConverter.java new file mode 100644 index 00000000..108c481b --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/BinaryMessageConverter.java @@ -0,0 +1,61 @@ +package com.jd.blockchain.utils.web.model; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.Serializable; +import java.util.Collections; +import java.util.List; + +import org.springframework.http.HttpInputMessage; +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.MediaType; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.HttpMessageNotReadableException; +import org.springframework.http.converter.HttpMessageNotWritableException; + +import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; + +/** + * 针对二进制对象的序列化和反序列化的 HTTP 消息转换器; + * + * @author huanghaiquan + * + */ +public class BinaryMessageConverter implements HttpMessageConverter { + + public static final String CONTENT_TYPE_VALUE = "application/bin-obj"; + + public static final MediaType CONTENT_TYPE = MediaType.valueOf(CONTENT_TYPE_VALUE); + + private static final List SUPPORTED_MEDIA_TYPES = Collections.singletonList(CONTENT_TYPE); + + @Override + public boolean canRead(Class clazz, MediaType mediaType) { + return CONTENT_TYPE.includes(mediaType) + && (clazz.isPrimitive() || Serializable.class.isAssignableFrom(clazz) || Externalizable.class.isAssignableFrom(clazz)); + } + + @Override + public boolean canWrite(Class clazz, MediaType mediaType) { + return CONTENT_TYPE.includes(mediaType) + && (clazz.isPrimitive() || Serializable.class.isAssignableFrom(clazz) || Externalizable.class.isAssignableFrom(clazz)); + } + + @Override + public List getSupportedMediaTypes() { + return SUPPORTED_MEDIA_TYPES; + } + + @Override + public Object read(Class clazz, HttpInputMessage inputMessage) + throws IOException, HttpMessageNotReadableException { + return BinarySerializeUtils.deserialize(inputMessage.getBody()); + } + + @Override + public void write(Object t, MediaType contentType, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + BinarySerializeUtils.serialize(t, outputMessage.getBody()); + } + +} diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/ErrorCode.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/ErrorCode.java new file mode 100644 index 00000000..3f24b0ac --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/ErrorCode.java @@ -0,0 +1,46 @@ +package com.jd.blockchain.utils.web.model; + +/** + * 错误代码; + */ +public enum ErrorCode { + + /** 参数格式不正确 */ + ILLEGAL_PARAMETE(1001, "参数格式不正确![parma=%s]"), + + /** 缺少必要参数 */ + MISSING_REQUIRED_PARAMETE(1002, "缺少必要参数![param=%s]"), + + /** 没有操作权限 */ + PERFORM_FORBIDDEN(1003, "没有该操作权限!"), + + UNEXPECTED(5000, "未预期的异常!"), + + REQUEST_PARAM_FORMAT_ILLEGAL(12001, "请求体格式不正确"), + + NULL_VALUE(4001, "空值!"), + + NULL_VALUE_PARAM(4001, "参数%s不能为空值!"); + + private int value; + + private String description; + + private ErrorCode(int value, String description) { + this.value = value; + this.description = description; + } + + public int getValue() { + return value; + } + + public String getDescription() { + return description; + } + + public String getDescription(String details) { + return details == null ? description : new StringBuilder(description).append(" --").append(details).toString(); + } + +} \ No newline at end of file diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/JsonWebResponseMessageConverter.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/JsonWebResponseMessageConverter.java new file mode 100644 index 00000000..c8921e1e --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/JsonWebResponseMessageConverter.java @@ -0,0 +1,72 @@ +package com.jd.blockchain.utils.web.model; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.Proxy; + +import org.springframework.http.HttpOutputMessage; +import org.springframework.http.converter.HttpMessageNotWritableException; + +import com.alibaba.fastjson.serializer.SerializerFeature; +import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter; + +public class JsonWebResponseMessageConverter extends FastJsonHttpMessageConverter{ + + public JsonWebResponseMessageConverter() { + this(false); + } + + public JsonWebResponseMessageConverter(boolean jsonPretty) { + if (jsonPretty) { + getFastJsonConfig().setSerializerFeatures(SerializerFeature.PrettyFormat); + } + } + + @Override + protected void writeInternal(Object obj, HttpOutputMessage outputMessage) + throws IOException, HttpMessageNotWritableException { + //把返回结果自动转换为 WebResponse; + if (obj instanceof WebResponse) { + super.writeInternal(obj, outputMessage); + return; + } else if (obj.getClass().isArray()) { + // 数组类型需要判断是否为代理对象 + Object[] objects = (Object[])obj; + if (objects != null && objects.length > 0) { + Object[] results = new Object[objects.length]; + for (int i = 0; i < objects.length; i++) { + Object o = objects[i]; + if (o instanceof Proxy) { + try { + results[i] = proxy2Obj(o); + } catch (Exception e) { + super.writeInternal(WebResponse.createSuccessResult(obj), outputMessage); + return; + } + } else { + results[i] = o; + } + } + super.writeInternal(WebResponse.createSuccessResult(results), outputMessage); + return; + } + } else if (obj instanceof Proxy) { + try { + Object result = proxy2Obj(obj); //获取Proxy对象进行转换 + super.writeInternal(WebResponse.createSuccessResult(result), outputMessage); + return; + } catch (Exception e) { + super.writeInternal(WebResponse.createSuccessResult(obj), outputMessage); + return; + } + } + super.writeInternal(WebResponse.createSuccessResult(obj), outputMessage); + } + + private Object proxy2Obj(Object obj) throws Exception { + Field field = obj.getClass().getSuperclass().getDeclaredField("h"); + field.setAccessible(true); + //获取指定对象中此字段的值 + return field.get(obj); //获取Proxy对象中的此字段的值 + } +} diff --git a/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/WebResponse.java b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/WebResponse.java new file mode 100644 index 00000000..97eaccd9 --- /dev/null +++ b/source/utils/utils-web/src/main/java/com/jd/blockchain/utils/web/model/WebResponse.java @@ -0,0 +1,103 @@ +package com.jd.blockchain.utils.web.model; + +import com.jd.blockchain.utils.serialize.json.JSONString; + +public class WebResponse { + + private boolean success; + + private Object data; + + private ErrorMessage error; + + private WebResponse(){ + + } + + public boolean isSuccess() { + return success; + } + + public void setSuccess(boolean success) { + this.success = success; + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + } + + public ErrorMessage getError() { + return error; + } + + public void setError(ErrorMessage error) { + this.error = error; + } + + public static WebResponse createSuccessResult(Object data){ + WebResponse resonseResult = new WebResponse(); + resonseResult.setSuccess(true); +// if (data != null) { +// JSONString jsonData = JSONString.toJSONString(data); +// resonseResult.setData(jsonData); +// } + resonseResult.setData(data); + return resonseResult; + } + + public static WebResponse createFailureResult(int code, String message){ + ErrorMessage errorMessage = new ErrorMessage(code, message); + return createFailureResult(errorMessage); + } + + public static WebResponse createFailureResult(ErrorMessage errorMessage){ + WebResponse resonseResult = new WebResponse(); + resonseResult.setSuccess(false); + resonseResult.setError(errorMessage); + return resonseResult; + } + + + + /** + * 错误消息实体 + * + * @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; + } + } +} diff --git a/tools/compile-latest.sh b/tools/compile-latest.sh new file mode 100644 index 00000000..7038be58 --- /dev/null +++ b/tools/compile-latest.sh @@ -0,0 +1,42 @@ + +DIST_BASE_DIR=/usr/local/blockchain/prototype/dist/peer + +cd /usr/local/blockchain/prototype/source/prototype.git/source/ + + + +echo "=========================================" +echo "Begin updating source code" +echo "=========================================" + +git checkout develop +git pull origin develop + +echo "=========================================" +echo "Source code was updated to latest!" +echo "=========================================" + + +echo "=========================================" +echo "Begin compiling" +echo "=========================================" + +mvn clean package -T 2C + +echo "=========================================" +echo "Begin copy artifacts to local dist dir!" +echo "=========================================" + +rm -rf $DIST_BASE_DIR/* + +cp peer/target/peer-*.jar $DIST_BASE_DIR + +cp -r peer/config $DIST_BASE_DIR +cp peer/target/classes/application.properties $DIST_BASE_DIR/config + +cp -r peer/shell/* $DIST_BASE_DIR + + +echo "=========================================" +echo "congratulation! Well done!" +echo "=========================================" diff --git a/tools/start.sh b/tools/start.sh new file mode 100644 index 00000000..001c9ecb --- /dev/null +++ b/tools/start.sh @@ -0,0 +1,5 @@ + +NAME=$1 + +java -jar peer-0.0.1-SNAPSHOT.jar --spring.config.location=file:config/application.properties --name=$NAME + diff --git a/tools/stop.sh b/tools/stop.sh new file mode 100644 index 00000000..45f1894a --- /dev/null +++ b/tools/stop.sh @@ -0,0 +1,10 @@ + +FILTER=$1 + +PID=`ps -ef | grep "name=$FILTER" | grep -v grep | awk '{print $2}'` + +PROC_PATH=`ps -ef | grep "name=$FILTER" | grep -v grep | awk '{print $8}'` +echo "Stopping peer[$PID][$FILTER] ......" +kill -9 $PID + +echo "Peer[$PID][$FILTER] stopped!" diff --git a/tools/sync-to-all.sh b/tools/sync-to-all.sh new file mode 100644 index 00000000..92fd3785 --- /dev/null +++ b/tools/sync-to-all.sh @@ -0,0 +1,51 @@ + +CUR_DIR=`pwd` + +FROM_DIR=/usr/local/blockchain/prototype/dist/peer +PROC_DIR=/usr/local/blockchain/prototype/peer + +REMOTE1=192.168.151.39 +REMOTE2=192.168.151.40 +REMOTE3=192.168.151.41 + + +echo "Begin synchronizing ......" +cd $PROC_DIR +sh $PROC_DIR/stop.sh + +cp -r $FROM_DIR/* $PROC_DIR + +cd $PROC_DIR +chmod +x start.sh stop.sh +sh $PROC_DIR/start.sh + +cd $CUR_DIR + +echo "=========================================" +echo "Success synchronizing local peer!" +echo "=========================================" + +./sync-to-remote.sh $FROM_DIR $REMOTE1 $PROC_DIR +cd $CUR_DIR + +echo "=========================================" +echo "Success synchronizing REMOTE peer[$REMOTE1]!" +echo "=========================================" + +./sync-to-remote.sh $FROM_DIR $REMOTE2 $PROC_DIR +cd $CUR_DIR + +echo "=========================================" +echo "Success synchronizing REMOTE peer[$REMOTE2]!" +echo "=========================================" + +#./sync-to-remote.sh $FROM_DIR $REMOTE3 $PROC_DIR + +#echo "=========================================" +#echo "Success synchronizing REMOTE peer[$REMOTE3]!" +#echo "=========================================" + + +echo "=========================================" +echo "Congratulations! All well done!" +echo "=========================================" diff --git a/tools/sync-to-remote.sh b/tools/sync-to-remote.sh new file mode 100644 index 00000000..9e70a1ab --- /dev/null +++ b/tools/sync-to-remote.sh @@ -0,0 +1,27 @@ + +#Require Eironment Variable: FROM_DIR, REMOTE, PROC_DIR + +FROM_DIR=$1 +REMOTE=$2 +PROC_DIR=$3 + +echo "FROM_DIR="$FROM_DIR +echo "REMOTE="$REMOTE +echo "PROC_DIR="$PROC_DIR + +REMOTE_DIR=@$REMOTE:$PROC_DIR + +echo "=========================================" +echo "Begin synchronizing to [$REMOTE_DIR]" +echo "=========================================" + +ssh $REMOTE "cd $PROC_DIR; sh $PROC_DIR/stop.sh; rm -rf $PROC_DIR/*" +scp -r $FROM_DIR/* $REMOTE_DIR +ssh $REMOTE "cd $PROC_DIR; chmod +x $PROC_DIR/*.sh; sh $PROC_DIR/start.sh" + +ssh $REMOTE< /dev/null 2>&1 & +exit +