| @@ -1,626 +1,120 @@ | |||
| [TOC] | |||
| #JD区块链 | |||
| [](https://www.apache.org/licenses/LICENSE-2.0.html) | |||
| [](https://maven-badges.herokuapp.com/maven-central/com.jd.blockchain/sdk-pack/) | |||
| [](https://travis-ci.org/blockchain-jd-com/jdchain) | |||
| ------------------------------------------------------------------------ | |||
| ### 版本修订历史 | |||
| <table> | |||
| <tr> | |||
| <th>版本号</th> | |||
| <th>作 者</th> | |||
| <th>修改日期</th> | |||
| <th>备 注</th> | |||
| </tr> | |||
| <tr> | |||
| <td>0.0.6</td> | |||
| <td>黄海泉</td> | |||
| <td>2017-11-10</td> | |||
| <td> | |||
| 定义JD区块链项目的目标与关键能力;<br> | |||
| 定义JD区块链的核心对象模型;<br> | |||
| 定义“账户”的生成算法和“区块/交易/操作/账户”的关键属性;<br> | |||
| 描述了编程接口的示例代码; | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td>0.0.7</td> | |||
| <td>黄海泉</td> | |||
| <td>2017-11-17</td> | |||
| <td> | |||
| 丰富了对“节点共识”、“节点分区”两项关键能力的详细描述; | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td>0.0.8</td> | |||
| <td>黄海泉</td> | |||
| <td>2018-07-17</td> | |||
| <td> | |||
| 增加部署图;增加智能合约开发的示例; | |||
| </td> | |||
| </tr> | |||
| </table> | |||
| ------------------------------------------------------------------------ | |||
| ## 一、概述 | |||
| JD区块链项目的目标是提供一个面向广泛的应用场景、满足企业核心需求的灵活和易用的区块链系统。 | |||
| 以下是 JD 区块链用以满足企业核心需求的关键能力,也是显著区别于其它区块链的重要特征: | |||
| - 快速共识 | |||
| - 节点分区 | |||
| - 并行多账本 | |||
| - 大数据伸缩存储 | |||
| - 条件检索 | |||
| - 面向对象的合约代码编程模型 | |||
| - 节点快速部署 | |||
| - 多终端灵活接入 | |||
| - 分布式自治的账户权限管理模型 | |||
| JD区块链对于关键能力的定义是建立在深入理解和抽象各种多样化需求的基础上的。 | |||
| - 快速共识(Efficient Consensus) | |||
| 我们认为,“快速”不仅仅体现在“用更短时间”达成共识,还要体现在交易(Transaction)要得到可靠地执行。 | |||
| 需要在算法和实现层面做出保障,确保所有提交的合法的交易会被系统在一个“确定性”和“足够短”的时间之内被严格地执行,系统不主动作出随机性地丢弃(不包括系统故障因素)。注:POW类算法产生的链分叉处理是一种系统随机性地丢弃交易的行为。 | |||
| 从使用者的视角来看,这种能力就是区块链系统的“可靠性”,这对于企业、金融场景而言尤其重要。 | |||
| - 节点分区(Peer Partition) | |||
| “分区”是一种分布式系统架构原则,通过将大范围目标按照某种相似特征分隔为一个个小范围目标,分别进行更高效地处理,这能从整体上提升整个系统的处理能力。 | |||
| 区块链系统也是一种分布式系统,沿用“分区”的思想这点来自以往的系统架构实践的经验,是有可能让区块链系统获得可媲美现有系统的处理能力。这种能力将可以无障碍地把区块链在应用于广泛的企业场景中。 | |||
| 在此,我们所说的“节点分区(Peer Partition)” 是共识过程中的物理通讯层面的分区,在共识过程中只有相关的物理节点才会建立通讯链路并复制和存储共识的状态数据。在一个区块链系统中,可以从节点全集中选择一个或多个节点子集,分别组成一个或多个节点分区(Peer Partition) | |||
| - 并行多账本 | |||
| 账本(Ledger) | |||
| - 大数据伸缩存储 | |||
| - 条件检索 | |||
| - 面向对象的合约代码编程模型 | |||
| - 节点快速部署 | |||
| - 多终端灵活接入 | |||
| - 分布式自治的账户权限管理模型 | |||
| ## 二、对象模型 | |||
| JD区块链的核心对象包括: | |||
| - 账本(Ledger) | |||
| 一份账本(Ledger)实质上是由两部分组成: | |||
| - 一组以“账户(Account)”表示的状态数据; | |||
| - 以“区块的链条(Block-chain)”表示的状态数据的变更历史; | |||
| JD区块链的“账本(Ledger)”是一个最顶层的管理数据的逻辑单元。在一个区块链节点构成的共识网络中,可以维护多套并行的“账本(Ledger)”。 | |||
| - 账户(Account) | |||
| - 在JD区块链中,账户(Account)被设计为包含“身份(Identity)”、“权限(Privilege)”、“状态(State)”、“控制规则(Control Rule)” 这4种属性的对象。 | |||
| - 其中,“身份(Identity)”、“权限(Privilege)”这两种属性是一个面向应用的系统的基本功能; | |||
| - “状态(State)”、“控制规则(Control Rule)” 这两种属性这是一个具备“图灵完备性”的系统的基本属性,使系统可以处理任意多样化的任务; | |||
| - 在这里,“身份(Identity)”是一个抽象表述,其形式上就是一个“区块链地址(Address)”和相应的“非对称秘钥钥对(KeyPair)”/证书。 简单来说,一个“账户(Account)”就是一个区块链地址、公私钥对以及一套的权限配置. | |||
| - 一个“账户(Account)”的“状态(State)”、“控制规则(Control Rule)” 这2种属性则是可选的,这提供了不同于其它区块链的账户/合约的一种新的使用方式,即一种数据和逻辑分离的应用设计思路(这在传统的web应用编程中被称为“贫血模型”)。同时,也意味着一个特定“账户”中的数据状态是可以跨账户/合约代码进行共享访问的。 | |||
| - 从应用的视角来看,对“账户”的使用方式可以有几种模式: | |||
| - 用于表示业务角色/用户: | |||
| - 用于表示业务数据:仅有“状态(State)”没有“控制规则(Control Rule)”的账户,可称为数据账户,就有些类似于关系数据库中的“表(Table)”的作用,不同业务类别的数据则使用不同的账户来管理。 | |||
| - 用于表示业务逻辑:仅有“控制规则(Control Rule)”没有“状态(State)”的账户,即所谓的合约账户,可表示某种用于处理数据的通用逻辑,可被授权访问特定的数据账户。 | |||
| - 区块(Block) | |||
| 在概念上,与通常所说的区块的概念是一致的。 | |||
| 在实现上,一个区块的主要内容是包含了某一时刻提交的所有交易以及交易执行之后的状态数据的快照的hash,而不存储具体的交易操作和状态数据。 | |||
| - 交易(Transaction) | |||
| 在概念上,与通常所说的Transaction的概念是一致的,表示一组需要原子执行的操作。 | |||
| - 操作(Operation) | |||
| 操作是针对“账户(Account)”的“写”指令,包括以下几类: | |||
| - 注册账户 | |||
| - 更新账户的状态数据 | |||
| - 更新账户的合约代码定义 | |||
| - 调用账户的合约代码 | |||
| - 合约代码(Contract Code) | |||
| 合约代码是一段用于对“账户(Account)”的状态数据执行操作的代码程序。 | |||
| ## 三、部署模型 | |||
| 1. 总体部署 | |||
|  | |||
| 2. 系统组件 | |||
| - 共识节点 | |||
| - 复制节点 | |||
| - SDK | |||
| - 网关 | |||
| - 终端 | |||
| 3. 配置和管理 | |||
| ## 四、账本结构 | |||
| ### 1. 账户生成算法 | |||
| - 公私钥对 | |||
| - 算法:默认ED25519 ,支持 SM2、CA; | |||
| - 公钥存储格式:版本 + 算法标识 + 公私钥原始内容 + 校验码 | |||
| - 字符编码方式:Base64 | |||
| - 地址 | |||
| - 算法 | |||
| - 给定公钥 P (或由私钥 R 算出公钥 P) | |||
| - 中间值 H1 = SHA256( P ) | |||
| - 中间值 H2 = RIPEMD-160( H1 ) | |||
| - 中间值 X = 版本 + 公钥算法标识 + H2 | |||
| - 校验和 C = 前4字节( SHA256( SHA256( X )) ) | |||
| - 地址 Address = Base58( X + C ) | |||
| ### 2. 区块 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>BlockHash</td> | |||
| <td>当前区块 hash</td> | |||
| <td>对区块中除此之外的其它所有属性一起进行哈希运算生成</td> | |||
| </tr> | |||
| <tr> | |||
| <td>BlockVersion </td> | |||
| <td>区块版本</td> | |||
| <td>表示区块-交易的属性结构的版本号;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>PreviousBlockHash</td> | |||
| <td>上一区块 hash</td> | |||
| <td></td> | |||
| </tr> | |||
| <tr> | |||
| <td>BlockNumber</td> | |||
| <td>区块高度</td> | |||
| <td>区块高度是一个区块在链中的序号;<br>创始区块的高度为 0,每个新区块的高度依次递增;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>AccountHash</td> | |||
| <td>账户树hash</td> | |||
| <td>账户的 Merkle Tree 根的 hash</td> | |||
| </tr> | |||
| <tr> | |||
| <td>AccountCount</td> | |||
| <td>账户数量</td> | |||
| <td>区块生成时账本中的全部账户的总数</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxTreeHash</td> | |||
| <td>交易树 hash</td> | |||
| <td>本区块的交易集合的 Merkle Tree 根的 hash</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxCount</td> | |||
| <td>区块交易数量</td> | |||
| <td>当前区块包含的交易的数量;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxTotalCount</td> | |||
| <td>账本交易总数</td> | |||
| <td>截止到当前区块为止当前账本的所有交易的总数量;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>CloseTime</td> | |||
| <td>区块关闭时间</td> | |||
| <td>生成当前区块时的区块链节点的网络时间;</td> | |||
| </tr> | |||
| </table> | |||
| ### 3. 交易 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>Hash</td> | |||
| <td>当前交易 hash</td> | |||
| <td>对交易中除此之外的其它所有属性一起进行哈希运算生成</td> | |||
| </tr> | |||
| <tr> | |||
| <td>LedgerNumber</td> | |||
| <td>区块高度</td> | |||
| <td>交易被包含的区块高度</td> | |||
| </tr> | |||
| <tr> | |||
| <td>BlobHash</td> | |||
| <td>交易数据块hash</td> | |||
| <td>交易的数据块是交易的原始数据,包含客户端提交的交易的全部操作及其参数; | |||
| <br>交易的参与者需要使用私钥对交易数据块进行签名;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Operations</td> | |||
| <td>操作列表</td> | |||
| <td>交易的操作列表;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Sponsor</td> | |||
| <td>交易发起人</td> | |||
| <td>交易发起人的账户地址;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>SequenceNumber</td> | |||
| <td>交易序号</td> | |||
| <td>交易序号记录了一个特定的发起人的交易的顺序号,等同于该发起人历史上发起的交易的总数;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Signatures</td> | |||
| <td>签名列表</td> | |||
| <td>由交易发起人和其它参与者对交易数据块的签名的列表;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Result</td> | |||
| <td>交易结果</td> | |||
| <td>0 - 表示执行成功;非零表示执行失败;<br>注:最终的账本只包含成功的交易;</td> | |||
| </tr> | |||
| </table> | |||
| ### 4. 操作 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>OpType</td> | |||
| <td>操作类型</td> | |||
| <td> | |||
| 一级操作类型包括:注册账户、配置权限、写入键值数据、写入对象数据、定义合约代码、调用合约代码;<br> | |||
| “键值数据写入”操作的子操作类型包括:填入键值、移除键、数值增加、数值减少;<br> | |||
| “对象数据写入”操作的自操作类型包括:插入对象、更新对象、移除对象; | |||
| </td> | |||
| </tr> | |||
| <tr> | |||
| <td>Args</td> | |||
| <td>参数列表</td> | |||
| <td>与操作类型相对应的参数列表;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>SubOps</td> | |||
| <td>子操作列表</td> | |||
| <td>“子操作”是“操作”的递归定义,由“操作类型”来标识;</td> | |||
| </tr> | |||
| </table> | |||
| ### 5. 账户 | |||
| <table> | |||
| <tr> | |||
| <th>属性</th> | |||
| <th>名称</th> | |||
| <th>说明</th> | |||
| </tr> | |||
| <tr> | |||
| <td>Address</td> | |||
| <td>地址</td> | |||
| <td>账户的唯一标识</td> | |||
| </tr> | |||
| <tr> | |||
| <td>RegNumber</td> | |||
| <td>注册号</td> | |||
| <td>账户被注册到区块链的区块高度;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>TxSquenceNumber</td> | |||
| <td>交易序列号</td> | |||
| <td>由账户发起的交易的序列号,初始为 0,账户每发起一个交易则增加1;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>ModelVersion</td> | |||
| <td>账户模型版本</td> | |||
| <td>表示构成一个账户结构的属性模型的程序版本号;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>Version</td> | |||
| <td>账户版本</td> | |||
| <td>初始为 0,对账户的每一次变更(包括对权限设置、状态和合约代码的变更)都会使账户状态版本增加 1 ;<br>注:交易序号的改变不会导致账户版本的增加;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>PrivilegeHash</td> | |||
| <td>权限 hash</td> | |||
| <td>权限树的根hash;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>PrivilegeVersion</td> | |||
| <td>权限版本</td> | |||
| <td>初始为 0, 每次对权限的变更都导致版本号加 1;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>StateType</td> | |||
| <td>状态类型</td> | |||
| <td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>StateVersion</td> | |||
| <td>状态版本</td> | |||
| <td>账户的状态类型有3种:空类型(NIL);键值类型;对象类型;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>StateHash</td> | |||
| <td>状态哈希</td> | |||
| <td>数据状态的 merkle tree 的根hash;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>CodeHash</td> | |||
| <td>合约代码哈希</td> | |||
| <td>由“账户地址+合约代码版本号+合约代码内容”生成的哈希;</td> | |||
| </tr> | |||
| <tr> | |||
| <td>CodeVersion</td> | |||
| <td>代码版本</td> | |||
| <td>初始为 0,每次对代码的变更都使版本加 1 ;</td> | |||
| </tr> | |||
| </table> | |||
| ## 五、编程接口 | |||
| ### 1. 服务连接 | |||
| ```java | |||
| //创建服务代理 | |||
| public static BlockchainKeyPair CLIENT_CERT = BlockchainKeyGenerator.getInstance().generate(); | |||
| final String GATEWAY_IP = "127.0.0.1"; | |||
| final int GATEWAY_PORT = 80; | |||
| final boolean SECURE = false; | |||
| GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IP, GATEWAY_PORT, SECURE, | |||
| CLIENT_CERT); | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| ``` | |||
| ## 一、项目介绍 | |||
| JD Chain 的目标是实现一个面向企业应用场景的通用区块链框架系统,能够作为企业级基础设施,为业务创新提供高效、灵活和安全的解决方案。 | |||
| ### 2. 用户注册 | |||
| ## 二、部署模型 | |||
| JD Chain 主要部署组件包括以下几种: | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义注册账号的 TX; | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||
| CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||
| BlockchainKeyPair user = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||
| txTemp.users().register(user.getIdentity()); | |||
| - 共识节点 | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| 共识节点即参与共识的节点,这是系统的核心组件,承担了运行共识协议、管理账本数据、运行智能合约的职责。 | |||
| // 提交交易; | |||
| prepTx.commit(); | |||
| ``` | |||
| 一个区块链网络由多个共识节点组成,共识节点的数量范围由选择的共识协议决定。 | |||
| 共识节点和账本是两个不同的概念,共识节点是个物理上的概念,账本是个逻辑上的概念。JD Chain 是一个多账本区块链系统,一个共识节点上可以装载运行多个账本。账本是数据维度的独立管理单元。共识节点和账本的关系,就像关系数据库系统中,数据库服务器和数据库实例的关系。 | |||
| ### 3. 数据账户注册 | |||
| 共识节点通常都部署在参与方的内部网络中,通过由网络管理员指定的安全的网络出口与其它的共识节点建立通讯连接。 | |||
| 共识节点在形态上是服务器中的一个处理进程,背后需要连接一个本地或者内网的NoSQL数据库系统作为账本的存储。当前版本,共识节点目前是单进程的,未来版本将实现多进程以及多服务器集群模式。 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义注册账号的 TX; | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| SignatureFunction signatureFunction = asymmetricCryptography.getSignatureFunction(CryptoAlgorithm.ED25519); | |||
| CryptoKeyPair cryptoKeyPair = signatureFunction.generateKeyPair(); | |||
| BlockchainKeyPair dataAccount = new BlockchainKeyPair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||
| txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| prepTx.commit(); | |||
| ``` | |||
| - 网关节点 | |||
| ### 4. 写入数据 | |||
| 网关节点是负责终端接入的节点,负责终端连接、协议转换、交易准入、本地密码运算、密钥管理等职责。 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| HashDigest ledgerHash = getLedgerHash(); | |||
| // 在本地定义注册账号的 TX; | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| // -------------------------------------- | |||
| // 将商品信息写入到指定的账户中; | |||
| // 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; | |||
| String commodityDataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
| Commodity commodity1 = new Commodity(); | |||
| txTemp.dataAccount(commodityDataAccount).set("ASSET_CODE", commodity1.getCode().getBytes(), -1); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| String txHash = ByteArray.toBase64(prepTx.getHash().toBytes()); | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| prepTx.sign(keyPair); | |||
| // 提交交易; | |||
| prepTx.commit(); | |||
| ``` | |||
| 网关节点是一种轻量节点,需要绑定一个特定参与方的密钥对,连接到一个或多个共识节点。 | |||
| ### 5. 查询数据 | |||
| 网关节点向共识节点的连接是需要通过认证的,绑定的参与方的密钥对必须事先已经注册到区块链账本中,且得到接入授权。 | |||
| > 注:详细的查询可参考模块sdk-samples中SDK_GateWay_Query_Test_相关测试用例 | |||
| - 终端 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 查询区块信息; | |||
| // 区块高度; | |||
| long ledgerNumber = service.getLedger(LEDGER_HASH).getLatestBlockHeight(); | |||
| // 最新区块; | |||
| LedgerBlock latestBlock = service.getBlock(LEDGER_HASH, ledgerNumber); | |||
| // 区块中的交易的数量; | |||
| long txCount = service.getTransactionCount(LEDGER_HASH, latestBlock.getHash()); | |||
| // 获取交易列表; | |||
| LedgerTransaction[] txList = service.getTransactions(LEDGER_HASH, ledgerNumber, 0, 100); | |||
| // 遍历交易列表 | |||
| for (LedgerTransaction ledgerTransaction : txList) { | |||
| TransactionContent txContent = ledgerTransaction.getTransactionContent(); | |||
| Operation[] operations = txContent.getOperations(); | |||
| if (operations != null && operations.length > 0) { | |||
| for (Operation operation : operations) { | |||
| operation = ClientOperationUtil.read(operation); | |||
| // 操作类型:数据账户注册操作 | |||
| if (operation instanceof DataAccountRegisterOperation) { | |||
| DataAccountRegisterOperation daro = (DataAccountRegisterOperation) operation; | |||
| BlockchainIdentity blockchainIdentity = daro.getAccountID(); | |||
| } | |||
| // 操作类型:用户注册操作 | |||
| else if (operation instanceof UserRegisterOperation) { | |||
| UserRegisterOperation uro = (UserRegisterOperation) operation; | |||
| BlockchainIdentity blockchainIdentity = uro.getUserID(); | |||
| } | |||
| // 操作类型:账本注册操作 | |||
| else if (operation instanceof LedgerInitOperation) { | |||
| LedgerInitOperation ledgerInitOperation = (LedgerInitOperation)operation; | |||
| LedgerInitSetting ledgerInitSetting = ledgerInitOperation.getInitSetting(); | |||
| ParticipantNode[] participantNodes = ledgerInitSetting.getConsensusParticipants(); | |||
| } | |||
| // 操作类型:合约发布操作 | |||
| else if (operation instanceof ContractCodeDeployOperation) { | |||
| ContractCodeDeployOperation ccdo = (ContractCodeDeployOperation) operation; | |||
| BlockchainIdentity blockchainIdentity = ccdo.getContractID(); | |||
| } | |||
| // 操作类型:合约执行操作 | |||
| else if (operation instanceof ContractEventSendOperation) { | |||
| ContractEventSendOperation ceso = (ContractEventSendOperation) operation; | |||
| } | |||
| // 操作类型:KV存储操作 | |||
| else if (operation instanceof DataAccountKVSetOperation) { | |||
| DataAccountKVSetOperation.KVWriteEntry[] kvWriteEntries = | |||
| ((DataAccountKVSetOperation) operation).getWriteSet(); | |||
| if (kvWriteEntries != null && kvWriteEntries.length > 0) { | |||
| for (DataAccountKVSetOperation.KVWriteEntry kvWriteEntry : kvWriteEntries) { | |||
| BytesValue bytesValue = kvWriteEntry.getValue(); | |||
| DataType dataType = bytesValue.getType(); | |||
| Object showVal = ClientOperationUtil.readValueByBytesValue(bytesValue); | |||
| System.out.println("writeSet.key=" + kvWriteEntry.getKey()); | |||
| System.out.println("writeSet.value=" + showVal); | |||
| System.out.println("writeSet.type=" + dataType); | |||
| System.out.println("writeSet.version=" + kvWriteEntry.getExpectedVersion()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| } | |||
| // 根据交易的 hash 获得交易;注:客户端生成 PrepareTransaction 时得到交易hash; | |||
| HashDigest txHash = txList[0].getTransactionContent().getHash(); | |||
| Transaction tx = service.getTransactionByContentHash(LEDGER_HASH, txHash); | |||
| // 获取数据; | |||
| String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
| String[] objKeys = new String[] { "x001", "x002" }; | |||
| KVDataEntry[] kvData = service.getDataEntries(LEDGER_HASH, commerceAccount, objKeys); | |||
| long payloadVersion = kvData[0].getVersion(); | |||
| // 获取数据账户下所有的KV列表 | |||
| KVDataEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||
| if (kvData != null && kvData.length > 0) { | |||
| for (KVDataEntry kvDatum : kvData) { | |||
| System.out.println("kvData.key=" + kvDatum.getKey()); | |||
| System.out.println("kvData.version=" + kvDatum.getVersion()); | |||
| System.out.println("kvData.type=" + kvDatum.getType()); | |||
| System.out.println("kvData.value=" + kvDatum.getValue()); | |||
| } | |||
| } | |||
| ``` | |||
| 终端泛指可以提交交易的客户端,典型来说,包括人、自动化设备、链外的信息系统等。 | |||
| 终端只能通过网关节点来提交交易。终端提交的交易需要用体现该终端身份的私钥来签署,产生一份电子签名。随后当交易提交给网关节点时,网关节点需要在把交易提交到共识节点之前,对交易请求以网关节点绑定的私钥追加一项“节点签名”。 | |||
| ### 6. 合约发布 | |||
| - 备份节点 | |||
| ```java | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| // 在本地定义TX模板 | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| 仅对账本数据提供备份,但不参与交易共识的节点。(注:目前版本中尚未实现,将在后续版本中提供) | |||
| // 合约内容读取 | |||
| byte[] contractBytes = FileUtils.readBytes(new File(CONTRACT_FILE)); | |||
| // 生成用户 | |||
| BlockchainIdentityData blockchainIdentity = new BlockchainIdentityData(getSponsorKey().getPubKey()); | |||
|  | |||
| // 发布合约 | |||
| txTemp.contracts().deploy(blockchainIdentity, contractBytes); | |||
| // TX 准备就绪; | |||
| PreparedTransaction prepTx = txTemp.prepare(); | |||
| ## 三、编译源代码 | |||
| // 使用私钥进行签名; | |||
| CryptoKeyPair keyPair = getSponsorKey(); | |||
| 1. 安装 Maven 环境 | |||
| prepTx.sign(keyPair); | |||
| JD Chain 当前版本以 Java 语言开发,需要安装配置 JVM 和 Maven,JDK 版本不低于1.8 。(没有特殊要求,请按标准方法安装,此处不赘述) | |||
| 2. 安装 Git 工具 | |||
| 为了能够执行 git clone 命令获取代码仓库。 (没有特殊要求,请按标准方法安装,此处不赘述) | |||
| 3. 工程代码 | |||
| // 提交交易; | |||
| TransactionResponse transactionResponse = prepTx.commit(); | |||
| JD Chain 源代码包括 3 个代码仓库 | |||
| assertTrue(transactionResponse.isSuccess()); | |||
| - jdchain | |||
| - 这是当前仓库,也是核心仓库,包含了共识节点、网关节点、SDK等一切部署组件。依赖于 explorer 和 bftsmart 这两个仓库先进行编译安装; | |||
| - explorer | |||
| - 这是区块链浏览器的前端Web页面的工程,需要编译成静态资源包,由网关节点集成到一起部署。 | |||
| - 地址:git@github.com:blockchain-jd-com/explorer.git | |||
| // 打印合约地址 | |||
| System.out.println(blockchainIdentity.getAddress().toBase58()); | |||
| - bftsmart | |||
| - 这是bftsmart共识协议的工程,需要先编译安装到本地 maven 仓库; | |||
| ``` | |||
| ### 7. 合约执行 | |||
| 4. 命令操作 | |||
| ```java | |||
| - 编译安装 explorer 到本地 maven 仓库; | |||
| ```sh | |||
| $ git clone git@github.com:blockchain-jd-com/explorer.git explorer | |||
| // 创建服务代理; | |||
| BlockchainService service = serviceFactory.getBlockchainService(); | |||
| $ cd explorer | |||
| // 在本地定义TX模板 | |||
| TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
| $ git checkout master | |||
| // 合约地址 | |||
| String contractAddressBase58 = ""; | |||
| // 使用接口方式调用合约 | |||
| TransferContract transferContract = txTpl.contract(contractAddress, TransferContract.class); | |||
| // 使用decode方式调用合约内部方法(create方法) | |||
| // 返回GenericValueHolder可通过get方法获取结果,但get方法需要在commit调用后执行 | |||
| GenericValueHolder<String> result = ContractReturnValue.decode(transferContract.create(address, account, money)); | |||
| PreparedTransaction ptx = txTpl.prepare(); | |||
| ptx.sign(adminKey); | |||
| TransactionResponse transactionResponse = ptx.commit(); | |||
| $ mvn clean install | |||
| String cotractExecResult = result.get(); | |||
| // TransactionResponse也提供了可供查询结果的接口 | |||
| OperationResult[] operationResults = transactionResponse.getOperationResults(); | |||
| ``` | |||
| - 编译安装 bftsmart 到本地 maven 仓库; | |||
| - 需要手动先安装一个第三方包,位于仓库根目录下 lib/core-0.1.4.jar | |||
| ```sh | |||
| $ git clone git@github.com:blockchain-jd-com/bftsmart.git bftsmart | |||
| $ cd bftsmart | |||
| $ git checkout master | |||
| // 通过OperationResult获取结果 | |||
| for (int i = 0; i < operationResults.length; i++) { | |||
| OperationResult opResult = operationResults[i]; | |||
| System.out.printf("Operation[%s].result = %s \r\n", | |||
| opResult.getIndex(), BytesValueEncoding.decode(opResult.getResult())); | |||
| } | |||
| $ mvn install:install-file -Dfile=lib/core-0.1.4.jar -DgroupId=com.yahoo.ycsb -DartifactId=core -Dversion=0.1.4 -Dpackaging=jar | |||
| $ mvn clean install | |||
| ``` | |||
| - 编译 jdchain 工程; | |||
| - 当编译完成后,共识节点的安装包位于 "仓库根目录"/source/deployment/deployment-peer/target/jdchain-peer-1.0.1.RELEASE.zip | |||
| - 当编译完成后,网关节点的安装包位于 "仓库根目录"/source/deployment/deployment-gateway/target/jdchain-gateway-1.0.1.RELEASE.zip | |||
| ``` | |||
| ```sh | |||
| $ git clone git@github.com:blockchain-jd-com/jdchain.git jdchain | |||
| $ cd jdchain/source | |||
| $ git checkout master | |||
| $ mvn clean package | |||
| ``` | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>base</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>binary-proto</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>consensus</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>consensus-bftsmart</artifactId> | |||
| @@ -5,7 +5,13 @@ import java.util.*; | |||
| import java.util.concurrent.CopyOnWriteArrayList; | |||
| import java.util.concurrent.ExecutorService; | |||
| import java.util.concurrent.Executors; | |||
| import bftsmart.consensus.app.BatchAppResultImpl; | |||
| import bftsmart.tom.*; | |||
| import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
| import com.jd.blockchain.consensus.service.*; | |||
| import com.jd.blockchain.ledger.*; | |||
| import com.jd.blockchain.transaction.TxResponseMessage; | |||
| import com.jd.blockchain.utils.serialize.binary.BinarySerializeUtils; | |||
| import org.slf4j.Logger; | |||
| import org.slf4j.LoggerFactory; | |||
| @@ -15,18 +21,11 @@ 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 { | |||
| @@ -189,94 +188,243 @@ public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer | |||
| return messageHandle.processUnordered(bytes).get(); | |||
| } | |||
| @Override | |||
| public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) { | |||
| return appExecuteBatch(commands, msgCtxs, fromConsensus, null); | |||
| /** | |||
| * | |||
| * Only block, no reply, used by state transfer when peer start | |||
| * | |||
| */ | |||
| private void block(List<byte[]> manageConsensusCmds) { | |||
| String batchId = messageHandle.beginBatch(realmName); | |||
| try { | |||
| int msgId = 0; | |||
| for (byte[] txContent : manageConsensusCmds) { | |||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||
| } | |||
| messageHandle.completeBatch(realmName, batchId); | |||
| messageHandle.commitBatch(realmName, batchId); | |||
| } catch (Exception e) { | |||
| // todo 需要处理应答码 404 | |||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); | |||
| } | |||
| } | |||
| @Override | |||
| public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus, List<ReplyContextMessage> replyList) { | |||
| /** | |||
| * | |||
| * Local peer has cid diff with remote peer, used by state transfer when peer start | |||
| * | |||
| */ | |||
| private byte[][] appExecuteDiffBatch(byte[][] commands, MessageContext[] msgCtxs) { | |||
| if (replyList == null || replyList.size() == 0) { | |||
| throw new IllegalArgumentException(); | |||
| } | |||
| // todo 此部分需要重新改造 | |||
| /** | |||
| * 默认BFTSmart接口提供的commands是一个或多个共识结果的顺序集合 | |||
| * 根据共识的规定,目前的做法是将其根据msgCtxs的内容进行分组,每组都作为一个结块标识来处理 | |||
| * 从msgCtxs可以获取对应commands的分组情况 | |||
| */ | |||
| int manageConsensusId = msgCtxs[0].getConsensusId(); | |||
| List<byte[]> manageConsensusCmds = new ArrayList<>(); | |||
| List<ReplyContextMessage> 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); | |||
| block(manageConsensusCmds); | |||
| // 重置链表和共识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); | |||
| block(manageConsensusCmds); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * | |||
| * Invoked by state transfer when peer start | |||
| * | |||
| */ | |||
| @Override | |||
| public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus) { | |||
| // Not from consensus outcomes, from state transfer | |||
| if (!fromConsensus) { | |||
| return appExecuteDiffBatch(commands, msgCtxs); | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * | |||
| * From consensus outcomes, do nothing now | |||
| * The operation of executing the batch was moved to the consensus stage 2 and 3, in order to guaranteed ledger consistency | |||
| */ | |||
| @Override | |||
| public byte[][] appExecuteBatch(byte[][] commands, MessageContext[] msgCtxs, boolean fromConsensus, List<ReplyContextMessage> replyList) { | |||
| // if (replyList == null || replyList.size() == 0) { | |||
| // throw new IllegalArgumentException(); | |||
| // } | |||
| // // todo 此部分需要重新改造 | |||
| // /** | |||
| // * 默认BFTSmart接口提供的commands是一个或多个共识结果的顺序集合 | |||
| // * 根据共识的规定,目前的做法是将其根据msgCtxs的内容进行分组,每组都作为一个结块标识来处理 | |||
| // * 从msgCtxs可以获取对应commands的分组情况 | |||
| // */ | |||
| // int manageConsensusId = msgCtxs[0].getConsensusId(); | |||
| // List<byte[]> manageConsensusCmds = new ArrayList<>(); | |||
| // List<ReplyContextMessage> 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; | |||
| } | |||
| /** | |||
| * | |||
| * Block and reply are moved to consensus completion stage | |||
| * | |||
| */ | |||
| private void blockAndReply(List<byte[]> manageConsensusCmds, List<ReplyContextMessage> replyList) { | |||
| // consensusBatchId = messageHandle.beginBatch(realmName); | |||
| // List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(manageConsensusCmds.size()); | |||
| // try { | |||
| // int msgId = 0; | |||
| // for (byte[] txContent : manageConsensusCmds) { | |||
| // AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, consensusBatchId); | |||
| // asyncFutureLinkedList.add(asyncFuture); | |||
| // } | |||
| // messageHandle.completeBatch(realmName, consensusBatchId); | |||
| // messageHandle.commitBatch(realmName, consensusBatchId); | |||
| // } catch (Exception e) { | |||
| // // todo 需要处理应答码 404 | |||
| // LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||
| // messageHandle.rollbackBatch(realmName, consensusBatchId, 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++; | |||
| // } | |||
| // }); | |||
| } | |||
| /** | |||
| * | |||
| * Used by consensus write phase, pre compute new block hash | |||
| * | |||
| */ | |||
| public BatchAppResultImpl preComputeAppHash(byte[][] commands) { | |||
| String batchId = messageHandle.beginBatch(realmName); | |||
| List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(manageConsensusCmds.size()); | |||
| List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | |||
| List<byte[]> responseLinkedList = new ArrayList<>(); | |||
| try { | |||
| int msgId = 0; | |||
| for (byte[] txContent : manageConsensusCmds) { | |||
| for (byte[] txContent : commands) { | |||
| AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||
| asyncFutureLinkedList.add(asyncFuture); | |||
| } | |||
| messageHandle.completeBatch(realmName, batchId); | |||
| messageHandle.commitBatch(realmName, batchId); | |||
| StateSnapshot stateSnapshot = messageHandle.completeBatch(realmName, batchId); | |||
| byte[] blockHashBytes = stateSnapshot.getSnapshot(); | |||
| for (int i = 0; i< asyncFutureLinkedList.size(); i++) { | |||
| responseLinkedList.add(asyncFutureLinkedList.get(i).get()); | |||
| } | |||
| return new BatchAppResultImpl(responseLinkedList, blockHashBytes, batchId); | |||
| } catch (Exception e) { | |||
| // todo 需要处理应答码 404 | |||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.CONSENSUS_ERROR.CODE); | |||
| LOGGER.error("Error occurred while processing ordered messages! --" + e.getMessage(), e); | |||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.IGNORED_BY_CONSENSUS_PHASE_PRECOMPUTE_ROLLBACK.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++; | |||
| } | |||
| }); | |||
| return null; | |||
| } | |||
| /** | |||
| * | |||
| * Consensus write phase will terminate, new block hash values are inconsistent, update batch messages execute state | |||
| * | |||
| */ | |||
| public List<byte[]> updateAppResponses(List<byte[]> asyncResponseLinkedList) { | |||
| List<byte[]> updatedResponses = new ArrayList<>(); | |||
| for(int i = 0; i < asyncResponseLinkedList.size(); i++) { | |||
| TransactionResponse txResponse = BinaryProtocol.decode(asyncResponseLinkedList.get(i)); | |||
| TxResponseMessage resp = new TxResponseMessage(txResponse.getContentHash()); | |||
| resp.setExecutionState(TransactionState.IGNORED_BY_CONSENSUS_PHASE_PRECOMPUTE_ROLLBACK); | |||
| updatedResponses.add(BinaryProtocol.encode(resp, TransactionResponse.class)); | |||
| } | |||
| return updatedResponses; | |||
| } | |||
| /** | |||
| * | |||
| * Decision has been made at the consensus stage, commit block | |||
| * | |||
| */ | |||
| public void preComputeAppCommit(String batchId) { | |||
| messageHandle.commitBatch(realmName, batchId); | |||
| } | |||
| /** | |||
| * | |||
| * Consensus write phase will terminate, new block hash values are inconsistent, rollback block | |||
| * | |||
| */ | |||
| public void preComputeAppRollback(String batchId) { | |||
| messageHandle.rollbackBatch(realmName, batchId, TransactionState.IGNORED_BY_CONSENSUS_PHASE_PRECOMPUTE_ROLLBACK.CODE); | |||
| LOGGER.debug("Rollback of operations that cause inconsistencies in the ledger"); | |||
| } | |||
| //notice | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>consensus</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>consensus-framework</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>consensus</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>consensus-mq</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>consensus</artifactId> | |||
| <packaging>pom</packaging> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>contract</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>contract-framework</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>contract</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>contract-jvm</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>contract</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>contract-maven-plugin</artifactId> | |||
| <packaging>maven-plugin</packaging> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <artifactId>contract</artifactId> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| @@ -0,0 +1,11 @@ | |||
| package com.jd.blockchain.contract; | |||
| @Contract | |||
| public interface RandomContract { | |||
| @ContractEvent(name = "random-put") | |||
| void put(String address, String key, String value); | |||
| @ContractEvent(name = "random-putAndGet") | |||
| String putAndGet(String address, String key, String value); | |||
| } | |||
| @@ -0,0 +1,43 @@ | |||
| package com.jd.blockchain.contract; | |||
| import com.jd.blockchain.crypto.HashDigest; | |||
| import java.util.Random; | |||
| public class RandomContractImpl implements RandomContract, EventProcessingAware { | |||
| private static final Random RANDOM_TIME = new Random(); | |||
| private ContractEventContext eventContext; | |||
| private HashDigest ledgerHash; | |||
| @Override | |||
| public void beforeEvent(ContractEventContext eventContext) { | |||
| this.eventContext = eventContext; | |||
| this.ledgerHash = eventContext.getCurrentLedgerHash(); | |||
| } | |||
| @Override | |||
| public void postEvent(ContractEventContext eventContext, Exception error) { | |||
| } | |||
| @Override | |||
| public void put(String address, String key, String value) { | |||
| String saveVal = value + "-" + RANDOM_TIME.nextInt(1024); | |||
| eventContext.getLedger().dataAccount(address).setText(key, saveVal, -1L); | |||
| } | |||
| @Override | |||
| public String putAndGet(String address, String key, String value) { | |||
| String saveVal = value + "-" + RANDOM_TIME.nextInt(1024); | |||
| eventContext.getLedger().dataAccount(address).setText(key, saveVal, -1L); | |||
| return address; | |||
| } | |||
| } | |||
| @@ -4,7 +4,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>contract</artifactId> | |||
| <packaging>pom</packaging> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>crypto</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>crypto-adv</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>crypto</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>crypto-classic</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>crypto</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>crypto-framework</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <artifactId>crypto</artifactId> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>crypto</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>crypto-sm</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>crypto</artifactId> | |||
| <packaging>pom</packaging> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>deployment</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>deployment-gateway</artifactId> | |||
| @@ -20,6 +20,7 @@ peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
| #若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 | |||
| #数据检索服务模块(Argus)需单独部署,若不部署其他功能仍可正常使用 | |||
| data.retrieval.url=http://127.0.0.1:10001 | |||
| schema.retrieval.url=http://192.168.151.40:8082 | |||
| #默认公钥的内容(Base58编码数据); | |||
| keys.default.pubkey= | |||
| @@ -5,5 +5,5 @@ GATEWAY=$(ls $HOME/lib | grep deployment-gateway-) | |||
| if [ ! -n "$GATEWAY" ]; then | |||
| echo "GateWay Is Null !!!" | |||
| else | |||
| nohup java -jar -server -Dgateway.log=$HOME $HOME/lib/$GATEWAY -c $HOME/config/gateway.conf $* >$HOME/bin/gw.out 2>&1 & | |||
| nohup java -jar -server -Djdchain.log=$HOME $HOME/lib/$GATEWAY -c $HOME/config/gateway.conf $* >$HOME/bin/gw.out 2>&1 & | |||
| fi | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>deployment</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>deployment-peer</artifactId> | |||
| @@ -25,11 +25,11 @@ | |||
| <artifactId>runtime-modular-booter</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <!--<dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ump-booter</artifactId> | |||
| <artifactId>manager-booter</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency>--> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>storage-composite</artifactId> | |||
| @@ -39,6 +39,18 @@ | |||
| <include>com.jd.blockchain:deployment-peer</include> | |||
| </includes> | |||
| </dependencySet> | |||
| <dependencySet> | |||
| <unpack>false</unpack> | |||
| <useProjectArtifact>true</useProjectArtifact> | |||
| <outputDirectory>manager</outputDirectory> | |||
| <includes> | |||
| <include>com.jd.blockchain:manager-booter</include> | |||
| <include>com.jd.blockchain:manager-model</include> | |||
| <include>com.jd.blockchain:manager-service</include> | |||
| <include>com.jd.blockchain:manager-web</include> | |||
| <include>com.jd.blockchain:ump-explorer</include> | |||
| </includes> | |||
| </dependencySet> | |||
| <dependencySet> | |||
| <unpack>false</unpack> | |||
| <useProjectArtifact>true</useProjectArtifact> | |||
| @@ -53,6 +65,11 @@ | |||
| <exclude>com.jd.blockchain:runtime-modular-booter</exclude> | |||
| <exclude>com.jd.blockchain:peer</exclude> | |||
| <exclude>com.jd.blockchain:deployment-peer</exclude> | |||
| <exclude>com.jd.blockchain:manager-booter</exclude> | |||
| <exclude>com.jd.blockchain:manager-model</exclude> | |||
| <exclude>com.jd.blockchain:manager-service</exclude> | |||
| <exclude>com.jd.blockchain:manager-web</exclude> | |||
| <exclude>com.jd.blockchain:ump-explorer</exclude> | |||
| </excludes> | |||
| </dependencySet> | |||
| @@ -5,5 +5,5 @@ boot_file=$(ls $HOME/libs | grep tools-initializer-booter-) | |||
| if [ ! -n "$boot_file" ]; then | |||
| echo "tools-initializer-booter is null" | |||
| else | |||
| java -jar -server -Dinit.log=$HOME $HOME/libs/$boot_file -l $HOME/config/init/local.conf -i $HOME/config/init/ledger.init $* | |||
| java -jar -server -Djdchain.log=$HOME $HOME/libs/$boot_file -l $HOME/config/init/local.conf -i $HOME/config/init/ledger.init $* | |||
| fi | |||
| @@ -4,13 +4,13 @@ | |||
| BOOT_HOME=$(cd `dirname $0`;cd ../; pwd) | |||
| #获取进程PID | |||
| PID=`ps -ef | grep $BOOT_HOME/ext/ump-booter | grep -v grep | awk '{print $2}'` | |||
| PID=`ps -ef | grep $BOOT_HOME/manager/manager-booter | grep -v grep | awk '{print $2}'` | |||
| #通过Kill命令将进程杀死 | |||
| if [ -z "$PID" ]; then | |||
| echo "Unable to find UMP PID. stop aborted." | |||
| echo "Unable to find JDChain Manager PID. stop aborted." | |||
| else | |||
| echo "Start to kill PID = $PID ..." | |||
| kill -9 $PID | |||
| echo "Unified Management Platform has been stopped ..." | |||
| echo "JDChain Manager has been stopped ..." | |||
| fi | |||
| @@ -1,9 +0,0 @@ | |||
| #!/bin/bash | |||
| HOME=$(cd `dirname $0`;cd ../; pwd) | |||
| UMP=$(ls $HOME/ext | grep ump-booter-) | |||
| if [ ! -n "UMP" ]; then | |||
| echo "Unified Management Platform Is Null !!!" | |||
| else | |||
| nohup java -jar -server -Djump.log=$HOME $HOME/ext/$UMP -p 8000 $* >$HOME/bin/jump.out 2>&1 & | |||
| fi | |||
| @@ -0,0 +1,9 @@ | |||
| #!/bin/bash | |||
| HOME=$(cd `dirname $0`;cd ../; pwd) | |||
| UMP=$(ls $HOME/manager | grep manager-booter-) | |||
| if [ ! -n "UMP" ]; then | |||
| echo "JDChain Manager Is Null !!!" | |||
| else | |||
| nohup java -jar -server -Djdchain.log=$HOME $HOME/manager/$UMP -home $HOME -p 8000 $* >$HOME/bin/jump.out 2>&1 & | |||
| fi | |||
| @@ -1,16 +0,0 @@ | |||
| #!/bin/bash | |||
| #启动Home路径 | |||
| BOOT_HOME=$(cd `dirname $0`;cd ../; pwd) | |||
| #获取进程PID | |||
| PID=`ps -ef | grep $BOOT_HOME/ext/ump-booter | grep -v grep | awk '{print $2}'` | |||
| #通过Kill命令将进程杀死 | |||
| if [ -z "$PID" ]; then | |||
| echo "Unable to find UMP PID. stop aborted." | |||
| else | |||
| echo "Start to kill PID = $PID ..." | |||
| kill -9 $PID | |||
| echo "Unified Management Platform has been stopped ..." | |||
| fi | |||
| @@ -5,5 +5,5 @@ PEER=$(ls $HOME/system | grep deployment-peer-) | |||
| if [ ! -n "$PEER" ]; then | |||
| echo "Peer Is Null !!!" | |||
| else | |||
| nohup java -jar -server -Xmx512m -Xms512m -Dpeer.log=$HOME $HOME/system/$PEER -home=$HOME -c $HOME/config/ledger-binding.conf -p 7080 $* >$HOME/bin/peer.out 2>&1 & | |||
| nohup java -jar -server -Xmx2g -Xms2g -Djdchain.log=$HOME $HOME/system/$PEER -home=$HOME -c $HOME/config/ledger-binding.conf -p 7080 $* >$HOME/bin/peer.out 2>&1 & | |||
| fi | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>deployment</artifactId> | |||
| <packaging>pom</packaging> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>gateway</artifactId> | |||
| @@ -45,7 +45,7 @@ | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>explorer</artifactId> | |||
| <artifactId>data-explorer</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| @@ -32,6 +32,7 @@ public class GatewayConfigProperties { | |||
| // 数据检索服务URL地址 | |||
| public static final String DATA_RETRIEVAL_URL="data.retrieval.url"; | |||
| public static final String SCHEMA_RETRIEVAL_URL="schema.retrieval.url"; | |||
| // 密钥相关配置项的键的前缀; | |||
| public static final String KEYS_PREFIX = "keys."; | |||
| @@ -54,6 +55,7 @@ public class GatewayConfigProperties { | |||
| private NetworkAddress masterPeerAddress; | |||
| private String dataRetrievalUrl; | |||
| private String schemaRetrievalUrl; | |||
| private KeysConfig keys = new KeysConfig(); | |||
| @@ -73,6 +75,14 @@ public class GatewayConfigProperties { | |||
| this.dataRetrievalUrl = dataRetrievalUrl; | |||
| } | |||
| public String getSchemaRetrievalUrl() { | |||
| return schemaRetrievalUrl; | |||
| } | |||
| public void setSchemaRetrievalUrl(String schemaRetrievalUrl) { | |||
| this.schemaRetrievalUrl = schemaRetrievalUrl; | |||
| } | |||
| public ProviderConfig providerConfig() { | |||
| return providerConfig; | |||
| } | |||
| @@ -120,6 +130,9 @@ public class GatewayConfigProperties { | |||
| String dataRetrievalUrl = getProperty(props, DATA_RETRIEVAL_URL, true); | |||
| configProps.dataRetrievalUrl = dataRetrievalUrl; | |||
| String schemaRetrievalUrl = getProperty(props, SCHEMA_RETRIEVAL_URL, true); | |||
| configProps.schemaRetrievalUrl = schemaRetrievalUrl; | |||
| String providers = getProperty(props, PEER_PROVIDERS, true); | |||
| if (providers == null || providers.length() <= 0) { | |||
| throw new IllegalArgumentException("Miss peer providers!"); | |||
| @@ -129,6 +129,7 @@ public class GatewayServerBooter { | |||
| ConsoleUtils.info("\r\n\r\nStart connecting to peer ...."); | |||
| BlockBrowserController blockBrowserController = appCtx.getBean(BlockBrowserController.class); | |||
| blockBrowserController.setDataRetrievalUrl(config.dataRetrievalUrl()); | |||
| blockBrowserController.setSchemaRetrievalUrl(config.getSchemaRetrievalUrl()); | |||
| PeerConnector peerConnector = appCtx.getBean(PeerConnector.class); | |||
| peerConnector.connect(config.masterPeerAddress(), defaultKeyPair, config.providerConfig().getProviders()); | |||
| ConsoleUtils.info("Peer[%s] is connected success!", config.masterPeerAddress().toString()); | |||
| @@ -18,4 +18,5 @@ package com.jd.blockchain.gateway.service; | |||
| public interface DataRetrievalService { | |||
| String retrieval(String url) throws Exception; | |||
| String retrievalPost(String url, String queryString) throws Exception; | |||
| } | |||
| @@ -8,6 +8,7 @@ | |||
| */ | |||
| package com.jd.blockchain.gateway.service; | |||
| import com.alibaba.fastjson.JSONObject; | |||
| import com.jd.blockchain.utils.http.agent.HttpClientPool; | |||
| import org.springframework.stereotype.Component; | |||
| @@ -24,4 +25,9 @@ public class DataRetrievalServiceHandler implements DataRetrievalService { | |||
| public String retrieval(String url) throws Exception { | |||
| return HttpClientPool.get(url); | |||
| } | |||
| @Override | |||
| public String retrievalPost(String url, String queryString) throws Exception { | |||
| return HttpClientPool.jsonPost(url,queryString); | |||
| } | |||
| } | |||
| @@ -56,6 +56,7 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
| private DataRetrievalService dataRetrievalService; | |||
| private String dataRetrievalUrl; | |||
| private String schemaRetrievalUrl; | |||
| private static final long BLOCK_MAX_DISPLAY = 3L; | |||
| @@ -511,6 +512,37 @@ public class BlockBrowserController implements BlockchainExtendQueryService { | |||
| return result; | |||
| } | |||
| /** | |||
| * querysql; | |||
| * @param request | |||
| * @return | |||
| */ | |||
| @RequestMapping(method = RequestMethod.POST, value = "schema/querysql") | |||
| public Object queryBySql(HttpServletRequest request,@RequestBody String queryString) { | |||
| String result; | |||
| if (schemaRetrievalUrl == null || schemaRetrievalUrl.length() <= 0) { | |||
| result = "{'message':'OK','data':'" + "schema.retrieval.url is empty" + "'}"; | |||
| } else { | |||
| String queryParams = request.getQueryString() == null ? "": request.getQueryString(); | |||
| String fullQueryUrl = new StringBuffer(schemaRetrievalUrl) | |||
| .append(request.getRequestURI()) | |||
| .append(BaseConstant.DELIMETER_QUESTION) | |||
| .append(queryParams) | |||
| .toString(); | |||
| try { | |||
| result = dataRetrievalService.retrievalPost(fullQueryUrl,queryString); | |||
| ConsoleUtils.info("request = {%s} \r\n result = {%s} \r\n", fullQueryUrl, result); | |||
| } catch (Exception e) { | |||
| result = "{'message':'error','data':'" + e.getMessage() + "'}"; | |||
| } | |||
| } | |||
| return result; | |||
| } | |||
| public void setSchemaRetrievalUrl(String schemaRetrievalUrl) { | |||
| this.schemaRetrievalUrl = schemaRetrievalUrl; | |||
| } | |||
| public void setDataRetrievalUrl(String dataRetrievalUrl) { | |||
| this.dataRetrievalUrl = dataRetrievalUrl; | |||
| } | |||
| @@ -19,6 +19,7 @@ peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
| #数据检索服务对应URL,格式:http://{ip}:{port},例如:http://127.0.0.1:10001 | |||
| #若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 | |||
| data.retrieval.url=http://127.0.0.1:10001 | |||
| schema.retrieval.url=http://192.168.151.39:8082 | |||
| #默认公钥的内容(Base58编码数据); | |||
| keys.default.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||
| @@ -13,12 +13,12 @@ | |||
| <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | |||
| </console> | |||
| <!--文件会打印出所有信息,这个log每次运行程序会自动清空,由append属性决定,适合临时测试用--> | |||
| <File name="log" fileName="${sys:gateway.log}/logs/gateway.temp.log" append="false"> | |||
| <File name="log" fileName="${sys:jdchain.log}/logs/gateway.temp.log" append="false"> | |||
| <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | |||
| </File> | |||
| <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> | |||
| <RollingFile name="GatewayRollingInfo" fileName="${sys:gateway.log}/logs/gateway.info.log" | |||
| filePattern="${sys:gateway.log}/logs/$${date:yyyy-MM}/gateway.info-%d{yyyy-MM-dd}-%i.log"> | |||
| <RollingFile name="GatewayRollingInfo" fileName="${sys:jdchain.log}/logs/gateway.info.log" | |||
| filePattern="${sys:jdchain.log}/logs/$${date:yyyy-MM}/gateway.info-%d{yyyy-MM-dd}-%i.log"> | |||
| <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | |||
| <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> | |||
| <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||
| @@ -27,8 +27,8 @@ | |||
| <SizeBasedTriggeringPolicy size="100 MB"/> | |||
| </Policies> | |||
| </RollingFile> | |||
| <RollingFile name="GatewayRollingWarn" fileName="${sys:gateway.log}/logs/gateway.warn.log" | |||
| filePattern="${sys:gateway.log}/logs/$${date:yyyy-MM}/gateway.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||
| <RollingFile name="GatewayRollingWarn" fileName="${sys:jdchain.log}/logs/gateway.warn.log" | |||
| filePattern="${sys:jdchain.log}/logs/$${date:yyyy-MM}/gateway.out.warn-%d{yyyy-MM-dd}-%i.log"> | |||
| <ThresholdFilter level="warn" onMatch="ACCEPT" onMismatch="DENY"/> | |||
| <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||
| <Policies> | |||
| @@ -38,8 +38,8 @@ | |||
| <!-- DefaultRolloverStrategy属性如不设置,则默认为最多同一文件夹下7个文件,这里设置了20 --> | |||
| <DefaultRolloverStrategy max="20"/> | |||
| </RollingFile> | |||
| <RollingFile name="GatewayRollingError" fileName="${sys:gateway.log}/logs/gateway.error.log" | |||
| filePattern="${sys:gateway.log}/logs/$${date:yyyy-MM}/gateway.error-%d{yyyy-MM-dd}-%i.log"> | |||
| <RollingFile name="GatewayRollingError" fileName="${sys:jdchain.log}/logs/gateway.error.log" | |||
| filePattern="${sys:jdchain.log}/logs/$${date:yyyy-MM}/gateway.error-%d{yyyy-MM-dd}-%i.log"> | |||
| <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||
| <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||
| <Policies> | |||
| @@ -23,19 +23,20 @@ public class GatewayConfigPropertiesTest { | |||
| 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()); | |||
| assertEquals("0.0.0.0", configProps.http().getHost()); | |||
| assertEquals(8081, configProps.http().getPort()); | |||
| assertNull(configProps.http().getContextPath()); | |||
| assertEquals("10.1.6.61", configProps.masterPeerAddress().getHost()); | |||
| assertEquals(7100, configProps.masterPeerAddress().getPort()); | |||
| assertEquals("127.0.0.1", configProps.masterPeerAddress().getHost()); | |||
| assertEquals(12000, 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()); | |||
| assertEquals("http://127.0.0.1:10001", configProps.dataRetrievalUrl()); | |||
| assertEquals("3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9", configProps.keys().getDefault().getPubKeyValue()); | |||
| assertNull(configProps.keys().getDefault().getPrivKeyPath()); | |||
| assertEquals("177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", configProps.keys().getDefault().getPrivKeyValue()); | |||
| assertEquals("DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY", configProps.keys().getDefault().getPrivKeyPassword()); | |||
| } catch (IOException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| @@ -1,29 +1,31 @@ | |||
| #网关的HTTP服务地址; | |||
| http.host=192.168.10.108 | |||
| http.host=0.0.0.0 | |||
| #网关的HTTP服务端口; | |||
| http.port=80 | |||
| http.port=8081 | |||
| #网关的HTTP服务上下文路径,可选; | |||
| #http.context-path= | |||
| #共识节点的服务地址; | |||
| peer.host=10.1.6.61 | |||
| #共识节点的服务端口; | |||
| peer.port=7100 | |||
| #共识节点的服务地址(与该网关节点连接的Peer节点的IP地址); | |||
| peer.host=127.0.0.1 | |||
| #共识节点的服务端口(与该网关节点连接的Peer节点的端口); | |||
| peer.port=12000 | |||
| #共识节点的服务是否启用安全证书; | |||
| peer.secure=true | |||
| #共识节点的服务提供解析器 | |||
| #BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
| #简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider | |||
| peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
| #数据检索服务对应URL | |||
| #数据检索服务对应URL,格式:http://{ip}:{port},例如:http://127.0.0.1:10001 | |||
| #若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 | |||
| data.retrieval.url=http://192.168.1.1:10001 | |||
| data.retrieval.url=http://127.0.0.1:10001 | |||
| schema.retrieval.url=http://192.168.151.39:8082 | |||
| #默认公钥的内容(Base58编码数据); | |||
| keys.default.pubkey=64hnNxUPcH4a8NxUPc | |||
| keys.default.pubkey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 | |||
| #默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; | |||
| keys.default.privkey-path=keys/default.priv | |||
| keys.default.privkey-path= | |||
| #默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; | |||
| keys.default.privkey=64hnH4a8n48LeEmNxUPcaZ1J | |||
| keys.default.privkey=177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x | |||
| #默认私钥的解码密码; | |||
| keys.default.privkey-password=a8n48LeEP | |||
| keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ledger</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>ledger-core</artifactId> | |||
| @@ -6,7 +6,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ledger</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>ledger-model</artifactId> | |||
| @@ -2,7 +2,6 @@ package com.jd.blockchain.ledger; | |||
| import com.jd.blockchain.crypto.PubKey; | |||
| import com.jd.blockchain.utils.net.NetworkAddress; | |||
| import org.omg.CORBA.PUBLIC_MEMBER; | |||
| /** | |||
| * 即将要注册的参与方的信息 | |||
| @@ -88,6 +88,13 @@ public enum TransactionState { | |||
| */ | |||
| IGNORED_BY_BLOCK_FULL_ROLLBACK((byte) 0x44), | |||
| /** | |||
| * | |||
| * 共识阶段加入新区块哈希预计算功能, 如果来自其他Peer的新区块哈希值不一致,本批次整体回滚 | |||
| * | |||
| */ | |||
| IGNORED_BY_CONSENSUS_PHASE_PRECOMPUTE_ROLLBACK((byte) 0x45), | |||
| /** | |||
| * 系统错误; | |||
| */ | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ledger</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>ledger-rpc</artifactId> | |||
| @@ -5,7 +5,7 @@ | |||
| <parent> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>jdchain-root</artifactId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <artifactId>ledger</artifactId> | |||
| <packaging>pom</packaging> | |||
| @@ -5,13 +5,13 @@ | |||
| <parent> | |||
| <artifactId>manager</artifactId> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>ump-booter</artifactId> | |||
| <artifactId>manager-booter</artifactId> | |||
| <name>ump-booter</name> | |||
| <name>manager-booter</name> | |||
| <properties> | |||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
| @@ -34,27 +34,22 @@ | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ump-web</artifactId> | |||
| <artifactId>manager-web</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ump-service</artifactId> | |||
| <artifactId>manager-service</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ump-model</artifactId> | |||
| <artifactId>manager-model</artifactId> | |||
| <version>${project.version}</version> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <artifactId>ump-explorer</artifactId> | |||
| </dependency> | |||
| <dependency> | |||
| <groupId>org.springframework.boot</groupId> | |||
| <artifactId>spring-boot-starter-log4j2</artifactId> | |||
| @@ -82,6 +77,33 @@ | |||
| </dependencies> | |||
| <build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-jar-plugin</artifactId> | |||
| <configuration> | |||
| <archive> | |||
| <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile> | |||
| <manifest> | |||
| <mainClass>com.jd.blockchain.ump.UmpBooter</mainClass> | |||
| <addClasspath>false</addClasspath> | |||
| <useUniqueVersions>false</useUniqueVersions> | |||
| </manifest> | |||
| </archive> | |||
| </configuration> | |||
| </plugin> | |||
| <plugin> | |||
| <groupId>org.apache.maven.plugins</groupId> | |||
| <artifactId>maven-deploy-plugin</artifactId> | |||
| <configuration> | |||
| <skip>true</skip> | |||
| </configuration> | |||
| </plugin> | |||
| </plugins> | |||
| </build> | |||
| <!--<build> | |||
| <plugins> | |||
| <plugin> | |||
| <groupId>org.springframework.boot</groupId> | |||
| @@ -128,5 +150,5 @@ | |||
| </resource> | |||
| </resources> | |||
| </build> | |||
| </build>--> | |||
| </project> | |||
| @@ -0,0 +1,322 @@ | |||
| package com.jd.blockchain.ump; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.lang.reflect.Method; | |||
| import java.net.MalformedURLException; | |||
| import java.net.URL; | |||
| import java.net.URLClassLoader; | |||
| import java.util.ArrayList; | |||
| import java.util.Collection; | |||
| import java.util.List; | |||
| import java.util.Properties; | |||
| public class UmpBooter { | |||
| private static final String ARG_HOME = "-home"; | |||
| private static final String ARG_PORT = "-p"; | |||
| private static final String ARG_HOST = "-h"; | |||
| private static final String PROTOCOL_FILE = "file:"; | |||
| private static final String TOOLS_JAR = "tools.jar"; | |||
| private static final String PATH_INNER_JARS = "/"; | |||
| private static final String CONFIG = "config.properties"; | |||
| private static final String CONFIG_APPLICATION = "application.properties"; | |||
| private static final String CONFIG_PROP_HOST = "server.host"; | |||
| private static final String CONFIG_PROP_HOST_DEFAULT = "0.0.0.0"; | |||
| private static final String CONFIG_PROP_PORT = "server.port"; | |||
| private static final String CONFIG_PROP_PORT_DEFAULT = "8080"; | |||
| private static final String CONFIG_PROP_DB_URL = "db.url"; | |||
| private static final String CONFIG_PROP_DB_URL_DEFAULT = "rocksdb://#project#/jumpdb"; | |||
| private static final String UMP_START_CLASS = "com.jd.blockchain.ump.UmpApplicationStarter"; | |||
| private static String HOME_DIR = null; | |||
| public static void main(String[] args) { | |||
| try { | |||
| // 设置相关参数 | |||
| Server server = server(args); | |||
| // 加载libs/manager下的jar包 | |||
| loadJars(); | |||
| // 启动Server | |||
| startServer(server); | |||
| System.out.println("JDChain Manager Server Start SUCCESS !!!"); | |||
| } catch (Exception e) { | |||
| System.err.println(e); | |||
| } | |||
| } | |||
| /** | |||
| * 启动Server | |||
| * | |||
| * @param server | |||
| * @throws Exception | |||
| */ | |||
| private static void startServer(Server server) throws Exception { | |||
| List<String> argList = new ArrayList<>(); | |||
| argList.add(String.format("--server.address=%s", server.host)); | |||
| argList.add(String.format("--server.port=%s", server.port)); | |||
| argList.add(String.format("--db.url=%s", server.dbUrl)); | |||
| String[] args = argList.toArray(new String[argList.size()]); | |||
| InputStream inputStream = UmpBooter.class.getResourceAsStream(PATH_INNER_JARS + CONFIG_APPLICATION); | |||
| if (inputStream == null) { | |||
| System.err.printf("File [%s]' inputStream is NULL !!! \r\n", CONFIG_APPLICATION); | |||
| } | |||
| Properties props = new Properties(); | |||
| try { | |||
| props.load(inputStream); | |||
| } catch (IOException e) { | |||
| System.err.println(e); | |||
| } | |||
| /** | |||
| * 通过ClassLoader调用如下方法 | |||
| * {@link UmpApplicationStarter#start(String[], Properties)} | |||
| * | |||
| */ | |||
| Class<?> applicationClass = Thread.currentThread().getContextClassLoader() | |||
| .loadClass(UMP_START_CLASS); | |||
| Method startMethod = applicationClass.getDeclaredMethod("start", String[].class, Properties.class); | |||
| startMethod.invoke(null, args, props); | |||
| } | |||
| /** | |||
| * 根据入参加载Server对象(配置) | |||
| * | |||
| * @param args | |||
| * @return | |||
| * @throws Exception | |||
| */ | |||
| private static Server server(String[] args) throws Exception { | |||
| Server defaultServer = serverFromConfig(); | |||
| if (args == null || args.length == 0) { | |||
| // 获取当前Class所在路径 | |||
| HOME_DIR = UmpBooter.class.getResource("").toURI().getPath(); | |||
| return defaultServer; | |||
| } | |||
| String host = null; | |||
| int port = 0; | |||
| // 读取参数列表 | |||
| for (int i = 0; i < args.length; i++) { | |||
| String arg = args[i]; | |||
| if (arg.equals(ARG_HOST)) { | |||
| host = args[i + 1]; | |||
| } else if (arg.equals(ARG_PORT)) { | |||
| port = Integer.parseInt(args[i + 1]); | |||
| } else if (arg.equals(ARG_HOME)) { | |||
| HOME_DIR = args[i + 1]; | |||
| } | |||
| } | |||
| // 参数列表中的数据不完整,则剩余部分数据从配置文件中获取 | |||
| if (host == null) { | |||
| host = defaultServer.host; | |||
| } | |||
| if (port == 0) { | |||
| port = defaultServer.port; | |||
| } | |||
| return new Server(host, port, defaultServer.dbUrl); | |||
| } | |||
| /** | |||
| * 配置文件中加载Server对象 | |||
| * | |||
| * @return | |||
| */ | |||
| private static Server serverFromConfig() { | |||
| try { | |||
| InputStream inputStream = UmpBooter.class.getResourceAsStream(PATH_INNER_JARS + CONFIG); | |||
| if (inputStream == null) { | |||
| System.err.printf("File [%s]' inputStream is NULL !!! \r\n", CONFIG); | |||
| } | |||
| Properties props = new Properties(); | |||
| props.load(inputStream); | |||
| String host = props.getProperty(CONFIG_PROP_HOST, CONFIG_PROP_HOST_DEFAULT); | |||
| int port = Integer.parseInt( | |||
| props.getProperty(CONFIG_PROP_PORT, CONFIG_PROP_PORT_DEFAULT)); | |||
| String dbUrl = props.getProperty(CONFIG_PROP_DB_URL, CONFIG_PROP_DB_URL_DEFAULT); | |||
| return new Server(host, port, dbUrl); | |||
| } catch (Exception e) { | |||
| throw new IllegalStateException(e); | |||
| } | |||
| } | |||
| /** | |||
| * 自定义ClassLoader加载Jar包 | |||
| * | |||
| */ | |||
| private static void loadJars() throws Exception { | |||
| // 获取两个路径下所有的正确的Jar包 | |||
| URL[] totalJars = totalURLs(); | |||
| // 自定义URLClassLoader | |||
| URLClassLoader totalClassLoader = new URLClassLoader(totalJars, | |||
| Thread.currentThread().getContextClassLoader().getParent()); | |||
| // 设置当前线程ClassLoader | |||
| Thread.currentThread().setContextClassLoader(totalClassLoader); | |||
| } | |||
| /** | |||
| * 获取指定路径下的所有Jar | |||
| * | |||
| * @return | |||
| */ | |||
| public static URL[] totalURLs() throws Exception { | |||
| List<URL> totalURLs = new ArrayList<>(); | |||
| totalURLs.addAll(libsPathURLs()); | |||
| totalURLs.addAll(managerPathURLs()); | |||
| URL toolsJarURL = toolsJarURL(); | |||
| if (toolsJarURL != null) { | |||
| totalURLs.add(toolsJarURL); | |||
| System.out.printf("Loaded tools.jar[%s]! \r\n", toolsJarURL); | |||
| } | |||
| URL[] totalURLArray = new URL[totalURLs.size()]; | |||
| return totalURLs.toArray(totalURLArray); | |||
| } | |||
| /** | |||
| * 加载JAVA_HOME下的tools.jar文件 | |||
| * | |||
| * @return | |||
| */ | |||
| public static URL toolsJarURL() throws Exception { | |||
| // tools.jar位于JAVA_HOME/....../lib/tools.jar | |||
| // 首先从classpath下进行加载 | |||
| String classPath = System.getProperty("java.class.path"); | |||
| String[] paths = classPath.split(":"); | |||
| for (String path : paths) { | |||
| if (path.endsWith("/" + TOOLS_JAR)) { | |||
| // 当前路径即为tools.jar所在路径 | |||
| return new URL(PROTOCOL_FILE + path); | |||
| } | |||
| } | |||
| // 获取其JAVA_HOME路径 | |||
| String javaHome = System.getenv("JAVA_HOME"); | |||
| if (javaHome != null && javaHome.length() > 0) { | |||
| String toolsJarPath = javaHome + File.separator + "lib" + File.separator + TOOLS_JAR; | |||
| File toolsJar = new File(toolsJarPath); | |||
| if (toolsJar.exists()) { | |||
| return new URL(PROTOCOL_FILE + toolsJarPath); | |||
| } | |||
| } | |||
| return null; | |||
| } | |||
| /** | |||
| * 获取libs目录下的相关Jar | |||
| * 排除JDChain项目中默认的其他booter对应的Jar包 | |||
| * | |||
| * @return | |||
| */ | |||
| public static List<URL> libsPathURLs() { | |||
| try { | |||
| File libsDir = new File(HOME_DIR + File.separator + "libs"); | |||
| File[] jars = libsDir.listFiles(f -> | |||
| f.getName().endsWith(".jar") && | |||
| f.isFile() && | |||
| !f.getName().contains("-booter-") && | |||
| !f.getName().contains("tools-initializer") | |||
| ); | |||
| List<URL> libsPathURLs = new ArrayList<>(); | |||
| if (jars != null && jars.length > 0) { | |||
| for (int i = 0; i < jars.length; i++) { | |||
| URL jarURL = jars[i].toURI().toURL(); | |||
| libsPathURLs.add(jarURL); | |||
| System.out.printf("Loaded libsPath Jar[%s]! \r\n", jarURL); | |||
| } | |||
| } | |||
| return libsPathURLs; | |||
| } catch (MalformedURLException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| } | |||
| } | |||
| /** | |||
| * 加载manager下的所有Jar | |||
| * | |||
| * @return | |||
| */ | |||
| public static List<URL> managerPathURLs() { | |||
| try { | |||
| File managerDir = new File(HOME_DIR + File.separator + "manager"); | |||
| File[] jars = managerDir.listFiles(f -> f.getName().endsWith(".jar") && f.isFile()); | |||
| List<URL> managerPathURLs = new ArrayList<>(); | |||
| if (jars != null && jars.length > 0) { | |||
| for (int i = 0; i < jars.length; i++) { | |||
| URL jarURL = jars[i].toURI().toURL(); | |||
| managerPathURLs.add(jarURL); | |||
| System.out.printf("Loaded ManagerPath Jar[%s]! \r\n", jarURL); | |||
| } | |||
| } | |||
| return managerPathURLs; | |||
| } catch (MalformedURLException e) { | |||
| throw new IllegalStateException(e.getMessage(), e); | |||
| } | |||
| } | |||
| private static class Server { | |||
| private String host; | |||
| private int port; | |||
| private String dbUrl; | |||
| public Server(String host, int port, String dbUrl) { | |||
| this.host = host; | |||
| this.port = port; | |||
| this.dbUrl = dbUrl; | |||
| } | |||
| public String getHost() { | |||
| return host; | |||
| } | |||
| public void setHost(String host) { | |||
| this.host = host; | |||
| } | |||
| public int getPort() { | |||
| return port; | |||
| } | |||
| public void setPort(int port) { | |||
| this.port = port; | |||
| } | |||
| public String getDbUrl() { | |||
| return dbUrl; | |||
| } | |||
| public void setDbUrl(String dbUrl) { | |||
| this.dbUrl = dbUrl; | |||
| } | |||
| } | |||
| } | |||
| @@ -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 | |||
| @@ -0,0 +1,8 @@ | |||
| server.tomcat.uri-encoding=utf-8 | |||
| spring.mvc.favicon.enabled=false | |||
| logging.config=classpath:log4j2-jump.xml | |||
| retrieval.schemaUrl=http://192.168.151.40:8082 | |||
| retrieval.taskUrl=http://192.168.151.40:10005 | |||
| @@ -5,4 +5,7 @@ server.host=0.0.0.0 | |||
| server.port=8080 | |||
| # 本地数据库存储位置 | |||
| db.url=rocksdb://#project#/jumpdb | |||
| db.url=rocksdb://#project#/jumpdb | |||
| schema.retrieval.url=http://192.168.151.40:8082 | |||
| task.retrieval.url=http://192.168.151.40:10005 | |||
| @@ -13,8 +13,8 @@ | |||
| <PatternLayout pattern="%d{HH:mm:ss.SSS} %-5level %class{36} %L %M - %msg%xEx%n"/> | |||
| </console> | |||
| <!-- 这个会打印出所有的info及以下级别的信息,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档--> | |||
| <RollingFile name="UmpRollingInfo" fileName="${sys:jump.log}/logs/ump.info.log" | |||
| filePattern="${sys:jump.log}/logs/$${date:yyyy-MM}/ump.info-%d{yyyy-MM-dd}-%i.log"> | |||
| <RollingFile name="UmpRollingInfo" fileName="${sys:jdchain.log}/logs/ump.info.log" | |||
| filePattern="${sys:jdchain.log}/logs/$${date:yyyy-MM}/ump.info-%d{yyyy-MM-dd}-%i.log"> | |||
| <!--控制台只输出level及以上级别的信息(onMatch),其他的直接拒绝(onMismatch)--> | |||
| <ThresholdFilter level="info" onMatch="ACCEPT" onMismatch="DENY"/> | |||
| <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||
| @@ -23,8 +23,8 @@ | |||
| <SizeBasedTriggeringPolicy size="100 MB"/> | |||
| </Policies> | |||
| </RollingFile> | |||
| <RollingFile name="UmpRollingError" fileName="${sys:jump.log}/logs/ump.error.log" | |||
| filePattern="${sys:jump.log}/logs/$${date:yyyy-MM}/ump.error-%d{yyyy-MM-dd}-%i.log"> | |||
| <RollingFile name="UmpRollingError" fileName="${sys:jdchain.log}/logs/ump.error.log" | |||
| filePattern="${sys:jdchain.log}/logs/$${date:yyyy-MM}/ump.error-%d{yyyy-MM-dd}-%i.log"> | |||
| <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/> | |||
| <PatternLayout pattern="[%d{HH:mm:ss:SSS}] [%p] - %l - %m%n"/> | |||
| <Policies> | |||
| @@ -5,13 +5,13 @@ | |||
| <parent> | |||
| <artifactId>manager</artifactId> | |||
| <groupId>com.jd.blockchain</groupId> | |||
| <version>1.1.0-SNAPSHOT</version> | |||
| <version>1.2.0-SNAPSHOT</version> | |||
| </parent> | |||
| <modelVersion>4.0.0</modelVersion> | |||
| <artifactId>ump-model</artifactId> | |||
| <artifactId>manager-model</artifactId> | |||
| <name>ump-model</name> | |||
| <name>manager-model</name> | |||
| <properties> | |||
| <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
| @@ -12,5 +12,9 @@ public interface DBConnection { | |||
| String get(String key); | |||
| <T> T get(String key, Class<T> type); | |||
| void delete(String key); | |||
| boolean exist(String dbUrl); | |||
| } | |||
| @@ -44,4 +44,15 @@ public class MemoryDBConnection implements DBConnection { | |||
| } | |||
| return false; | |||
| } | |||
| @Override | |||
| public <T> T get(String key, Class<T> type) { | |||
| String record = memory.get(key); | |||
| return JSON.parseObject(record,type); | |||
| } | |||
| @Override | |||
| public void delete(String key) { | |||
| memory.remove(key); | |||
| } | |||
| } | |||
| @@ -73,6 +73,32 @@ public class RocksDBConnection implements DBConnection { | |||
| return null; | |||
| } | |||
| @Override | |||
| public <T> T get(String key, Class<T> type) { | |||
| try { | |||
| byte[] value = this.rocksDB.get(key.getBytes(UTF_8)); | |||
| if (value != null && value.length > 0) { | |||
| String strObj = new String(value, UTF_8); | |||
| return JSON.parseObject(strObj,type); | |||
| } | |||
| } catch (Exception e) { | |||
| throw new IllegalStateException(e); | |||
| } | |||
| return null; | |||
| } | |||
| @Override | |||
| public void delete(String key) { | |||
| try { | |||
| byte[] value = this.rocksDB.get(key.getBytes(UTF_8)); | |||
| if (value != null && value.length > 0) { | |||
| this.rocksDB.delete(value); | |||
| } | |||
| } catch (Exception e) { | |||
| throw new IllegalStateException(e); | |||
| } | |||
| } | |||
| @Override | |||
| public boolean exist(String dbUrl) { | |||
| // 首先该dbUrl是Rocksdb | |||
| @@ -68,6 +68,16 @@ public class UmpDaoHandler implements UmpDao, CommandLineRunner, DBConnection { | |||
| return dbConnection.get(key); | |||
| } | |||
| @Override | |||
| public <T> T get(String key, Class<T> type) { | |||
| return dbConnection.get(key,type); | |||
| } | |||
| @Override | |||
| public void delete(String key) { | |||
| dbConnection.delete(key); | |||
| } | |||
| @Override | |||
| public boolean exist(String dbUrl) { | |||
| try { | |||
| @@ -0,0 +1,73 @@ | |||
| package com.jd.blockchain.ump.model; | |||
| /** | |||
| * @author zhaogw | |||
| * date 2019/7/10 15:31 | |||
| */ | |||
| public class ApiResult { | |||
| /** | |||
| * 错误码,对应{@link ErrorCode},表示一种错误类型 | |||
| * 如果是成功,则code为1 | |||
| */ | |||
| private int code; | |||
| /** | |||
| * 具体解释 | |||
| */ | |||
| private String message; | |||
| /** | |||
| * 返回的结果包装在value中,value可以是单个对象 | |||
| */ | |||
| private Object value; | |||
| public ApiResult (){ } | |||
| public ApiResult (Object obj){ | |||
| this.value = obj; | |||
| } | |||
| public ApiResult (int code, String message){ | |||
| this.code = code; | |||
| this.message = message; | |||
| } | |||
| public ApiResult(ErrorCode errorCode){ | |||
| this.code = errorCode.getCode(); | |||
| this.message = errorCode.getMsg(); | |||
| } | |||
| public ApiResult(ErrorCode errorCode, Object obj){ | |||
| this.code = errorCode.getCode(); | |||
| this.message = errorCode.getMsg(); | |||
| this.value = obj; | |||
| } | |||
| public ApiResult (int code, String message, Object value){ | |||
| this.code = code; | |||
| this.message = message; | |||
| this.value = value; | |||
| } | |||
| public int getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(int code) { | |||
| this.code = code; | |||
| } | |||
| public String getMessage() { | |||
| return message; | |||
| } | |||
| public void setMessage(String message) { | |||
| this.message = message; | |||
| } | |||
| public Object getValue() { | |||
| return value; | |||
| } | |||
| public void setValue(Object value) { | |||
| this.value = value; | |||
| } | |||
| } | |||
| @@ -0,0 +1,33 @@ | |||
| package com.jd.blockchain.ump.model; | |||
| /** | |||
| * @author zhaogw | |||
| * date 2019/7/10 15:32 | |||
| */ | |||
| public enum ErrorCode { | |||
| SUCCESS(1,"成功"), | |||
| NO_PERMISSION(2,"权限不足"), | |||
| SERVER_ERROR(3,"服务器异常"), | |||
| AUTH_ERROR(4,"认证失败"), | |||
| PARAMS_ERROR(5,"参数错误"), | |||
| JSON_PARSE_ERROR(6,"Json解析错误"), | |||
| ILLEAGAL_STRING(7,"非法字符串"), | |||
| GEN_KEY_INPUT_LACK(8,"缺少必要的输入参数:name/randomSeed/basePath/password"), | |||
| UNKNOW_ERROR(10000,"未知错误"); | |||
| private int code; | |||
| private String msg; | |||
| ErrorCode(int code,String msg){ | |||
| this.code = code; | |||
| this.msg = msg; | |||
| } | |||
| public int getCode() { | |||
| return code; | |||
| } | |||
| public String getMsg() { | |||
| return msg; | |||
| } | |||
| } | |||
| @@ -17,7 +17,7 @@ public class PartiNode { | |||
| private boolean isSecure; | |||
| public List<String> toConfigChars() { | |||
| public List<String> toConfigChars(List<String> partiRoleConfigs) { | |||
| List<String> configCharList = new ArrayList<>(); | |||
| @@ -25,6 +25,10 @@ public class PartiNode { | |||
| configCharList.add(formatConfig(UmpConstant.PARTINODE_PUBKEY_FORMAT, pubKey)); | |||
| if (partiRoleConfigs != null && !partiRoleConfigs.isEmpty()) { | |||
| configCharList.addAll(partiRoleConfigs); | |||
| } | |||
| configCharList.add(formatConfig(UmpConstant.PARTINODE_INIT_HOST_FORMAT, initHost)); | |||
| configCharList.add(formatConfig(UmpConstant.PARTINODE_INIT_PORT_FORMAT, initPort)); | |||
| @@ -139,13 +139,15 @@ public class PeerSharedConfigs { | |||
| } | |||
| } | |||
| public synchronized LedgerInitConfig ledgerInitConfig(String seed, String createTime) { | |||
| public synchronized LedgerInitConfig ledgerInitConfig(String seed, String createTime, | |||
| List<String> securityConfigs, List<String> partiRoleConfigs) { | |||
| if (ledgerInitConfig != null) { | |||
| return ledgerInitConfig; | |||
| } | |||
| // 处理该ledgerInitConfig | |||
| ledgerInitConfig = new LedgerInitConfig(seed, ledgerName, createTime, consensusProvider, waitNodeSize); | |||
| ledgerInitConfig = new LedgerInitConfig(seed, ledgerName, createTime, consensusProvider, waitNodeSize, | |||
| securityConfigs, partiRoleConfigs); | |||
| // 添加参与方 | |||
| for (int i = 0; i < sharedConfigs.size(); i++) { | |||
| @@ -32,6 +32,10 @@ public class UmpConstant { | |||
| public static final String PARTINODE_PUBKEY_FORMAT = PARTINODE_FORMAT + ".pubkey"; | |||
| public static final String PARTINODE_ROLES_FORMAT = PARTINODE_FORMAT + ".roles"; | |||
| public static final String PARTINODE_ROLES_POLICY_FORMAT = PARTINODE_FORMAT + ".roles-policy"; | |||
| public static final String PARTINODE_INIT_FORMAT = PARTINODE_FORMAT + ".initializer"; | |||
| public static final String PARTINODE_INIT_HOST_FORMAT = PARTINODE_INIT_FORMAT + ".host"; | |||
| @@ -48,14 +52,32 @@ public class UmpConstant { | |||
| public static final String CREATE_TIME_PREFIX = "created-time"; | |||
| public static final String SECURITY_PREFIX = "security"; | |||
| public static final String SECURITY_ROLES = SECURITY_PREFIX + ".roles"; | |||
| public static final String SECURITY_ROLES_PRIVILEGES_LEDGER_FORMAT = SECURITY_ROLES + ".%s.ledger-privileges"; | |||
| public static final String SECURITY_ROLES_PRIVILEGES_TX_FORMAT = SECURITY_ROLES + ".%s.tx-privileges"; | |||
| public static final String SECURITY_PARTI_PREFIX = "participant.default"; | |||
| public static final String SECURITY_PARTI_ROLES = SECURITY_PARTI_PREFIX + ".roles"; | |||
| public static final String SECURITY_PARTI_ROLES_POLICY = SECURITY_PARTI_PREFIX + ".roles-policy"; | |||
| public static final String CRYPTO_PREFIX = "crypto"; | |||
| public static final String CRYPTO_HASH_VERIFY = CRYPTO_PREFIX + ".verify-hash"; | |||
| public static final String CRYPTO_HASH_ALGORITHM = CRYPTO_PREFIX + ".hash-algorithm"; | |||
| public static final String CONSENSUS_PREFIX = "consensus"; | |||
| public static final String CONSENSUS_PROVIDER_PREFIX = CONSENSUS_PREFIX + ".service-provider"; | |||
| public static final String CONSENSUS_CONF_PREFIX = CONSENSUS_PREFIX + ".conf"; | |||
| public static final String CRYPTO_PREFIX = "crypto"; | |||
| public static final String CRYPTO_PROVIDERS_PREFIX = CRYPTO_PREFIX + ".service-providers"; | |||
| public static final String LOCAL_PREFIX = "local"; | |||
| @@ -84,7 +106,7 @@ public class UmpConstant { | |||
| public static final String PATH_LEDGER_INIT_BIN = PATH_BIN + File.separator + "ledger-init.sh"; | |||
| public static final String PATH_STARTUP_BIN = PATH_BIN + File.separator + "startup.sh"; | |||
| public static final String PATH_PEER_STARTUP_BIN = PATH_BIN + File.separator + "peer-startup.sh"; | |||
| public static final String PATH_LIBS = File.separator + "libs"; | |||
| @@ -101,4 +123,17 @@ public class UmpConstant { | |||
| public static final String PATH_LOCAL_CONFIG = PATH_CONFIG_INIT + File.separator + "local.conf"; | |||
| public static final String PATH_LEDGER_INIT_CONFIG = PATH_CONFIG_INIT + File.separator + "ledger.init"; | |||
| public static final String PEER_HOST_IP = "peerHostIp"; | |||
| public static final String INIT_PORT = "iPort"; | |||
| public static final String CONSENSUS_PORT = "cPort"; | |||
| public static final String DELIMETER_QUESTION = "?"; //逗号分隔符; | |||
| public static final String LEDGER_LIST = "ledger_list"; //the key that save all the ledger hash in the rocksdb; | |||
| public static final int MEMORY_MAP_MAX_COUNT=10000; | |||
| public static final int MEMORY_MAP_REMOVE_COUNT=50; | |||
| public static final String ALL_LEDGER="all_ledger"; | |||
| public static final String DELIMETER_MINUS = "-"; | |||
| public static final String SCHEMA_PREFIX = "schema_"; | |||
| public static final String SCHEMA_RETRIEVAL_URL = "retrieval.schemaUrl"; | |||
| public static final String TASK_RETRIEVAL_URL = "retrieval.taskUrl"; | |||
| } | |||
| @@ -22,18 +22,25 @@ public class LedgerInitConfig { | |||
| "com.jd.blockchain.crypto.service.classic.ClassicCryptoService, " + | |||
| "com.jd.blockchain.crypto.service.sm.SMCryptoService"; | |||
| List<String> securityConfigs = null; | |||
| List<String> partiRolesConfigs = null; | |||
| private List<PartiNode> partiNodes = new ArrayList<>(); | |||
| public LedgerInitConfig() { | |||
| } | |||
| public LedgerInitConfig(String seed, String name, String createTime, String consensusProvider, int nodeSize) { | |||
| public LedgerInitConfig(String seed, String name, String createTime, String consensusProvider, int nodeSize, | |||
| List<String> securityConfigs, List<String> partiRolesConfigs) { | |||
| this.seed = seed; | |||
| this.name = name; | |||
| this.createTime = createTime; | |||
| this.consensusProvider = consensusProvider; | |||
| this.nodeSize = nodeSize; | |||
| this.securityConfigs = securityConfigs; | |||
| this.partiRolesConfigs = partiRolesConfigs; | |||
| } | |||
| public List<String> toConfigChars(String consensusConf) { | |||
| @@ -54,8 +61,12 @@ public class LedgerInitConfig { | |||
| configChars.add(toConfigChars(UmpConstant.PARTINODE_COUNT, partiNodes.size())); | |||
| if (securityConfigs != null && !securityConfigs.isEmpty()) { | |||
| configChars.addAll(securityConfigs); | |||
| } | |||
| for (PartiNode partiNode : partiNodes) { | |||
| configChars.addAll(partiNode.toConfigChars()); | |||
| configChars.addAll(partiNode.toConfigChars(this.partiRolesConfigs)); | |||
| } | |||
| return configChars; | |||
| @@ -16,7 +16,7 @@ public class PeerLocalConfig extends PeerSharedConfig { | |||
| private String peerPath; | |||
| private String consensusConf = "bftsmart.config"; // 默认为bftsmart配置 | |||
| private String consensusConf = "bftsmart.default.config"; // 默认为bftsmart配置 | |||
| private String privKey; | |||
| @@ -0,0 +1,47 @@ | |||
| package com.jd.blockchain.ump.model.penetrate; | |||
| import java.util.List; | |||
| /** | |||
| * 数据账户信息 | |||
| * @author zhaogw | |||
| * date 2019/7/26 14:49 | |||
| */ | |||
| public class DataAccountSchema { | |||
| private String ledgerHash; | |||
| private String dataAccount; | |||
| private String memo; | |||
| private List<FieldSchema> fieldSchemaList; | |||
| public String getLedgerHash() { | |||
| return ledgerHash; | |||
| } | |||
| public void setLedgerHash(String ledgerHash) { | |||
| this.ledgerHash = ledgerHash; | |||
| } | |||
| public String getDataAccount() { | |||
| return dataAccount; | |||
| } | |||
| public void setDataAccount(String dataAccount) { | |||
| this.dataAccount = dataAccount; | |||
| } | |||
| public String getMemo() { | |||
| return memo; | |||
| } | |||
| public void setMemo(String memo) { | |||
| this.memo = memo; | |||
| } | |||
| public List<FieldSchema> getFieldSchemaList() { | |||
| return fieldSchemaList; | |||
| } | |||
| public void setFieldSchemaList(List<FieldSchema> fieldSchemaList) { | |||
| this.fieldSchemaList = fieldSchemaList; | |||
| } | |||
| } | |||
| @@ -0,0 +1,46 @@ | |||
| package com.jd.blockchain.ump.model.penetrate; | |||
| /** | |||
| * ump中记录的字段信息; | |||
| * @author zhaogw | |||
| * date 2019/7/26 14:50 | |||
| */ | |||
| public class FieldSchema { | |||
| private String code; | |||
| private String fieldType; | |||
| private boolean isPrimary; | |||
| //备注; | |||
| private String memo; | |||
| public String getCode() { | |||
| return code; | |||
| } | |||
| public void setCode(String code) { | |||
| this.code = code; | |||
| } | |||
| public String getFieldType() { | |||
| return fieldType; | |||
| } | |||
| public void setFieldType(String fieldType) { | |||
| this.fieldType = fieldType; | |||
| } | |||
| public boolean isPrimary() { | |||
| return isPrimary; | |||
| } | |||
| public void setPrimary(boolean primary) { | |||
| isPrimary = primary; | |||
| } | |||
| public String getMemo() { | |||
| return memo; | |||
| } | |||
| public void setMemo(String memo) { | |||
| this.memo = memo; | |||
| } | |||
| } | |||
| @@ -0,0 +1,79 @@ | |||
| package com.jd.blockchain.ump.model.penetrate; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import java.util.List; | |||
| /** | |||
| * mediator's domain; | |||
| * @author zhaogw | |||
| * date 2019/7/2 18:02 | |||
| */ | |||
| public class LeaderDomain { | |||
| @JSONField(name="host") | |||
| private String host; | |||
| @JSONField(name="port") | |||
| private String port; | |||
| @JSONField(name="createTime") | |||
| private String createTime; | |||
| @JSONField(name="ledgerSeed") | |||
| private String ledgerSeed; | |||
| @JSONField(name="peerDomainList") | |||
| private List<PeerDomain> peerDomainList; | |||
| @JSONField(name="ledgerHash") | |||
| private String ledgerHash; | |||
| public List<PeerDomain> getPeerDomainList() { | |||
| return peerDomainList; | |||
| } | |||
| public void setPeerDomainList(List<PeerDomain> peerDomainList) { | |||
| this.peerDomainList = peerDomainList; | |||
| } | |||
| public String getCreateTime() { | |||
| return createTime; | |||
| } | |||
| public void setCreateTime(String createTime) { | |||
| this.createTime = createTime; | |||
| } | |||
| public String getLedgerSeed() { | |||
| return ledgerSeed; | |||
| } | |||
| public void setLedgerSeed(String ledgerSeed) { | |||
| this.ledgerSeed = ledgerSeed; | |||
| } | |||
| public String getHost() { | |||
| return host; | |||
| } | |||
| public void setHost(String host) { | |||
| this.host = host; | |||
| } | |||
| public String getPort() { | |||
| return port; | |||
| } | |||
| public void setPort(String port) { | |||
| this.port = port; | |||
| } | |||
| public String getLedgerHash() { | |||
| return ledgerHash; | |||
| } | |||
| public void setLedgerHash(String ledgerHash) { | |||
| this.ledgerHash = ledgerHash; | |||
| } | |||
| } | |||
| @@ -0,0 +1,177 @@ | |||
| package com.jd.blockchain.ump.model.penetrate; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| /** | |||
| * @author zhaogw | |||
| * date 2019/7/2 10:10 | |||
| */ | |||
| public class PeerDomain { | |||
| @JSONField(name="peerId") | |||
| private int peerId; | |||
| private String basePath; | |||
| @JSONField(name="peerName") | |||
| private String peerName; | |||
| @JSONField(name="host") | |||
| private String host; | |||
| private String serverPort; | |||
| @JSONField(name="initPort") | |||
| private String initPort; | |||
| @JSONField(name="consensusPort") | |||
| private String consensusPort; | |||
| @JSONField(name="visitPort") | |||
| private String visitPort; | |||
| @JSONField(name="mediatorUrl") | |||
| private String mediatorUrl; | |||
| private String dbUri; | |||
| private boolean gatewayBindPeer; | |||
| //the random String from front input by mouse device; | |||
| private String randomSeed; | |||
| @JSONField(name="peerPubKey") | |||
| private String peerPubKey; | |||
| private String peerPrivKey; | |||
| private String peerRawPasswd; | |||
| private String peerPasswd; | |||
| public int getPeerId() { | |||
| return peerId; | |||
| } | |||
| public void setPeerId(int peerId) { | |||
| this.peerId = peerId; | |||
| } | |||
| public String getBasePath() { | |||
| return basePath; | |||
| } | |||
| public void setBasePath(String basePath) { | |||
| this.basePath = basePath; | |||
| } | |||
| public String getPeerName() { | |||
| return peerName; | |||
| } | |||
| public void setPeerName(String peerName) { | |||
| this.peerName = peerName; | |||
| } | |||
| public String getHost() { | |||
| return host; | |||
| } | |||
| public void setHost(String host) { | |||
| this.host = host; | |||
| } | |||
| public String getServerPort() { | |||
| return serverPort; | |||
| } | |||
| public void setServerPort(String serverPort) { | |||
| this.serverPort = serverPort; | |||
| } | |||
| public String getInitPort() { | |||
| return initPort; | |||
| } | |||
| public void setInitPort(String initPort) { | |||
| this.initPort = initPort; | |||
| } | |||
| public String getConsensusPort() { | |||
| return consensusPort; | |||
| } | |||
| public void setConsensusPort(String consensusPort) { | |||
| this.consensusPort = consensusPort; | |||
| } | |||
| public String getVisitPort() { | |||
| return visitPort; | |||
| } | |||
| public void setVisitPort(String visitPort) { | |||
| this.visitPort = visitPort; | |||
| } | |||
| public String getMediatorUrl() { | |||
| return mediatorUrl; | |||
| } | |||
| public void setMediatorUrl(String mediatorUrl) { | |||
| this.mediatorUrl = mediatorUrl; | |||
| } | |||
| public String getDbUri() { | |||
| return dbUri; | |||
| } | |||
| public void setDbUri(String dbUri) { | |||
| this.dbUri = dbUri; | |||
| } | |||
| public boolean isGatewayBindPeer() { | |||
| return gatewayBindPeer; | |||
| } | |||
| public void setGatewayBindPeer(boolean gatewayBindPeer) { | |||
| this.gatewayBindPeer = gatewayBindPeer; | |||
| } | |||
| public String getRandomSeed() { | |||
| return randomSeed; | |||
| } | |||
| public void setRandomSeed(String randomSeed) { | |||
| this.randomSeed = randomSeed; | |||
| } | |||
| public String getPeerPubKey() { | |||
| return peerPubKey; | |||
| } | |||
| public void setPeerPubKey(String peerPubKey) { | |||
| this.peerPubKey = peerPubKey; | |||
| } | |||
| public String getPeerPrivKey() { | |||
| return peerPrivKey; | |||
| } | |||
| public void setPeerPrivKey(String peerPrivKey) { | |||
| this.peerPrivKey = peerPrivKey; | |||
| } | |||
| public String getPeerRawPasswd() { | |||
| return peerRawPasswd; | |||
| } | |||
| public void setPeerRawPasswd(String peerRawPasswd) { | |||
| this.peerRawPasswd = peerRawPasswd; | |||
| } | |||
| public String getPeerPasswd() { | |||
| return peerPasswd; | |||
| } | |||
| public void setPeerPasswd(String peerPasswd) { | |||
| this.peerPasswd = peerPasswd; | |||
| } | |||
| } | |||
| @@ -0,0 +1,90 @@ | |||
| package com.jd.blockchain.ump.model.penetrate; | |||
| import com.alibaba.fastjson.annotation.JSONField; | |||
| import com.jd.blockchain.ump.model.UmpConstant; | |||
| import org.springframework.util.CollectionUtils; | |||
| import java.util.List; | |||
| /** | |||
| * @author zhaogw | |||
| * date 2019/7/19 11:33 | |||
| */ | |||
| public class SchemaDomain { | |||
| @JSONField(serialize = false) | |||
| private String schemaId; | |||
| @JSONField(serialize = false) | |||
| private String schemaAllId; | |||
| @JSONField(name="ledger") | |||
| private String ledgerHash; | |||
| @JSONField(name="associate_account") | |||
| private String dataAccount; | |||
| @JSONField(serialize = false) | |||
| private List<FieldSchema> fieldSchemaList; | |||
| private String content; | |||
| public String getSchemaId() { | |||
| return schemaId; | |||
| } | |||
| public void setSchemaId(String schemaId) { | |||
| this.schemaId = schemaId; | |||
| } | |||
| public String getLedgerHash() { | |||
| return ledgerHash; | |||
| } | |||
| public void setLedgerHash(String ledgerHash) { | |||
| this.ledgerHash = ledgerHash; | |||
| } | |||
| public String getDataAccount() { | |||
| return dataAccount; | |||
| } | |||
| public void setDataAccount(String dataAccount) { | |||
| this.dataAccount = dataAccount; | |||
| } | |||
| public String getContent() { | |||
| if(CollectionUtils.isEmpty(fieldSchemaList)){ | |||
| throw new IllegalStateException("content is empty! you must choose the field first!"); | |||
| } | |||
| StringBuffer stringBuffer = new StringBuffer(); | |||
| stringBuffer.append("type "+this.schemaId).append("{").append(" "); | |||
| for(FieldSchema fieldSchema : fieldSchemaList){ | |||
| if(fieldSchema.isPrimary()){ | |||
| stringBuffer.append(fieldSchema.getCode()+"(isPrimaryKey: Boolean = true):"+fieldSchema.getFieldType()).append(" "); | |||
| }else { | |||
| stringBuffer.append(fieldSchema.getCode()+":"+fieldSchema.getFieldType()).append(" "); | |||
| } | |||
| } | |||
| stringBuffer.append("}"); | |||
| return stringBuffer.toString(); | |||
| } | |||
| public void setContent(String content) { | |||
| this.content = content; | |||
| } | |||
| public List<FieldSchema> getFieldSchemaList() { | |||
| return fieldSchemaList; | |||
| } | |||
| public void setFieldSchemaList(List<FieldSchema> fieldSchemaList) { | |||
| this.fieldSchemaList = fieldSchemaList; | |||
| } | |||
| public String getSchemaAllId() { | |||
| this.schemaAllId = schemaId+ UmpConstant.DELIMETER_MINUS+ledgerHash.substring(0,6)+ | |||
| UmpConstant.DELIMETER_MINUS+ dataAccount.substring(0,6); | |||
| return schemaAllId; | |||
| } | |||
| } | |||
| @@ -0,0 +1,70 @@ | |||
| package com.jd.blockchain.ump.model.penetrate.store; | |||
| import com.jd.blockchain.ump.model.UmpConstant; | |||
| import java.util.concurrent.BlockingQueue; | |||
| import java.util.concurrent.LinkedBlockingQueue; | |||
| /** | |||
| * 通过枚举的方式来实现内存队列;避免大并发时引起的不必要的多次实例化; | |||
| * | |||
| * @author zhaoguangwei | |||
| * | |||
| */ | |||
| public enum MemQueue { | |||
| instance; | |||
| private BlockingQueue<String> queue = null; | |||
| private MemQueue() { | |||
| queue = new LinkedBlockingQueue<String>(); | |||
| } | |||
| /** | |||
| * 记录放入内存队列; | |||
| * @param key | |||
| * @return | |||
| */ | |||
| public boolean put(String key) { | |||
| boolean rtn = false; | |||
| try { | |||
| while (queue.size() >= UmpConstant.MEMORY_MAP_MAX_COUNT) { | |||
| queue.remove(); | |||
| } | |||
| queue.add(key); | |||
| rtn = true; | |||
| } catch (Exception e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return rtn; | |||
| } | |||
| /** | |||
| * 从内存队列取出一条记录; | |||
| * | |||
| * @return | |||
| */ | |||
| public String get() { | |||
| String record = null; | |||
| try { | |||
| record = queue.take(); | |||
| } catch (InterruptedException e) { | |||
| e.printStackTrace(); | |||
| } | |||
| return record; | |||
| } | |||
| /** | |||
| * 内存队列清除; | |||
| */ | |||
| public void clear() { | |||
| queue.clear(); | |||
| } | |||
| /** | |||
| * 获得记录数; | |||
| * | |||
| * @return | |||
| */ | |||
| public int size() { | |||
| return queue.size(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,49 @@ | |||
| package com.jd.blockchain.ump.model.penetrate.store; | |||
| import com.jd.blockchain.ump.model.UmpConstant; | |||
| import java.util.Map; | |||
| import java.util.concurrent.ConcurrentHashMap; | |||
| import static com.jd.blockchain.ump.model.UmpConstant.MEMORY_MAP_MAX_COUNT; | |||
| import static com.jd.blockchain.ump.model.UmpConstant.MEMORY_MAP_REMOVE_COUNT; | |||
| /** | |||
| * @author zhaogw | |||
| * date 2019/7/17 17:10 | |||
| */ | |||
| public enum MemStore { | |||
| instance; | |||
| private Map<String,Object> records = null; | |||
| MemStore(){ | |||
| records = new ConcurrentHashMap(); | |||
| } | |||
| public Object get(String key){ | |||
| return records.get(key); | |||
| } | |||
| public Object remove(String key){ | |||
| return records.remove(key); | |||
| } | |||
| public boolean put(String key, Object obj){ | |||
| boolean rtn = false; | |||
| MemQueue.instance.put(key); | |||
| if(records.size()>MEMORY_MAP_MAX_COUNT){ | |||
| //clear 50 records; | |||
| for(int i=0; i< MEMORY_MAP_REMOVE_COUNT; i++){ | |||
| String _key = MemQueue.instance.get(); | |||
| if(_key.equals(UmpConstant.ALL_LEDGER)){ | |||
| //don't remove the all_ledger; | |||
| continue; | |||
| } | |||
| records.remove(_key); | |||
| } | |||
| } | |||
| records.put(key,obj); | |||
| return true; | |||
| } | |||
| } | |||