* feature/1.4.0-SNAPSHOT: (82 commits) completed the integration of core, framework and test module with version 1.4.0-SNAPSHOT; created 1.4.0-SNAPSHOT version; publish 1.3.0.RELEASE; add the part 10; udpate; update; update; update event update for jira-446; update core update modify the code_example.MD; Update code_example.MD update; fix jira 444, modify active participant sdk update; update explorer; upate relation; event sample and doc update ... # Conflicts: # libs/bft-smarttags/1.4.0^2
@@ -1 +1 @@ | |||
Subproject commit a0eadf0f73e8bcafc6a356741d60f07f892dcaec | |||
Subproject commit 74fe2d114ebdaeb163bca256ca555aa83bc27f3d |
@@ -5,10 +5,10 @@ | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>deploy-root</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.4.0-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>deploy-gateway</artifactId> | |||
<dependencies> | |||
<dependency> | |||
@@ -45,7 +45,7 @@ | |||
<plugin> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-maven-plugin</artifactId> | |||
<!-- <executions> <execution> <goals> <goal>repackage</goal> </goals> | |||
<!-- <executions> <execution> <goals> <goal>repackage</goal> </goals> | |||
</execution> </executions> --> | |||
</plugin> | |||
<plugin> | |||
@@ -91,4 +91,4 @@ | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> | |||
</project> |
@@ -1,16 +1,25 @@ | |||
#网关的HTTP服务地址,建议直接使用0.0.0.0; | |||
#网关的HTTP服务地址; | |||
http.host=0.0.0.0 | |||
#网关的HTTP服务端口; | |||
http.port=8081 | |||
http.port=8080 | |||
#网关的HTTP服务上下文路径,可选; | |||
#http.context-path= | |||
#对端Peer节点的数量 | |||
peer.size=2 | |||
#共识节点的服务地址(与该网关节点连接的Peer节点的IP地址); | |||
peer.host=127.0.0.1 | |||
#共识节点的服务端口(与该网关节点连接的Peer节点的端口); | |||
peer.port=7080 | |||
peer.0.host=127.0.0.1 | |||
#共识节点的服务端口(与该网关节点连接的Peer节点的端口,即在Peer节点的peer-startup.sh中定义的端口); | |||
peer.0.port=7080 | |||
#共识节点的服务是否启用安全证书; | |||
peer.secure=false | |||
peer.0.secure=false | |||
#共识节点的服务地址(与该网关节点连接的Peer节点的IP地址); | |||
peer.1.host=127.0.0.1 | |||
#共识节点的服务端口(与该网关节点连接的Peer节点的端口,即在Peer节点的peer-startup.sh中定义的端口); | |||
peer.1.port=7081 | |||
#共识节点的服务是否启用安全证书; | |||
peer.1.secure=false | |||
#共识节点的服务提供解析器 | |||
#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider | |||
@@ -18,7 +27,6 @@ peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
#数据检索服务对应URL,格式:http://{ip}:{port},例如:http://127.0.0.1:10001 | |||
#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 | |||
#数据检索服务模块(Argus)需单独部署,若不部署其他功能仍可正常使用 | |||
data.retrieval.url=http://127.0.0.1:10001 | |||
schema.retrieval.url=http://127.0.0.1:8082 | |||
@@ -29,4 +37,4 @@ keys.default.privkey-path= | |||
#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey= | |||
#默认私钥的解码密码; | |||
keys.default.privkey-password= | |||
keys.default.privkey-password= |
@@ -2132,3 +2132,488 @@ http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/contracts | |||
|address.value|合约地址| | |||
|pubKey.value|合约公钥| | |||
|rootHash|合约根Hash| | |||
## 9 用户自定义事件 | |||
### 9.1 获取事件账户列表 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts?fromIndex={start_index}&count={count} | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|query|start_index|否|查询的起始序号,默认为0|数字| | |||
|query|count|否|查询返回事件账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts?fromIndex=0&count=-1 | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data":[ | |||
{ | |||
"address":{ | |||
"value":"LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1" | |||
}, | |||
"pubKey":{ | |||
"value":"7VeRBi3xDfT1E11vFs9q5Q9gFo23RR7SoobPzivqxw9Uubzq" | |||
} | |||
} | |||
], | |||
"success":true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|address.value|账户地址| | |||
|pubKey.value|账户公钥| | |||
### 9.2 获取事件账户 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/{address} | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|address|账户地址|是|事件账户地址|字符串| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1 | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data":[ | |||
{ | |||
"address":{ | |||
"value":"LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1" | |||
}, | |||
"pubKey":{ | |||
"value":"7VeRBi3xDfT1E11vFs9q5Q9gFo23RR7SoobPzivqxw9Uubzq" | |||
} | |||
} | |||
], | |||
"success":true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|address.value|账户地址| | |||
|pubKey.value|账户公钥| | |||
### 9.3 获取事件账户总数 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/count | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/count | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": 1, | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|data|事件账户数量| | |||
### 9.4 获取事件名数量 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/{address}/names/count | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|path|address|是|事件账户地址|字符串| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1/names/count | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": 2, | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|data|事件名数量| | |||
### 9.5 获取事件名列表 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/{address}/names?fromIndex={start_index}&count={count} | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|path|address|是|事件账户地址|字符串| | |||
|query|start_index|否|查询的起始序号,默认为0|数字| | |||
|query|count|否|查询返回事件账户的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1/names?fromIndex=0&count=100 | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": ["test1", "test2"], | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|data|事件名数量数组| | |||
### 9.6 获取最新事件 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/{address}/names/{event_name}/latest | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|path|address|是|事件账户地址|字符串| | |||
|path|event_name|是|事件名|字符串| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1/names/test1/latest | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": { | |||
"sequence": 0, | |||
"transactionSource": { | |||
"value": "j5rENX3rsdEgi5toeNUUv7ycUUivjNxAUb9Fme6oLCU851" | |||
}, | |||
"blockHeight": 10, | |||
"contractSource": "", | |||
"eventAccount": { | |||
"value": "LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1" | |||
}, | |||
"name": "test1", | |||
"content": { | |||
"nil": false, | |||
"bytes": { | |||
"value": "Ctt6Eur" | |||
}, | |||
"type": "TEXT", | |||
"value": "imuge" | |||
} | |||
}, | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|sequence|事件序列| | |||
|transactionSource.value|交易哈希| | |||
|blockHeight|时间产生区块高度| | |||
|contractSource|合约地址| | |||
|eventAccount.value|事件账户地址| | |||
|name|事件名| | |||
|content.nil|事件内容是否为空| | |||
|content.bytes.value|事件内容字节| | |||
|content.type|事件内容类型| | |||
|content.value|事件内容| | |||
### 9.7 获取事件数量 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/{address}/names/{event_name}/count | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|path|address|是|事件账户地址|字符串| | |||
|path|event_name|是|事件名|字符串| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1/names/test1/count | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": 1, | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|data|事件数量| | |||
### 9.8 获取事件列表 | |||
```http | |||
GET /ledgers/{ledger}/events/user/accounts/{address}/names/{event_name}?fromSequence={from_sequence}&count={count} | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型| | |||
|---|---|---|---|---| | |||
|path|ledger|是|账本哈希|字符串| | |||
|path|address|是|事件账户地址|字符串| | |||
|path|event_name|是|事件名|字符串| | |||
|query|from_sequence|否|查询的起始序号,默认为0|数字| | |||
|query|count|否|查询返回事件的数量,默认最大返回值为100,小于0或大于100均返回最大可返回结果集|数字| | |||
#### 请求实例 | |||
```http | |||
http://localhost/ledgers/657TQAw6ssVoeKniWGwbovk7njvCTvikPambM9eBv6ezs/events/user/accounts/LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1/names/test1?fromSequenct=0&count=100 | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": [{ | |||
"sequence": 0, | |||
"transactionSource": { | |||
"value": "j5rENX3rsdEgi5toeNUUv7ycUUivjNxAUb9Fme6oLCU851" | |||
}, | |||
"blockHeight": 10, | |||
"contractSource": "", | |||
"eventAccount": { | |||
"value": "LdeP1yuk8Medq3Sph5ur9y1yE6nJ71XRVPPx1" | |||
}, | |||
"name": "test1", | |||
"content": { | |||
"nil": false, | |||
"bytes": { | |||
"value": "Ctt6Eur" | |||
}, | |||
"type": "TEXT", | |||
"value": "imuge" | |||
} | |||
}], | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|data|事件列表| | |||
|sequence|事件序列| | |||
|transactionSource.value|交易哈希| | |||
|blockHeight|时间产生区块高度| | |||
|contractSource|合约地址| | |||
|eventAccount.value|事件账户地址| | |||
|name|事件名| | |||
|content.nil|事件内容是否为空| | |||
|content.bytes.value|事件内容字节| | |||
|content.type|事件内容类型| | |||
|content.value|事件内容| | |||
## 10.权限对外提供的API接口使用 | |||
### 10.1根据角色获取权限信息 | |||
```http | |||
GET /ledgers/{ledgerHash}/authorization/role/{roleName} | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型 | |||
|---|---|---|---|---| | |||
|get|ledger|是|账本哈希|字符串 | |||
|--|roleName|是|角色名|字符串 | |||
#### 请求实例 | |||
```http | |||
http://localhost:11000/ledgers/j5pSJLyVpS8QG2wL95fiDWHHnweh2YdqNhgmnb64SBMjUh/authorization/role/DEFAULT | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": { | |||
"roleName": "DEFAULT", | |||
"transactionPrivilege": { | |||
"privilege": [ | |||
"DIRECT_OPERATION", | |||
"CONTRACT_OPERATION" | |||
], | |||
"permissionCount": 2 | |||
}, | |||
"ledgerPrivilege": { | |||
"privilege": [ | |||
"CONFIGURE_ROLES", | |||
"AUTHORIZE_USER_ROLES", | |||
"SET_CONSENSUS", | |||
"SET_CRYPTO", | |||
"REGISTER_PARTICIPANT", | |||
"REGISTER_USER", | |||
"REGISTER_DATA_ACCOUNT", | |||
"REGISTER_CONTRACT", | |||
"UPGRADE_CONTRACT", | |||
"SET_USER_ATTRIBUTES", | |||
"WRITE_DATA_ACCOUNT", | |||
"APPROVE_TX", | |||
"CONSENSUS_TX", | |||
"REGISTER_EVENT_ACCOUNT", | |||
"WRITE_EVENT_ACCOUNT" | |||
], | |||
"permissionCount": 15 | |||
}, | |||
"version": 0 | |||
}, | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|roleName|角色名称| | |||
|transactionPrivilege|交易权限| | |||
|transactionPrivilege -> privilege|交易权限->权限类别| | |||
|transactionPrivilege -> permissionCount|交易权限->权限总数| | |||
|ledgerPrivilege|账本权限| | |||
|ledgerPrivilege -> privilege|账本权限->权限类别| | |||
|ledgerPrivilege -> permissionCount|账本权限->权限总数| | |||
### 10.2根据用户获取权限信息 | |||
```http | |||
GET /ledgers/{ledgerHash}/authorization/user/{userAddress} | |||
``` | |||
#### 参数 | |||
|请求类型|名称|是否必需|说明|数据类型 | |||
|---|---|---|---|---| | |||
|get|ledger|是|账本哈希|字符串 | |||
|--|userAddress|是|用户地址|字符串 | |||
#### 请求实例 | |||
```http | |||
http://localhost:11000/ledgers/j5pSJLyVpS8QG2wL95fiDWHHnweh2YdqNhgmnb64SBMjUh/authorization/user/LdeNwH71wxtbf1UM8ExRG8qbPnu17MdnRSVva | |||
``` | |||
#### 返回实例 | |||
```json | |||
{ | |||
"data": { | |||
"userAddress": { | |||
"value": "LdeNwH71wxtbf1UM8ExRG8qbPnu17MdnRSVva" | |||
}, | |||
"transactionPrivilegesBitset": { | |||
"privilege": [ | |||
"DIRECT_OPERATION" | |||
], | |||
"permissionCount": 1 | |||
}, | |||
"userRole": [ | |||
"MANAGER1", | |||
"MANAGER0" | |||
], | |||
"ledgerPrivilegesBitset": { | |||
"privilege": [ | |||
"CONFIGURE_ROLES", | |||
"REGISTER_USER" | |||
], | |||
"permissionCount": 2 | |||
} | |||
}, | |||
"success": true | |||
} | |||
``` | |||
说明 | |||
|名称|说明| | |||
|---|---| | |||
|userRole|用户角色| | |||
|transactionPrivilegesBitset|交易权限集| | |||
|ledgerPrivilegesBitset|账本权限集| |
@@ -0,0 +1,361 @@ | |||
# 1. maven坐标 | |||
```java | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>sdk-client</artifactId> | |||
<version>1.3.0.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-starter</artifactId> | |||
<version>1.3.0.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-classic</artifactId> | |||
<version>1.3.0.RELEASE</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-sm</artifactId> | |||
<version>1.3.0.RELEASE</version> | |||
</dependency> | |||
``` | |||
# 2. 数据快速上链 | |||
## 2.1. 服务连接 | |||
```java | |||
//使用已注册用户信息进行连接; | |||
String GW_PUB_KEY = "3snxxx"; | |||
String GW_PRIV_KEY = "177xxx"; | |||
String GW_PASSWORD = "xxx"; | |||
PrivKey gwPrivkey0 = KeyGenUtils.decodePrivKey(GW_PRIV_KEY, GW_PASSWORD); | |||
PubKey gwPubKey0 = KeyGenUtils.decodePubKey(GW_PUB_KEY); | |||
BlockchainKeypair adminKey = new BlockchainKeypair(gwPubKey0, gwPrivkey0); | |||
//创建服务代理 | |||
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, | |||
adminKey); | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
HashDigest[] ledgerHashs = service.getLedgerHashs(); | |||
// 获取当前账本Hash | |||
HashDigest ledgerHash = ledgerHashs[0]; | |||
``` | |||
## 2.2. 用户注册 | |||
```java | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
// 在本地定义注册账号的 TX; | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
BlockchainKeypair user = BlockchainKeyGenerator.getInstance().generate(); | |||
txTemp.users().register(user.getIdentity()); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
prepTx.commit(); | |||
``` | |||
## 2.3. 数据账户注册 | |||
```java | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
// 在本地定义注册账号的 TX; | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
prepTx.commit(); | |||
``` | |||
## 2.4. 写入数据 | |||
```java | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
// 在本地定义注册账号的 TX; | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
// -------------------------------------- | |||
// 将商品信息写入到指定的账户中; | |||
// 对象将被序列化为 JSON 形式存储,并基于 JSON 结构建立查询索引; | |||
String commodityDataAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
txTemp.dataAccount(commodityDataAccount).setText("ASSET_CODE", "value1", -1); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
String txHash = Base64Utils.encodeToUrlSafeString(prepTx.getHash().toBytes()); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
prepTx.commit(); | |||
``` | |||
## 2.5. 查询数据 | |||
> 注:详细的查询可参考模块sdk-samples中SDK_GateWay_Query_Test_相关测试用例 | |||
```java | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
// 查询区块信息; | |||
// 区块高度; | |||
long ledgerNumber = service.getLedger(ledgerHash).getLatestBlockHeight(); | |||
// 最新区块; | |||
LedgerBlock latestBlock = service.getBlock(ledgerHash, ledgerNumber); | |||
// 区块中的交易的数量; | |||
long txCount = service.getTransactionCount(ledgerHash, latestBlock.getHash()); | |||
// 获取交易列表; | |||
LedgerTransaction[] txList = service.getTransactions(ledgerHash, 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 = ClientResolveUtil.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 = ClientResolveUtil.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(ledgerHash, txHash); | |||
// 获取数据; | |||
String commerceAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
String[] objKeys = new String[] { "x001", "x002" }; | |||
TypedKVEntry[] kvData = service.getDataEntries(ledgerHash, commerceAccount, objKeys); | |||
long payloadVersion = kvData[0].getVersion(); | |||
// 获取数据账户下所有的KV列表 | |||
TypedKVEntry[] kvDatas = service.getDataEntries(ledgerHash, commerceAccount, 0, 100); | |||
if (kvData != null && kvData.length > 0) { | |||
for (TypedKVEntry kvDatum : kvDatas) { | |||
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()); | |||
} | |||
} | |||
``` | |||
## 2.6. 合约发布 | |||
```java | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
// 在本地定义TX模板 | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
// 合约内容读取 | |||
byte[] contractBytes = FileUtils.readBytes(new File("CONTRACT_FILE")); | |||
// 生成用户 | |||
BlockchainKeypair contractKeyPair = BlockchainKeyGenerator.getInstance().generate(); | |||
// 发布合约 | |||
txTemp.contracts().deploy(contractKeyPair.getIdentity(), contractBytes); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
TransactionResponse transactionResponse = prepTx.commit(); | |||
assertTrue(transactionResponse.isSuccess()); | |||
// 打印合约地址 | |||
System.out.println(contractKeyPair.getIdentity().getAddress().toBase58()); | |||
``` | |||
## 2.7. 合约执行 | |||
```java | |||
// 创建服务代理; | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
// 在本地定义TX模板 | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
// 合约地址 | |||
String contractAddress = ""; | |||
// 使用接口方式调用合约 | |||
TransferContract transferContract = txTemp.contract(contractAddress, TransferContract.class); | |||
// 使用decode方式调用合约内部方法(create方法) | |||
// 返回GenericValueHolder可通过get方法获取结果,但get方法需要在commit调用后执行 | |||
String address = "address"; | |||
String account = "fill account"; | |||
long money = 100000000L; | |||
GenericValueHolder<String> result = ContractReturnValue.decode(transferContract.create(address, account, money)); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse transactionResponse = ptx.commit(); | |||
String cotractExecResult = result.get(); | |||
// TransactionResponse也提供了可供查询结果的接口 | |||
OperationResult[] operationResults = transactionResponse.getOperationResults(); | |||
// 通过OperationResult获取结果 | |||
for (int i = 0; i < operationResults.length; i++) { | |||
OperationResult opResult = operationResults[i]; | |||
System.out.printf("Operation[%s].result = %s \r\n", | |||
opResult.getIndex(), BytesValueEncoding.decode(opResult.getResult())); | |||
} | |||
``` | |||
## 2.8. 事件账户注册 | |||
```java | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
BlockchainKeypair eventAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
txTemp.eventAccounts().register(eventAccount.getIdentity()); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
prepTx.commit(); | |||
``` | |||
## 2.9. 事件发布 | |||
```java | |||
BlockchainService service = serviceFactory.getBlockchainService(); | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHash); | |||
// 发布事件到指定的账户中; | |||
String eventAccount = "GGhhreGeasdfasfUUfehf9932lkae99ds66jf=="; | |||
txTemp.eventAccount(eventAccount).publish("event_name", "string", -1) | |||
.publish("event_name", 0, 0); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
prepTx.commit(); | |||
``` | |||
## 2.10. 事件监听 | |||
- 系统事件 | |||
> 目前仅支持新区块产生事件 | |||
```java | |||
EventListenerHandle<SystemEventPoint> handler = blockchainService.monitorSystemEvent(ledgerHash, | |||
SystemEvent.NEW_BLOCK_CREATED, 0, new SystemEventListener<SystemEventPoint>() { | |||
@Override | |||
public void onEvents(Event[] eventMessages, EventContext<SystemEventPoint> eventContext) { | |||
for (Event eventMessage : eventMessages) { | |||
BytesValue content = eventMessage.getContent(); | |||
// content中存放的是当前链上最新高度 | |||
System.out.println(BytesUtils.toLong(content.getBytes().toBytes())); | |||
} | |||
// 关闭监听的两种方式:1 | |||
eventContext.getHandle().cancel(); | |||
} | |||
}); | |||
// 关闭监听的两种方式:2 | |||
handler.cancel(); | |||
``` | |||
- 用户自定义事件 | |||
```java | |||
EventListenerHandle<UserEventPoint> handler = blockchainService.monitorUserEvent(ledgerHash, | |||
eventAccount.getAddress().toBase58(), eventName, 0, new UserEventListener<UserEventPoint>() { | |||
@Override | |||
public void onEvent(Event eventMessage, EventContext<UserEventPoint> eventContext) { | |||
BytesValue content = eventMessage.getContent(); | |||
switch (content.getType()) { | |||
case TEXT: | |||
System.out.println(content.getBytes().toUTF8String()); | |||
break; | |||
case INT64: | |||
System.out.println(BytesUtils.toLong(content.getBytes().toBytes())); | |||
break; | |||
default: | |||
break; | |||
} | |||
// 关闭监听的两种方式:1 | |||
eventContext.getHandle().cancel(); | |||
} | |||
}); | |||
// 关闭监听的两种方式:2 | |||
handler.cancel(); | |||
``` |
@@ -0,0 +1,436 @@ | |||
# 智能合约开发手册 | |||
版本|修改时间|修改人 | |||
---|:--:|---: | |||
V1.0|2020-04-17|huanghaiquan | |||
--- | |||
### 1. 简介 | |||
JD Chain 智能合约系统由5个部分组成:合约代码语言、合约引擎、合约账户、合约开发框架、合约开发插件。 | |||
合约代码语言是用来编写智能合约的编程语言,合约引擎是解释和执行合约代码的虚拟机。 | |||
JD Chain 账本中以合约账户的方式对合约代码进行管理。一份部署上链的合约代码需要关联到一个唯一的公钥上,并生成与公钥对应的区块链账户地址,在账本中注册为一个合约账户。在执行之前,系统从账本中读出合约代码并将其加载到合约引擎,由交易执行器调用合约引擎触发合约执行。 | |||
JD Chain 账本定义了一组标准的账本操作指令,合约代码的执行过程实质上是向账本输出一串操作指令序列,这些指令对账本中的数据产生了变更,形成合约执行的最终结果。 | |||
合约开发框架定义了进行合约代码开发中需要依赖的一组编程接口和类库。合约开发插件提供了更方便与IDE集成的合约编译、部署工具,可以简化操作,并与持续集成过程结合。 | |||
JD Chain 以 Java 语言作为合约代码语言,合约引擎是基于 JVM 构建的安全沙盒。为了实现与主流的应用开发方式无缝兼容, JD Chain 支持以 Maven 来管理合约代码的工程项目,并提供相应的 maven 插件来简化合约的编译和部署。 | |||
>智能合约是一种可以由计算机执行的合同/协议。不同于现实生活中的合同是由自然语言来编写并约定相关方的权利和义务,智能合约是用合约代码语言来编写,以合约代码的形式存在和被执行。通过账本中的数据状态来表示合同/协议相关条款信息,合约代码的运行过程体现了合同/协议条款的执行,并记录相应的结果。 | |||
### 2. 快速入门 | |||
#### 2.1. 准备开发环境 | |||
按照正常的 Java 应用开发环境要求进行准备,以 Maven 作为代码工程的构建管理工具,无其它特殊要求。 | |||
>检查 JDK 版本不低于 1.8 ,Maven 版本不低于 3.0。 | |||
#### 2.2. 创建合约代码工程 | |||
创建一个普通的 Java Maven 工程,打开 pom.xml 把 packaging 设为 contract . | |||
``` xml | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>your.group.id</groupId> | |||
<artifactId>your.project</artifactId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
<!-- 声明为合约代码工程,编译输出扩展名为".car"合约代码 --> | |||
<packaging>contract</packaging> | |||
<dependencies> | |||
<!-- 合约项目的依赖 --> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<!-- 合约项目的插件 --> | |||
</plugins> | |||
</build> | |||
</project> | |||
``` | |||
> 注:合约代码工程也是一个普通的 Java Maven 工程,因此尽管不同 IDE 创建 Maven 工程有不同的操作方式,由于对于合约开发而言并无特殊要求,故在此不做详述。 | |||
#### 2.3. 加入合约开发依赖 | |||
在合约代码工程 pom.xml 加入对合约开发 SDK 的依赖: | |||
``` xml | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-starter</artifactId> | |||
<version>${jdchain.version}</version> | |||
</dependency> | |||
``` | |||
#### 2.4. 加入合约插件 | |||
在合约代码工程的 pom.xml 加入 contract-maven-plugin 插件: | |||
``` xml | |||
<plugin> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-maven-plugin</artifactId> | |||
<version>${jdchain.version}</version> | |||
<extensions>true</extensions> | |||
</plugin> | |||
``` | |||
完整的 pom.xml 如下: | |||
``` xml | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<groupId>your.group.id</groupId> | |||
<artifactId>your.project</artifactId> | |||
<version>0.0.1-SNAPSHOT</version> | |||
<!-- 声明为合约代码工程,编译输出扩展名为".car"合约代码 --> | |||
<packaging>contract</packaging> | |||
<properties> | |||
<jdchain.version>1.2.0.RELEASE</jdchain.version> | |||
</properties> | |||
<dependencies> | |||
<!-- 合约项目的依赖 --> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-starter</artifactId> | |||
<version>${jdchain.version}</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-compiler-plugin</artifactId> | |||
<version>3.8.1</version> | |||
<configuration> | |||
<source>1.8</source> | |||
<target>1.8</target> | |||
<encoding>UTF-8</encoding> | |||
<optimize>false</optimize> | |||
<debug>true</debug> | |||
<showDeprecation>false</showDeprecation> | |||
<showWarnings>false</showWarnings> | |||
</configuration> | |||
</plugin> | |||
<!-- 合约项目的插件 --> | |||
<plugin> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-maven-plugin</artifactId> | |||
<version>${jdchain.version}</version> | |||
<extensions>true</extensions> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> | |||
``` | |||
#### 2.5. 编写合约代码 | |||
2.5.1. **注意事项** | |||
``` | |||
1、不允许合约(包括合约接口和合约实现类)使用com.jd.blockchain开头的package; | |||
2、必须有且只有一个接口使用@Contract注解,且其中的event必须大于等于一个; | |||
3、使用@Contract注解的接口有且只有一个实现类; | |||
4、黑名单调用限制(具体黑名单可查看配置文件),需要注意的是,黑名单分析策略会递归分析类实现的接口和父类,也就是说调用一个实现了指定黑名单接口的类也是不允许的; | |||
目前设置的黑名单如下: | |||
java.io.File | |||
java.io.InputStream | |||
java.io.OutputStream | |||
java.io.DataInput | |||
java.io.DataOutput | |||
java.io.Reader | |||
java.io.Writer | |||
java.io.Flushable | |||
java.nio.channels.* | |||
java.nio.file.* | |||
java.net.* | |||
java.sql.* | |||
java.lang.reflect.* | |||
java.lang.Class | |||
java.lang.ClassLoader | |||
java.util.Random | |||
java.lang.System-currentTimeMillis | |||
java.lang.System-nanoTime | |||
com.jd.blockchain.ledger.BlockchainKeyGenerator | |||
``` | |||
2.5.2. **声明合约** | |||
``` java | |||
/** | |||
* 声明合约接口; | |||
**/ | |||
@Contract | |||
public interface AssetContract { | |||
@ContractEvent(name = "transfer") | |||
String transfer(String address, String from, String to, long amount); | |||
} | |||
``` | |||
2.5.3. **实现合约** | |||
``` java | |||
/** | |||
* 实现合约; | |||
* | |||
* 实现 EventProcessingAware 接口是可选的,目的获得 ContractEventContext 上下文对象, | |||
* 通过该对象可以进行账本操作; | |||
*/ | |||
public class AssetContractImpl implements AssetContract, EventProcessingAware { | |||
// 合约事件上下文; | |||
private ContractEventContext eventContext; | |||
/** | |||
* 执行交易请求中对 AssetContract 合约的 transfer 调用操作; | |||
*/ | |||
public String transfer(String address, String from, String to, long amount) { | |||
//当前账本的哈希; | |||
HashDigest ledgerHash = eventContext.getCurrentLedgerHash(); | |||
//当前账本上下文; | |||
LedgerContext ledgerContext = eventContext.getLedger(); | |||
//做操作; | |||
// ledgerContext. | |||
//返回合约操作的结果; | |||
return "success"; | |||
} | |||
/** | |||
* 准备执行交易中的合约调用操作; | |||
*/ | |||
@Override | |||
public void beforeEvent(ContractEventContext eventContext) { | |||
this.eventContext = eventContext; | |||
} | |||
/** | |||
* 完成执行交易中的合约调用操作; | |||
*/ | |||
@Override | |||
public void postEvent(ContractEventContext eventContext, Exception error) { | |||
this.eventContext = null; | |||
} | |||
} | |||
``` | |||
#### 2.6. 编译打包合约代码 | |||
合约代码工程的编译打包操作与普通的 maven 工程是相同的,在工程的根目录下输入以下命令: | |||
``` bash | |||
mvn clean package | |||
``` | |||
执行成功之后,在 target 目录中输出合约代码文件 \<project-name>.\<version>.car 。 | |||
如果合约代码加入了除 com.jd.blockchain:contract-starter 之外的其它依赖,默认配置下,第三方依赖包将与 .car 文件一起打包一起部署。(也可以把第三方依赖包独立打包,具体参见以下 “3. 合约插件详细配置” | |||
> 注意:合约代码虽然利用了 Java 语言,遵照 Java 语法进行编写,但本质上是作为一种运行于受限环境(合约虚拟机)的语言来使用,因而一些 Java 语法和 SDK 的 API 是不被允许使用的,在编译过程中将对此进行检查。 | |||
#### 2.7. 部署合约代码 | |||
##### 2.7.1. 在项目中部署合约代码 | |||
如果希望在构建打包的同时将合约代码部署到指定的区块链网络,可以在合约代码工程 pom.xml 的 contract-maven-plugin 插件配置中加入合约部署相关的信息(具体更详细的配置可以参考“3. 合约插件详细配置”)。 | |||
``` xml | |||
<plugin> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-maven-plugin</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<!-- 合约部署配置 --> | |||
<deployment> | |||
<!-- 合约要部署的目标账本的哈希;Base58 格式; --> | |||
<ledger>j5rpuGWVxSuUbU3gK7MDREfui797AjfdHzvAMiSaSzydu7</ledger> | |||
<!-- 区块链网络的网关地址 --> | |||
<gateway> | |||
<host>192.168.10.10</host> | |||
<port>8081</port> | |||
</gateway> | |||
<!-- 合约账户 --> | |||
<ContractAddress> | |||
<pubKey>3snPdw7i7Po4fYcXFxS4QztR8Dm4kLBdBpjsemuGPZRyZRBmtn5Z5u</pubKey> | |||
</ContractAddress> | |||
<!-- 合约部署交易的签名账户;该账户必须具备合约部署的权限; --> | |||
<signer> | |||
<pubKey>3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9</pubKey> | |||
<privKey>177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x</privKey> | |||
<privKeyPwd>DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY</privKeyPwd> | |||
</signer> | |||
</deployment> | |||
</configuration> | |||
</plugin> | |||
``` | |||
加入部署配置信息之后,对工程执行编译打包操作,输出的合约代码(.car)将自动部署到指定的区块链网络。 | |||
``` bash | |||
mvn clean deploy | |||
``` | |||
##### 2.7.2. 发布已编译好的car | |||
如果已经通过插件的打包方式,编译打包完成一个合约文件(.car),可通过命令行的方式进行发布,命令行要求与开发环境一致的Maven环境(包括环境变量及Setting都已配置完成)。 | |||
``` bash | |||
mvn com.jd.blockchain:contract-maven-plugin:${version}:deploy | |||
-DcarPath= | |||
-Dledger= | |||
-DgatewayHost= | |||
-DgatewayPort= | |||
-DcontractPubKey= | |||
-DcontractAddress= | |||
-DsignerPubKey= | |||
-DsignerPrivKey= | |||
-DsignerPrivKeyPwd= | |||
``` | |||
各参数说明如下: | |||
| 参数名 | 含义 |是否必填| | |||
| ---- | ---- | ---- | | |||
| ${version} | 合约插件的版本号 | 否,系统会自动选择发布的最新的RELEASE版本,SNAPSHOT版本必须填写 | | |||
| carPath | 合约文件所在路径 | 是 | | |||
| ledger | 账本Hash(Base58编码) | 否,会自动选择线上第一个账本| | |||
| gatewayHost | 可访问的网关节点地址,域名或IP地址 | 是| | |||
| gatewayPort | 网关节点监听端口 | 是 | | |||
| contractPubKey | 合约账户的公钥(Base58编码)| 否,会自动创建 | | |||
| contractAddress | 合约账户的地址(Base58编码)|否,会根据contractPubKey生成| | |||
| signerPubKey | 合约签名公钥信息(Base58编码)|是| | |||
| signerPrivKey | 合约签名私钥信息(Base58编码)|是| | |||
| signerPrivKeyPwd | 合约签名私钥解密密钥(Base58编码)|是| | |||
下面是一个示例,供参考: | |||
``` bash | |||
mvn com.jd.blockchain:contract-maven-plugin:1.2.0.RELEASE:deploy \ | |||
-DcarPath=/root/jdchain/contracts/contract-test-1.0-SNAPSHOT.car \ | |||
-Dledger=j5tW5HUvMjEtm2yB7E6MHoSByoH1DXvMwvF2HurEgMSaLW \ | |||
-DgatewayHost=127.0.0.1 \ | |||
-DgatewayPort=11000 \ | |||
-DcontractPubKey= 3snPdw7i7PajLB35tEau1kmixc6ZrjLXgxwKbkv5bHhP7nT5dhD9eX \ | |||
-DcontractAddress= LdeNt7sEmTirh9PmE7axKvA2txTrbB9kxz6KB \ | |||
-DsignerPubKey=3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9 \ | |||
-DsignerPrivKey=177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x \ | |||
-DsignerPrivKeyPwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY | |||
``` | |||
> 重点说明: | |||
命令行中输入参数的优先级高于配置文件,就是说通过2.7.1方式发布合约时也可以采用命令行的参数(指-D相关配置),其优先级高于配置文件。 | |||
### 3. 合约插件详细配置 | |||
``` xml | |||
<plugin> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-maven-plugin</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<!-- 是否把所有的依赖项打包输出到一个独立的 “库文件(.lib)”,默认为 false--> | |||
<!-- 设置为 false 时 ,合约代码和依赖项一起打包输出到 “合约代码文件(.car)” --> | |||
<!-- 设置为 true ,合约代码和依赖项分别打包,分别输出 “合约代码文件(.car)” 和 “库文件(.lib)” --> | |||
<!-- 注: | |||
1. 如果“合约代码文件(.car)”的尺寸超出最大尺寸将引发异常,可把此项配置设置为 true 以减小“合约代码文件(.car)”的尺寸。 | |||
2. “合约代码文件(.car)”的默认最大尺寸为 1 MB,由区块链网络的配置设定,如果不满足则需要由区块链网络的管理员进行调整。 | |||
3. “合约库文件(.lib)”的尺寸不受“合约代码文件(.car)”的最大尺寸限制,部署过程只有“哈希上链”,库文件通过链下的分发网络自动同步至各个共识节点。 | |||
--> | |||
<outputLibrary>false</outputLibrary> | |||
<!-- 合约代码最大字节数;可选;--> | |||
<!-- 默认为 1 (MB);如果超出该值将给予错误提示;如果值小于等于 0,则不做校验 --> | |||
<!-- 注:此参数仅影响编译打包时的本地校验,实际部署时仍然由区块链网络上的配置决定 --> | |||
<maxCarSize>1</maxCarSize> | |||
<!-- 合约代码最大字节数的单位;--> | |||
<!-- 合法值的格式为“整数值+单位”;可选单位有: Byte, KB, MB;不区分大小写;--> | |||
<maxCarSizeUnit>MB</maxCarSizeUnit> | |||
<!-- 合约部署配置;可选 --> | |||
<deployment> | |||
<!-- 账本的哈希;Base58 格式;非必填项,会自动选择线上第一个账本 --> | |||
<ledger></ledger> | |||
<!-- 区块链网络的网关地址 --> | |||
<gateway> | |||
<host></host> | |||
<port></port> | |||
</gateway> | |||
<!-- 合约账户 --> | |||
<!-- 合约账户的地址address(Base58编码),会根据pubKey生成> --> | |||
<contractAddress> | |||
<pubKey></pubKey> | |||
<address></address> | |||
</contractAddress> | |||
<!-- 合约部署交易的签名账户;该账户必须具备合约部署的权限; --> | |||
<signer> | |||
<!-- 账户公钥;Base58 格式; --> | |||
<pubKey></pubKey> | |||
<!-- 账户私钥;Base58 格式; --> | |||
<privKey></privKey> | |||
<!-- 账户私钥解密密码;Base58 格式; --> | |||
<privKeyPwd></privKeyPwd> | |||
</signer> | |||
</deployment> | |||
</configuration> | |||
</plugin> | |||
``` | |||
### 4. 最简化合约插件配置示例 | |||
在pom.xml中有部分配置是非必填项,下面是一份最简化的合约发布(deploy)配置示例,供参考: | |||
``` xml | |||
<plugin> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-maven-plugin</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<extensions>true</extensions> | |||
<configuration> | |||
<!-- 合约部署配置--> | |||
<deployment> | |||
<!-- 区块链网络的网关地址 --> | |||
<gateway> | |||
<host>127.0.0.1</host> | |||
<port>8081</port> | |||
</gateway> | |||
<!-- 合约部署交易的签名账户;该账户必须具备合约部署的权限; --> | |||
<signer> | |||
<pubKey>3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9</pubKey> | |||
<privKey>177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x</privKey> | |||
<privKeyPwd>DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY</privKeyPwd> | |||
</signer> | |||
</deployment> | |||
</configuration> | |||
</plugin> | |||
``` |
@@ -5,49 +5,49 @@ | |||
<parent> | |||
<artifactId>deploy-root</artifactId> | |||
<groupId>com.jd.blockchain</groupId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.4.0-SNAPSHOT</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>deploy-kvdb</artifactId> | |||
<version>1.0.0.RELEASE</version> | |||
<version>1.0.1.RELEASE</version> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-protocol</artifactId> | |||
<version>${version}</version> | |||
<version>${kvdb.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-engine</artifactId> | |||
<version>${version}</version> | |||
<version>${kvdb.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-server</artifactId> | |||
<version>${version}</version> | |||
<version>${kvdb.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-cli</artifactId> | |||
<version>${version}</version> | |||
<version>${kvdb.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-benchmark</artifactId> | |||
<version>${version}</version> | |||
<version>${kvdb.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-client</artifactId> | |||
<version>${version}</version> | |||
<version>${kvdb.version}</version> | |||
</dependency> | |||
</dependencies> | |||
@@ -96,4 +96,4 @@ | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> | |||
</project> |
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>deploy-root</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.4.0-SNAPSHOT</version> | |||
</parent> | |||
<artifactId>deploy-peer</artifactId> | |||
@@ -62,13 +62,13 @@ | |||
<build> | |||
<plugins> | |||
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> | |||
<executions> <execution> <id>copy-dependencies</id> <phase>package</phase> | |||
<goals> <goal>copy-dependencies</goal> </goals> <configuration> ${project.build.directory} | |||
class的输出目录不做设置的话默认代表项目根目录的target目录; 也可以使用“自定义文件夹/自定义文件夹 例如:a/b”,也可以使用绝对路径如:“D:\test” | |||
<outputDirectory>${project.build.directory}/dependencies</outputDirectory> | |||
<excludeTransitive>false</excludeTransitive> <stripVersion>false</stripVersion> | |||
<includeScope>runtime</includeScope> </configuration> </execution> </executions> | |||
<!-- <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> | |||
<executions> <execution> <id>copy-dependencies</id> <phase>package</phase> | |||
<goals> <goal>copy-dependencies</goal> </goals> <configuration> ${project.build.directory} | |||
class的输出目录不做设置的话默认代表项目根目录的target目录; 也可以使用“自定义文件夹/自定义文件夹 例如:a/b”,也可以使用绝对路径如:“D:\test” | |||
<outputDirectory>${project.build.directory}/dependencies</outputDirectory> | |||
<excludeTransitive>false</excludeTransitive> <stripVersion>false</stripVersion> | |||
<includeScope>runtime</includeScope> </configuration> </execution> </executions> | |||
</plugin> --> | |||
<plugin> | |||
@@ -129,4 +129,4 @@ | |||
</plugins> | |||
</build> | |||
</project> | |||
</project> |
@@ -1,5 +1,8 @@ | |||
package com.jd.blockchain.boot.peer; | |||
import com.jd.blockchain.runtime.boot.HomeBooter; | |||
import com.jd.blockchain.runtime.boot.HomeContext; | |||
import java.io.File; | |||
import java.io.FileOutputStream; | |||
import java.io.IOException; | |||
@@ -11,12 +14,9 @@ import java.util.ArrayList; | |||
import java.util.Date; | |||
import java.util.List; | |||
import com.jd.blockchain.runtime.boot.HomeBooter; | |||
import com.jd.blockchain.runtime.boot.HomeContext; | |||
/** | |||
* Peer 启动器; | |||
* | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@@ -82,13 +82,6 @@ cons_parti.0.pubkey= | |||
#第0个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.0.roles-policy=UNION | |||
#第0个参与方的共识服务的主机地址; | |||
cons_parti.0.consensus.host=127.0.0.1 | |||
#第0个参与方的共识服务的端口; | |||
cons_parti.0.consensus.port=8900 | |||
#第0个参与方的共识服务是否开启安全连接; | |||
cons_parti.0.consensus.secure=false | |||
#第0个参与方的账本初始服务的主机; | |||
cons_parti.0.initializer.host=127.0.0.1 | |||
#第0个参与方的账本初始服务的端口; | |||
@@ -96,7 +89,6 @@ cons_parti.0.initializer.port=8800 | |||
#第0个参与方的账本初始服务是否开启安全连接; | |||
cons_parti.0.initializer.secure=false | |||
#--------------------- | |||
#第1个参与方的名称; | |||
cons_parti.1.name= | |||
@@ -110,13 +102,6 @@ cons_parti.1.pubkey= | |||
#第1个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.1.roles-policy=UNION | |||
#第1个参与方的共识服务的主机地址; | |||
cons_parti.1.consensus.host=127.0.0.1 | |||
#第1个参与方的共识服务的端口; | |||
cons_parti.1.consensus.port=8910 | |||
#第1个参与方的共识服务是否开启安全连接; | |||
cons_parti.1.consensus.secure=false | |||
#第1个参与方的账本初始服务的主机; | |||
cons_parti.1.initializer.host=127.0.0.1 | |||
#第1个参与方的账本初始服务的端口; | |||
@@ -137,13 +122,6 @@ cons_parti.2.pubkey= | |||
#第2个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.2.roles-policy=UNION | |||
#第2个参与方的共识服务的主机地址; | |||
cons_parti.2.consensus.host=127.0.0.1 | |||
#第2个参与方的共识服务的端口; | |||
cons_parti.2.consensus.port=8920 | |||
#第2个参与方的共识服务是否开启安全连接; | |||
cons_parti.2.consensus.secure=false | |||
#第2个参与方的账本初始服务的主机; | |||
cons_parti.2.initializer.host=127.0.0.1 | |||
#第2个参与方的账本初始服务的端口; | |||
@@ -164,13 +142,6 @@ cons_parti.3.pubkey= | |||
#第3个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.3.roles-policy=INTERSECT | |||
#第3个参与方的共识服务的主机地址; | |||
cons_parti.3.consensus.host=127.0.0.1 | |||
#第3个参与方的共识服务的端口; | |||
cons_parti.3.consensus.port=8930 | |||
#第3个参与方的共识服务是否开启安全连接; | |||
cons_parti.3.consensus.secure=false | |||
#第3个参与方的账本初始服务的主机; | |||
cons_parti.3.initializer.host=127.0.0.1 | |||
#第3个参与方的账本初始服务的端口; | |||
@@ -1,6 +1,6 @@ | |||
# JDChain安装部署指南 | |||
本部署指南基于JDChain1.0.0.RELEASE版本来构建。 | |||
本部署指南基于JDChain1.3.0.RELEASE版本来构建。 | |||
## 1. 部署环境 | |||
### 1.1 系统部署结构 | |||
 | |||
@@ -21,7 +21,7 @@ JDChain默认共识机制采用的是BFTSmart,该共识算法要求最低四 | |||
### 1.2 服务器配置 | |||
#### 1.2.1 硬件配置 | |||
为了更好的运行,JDChain推荐使用 **Linux** 操作系统,Peer节点自身运行对内存要求较低,但对于Rocksdb存储而言,其对运行环境有较高要求,以下为单Peer节点可正常运行 **亿级交易** 的参考指标: | |||
为了更好的运行,JDChain推荐使用 **Linux** 操作系统,Peer节点自身运行对内存要求较低(单节点建议Xms>=4g),但对于Rocksdb存储而言,其对运行环境有较高要求,以下为单Peer节点可正常运行 **亿级交易** 的参考指标: | |||
+ 内核2.7.x及以上; | |||
+ CPU主频 ≥ 2.0GHz; | |||
+ 内存 ≥ 64G; | |||
@@ -59,8 +59,10 @@ Peer打包程序解压完后的安装包结构如下: | |||
+ bin | |||
- keygen.sh | |||
- ledger-init.sh | |||
- startup.sh | |||
- shutdown.sh | |||
- peer-startup.sh | |||
- peer-shutdown.sh | |||
- manager-startup.sh | |||
- manager-shutdown.sh | |||
+ config | |||
- init | |||
+ ledger.init | |||
@@ -71,7 +73,6 @@ Peer打包程序解压完后的安装包结构如下: | |||
+ %.pub | |||
+ %.pwd | |||
- ledger-binding.conf | |||
- application.properties | |||
+ docs | |||
+ libs | |||
+ system | |||
@@ -152,8 +153,9 @@ local.parti.pwd=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY | |||
ledger.binding.out=../config | |||
#账本数据库的连接字符,下为rocksdb样例 | |||
#rocksdb数据库连接格式:rocksdb://{path} | |||
#redis数据库连接格式:redis://{ip}:{prot}/{db} | |||
#rocksdb数据库连接格式:rocksdb://{path},例如:rocksdb:///export/App08/peer/rocks.db/rocksdb0.db | |||
#redis数据库连接格式:redis://{ip}:{prot}/{db},例如:redis://127.0.0.1:6379/0 | |||
#kvdb数据库连接格式:kvdb://{ip}:{prot}/{db},例如:kvdb://127.0.0.1:7078/test | |||
ledger.db.uri=rocksdb:///export/app/peer/rocks.db/rocksdb0.db | |||
#账本数据库的连接口令 | |||
@@ -168,32 +170,86 @@ ledger.db.pwd= | |||
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | |||
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; | |||
#ledger.name= | |||
#声明的账本创建时间;格式为 “yyyy-MM-dd HH:mm:ss.SSSZ”,表示”年-月-日 时:分:秒:毫秒时区“;例如:“2019-10-17 05:21:58.069+0800”,其中,+0800 表示时区是东8区 | |||
created-time=2019-10-17 05:21:58.069+0800 | |||
ledger.name= | |||
#声明的账本创建时间;格式为 “yyyy-MM-dd HH:mm:ss.SSSZ”,表示”年-月-日 时:分:秒:毫秒时区“;例如:“2019-08-01 14:26:58.069+0800”,其中,+0800 表示时区是东8区 | |||
created-time=2019-08-01 14:26:58.069+0800 | |||
#----------------------------------------------- | |||
# 初始的角色名称列表;可选项; | |||
# 角色名称不区分大小写,最长不超过20个字符;多个角色名称之间用半角的逗点“,”分隔; | |||
# 系统会预置一个默认角色“DEFAULT”,所有未指定角色的用户都以赋予该角色的权限;若初始化时未配置默认角色的权限,则为默认角色分配所有权限; | |||
# | |||
# 注:如果声明了角色,但未声明角色对应的权限清单,这会忽略该角色的初始化; | |||
# | |||
#security.roles=DEFAULT, ADMIN, MANAGER, GUEST | |||
# 赋予角色的账本权限清单;可选项; | |||
# 可选的权限如下; | |||
# AUTHORIZE_ROLES, SET_CONSENSUS, SET_CRYPTO, REGISTER_PARTICIPANT, | |||
# REGISTER_USER, REGISTER_DATA_ACCOUNT, REGISTER_CONTRACT, UPGRADE_CONTRACT, | |||
# SET_USER_ATTRIBUTES, WRITE_DATA_ACCOUNT, | |||
# APPROVE_TX, CONSENSUS_TX | |||
# 多项权限之间用逗点“,”分隔; | |||
# | |||
#security.role.DEFAULT.ledger-privileges=REGISTER_USER, REGISTER_DATA_ACCOUNT | |||
# 赋予角色的交易权限清单;可选项; | |||
# 可选的权限如下; | |||
# DIRECT_OPERATION, CONTRACT_OPERATION | |||
# 多项权限之间用逗点“,”分隔; | |||
# | |||
#security.role.DEFAULT.tx-privileges=DIRECT_OPERATION, CONTRACT_OPERATION | |||
# 其它角色的配置示例; | |||
# 系统管理员角色:只能操作全局性的参数配置和用户注册,只能执行直接操作指令; | |||
#security.role.ADMIN.ledger-privileges=CONFIGURE_ROLES, AUTHORIZE_USER_ROLES, SET_CONSENSUS, SET_CRYPTO, REGISTER_PARTICIPANT, REGISTER_USER | |||
#security.role.ADMIN.tx-privileges=DIRECT_OPERATION | |||
# 业务主管角色:只能够执行账本数据相关的操作,包括注册用户、注册数据账户、注册合约、升级合约、写入数据等;能够执行直接操作指令和调用合约; | |||
#security.role.MANAGER.ledger-privileges=CONFIGURE_ROLES, AUTHORIZE_USER_ROLES, REGISTER_USER, REGISTER_DATA_ACCOUNT, REGISTER_CONTRACT, UPGRADE_CONTRACT, SET_USER_ATTRIBUTES, WRITE_DATA_ACCOUNT, | |||
#security.role.MANAGER.tx-privileges=DIRECT_OPERATION, CONTRACT_OPERATION | |||
# 访客角色:不具备任何的账本权限,只有数据读取的操作;也只能够通过调用合约来读取数据; | |||
#security.role.GUEST.ledger-privileges= | |||
#security.role.GUEST.tx-privileges=CONTRACT_OPERATION | |||
#----------------------------------------------- | |||
#共识服务提供者;必须; | |||
consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
#共识服务的参数配置;必须; | |||
#consensus.conf=/export/app/peer/config/init/bftsmart.config | |||
#共识服务的参数配置;推荐使用绝对路径;必须; | |||
consensus.conf=bftsmart.config | |||
#密码服务提供者列表,以英文逗点“,”分隔;必须; | |||
crypto.service-providers=com.jd.blockchain.crypto.service.classic.ClassicCryptoService, \ | |||
com.jd.blockchain.crypto.service.sm.SMCryptoService | |||
#从存储中加载账本数据时,是否校验哈希;可选; | |||
crypto.verify-hash=true | |||
#哈希算法; | |||
crypto.hash-algorithm=SHA256 | |||
#参与方的个数,后续以 cons_parti.id 分别标识每一个参与方的配置; | |||
cons_parti.count=4 | |||
#--------------------- | |||
#第0个参与方的名称; | |||
cons_parti.0.name= | |||
#第0个参与方的公钥文件路径; | |||
cons_parti.0.pubkey-path= | |||
#第0个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||
cons_parti.0.pubkey= | |||
#第0个参与方的角色清单;可选项; | |||
#cons_parti.0.roles=ADMIN, MANAGER | |||
#第0个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.0.roles-policy=UNION | |||
#第0个参与方的账本初始服务的主机; | |||
cons_parti.0.initializer.host=127.0.0.1 | |||
#第0个参与方的账本初始服务的端口; | |||
@@ -201,24 +257,71 @@ cons_parti.0.initializer.port=8800 | |||
#第0个参与方的账本初始服务是否开启安全连接; | |||
cons_parti.0.initializer.secure=false | |||
#--------------------- | |||
#第1个参与方的名称; | |||
cons_parti.1.name= | |||
#第1个参与方的公钥文件路径; | |||
cons_parti.1.pubkey-path= | |||
#第1个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||
cons_parti.1.pubkey= | |||
#第1个参与方的角色清单;可选项; | |||
#cons_parti.1.roles=MANAGER | |||
#第1个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.1.roles-policy=UNION | |||
#第1个参与方的账本初始服务的主机; | |||
cons_parti.1.initializer.host=127.0.0.1 | |||
#第1个参与方的账本初始服务的端口; | |||
cons_parti.1.initializer.port=8810 | |||
#第1个参与方的账本初始服务是否开启安全连接; | |||
cons_parti.1.initializer.secure=false | |||
#--------------------- | |||
#第2个参与方的名称; | |||
cons_parti.2.name= | |||
#第2个参与方的公钥文件路径; | |||
cons_parti.2.pubkey-path= | |||
#第2个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||
cons_parti.2.pubkey= | |||
#第2个参与方的角色清单;可选项; | |||
#cons_parti.2.roles=MANAGER | |||
#第2个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.2.roles-policy=UNION | |||
#第2个参与方的账本初始服务的主机; | |||
cons_parti.2.initializer.host=127.0.0.1 | |||
#第2个参与方的账本初始服务的端口; | |||
cons_parti.2.initializer.port=8820 | |||
#第2个参与方的账本初始服务是否开启安全连接; | |||
cons_parti.2.initializer.secure=false | |||
#--------------------- | |||
#第3个参与方的名称; | |||
cons_parti.3.name= | |||
#第3个参与方的公钥文件路径; | |||
cons_parti.3.pubkey-path= | |||
#第3个参与方的公钥内容(由keygen工具生成);此参数优先于 pubkey-path 参数; | |||
cons_parti.3.pubkey= | |||
#第3个参与方的角色清单;可选项; | |||
#cons_parti.3.roles=GUEST | |||
#第3个参与方的角色权限策略,可选值有:UNION(并集),INTERSECT(交集);可选项; | |||
#cons_parti.3.roles-policy=INTERSECT | |||
#第3个参与方的账本初始服务的主机; | |||
cons_parti.3.initializer.host=127.0.0.1 | |||
#第3个参与方的账本初始服务的端口; | |||
cons_parti.3.initializer.port=8830 | |||
#第3个参与方的账本初始服务是否开启安全连接; | |||
cons_parti.3.initializer.secure=false | |||
``` | |||
账本初始化过程中,需要其他Peer节点的公钥信息进行验证及保存,因此每个节点都需要获取其他Peer节点的公钥信息至本地。配置中cons_parti.N.*****中的N需按照实际每个参与方定义的序号配置(该配置为local.conf中的local.parti.id)。其他参数根据实际环境进行配置。 | |||
> 注意:上述config中只显示了两个参与方(Peer节点)的配置信息,其他参与方(默认是4个节点)的配置方式一致! | |||
##### 2.3.1.1 bftsmart.config配置 | |||
##### 2.3.1.3 bftsmart.config配置 | |||
```config | |||
system.server.0.network.host=127.0.0.1 | |||
system.server.0.network.port=16000 | |||
@@ -247,25 +350,33 @@ system.initial.view = 0,1,2,3 | |||
cd bin | |||
./ledger-init.sh | |||
``` | |||
执行命令之后,会在 *config* 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。 | |||
执行命令之后,控制台会显示如下类似信息: | |||
``` | |||
------ Web controller of Ledger Initializer[127.0.0.1:30010] was started. ------ | |||
Init settings and sign permision... | |||
[PERMISSION_READY] Ledger init permission has already prepared! Any key to continue... | |||
``` | |||
然后需要依次启动剩余节点的初始化脚本(*ledger-init.sh*),根据提示按任意键继续。 | |||
如果其它节点没有准备好,控制台会展示“Connection refused”异常信息,不断进行重试操作。目前默认设置为重试180次操作,每次间隔时间10秒。 | |||
执行成功后会在 *config* 目录下生成ledger-binding.conf文件,该文件即账本初始化生成的文件,Peer节点启动时需要依赖该文件。 | |||
> 1)注意:因为JDChain支持多账本形式,若config/ledger-binding.conf文件在初始化之前就存在的话,初始化操作后不会覆盖其中的内容,会以追加的方式写入。若第一次创建账本,建议先将该文件删除再进行初始化! | |||
> 2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点!目前默认时间为封5自动更新,即:每个小时的5/15/25/35/45/55分钟会执行; | |||
> 2)注意:Peer节点会定时检测ledger-binding.conf,有新账本加入时会自动进行更新,不需要重启Peer节点!目前默认时间每隔5秒钟自动更新; | |||
账本初始化成功后,每个Peer节点对应的Rocksdb都会写入该账本相关的数据。 | |||
### 2.4 Peer节点安装 | |||
Peer节点启动依赖于 *config* 目录下ledger-binding.conf的配置,该文件由 *2.3章节* 操作生成,无须做任何修改;application.properties文件中主要用于配置Peer节点对外HTTP端口。 | |||
> 注意:application.properties文件可能不存在,可手动创建。该文件中配置适用于SpringBoot2.x版本的相关参数,不限于目前内置的启动端口。 | |||
Peer节点启动依赖于 *config* 目录下ledger-binding.conf的配置,该文件由 *2.3章节* 操作生成,无须做任何修改。 | |||
由于Peer节点启动后会自动与其他参与节点进行通信,因此需要同时启动4个Peer节点,只需要执行startup.sh即可,参考命令: | |||
```shell | |||
cd bin | |||
./startup.sh | |||
./peer-startup.sh | |||
``` | |||
> 1)注意:startup.sh命令中可修改启动端口,默认为:-p 7080; | |||
> 2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。目前默认设置为重试16次操作,每次间隔时间2秒。 | |||
> 2)注意:Peer节点会与账本中涉及到的参与方进行通信,当通信不成功(例如有节点尚未启动)时,会自动进行重试,因此多个Peer节点启动可不必完全同时进行。目前默认设置为重试601次操作,每次间隔时间3秒。 | |||
### 2.5 Gateway节点安装 | |||
GateWay(网关)节点可以认为是一个过滤节点,交易的提交及账本的查询都需要通过网关节点与Peer节点进行通信。 | |||
@@ -9,11 +9,12 @@ | |||
<relativePath>../project/parent</relativePath> | |||
</parent> | |||
<artifactId>deploy-root</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.4.0-SNAPSHOT</version> | |||
<packaging>pom</packaging> | |||
<properties> | |||
<core.version>1.3.0-SNAPSHOT</core.version> | |||
<core.version>1.4.0-SNAPSHOT</core.version> | |||
<kvdb.version>1.0.1.RELEASE</kvdb.version> | |||
</properties> | |||
<modules> | |||
@@ -23,4 +24,4 @@ | |||
<module>deploy-kvdb</module> | |||
</modules> | |||
</project> | |||
</project> |
@@ -1 +1 @@ | |||
Subproject commit 7656ac3b4e434e07e338d9772767158922c1b8c7 | |||
Subproject commit afbd2eaa6d3c9019f36d9a6d86126864713b6fe6 |
@@ -5,7 +5,7 @@ | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.2.1.RELEASE</version> | |||
<packaging>pom</packaging> | |||
<description>jdchain root project</description> | |||
@@ -6,7 +6,7 @@ | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-samples</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.2.1.RELEASE</version> | |||
</parent> | |||
<artifactId>contract-samples</artifactId> | |||
@@ -15,7 +15,7 @@ | |||
<packaging>pom</packaging> | |||
<properties> | |||
<framework.version>1.2.0.RELEASE</framework.version> | |||
<framework.version>1.3.0.RELEASE</framework.version> | |||
</properties> | |||
<modules> | |||
@@ -5,7 +5,7 @@ | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-samples</artifactId> | |||
<version>1.2.0.RELEASE</version> | |||
<version>1.2.1.RELEASE</version> | |||
</parent> | |||
<artifactId>sdk-samples</artifactId> | |||
@@ -0,0 +1,69 @@ | |||
package com.jd.blockchain.sdk.samples; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.utils.http.ResponseConverter; | |||
import com.jd.blockchain.utils.web.client.WebResponseConverter; | |||
import org.apache.http.HttpResponse; | |||
import org.apache.http.client.HttpClient; | |||
import org.apache.http.client.entity.UrlEncodedFormEntity; | |||
import org.apache.http.client.methods.HttpPost; | |||
import org.apache.http.impl.client.HttpClients; | |||
import org.apache.http.message.BasicNameValuePair; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
/** | |||
* @Author: zhangshuang | |||
* @Date: 2020/5/27 5:18 PM | |||
* Version 1.0 | |||
*/ | |||
public class SDKDemo_ActiveParticipant { | |||
// 接受激活参与方操作的共识节点Http服务地址, 根据具体环境配置进行修改 | |||
private static String httpIp = "127.0.0.1"; | |||
private static String httpPort = "7085"; | |||
public static void main(String[] args) { | |||
String url = "http://" + httpIp + ":" + httpPort + "/management/delegate/activeparticipant"; | |||
System.out.println("url = " + url); | |||
HttpPost httpPost = new HttpPost(url); | |||
List<BasicNameValuePair> para=new ArrayList<BasicNameValuePair>(); | |||
// 账本值根据具体情况进行修改 | |||
BasicNameValuePair base58LedgerHash = new BasicNameValuePair("ledgerHash", "j5n3SmYT3rS5aDgwQbDUxBYUMEP9GvVfMiQK3Tcr3vxz3M"); | |||
// 激活的新参与方的共识网络地址 | |||
BasicNameValuePair host = new BasicNameValuePair("consensusHost", "127.0.0.1"); | |||
BasicNameValuePair port = new BasicNameValuePair("consensusPort", "20000"); | |||
// 指定已经启动的其他共识节点的HTTP管理端口 | |||
BasicNameValuePair manageHost = new BasicNameValuePair("remoteManageHost", "127.0.0.1"); | |||
BasicNameValuePair managePort = new BasicNameValuePair("remoteManagePort", "12000"); | |||
para.add(base58LedgerHash); | |||
para.add(host); | |||
para.add(port); | |||
para.add(manageHost); | |||
para.add(managePort); | |||
try { | |||
httpPost.setEntity(new UrlEncodedFormEntity(para,"UTF-8")); | |||
HttpClient httpClient = HttpClients.createDefault(); | |||
HttpResponse response = httpClient.execute(httpPost); | |||
ResponseConverter responseConverter = new WebResponseConverter(TransactionResponse.class); | |||
Object converterResponse = responseConverter.getResponse(null, response.getEntity().getContent(), null); | |||
System.out.println("response result = " + ((TransactionResponse)converterResponse).getExecutionState()); | |||
} catch (Exception e) { | |||
e.printStackTrace(); | |||
System.out.println("Active participant post request error!"); | |||
} | |||
} | |||
} |
@@ -0,0 +1,85 @@ | |||
package com.jd.blockchain.sdk.samples; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.*; | |||
import com.jd.blockchain.ledger.*; | |||
import com.jd.blockchain.sdk.BlockchainService; | |||
import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||
import com.jd.blockchain.utils.net.NetworkAddress; | |||
/** | |||
* @Author: zhangshuang | |||
* @Date: 2020/5/27 5:18 PM | |||
* Version 1.0 | |||
*/ | |||
public class SDKDemo_RegistParticipant { | |||
public static void main(String[] args) { | |||
PrivKey privKey; | |||
PubKey pubKey; | |||
BlockchainKeypair CLIENT_CERT = null; | |||
String GATEWAY_IPADDR = null; | |||
int GATEWAY_PORT; | |||
boolean SECURE; | |||
BlockchainService service; | |||
//根据密码工具产生的公私钥 | |||
String PUB = "3snPdw7i7PkdgqiGX7GbZuFSi1cwZn7vtjw4vifb1YoXgr9k6Kfmis"; | |||
String PRIV = "177gjtZu8w1phqHFVNiFhA35cfimXmP6VuqrBFhfbXBWK8s4TRwro2tnpffwP1Emwr6SMN6"; | |||
privKey = SDKDemo_Params.privkey1; | |||
pubKey = SDKDemo_Params.pubKey1; | |||
CLIENT_CERT = new BlockchainKeypair(SDKDemo_Params.pubKey0, SDKDemo_Params.privkey0); | |||
GATEWAY_IPADDR = "127.0.0.1"; | |||
GATEWAY_PORT = 11000; | |||
SECURE = false; | |||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, | |||
CLIENT_CERT); | |||
service = serviceFactory.getBlockchainService(); | |||
DataContractRegistry.register(TransactionContent.class); | |||
DataContractRegistry.register(TransactionContentBody.class); | |||
DataContractRegistry.register(TransactionRequest.class); | |||
DataContractRegistry.register(NodeRequest.class); | |||
DataContractRegistry.register(EndpointRequest.class); | |||
DataContractRegistry.register(TransactionResponse.class); | |||
DataContractRegistry.register(ParticipantRegisterOperation.class); | |||
DataContractRegistry.register(ParticipantStateUpdateOperation.class); | |||
DataContractRegistry.register(ConsensusSettingsUpdateOperation.class); | |||
HashDigest[] ledgerHashs = service.getLedgerHashs(); | |||
// 在本地定义注册账号的 TX; | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHashs[0]); | |||
//existed signer | |||
AsymmetricKeypair keyPair = new BlockchainKeypair(pubKey, privKey); | |||
privKey = KeyGenUtils.decodePrivKeyWithRawPassword(PRIV, SDKDemo_Constant.PASSWORD); | |||
pubKey = KeyGenUtils.decodePubKey(PUB); | |||
System.out.println("Address = " + AddressEncoding.generateAddress(pubKey)); | |||
BlockchainKeypair user = new BlockchainKeypair(pubKey, privKey); | |||
// 注册参与方 | |||
txTemp.participants().register("Peer4", user.getIdentity()); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(keyPair); | |||
// 提交交易; | |||
TransactionResponse transactionResponse = prepTx.commit(); | |||
} | |||
} |
@@ -0,0 +1,96 @@ | |||
package com.jd.blockchain.sdk.samples; | |||
import com.jd.blockchain.ledger.BlockchainKeyGenerator; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.Event; | |||
import com.jd.blockchain.ledger.SystemEvent; | |||
import com.jd.blockchain.ledger.TransactionTemplate; | |||
import com.jd.blockchain.sdk.EventContext; | |||
import com.jd.blockchain.sdk.EventListenerHandle; | |||
import com.jd.blockchain.sdk.SystemEventListener; | |||
import com.jd.blockchain.sdk.SystemEventPoint; | |||
import com.jd.blockchain.sdk.UserEventListener; | |||
import com.jd.blockchain.sdk.UserEventPoint; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
public class SDK_Event_Demo extends SDK_Base_Demo { | |||
// 注册事件账户 | |||
private BlockchainKeypair createEventAccount() { | |||
BlockchainKeypair eventAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
txTpl.eventAccounts().register(eventAccount.getIdentity()); | |||
commit(txTpl); | |||
return eventAccount; | |||
} | |||
/** | |||
* 发布事件 | |||
* | |||
* @param eventAccount 事件账户 | |||
*/ | |||
private void publishEvent(BlockchainKeypair eventAccount) { | |||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
// sequence传递当前事件名链上最新序号,不存在时传-1 | |||
Event event = blockchainService.getLatestEvent(ledgerHash, eventAccount.getAddress().toBase58(), "name"); | |||
long sequence = null != event ? event.getSequence() : -1; | |||
txTpl.eventAccount(eventAccount.getAddress()).publish("name", "string", sequence + 1); | |||
txTpl.eventAccount(eventAccount.getAddress()).publish("name", 0, sequence + 2); | |||
commit(txTpl); | |||
} | |||
/** | |||
* 监听用户自定义事件 | |||
* | |||
* @param eventAccount 事件账户 | |||
* @param eventName 事件名 | |||
*/ | |||
private void monitorUserEvent(BlockchainKeypair eventAccount, String eventName) { | |||
EventListenerHandle<UserEventPoint> handler = blockchainService.monitorUserEvent(ledgerHash, eventAccount.getAddress().toBase58(), eventName, 0, new UserEventListener<UserEventPoint>() { | |||
@Override | |||
public void onEvent(Event eventMessage, EventContext<UserEventPoint> eventContext) { | |||
BytesValue content = eventMessage.getContent(); | |||
switch (content.getType()) { | |||
case TEXT: | |||
System.out.println(content.getBytes().toUTF8String()); | |||
break; | |||
case INT64: | |||
System.out.println(BytesUtils.toLong(content.getBytes().toBytes())); | |||
break; | |||
default: | |||
break; | |||
} | |||
// 关闭监听的两种方式:1 | |||
eventContext.getHandle().cancel(); | |||
} | |||
}); | |||
// 关闭监听的两种方式:2 | |||
handler.cancel(); | |||
} | |||
/** | |||
* 监听新区块生成事件 | |||
*/ | |||
private void monitorNewBlockCreatedEvent() { | |||
EventListenerHandle<SystemEventPoint> handler = blockchainService.monitorSystemEvent(ledgerHash, SystemEvent.NEW_BLOCK_CREATED, 0, new SystemEventListener<SystemEventPoint>() { | |||
@Override | |||
public void onEvents(Event[] eventMessages, EventContext<SystemEventPoint> eventContext) { | |||
for (Event eventMessage : eventMessages) { | |||
BytesValue content = eventMessage.getContent(); | |||
// content中存放的是当前链上最新高度 | |||
System.out.println(BytesUtils.toLong(content.getBytes().toBytes())); | |||
} | |||
// 关闭监听的两种方式:1 | |||
eventContext.getHandle().cancel(); | |||
} | |||
}); | |||
// 关闭监听的两种方式:2 | |||
handler.cancel(); | |||
} | |||
} |
@@ -0,0 +1,47 @@ | |||
package com.jd.blockchain.sdk.samples; | |||
import com.jd.blockchain.crypto.AddressEncoding; | |||
import com.jd.blockchain.crypto.KeyGenUtils; | |||
import com.jd.blockchain.crypto.PrivKey; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.*; | |||
public class SDK_RegistParticipant_Demo extends SDK_Base_Demo { | |||
public static void main(String[] args) { | |||
new SDK_RegistParticipant_Demo().regParticipant(); | |||
} | |||
public void regParticipant() { | |||
//新参与方的公私钥 | |||
String PUB = "3snPdw7i7PjVKiTH2VnXZu5H8QmNaSXpnk4ei533jFpuifyjS5zzH9"; | |||
String PRIV = "177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x"; | |||
PrivKey privKey = KeyGenUtils.decodePrivKeyWithRawPassword(PRIV, SDKDemo_Constant.PASSWORD); | |||
PubKey pubKey = KeyGenUtils.decodePubKey(PUB); | |||
System.out.println("Address = " + AddressEncoding.generateAddress(pubKey)); | |||
BlockchainKeypair user = new BlockchainKeypair(pubKey, privKey); | |||
// 定义交易模板 | |||
TransactionTemplate txTpl = blockchainService.newTransaction(ledgerHash); | |||
// 注册参与方 | |||
txTpl.participants().register("Peer4", user.getIdentity()); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTpl.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(adminKey); | |||
// 提交交易; | |||
TransactionResponse transactionResponse = prepTx.commit(); | |||
System.out.println(transactionResponse.isSuccess()); | |||
} | |||
} |
@@ -2,6 +2,7 @@ package test.com.jd.blockchain.sdk.test; | |||
import static org.junit.Assert.assertTrue; | |||
import com.jd.blockchain.ledger.*; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
@@ -12,17 +13,6 @@ import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.KeyGenUtils; | |||
import com.jd.blockchain.crypto.PrivKey; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.EndpointRequest; | |||
import com.jd.blockchain.ledger.NodeRequest; | |||
import com.jd.blockchain.ledger.ParticipantRegisterOperation; | |||
import com.jd.blockchain.ledger.ParticipantStateUpdateOperation; | |||
import com.jd.blockchain.ledger.PreparedTransaction; | |||
import com.jd.blockchain.ledger.TransactionContent; | |||
import com.jd.blockchain.ledger.TransactionContentBody; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.ledger.TransactionTemplate; | |||
import com.jd.blockchain.sdk.BlockchainService; | |||
import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||
import com.jd.blockchain.sdk.samples.SDKDemo_Constant; | |||
@@ -76,6 +66,7 @@ public class SDK_GateWay_Participant_Regist_Test_ { | |||
DataContractRegistry.register(TransactionResponse.class); | |||
DataContractRegistry.register(ParticipantRegisterOperation.class); | |||
DataContractRegistry.register(ParticipantStateUpdateOperation.class); | |||
DataContractRegistry.register(ConsensusSettingsUpdateOperation.class); | |||
} | |||
@Test | |||
@@ -95,10 +86,8 @@ public class SDK_GateWay_Participant_Regist_Test_ { | |||
BlockchainKeypair user = new BlockchainKeypair(pubKey, privKey); | |||
NetworkAddress networkAddress = new NetworkAddress("127.0.0.1", 20000); | |||
// 注册参与方 | |||
txTemp.participants().register("Peer4", user.getIdentity(), networkAddress); | |||
txTemp.participants().register("Peer4", user.getIdentity()); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
@@ -1,113 +0,0 @@ | |||
package test.com.jd.blockchain.sdk.test; | |||
import static org.junit.Assert.assertTrue; | |||
import org.junit.Before; | |||
import org.junit.Test; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.AddressEncoding; | |||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.KeyGenUtils; | |||
import com.jd.blockchain.crypto.PrivKey; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BlockchainKeypair; | |||
import com.jd.blockchain.ledger.EndpointRequest; | |||
import com.jd.blockchain.ledger.NodeRequest; | |||
import com.jd.blockchain.ledger.ParticipantNodeState; | |||
import com.jd.blockchain.ledger.ParticipantRegisterOperation; | |||
import com.jd.blockchain.ledger.ParticipantStateUpdateOperation; | |||
import com.jd.blockchain.ledger.PreparedTransaction; | |||
import com.jd.blockchain.ledger.TransactionContent; | |||
import com.jd.blockchain.ledger.TransactionContentBody; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.ledger.TransactionTemplate; | |||
import com.jd.blockchain.sdk.BlockchainService; | |||
import com.jd.blockchain.sdk.client.GatewayServiceFactory; | |||
import com.jd.blockchain.sdk.samples.SDKDemo_Constant; | |||
import com.jd.blockchain.utils.net.NetworkAddress; | |||
/** | |||
* 参与方状态更新测试 | |||
* @author zhangshuang | |||
* @create 2019/7/18 | |||
* @since 1.0.0 | |||
*/ | |||
public class SDK_GateWay_Participant_State_Update_Test_ { | |||
private PrivKey privKey; | |||
private PubKey pubKey; | |||
private BlockchainKeypair CLIENT_CERT = null; | |||
private String GATEWAY_IPADDR = null; | |||
private int GATEWAY_PORT; | |||
private boolean SECURE; | |||
private BlockchainService service; | |||
//根据密码工具产生的公私钥 | |||
static String PUB = "3snPdw7i7PkdgqiGX7GbZuFSi1cwZn7vtjw4vifb1YoXgr9k6Kfmis"; | |||
String PRIV = "177gjtZu8w1phqHFVNiFhA35cfimXmP6VuqrBFhfbXBWK8s4TRwro2tnpffwP1Emwr6SMN6"; | |||
@Before | |||
public void init() { | |||
privKey = SDK_GateWay_KeyPair_Para.privkey1; | |||
pubKey = SDK_GateWay_KeyPair_Para.pubKey1; | |||
CLIENT_CERT = new BlockchainKeypair(SDK_GateWay_KeyPair_Para.pubKey0, SDK_GateWay_KeyPair_Para.privkey0); | |||
GATEWAY_IPADDR = "127.0.0.1"; | |||
GATEWAY_PORT = 11000; | |||
SECURE = false; | |||
GatewayServiceFactory serviceFactory = GatewayServiceFactory.connect(GATEWAY_IPADDR, GATEWAY_PORT, SECURE, | |||
CLIENT_CERT); | |||
service = serviceFactory.getBlockchainService(); | |||
DataContractRegistry.register(TransactionContent.class); | |||
DataContractRegistry.register(TransactionContentBody.class); | |||
DataContractRegistry.register(TransactionRequest.class); | |||
DataContractRegistry.register(NodeRequest.class); | |||
DataContractRegistry.register(EndpointRequest.class); | |||
DataContractRegistry.register(TransactionResponse.class); | |||
DataContractRegistry.register(ParticipantRegisterOperation.class); | |||
DataContractRegistry.register(ParticipantStateUpdateOperation.class); | |||
DataContractRegistry.register(ParticipantStateUpdateOperation.class); | |||
} | |||
@Test | |||
public void updateParticipantState_Test() { | |||
HashDigest[] ledgerHashs = service.getLedgerHashs(); | |||
// 在本地定义注册账号的 TX; | |||
TransactionTemplate txTemp = service.newTransaction(ledgerHashs[0]); | |||
//existed signer | |||
AsymmetricKeypair keyPair = new BlockchainKeypair(pubKey, privKey); | |||
PrivKey privKey = KeyGenUtils.decodePrivKeyWithRawPassword(PRIV, SDKDemo_Constant.PASSWORD); | |||
PubKey pubKey = KeyGenUtils.decodePubKey(PUB); | |||
System.out.println("Address = "+AddressEncoding.generateAddress(pubKey)); | |||
BlockchainKeypair user = new BlockchainKeypair(pubKey, privKey); | |||
NetworkAddress networkAddress = new NetworkAddress("127.0.0.1", 20000); | |||
txTemp.states().update(user.getIdentity(),networkAddress, ParticipantNodeState.ACTIVED); | |||
// TX 准备就绪; | |||
PreparedTransaction prepTx = txTemp.prepare(); | |||
// 使用私钥进行签名; | |||
prepTx.sign(keyPair); | |||
// 提交交易; | |||
TransactionResponse transactionResponse = prepTx.commit(); | |||
assertTrue(transactionResponse.isSuccess()); | |||
} | |||
} |
@@ -1,18 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>base</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,30 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>binary-proto</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,77 +0,0 @@ | |||
package com.jd.blockchain.binaryproto; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import com.jd.blockchain.binaryproto.impl.DataContractContext; | |||
import com.jd.blockchain.binaryproto.impl.HeaderEncoder; | |||
import com.jd.blockchain.utils.io.BytesOutputBuffer; | |||
import com.jd.blockchain.utils.io.BytesSlice; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
public class BinaryProtocol { | |||
public static void encode(Object data, Class<?> contractType, OutputStream out) { | |||
DataContractEncoder encoder = DataContractContext.resolve(contractType); | |||
if (encoder == null) { | |||
throw new IllegalArgumentException("Contract Type not exist!--" + contractType.getName()); | |||
} | |||
BytesOutputBuffer buffer = new BytesOutputBuffer(); | |||
encoder.encode(data, buffer); | |||
buffer.writeTo(out); | |||
} | |||
public static byte[] encode(Object data, Class<?> contractType) { | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
encode(data, contractType, out); | |||
return out.toByteArray(); | |||
} | |||
public static <T> T decode(InputStream in) { | |||
byte[] bytes = BytesUtils.copyToBytes(in); | |||
return decode(bytes); | |||
} | |||
public static <T> T decode(byte[] dataSegment) { | |||
BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length); | |||
int code = HeaderEncoder.resolveCode(bytes); | |||
long version = HeaderEncoder.resolveVersion(bytes); | |||
DataContractEncoder encoder = DataContractContext.ENCODER_LOOKUP.lookup(code, version); | |||
if (encoder == null) { | |||
throw new DataContractException( | |||
String.format("No data contract was registered with code[%s] and version[%s]!", code, version)); | |||
} | |||
return encoder.decode(bytes.getInputStream()); | |||
} | |||
public static <T> T decode(byte[] dataSegment, Class<T> contractType) { | |||
return decode(dataSegment, contractType, true); | |||
} | |||
public static <T> T decode(byte[] dataSegment, Class<T> contractType, boolean autoRegister) { | |||
DataContractEncoder encoder = DataContractContext.ENCODER_LOOKUP.lookup(contractType); | |||
if (encoder == null) { | |||
if (autoRegister) { | |||
encoder = DataContractContext.resolve(contractType); | |||
} else { | |||
throw new DataContractException("Contract type is not registered! --" + contractType.toString()); | |||
} | |||
} | |||
BytesSlice bytes = new BytesSlice(dataSegment, 0, dataSegment.length); | |||
return encoder.decode(bytes.getInputStream()); | |||
} | |||
@Deprecated | |||
public static <T> T decodeAs(byte[] dataSegment, Class<T> contractType) { | |||
return decode(dataSegment, contractType, true); | |||
} | |||
@Deprecated | |||
public static <T> T decodeAs(byte[] dataSegment, Class<T> contractType, boolean autoRegister) { | |||
return decode(dataSegment, contractType, autoRegister); | |||
} | |||
} |
@@ -1,66 +0,0 @@ | |||
package com.jd.blockchain.binaryproto; | |||
/** | |||
* 键值操作的数据类型; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public enum PrimitiveType { | |||
/** | |||
* 空; | |||
*/ | |||
NIL(BaseType.NIL), | |||
/** | |||
* 布尔型; | |||
*/ | |||
BOOLEAN(BaseType.BOOLEAN), | |||
/** | |||
* 8位的整数: | |||
*/ | |||
INT8(BaseType.INT8), | |||
/** | |||
* 16位整数; | |||
*/ | |||
INT16(BaseType.INT16), | |||
/** | |||
* 32位整数; | |||
*/ | |||
INT32(BaseType.INT32), | |||
/** | |||
* 64位整数; | |||
*/ | |||
INT64(BaseType.INT64), | |||
/** | |||
* 文本; | |||
*/ | |||
TEXT(BaseType.TEXT), | |||
/** | |||
* 二进制数据; | |||
*/ | |||
BYTES(BaseType.BYTES); | |||
public final byte CODE; | |||
private PrimitiveType(byte code) { | |||
this.CODE = code; | |||
} | |||
public static PrimitiveType valueOf(byte code) { | |||
for (PrimitiveType dataType : PrimitiveType.values()) { | |||
if (dataType.CODE == code) { | |||
return dataType; | |||
} | |||
} | |||
throw new IllegalArgumentException("Code[" + code + "] not suppported by PrimitiveType!"); | |||
} | |||
} |
@@ -1,74 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>consensus-bftsmart</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>bft-smart</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>base</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.commons</groupId> | |||
<artifactId>commons-pool2</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-model</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>tools-keygen</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<!-- <dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-core</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> --> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,75 +0,0 @@ | |||
package com.jd.blockchain.consensus.bftsmart.client; | |||
import bftsmart.tom.AsynchServiceProxy; | |||
import com.jd.blockchain.consensus.MessageService; | |||
import com.jd.blockchain.utils.concurrent.AsyncFuture; | |||
import com.jd.blockchain.utils.concurrent.CompletableAsyncFuture; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
public class BftsmartMessageService implements MessageService { | |||
private BftsmartPeerProxyPool asyncPeerProxyPool; | |||
public BftsmartMessageService(BftsmartPeerProxyPool peerProxyPool) { | |||
this.asyncPeerProxyPool = peerProxyPool; | |||
} | |||
@Override | |||
public AsyncFuture<byte[]> sendOrdered(byte[] message) { | |||
return sendOrderedMessage(message); | |||
} | |||
private AsyncFuture<byte[]> sendOrderedMessage(byte[] message) { | |||
CompletableAsyncFuture<byte[]> asyncFuture = new CompletableAsyncFuture<>(); | |||
AsynchServiceProxy asynchServiceProxy = null; | |||
try { | |||
asynchServiceProxy = asyncPeerProxyPool.borrowObject(); | |||
// //0: Transaction msg, 1: Commitblock msg | |||
// byte[] msgType = BytesUtils.toBytes(0); | |||
// byte[] wrapMsg = new byte[message.length + 4]; | |||
// System.arraycopy(message, 0, wrapMsg, 4, message.length); | |||
// System.arraycopy(msgType, 0, wrapMsg, 0, 4); | |||
// | |||
// System.out.printf("BftsmartMessageService invokeOrdered time = %s, id = %s threadId = %s \r\n", | |||
// System.currentTimeMillis(), asynchServiceProxy.getProcessId(), Thread.currentThread().getId()); | |||
byte[] result = asynchServiceProxy.invokeOrdered(message); | |||
asyncFuture.complete(result); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} finally { | |||
if (asynchServiceProxy != null) { | |||
asyncPeerProxyPool.returnObject(asynchServiceProxy); | |||
} | |||
} | |||
return asyncFuture; | |||
} | |||
@Override | |||
public AsyncFuture<byte[]> sendUnordered(byte[] message) { | |||
return sendUnorderedMessage(message); | |||
} | |||
private AsyncFuture<byte[]> sendUnorderedMessage(byte[] message) { | |||
CompletableAsyncFuture<byte[]> asyncFuture = new CompletableAsyncFuture<>(); | |||
AsynchServiceProxy asynchServiceProxy = null; | |||
try { | |||
asynchServiceProxy = asyncPeerProxyPool.borrowObject(); | |||
byte[] result = asynchServiceProxy.invokeUnordered(message); | |||
asyncFuture.complete(result); | |||
} catch (Exception e) { | |||
throw new RuntimeException(e); | |||
} finally { | |||
asyncPeerProxyPool.returnObject(asynchServiceProxy); | |||
} | |||
return asyncFuture; | |||
} | |||
} |
@@ -1,549 +0,0 @@ | |||
package com.jd.blockchain.consensus.bftsmart.service; | |||
import java.io.ByteArrayOutputStream; | |||
import java.util.*; | |||
import java.util.concurrent.CopyOnWriteArrayList; | |||
import java.util.concurrent.ExecutorService; | |||
import java.util.concurrent.Executors; | |||
import bftsmart.consensus.app.BatchAppResultImpl; | |||
import bftsmart.tom.*; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.consensus.service.*; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
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; | |||
import com.jd.blockchain.consensus.ConsensusManageService; | |||
import com.jd.blockchain.consensus.NodeSettings; | |||
import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider; | |||
import com.jd.blockchain.consensus.bftsmart.BftsmartConsensusSettings; | |||
import com.jd.blockchain.consensus.bftsmart.BftsmartNodeSettings; | |||
import com.jd.blockchain.consensus.bftsmart.BftsmartTopology; | |||
import com.jd.blockchain.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.server.defaultservices.DefaultRecoverable; | |||
public class BftsmartNodeServer extends DefaultRecoverable implements NodeServer { | |||
private static Logger LOGGER = LoggerFactory.getLogger(BftsmartNodeServer.class); | |||
// private static final String DEFAULT_BINDING_HOST = "0.0.0.0"; | |||
private List<StateHandle> stateHandles = new CopyOnWriteArrayList<>(); | |||
// TODO 暂不处理队列溢出问题 | |||
private ExecutorService notifyReplyExecutors = Executors.newSingleThreadExecutor(); | |||
private volatile Status status = Status.STOPPED; | |||
private final Object mutex = new Object(); | |||
private volatile ServiceReplica replica; | |||
private StateMachineReplicate stateMachineReplicate; | |||
private ServerSettings serverSettings; | |||
private BftsmartConsensusManageService manageService; | |||
private volatile BftsmartTopology topology; | |||
private volatile BftsmartConsensusSettings setting; | |||
private TOMConfiguration tomConfig; | |||
// private TOMConfiguration outerTomConfig; | |||
private HostsConfig hostsConfig; | |||
private Properties systemConfig; | |||
private MessageHandle messageHandle; | |||
private String providerName; | |||
private String realmName; | |||
private int serverId; | |||
public BftsmartNodeServer() { | |||
} | |||
public BftsmartNodeServer(ServerSettings serverSettings, MessageHandle messageHandler, StateMachineReplicate stateMachineReplicate) { | |||
this.serverSettings = serverSettings; | |||
this.realmName = serverSettings.getRealmName(); | |||
//used later | |||
this.stateMachineReplicate = stateMachineReplicate; | |||
this.messageHandle = messageHandler; | |||
createConfig(); | |||
serverId = findServerId(); | |||
initConfig(serverId, systemConfig, hostsConfig); | |||
this.manageService = new BftsmartConsensusManageService(this); | |||
} | |||
protected int findServerId() { | |||
int serverId = 0; | |||
for (int i = 0; i < hostsConfig.getNum(); i++) { | |||
String host = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getHost(); | |||
int port = ((BftsmartNodeSettings)serverSettings.getReplicaSettings()).getNetworkAddress().getPort(); | |||
if (hostsConfig.getHost(i).equals(host) && hostsConfig.getPort(i) == port) { | |||
serverId = i; | |||
break; | |||
} | |||
} | |||
return serverId; | |||
} | |||
public int getServerId() { | |||
return serverId; | |||
} | |||
protected void createConfig() { | |||
setting = ((BftsmartServerSettings) serverSettings).getConsensusSettings(); | |||
List<HostsConfig.Config> configList = new ArrayList<>(); | |||
NodeSettings[] nodeSettingsArray = setting.getNodes(); | |||
for (NodeSettings nodeSettings : nodeSettingsArray) { | |||
BftsmartNodeSettings node = (BftsmartNodeSettings)nodeSettings; | |||
configList.add(new HostsConfig.Config(node.getId(), node.getNetworkAddress().getHost(), node.getNetworkAddress().getPort())); | |||
} | |||
//create HostsConfig instance based on consensus realm nodes | |||
hostsConfig = new HostsConfig(configList.toArray(new HostsConfig.Config[configList.size()])); | |||
systemConfig = PropertiesUtils.createProperties(setting.getSystemConfigs()); | |||
return; | |||
} | |||
protected void initConfig(int id, Properties systemsConfig, HostsConfig hostConfig) { | |||
// byte[] serialHostConf = BinarySerializeUtils.serialize(hostConfig); | |||
// Properties sysConfClone = (Properties)systemsConfig.clone(); | |||
// int port = hostConfig.getPort(id); | |||
// hostConfig.add(id, DEFAULT_BINDING_HOST, port); | |||
this.tomConfig = new TOMConfiguration(id, systemsConfig, hostConfig); | |||
// this.outerTomConfig = new TOMConfiguration(id, systemsConfig, hostConfig); | |||
} | |||
@Override | |||
public ConsensusManageService getManageService() { | |||
return manageService; | |||
} | |||
@Override | |||
public ServerSettings getSettings() { | |||
return serverSettings; | |||
} | |||
@Override | |||
public String getProviderName() { | |||
return BftsmartConsensusProvider.NAME; | |||
} | |||
public TOMConfiguration getTomConfig() { | |||
return tomConfig; | |||
} | |||
public int getId() { | |||
return tomConfig.getProcessId(); | |||
} | |||
public void setId(int id) { | |||
if (id < 0) { | |||
throw new IllegalArgumentException("ReplicaID is negative!"); | |||
} | |||
this.tomConfig.setProcessId(id); | |||
// this.outerTomConfig.setProcessId(id); | |||
} | |||
public BftsmartConsensusSettings getConsensusSetting() { | |||
return setting; | |||
} | |||
public BftsmartTopology getTopology() { | |||
return topology; | |||
} | |||
public Status getStatus() { | |||
return status; | |||
} | |||
@Override | |||
public boolean isRunning() { | |||
return status == Status.RUNNING; | |||
} | |||
public byte[] appExecuteUnordered(byte[] bytes, MessageContext messageContext) { | |||
return messageHandle.processUnordered(bytes).get(); | |||
} | |||
/** | |||
* | |||
* 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); | |||
} | |||
} | |||
/** | |||
* | |||
* Local peer has cid diff with remote peer, used by state transfer when peer start | |||
* | |||
*/ | |||
private byte[][] appExecuteDiffBatch(byte[][] commands, MessageContext[] msgCtxs) { | |||
int manageConsensusId = msgCtxs[0].getConsensusId(); | |||
List<byte[]> manageConsensusCmds = new ArrayList<>(); | |||
int index = 0; | |||
for (MessageContext msgCtx : msgCtxs) { | |||
if (msgCtx.getConsensusId() == manageConsensusId) { | |||
manageConsensusCmds.add(commands[index]); | |||
} else { | |||
// 达到结块标准,需要进行结块并应答 | |||
block(manageConsensusCmds); | |||
// 重置链表和共识ID | |||
manageConsensusCmds = new ArrayList<>(); | |||
manageConsensusId = msgCtx.getConsensusId(); | |||
manageConsensusCmds.add(commands[index]); | |||
} | |||
index++; | |||
} | |||
// 结束时,肯定有最后一个结块请求未处理 | |||
if (!manageConsensusCmds.isEmpty()) { | |||
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) { | |||
List<AsyncFuture<byte[]>> asyncFutureLinkedList = new ArrayList<>(commands.length); | |||
List<byte[]> responseLinkedList = new ArrayList<>(); | |||
StateSnapshot newStateSnapshot = null; | |||
StateSnapshot preStateSnapshot = null; | |||
StateSnapshot genisStateSnapshot = null; | |||
BatchAppResultImpl result = null; | |||
String batchId = null; | |||
int msgId = 0; | |||
try { | |||
batchId = messageHandle.beginBatch(realmName); | |||
genisStateSnapshot = messageHandle.getGenisStateSnapshot(realmName); | |||
preStateSnapshot = messageHandle.getStateSnapshot(realmName); | |||
if (preStateSnapshot == null) { | |||
throw new IllegalStateException("Pre block state snapshot is null!"); | |||
} | |||
for (int i = 0; i < commands.length; i++) { | |||
byte[] txContent = commands[i]; | |||
AsyncFuture<byte[]> asyncFuture = messageHandle.processOrdered(msgId++, txContent, realmName, batchId); | |||
asyncFutureLinkedList.add(asyncFuture); | |||
} | |||
newStateSnapshot = messageHandle.completeBatch(realmName, batchId); | |||
for (int i = 0; i < asyncFutureLinkedList.size(); i++) { | |||
responseLinkedList.add(asyncFutureLinkedList.get(i).get()); | |||
} | |||
result = new BatchAppResultImpl(responseLinkedList, newStateSnapshot.getSnapshot(), batchId, genisStateSnapshot.getSnapshot()); | |||
result.setErrorCode((byte) 0); | |||
} catch (Exception e) { | |||
LOGGER.error("Error occurred while pre compute app! --" + e.getMessage(), e); | |||
for (int i = 0; i < commands.length; i++) { | |||
responseLinkedList.add(createAppResponse(commands[i],TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK)); | |||
} | |||
result = new BatchAppResultImpl(responseLinkedList,preStateSnapshot.getSnapshot(), batchId, genisStateSnapshot.getSnapshot()); | |||
result.setErrorCode((byte) 1); | |||
} | |||
return result; | |||
} | |||
// Block full rollback responses, generated in pre compute phase, due to tx exception | |||
private byte[] createAppResponse(byte[] command, TransactionState transactionState) { | |||
TransactionRequest txRequest = BinaryProtocol.decode(command); | |||
TxResponseMessage resp = new TxResponseMessage(txRequest.getTransactionContent().getHash()); | |||
resp.setExecutionState(transactionState); | |||
return BinaryProtocol.encode(resp, TransactionResponse.class); | |||
} | |||
public List<byte[]> updateAppResponses(List<byte[]> asyncResponseLinkedList, byte[] commonHash, boolean isConsistent) { | |||
List<byte[]> updatedResponses = new ArrayList<>(); | |||
TxResponseMessage resp = null; | |||
for(int i = 0; i < asyncResponseLinkedList.size(); i++) { | |||
TransactionResponse txResponse = BinaryProtocol.decode(asyncResponseLinkedList.get(i)); | |||
if (isConsistent) { | |||
resp = new TxResponseMessage(txResponse.getContentHash()); | |||
} | |||
else { | |||
resp = new TxResponseMessage(new HashDigest(commonHash)); | |||
} | |||
resp.setExecutionState(TransactionState.IGNORED_BY_BLOCK_FULL_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) { | |||
try { | |||
messageHandle.commitBatch(realmName, batchId); | |||
} catch (BlockRollbackException e) { | |||
LOGGER.error("Error occurred while pre compute commit --" + e.getMessage(), e); | |||
throw e; | |||
} | |||
} | |||
/** | |||
* | |||
* 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_BLOCK_FULL_ROLLBACK.CODE); | |||
LOGGER.debug("Rollback of operations that cause inconsistencies in the ledger"); | |||
} | |||
//notice | |||
public byte[] getSnapshot() { | |||
LOGGER.debug("------- GetSnapshot...[replica.id=" + this.getId() + "]"); | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
BytesUtils.writeInt(stateHandles.size(), out); | |||
for (StateHandle stateHandle : stateHandles) { | |||
// TODO: 测试代码; | |||
return stateHandle.takeSnapshot(); | |||
} | |||
return out.toByteArray(); | |||
} | |||
public void installSnapshot(byte[] snapshot) { | |||
// System.out.println("Not implement!"); | |||
} | |||
@Override | |||
public void start() { | |||
if (this.getId() < 0) { | |||
throw new IllegalStateException("Unset server node ID!"); | |||
} | |||
LOGGER.debug("=============================== Start replica ==================================="); | |||
if (status != Status.STOPPED) { | |||
return; | |||
} | |||
synchronized (mutex) { | |||
if (status != Status.STOPPED) { | |||
return; | |||
} | |||
status = Status.STARTING; | |||
try { | |||
LOGGER.debug("Start replica...[ID=" + getId() + "]"); | |||
this.replica = new ServiceReplica(tomConfig, this, this); | |||
this.topology = new BftsmartTopology(replica.getReplicaContext().getCurrentView()); | |||
status = Status.RUNNING; | |||
// createProxyClient(); | |||
LOGGER.debug( | |||
"=============================== Replica started success! ==================================="); | |||
} catch (RuntimeException e) { | |||
status = Status.STOPPED; | |||
throw e; | |||
} | |||
} | |||
} | |||
@Override | |||
public void stop() { | |||
if (status != Status.RUNNING) { | |||
return; | |||
} | |||
synchronized (mutex) { | |||
if (status != Status.RUNNING) { | |||
return; | |||
} | |||
status = Status.STOPPING; | |||
try { | |||
ServiceReplica rep = this.replica; | |||
if (rep != null) { | |||
LOGGER.debug("Stop replica...[ID=" + rep.getId() + "]"); | |||
this.replica = null; | |||
this.topology = null; | |||
rep.kill(); | |||
LOGGER.debug("Replica had stopped! --[ID=" + rep.getId() + "]"); | |||
} | |||
} finally { | |||
status = Status.STOPPED; | |||
} | |||
} | |||
} | |||
enum Status { | |||
STARTING, | |||
RUNNING, | |||
STOPPING, | |||
STOPPED | |||
} | |||
} |
@@ -1,34 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>consensus-framework</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>binary-proto</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-model</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,25 +0,0 @@ | |||
package com.jd.blockchain.consensus; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@Target({ ElementType.PARAMETER }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface ActionMessage { | |||
/** | |||
* 请求参数转换器; | |||
* <p> | |||
* 指定一个 {@link BinaryMessageConverter} 接口的实现类; | |||
* | |||
* @return | |||
*/ | |||
Class<?> converter(); | |||
} |
@@ -1,18 +0,0 @@ | |||
package com.jd.blockchain.consensus; | |||
public class ConsensusSecurityException extends Exception{ | |||
private static final long serialVersionUID = -164820276123627155L; | |||
public ConsensusSecurityException() { | |||
} | |||
public ConsensusSecurityException(String message) { | |||
super(message); | |||
} | |||
public ConsensusSecurityException(String message, Throwable cause) { | |||
super(message, cause); | |||
} | |||
} |
@@ -1,12 +0,0 @@ | |||
package com.jd.blockchain.consensus; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
@Target({ ElementType.TYPE }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface ConsensusService { | |||
} |
@@ -1,35 +0,0 @@ | |||
package com.jd.blockchain.consensus; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* 标识一个共识方法调用模式为“有序的消息调用”; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@Target({ ElementType.METHOD }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface OrderedAction { | |||
/** | |||
* 请求分组的索引器;<br> | |||
* | |||
* 指定一个 {@link GroupIndexer} 接口的实现类,用于根据请求消息列表来生成共识的分组ID; | |||
* @return | |||
*/ | |||
Class<?> groupIndexer() ; | |||
/** | |||
* 回复消息转换器; | |||
* <p> | |||
* 指定一个 {@link BinaryMessageConverter} 接口的实现类; | |||
* | |||
* @return | |||
*/ | |||
Class<?> responseConverter(); | |||
} |
@@ -1,22 +0,0 @@ | |||
package com.jd.blockchain.consensus; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
/** | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@Target({ ElementType.METHOD }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface UnorderedAction { | |||
/** | |||
* 请求分组的索引器;<br> | |||
* | |||
* 指定一个 {@link GroupIndexer} 接口的实现类,用于根据请求消息列表来生成共识的分组ID; | |||
* @return | |||
*/ | |||
Class<?> groupIndexer() ; | |||
} |
@@ -1,49 +0,0 @@ | |||
package com.jd.blockchain.consensus.action; | |||
public class ActionResponseData implements ActionResponse { | |||
private byte[] message; | |||
private boolean error = false; | |||
private String errorMessage; | |||
private String errorType; | |||
@Override | |||
public byte[] getMessage() { | |||
return message; | |||
} | |||
public void setMessage(byte[] message) { | |||
this.message = message; | |||
} | |||
@Override | |||
public boolean getError() { | |||
return error; | |||
} | |||
public void setError(boolean error) { | |||
this.error = error; | |||
} | |||
@Override | |||
public String getErrorMessage() { | |||
return errorMessage; | |||
} | |||
public void setErrorMessage(String errorMessage) { | |||
this.errorMessage = errorMessage; | |||
} | |||
@Override | |||
public String getErrorType() { | |||
return errorType; | |||
} | |||
public void setErrorType(String errorType) { | |||
this.errorType = errorType; | |||
} | |||
} |
@@ -1,79 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>consensus-mq</artifactId> | |||
<name>consensus-mq</name> | |||
<properties> | |||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> | |||
<maven.compiler.source>1.8</maven.compiler.source> | |||
<maven.compiler.target>1.8</maven.compiler.target> | |||
</properties> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>tools-keygen</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-model</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.lmax</groupId> | |||
<artifactId>disruptor</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>io.nats</groupId> | |||
<artifactId>jnats</artifactId> | |||
</dependency> | |||
<!-- rabbitmq --> | |||
<dependency> | |||
<groupId>com.rabbitmq</groupId> | |||
<artifactId>amqp-client</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>junit</groupId> | |||
<artifactId>junit</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,18 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>consensus</artifactId> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>consensus-framework</module> | |||
<module>consensus-bftsmart</module> | |||
<module>consensus-mq</module> | |||
</modules> | |||
</project> |
@@ -1,44 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>contract-framework</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-model</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,51 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>contract-jvm</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>runtime-context</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>runtime-modular</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,74 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>contract-maven-plugin</artifactId> | |||
<packaging>maven-plugin</packaging> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-model</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>sdk-client</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>tools-keygen</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.maven</groupId> | |||
<artifactId>maven-plugin-api</artifactId> | |||
<version>3.3.9</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.maven.plugin-tools</groupId> | |||
<artifactId>maven-plugin-annotations</artifactId> | |||
<version>3.6.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-assembly-plugin</artifactId> | |||
<version>2.6</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.ow2.asm</groupId> | |||
<artifactId>asm</artifactId> | |||
<version>5.0.4</version> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-plugin-plugin</artifactId> | |||
<version>3.5</version> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,19 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>contract</artifactId> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>contract-framework</module> | |||
<module>contract-jvm</module> | |||
<module>contract-maven-plugin</module> | |||
<module>contract-samples</module> | |||
</modules> | |||
</project> |
@@ -1,56 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>crypto-adv</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-sm</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.bouncycastle</groupId> | |||
<artifactId>bcprov-jdk15on</artifactId> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on --> | |||
<dependency> | |||
<groupId>org.bouncycastle</groupId> | |||
<artifactId>bcpkix-jdk15on</artifactId> | |||
<version>1.61</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>binary-proto</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>net.i2p.crypto</groupId> | |||
<artifactId>eddsa</artifactId> | |||
<version>0.1.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>net.java.dev.jna</groupId> | |||
<artifactId>jna</artifactId> | |||
<version>5.1.0</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
<scope>compile</scope> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,26 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>crypto-classic</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on --> | |||
<dependency> | |||
<groupId>org.bouncycastle</groupId> | |||
<artifactId>bcpkix-jdk15on</artifactId> | |||
<version>1.61</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,34 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>crypto-framework</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>base</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-serialize</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>binary-proto</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,65 +0,0 @@ | |||
package com.jd.blockchain.crypto; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.OutputStream; | |||
import java.util.Arrays; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.io.BytesEncoding; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import com.jd.blockchain.utils.io.NumberMask; | |||
import com.jd.blockchain.utils.security.RipeMD160Utils; | |||
import com.jd.blockchain.utils.security.ShaUtils; | |||
public class AddressEncoding { | |||
/** | |||
* 将区块链地址写入到输出流;<br> | |||
* | |||
* 现将地址按 Base58 解码为字节数组,并将字节数组以 {@link BytesEncoding} 的方式写入输出流;<br> | |||
* | |||
* 如果指定的地址为 null,则仅写入空字节数组;注:此种情况下,输出流并不是完全没有写入,而是实际上会被写入一个表示内容长度为 0 的头部字节;<br> | |||
* | |||
* @param address | |||
* 要写入的区块链地址; | |||
* @param out | |||
* 输出流; | |||
* @return 写入的地址的字节数;如果指定地址为 null,则返回值为写入的头部字节数;; | |||
*/ | |||
public static int writeAddress(Bytes address, OutputStream out) { | |||
return address.writeTo(out); | |||
} | |||
/** | |||
* 从流中读取区块链地址; | |||
* | |||
* @param in | |||
* @return | |||
* @throws IOException | |||
*/ | |||
public static Bytes readAddress(InputStream in) throws IOException { | |||
byte[] bytesAddress = BytesEncoding.read(NumberMask.TINY, in); | |||
if (bytesAddress.length == 0) { | |||
return null; | |||
} | |||
return new Bytes(bytesAddress); | |||
} | |||
/** | |||
* 从公钥生成地址; | |||
* | |||
* @param pubKey | |||
* @return | |||
*/ | |||
public static Bytes generateAddress(PubKey pubKey) { | |||
byte[] h1Bytes = ShaUtils.hash_256(pubKey.getRawKeyBytes()); | |||
byte[] h2Bytes = RipeMD160Utils.hash(h1Bytes); | |||
byte[] xBytes = BytesUtils.concat(new byte[] { AddressVersion.V1.CODE}, BytesUtils.toBytes(pubKey.getAlgorithm()), h2Bytes); | |||
byte[] checksum = Arrays.copyOf(ShaUtils.hash_256(ShaUtils.hash_256(xBytes)), 4); | |||
byte[] addressBytes = BytesUtils.concat(xBytes, checksum); | |||
return new Bytes(addressBytes); | |||
} | |||
} |
@@ -1,24 +0,0 @@ | |||
package com.jd.blockchain.crypto; | |||
/** | |||
* The version of Blockchain Address generation rule; <br> | |||
* | |||
* | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public enum AddressVersion { | |||
V1((byte) 0x91); | |||
// Note: Implementor can only add new enum items, cann't remove or modify | |||
// existing enum items; | |||
public final byte CODE; | |||
AddressVersion(byte code) { | |||
CODE = code; | |||
} | |||
} |
@@ -1,25 +0,0 @@ | |||
package com.jd.blockchain.crypto; | |||
/** | |||
* 密钥; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public interface CryptoKey extends CryptoBytes { | |||
/** | |||
* 密钥的类型; | |||
* @return | |||
*/ | |||
CryptoKeyType getKeyType(); | |||
/** | |||
* 原始的密钥数据; | |||
* | |||
* @return | |||
*/ | |||
byte[] getRawKeyBytes(); | |||
} |
@@ -1,29 +0,0 @@ | |||
package com.jd.blockchain.crypto; | |||
/** | |||
* 私钥; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class PrivKey extends BaseCryptoKey { | |||
private static final long serialVersionUID = 6265440395252295646L; | |||
public PrivKey(short algorithm, byte[] rawCryptoBytes) { | |||
super(algorithm, rawCryptoBytes, CryptoKeyType.PRIVATE); | |||
} | |||
public PrivKey(CryptoAlgorithm algorithm, byte[] rawCryptoBytes) { | |||
super(algorithm, rawCryptoBytes, CryptoKeyType.PRIVATE); | |||
} | |||
public PrivKey(byte[] cryptoBytes) { | |||
super(cryptoBytes); | |||
} | |||
@Override | |||
public CryptoKeyType getKeyType() { | |||
return CryptoKeyType.PRIVATE; | |||
} | |||
} |
@@ -1,34 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<parent> | |||
<artifactId>crypto</artifactId> | |||
<groupId>com.jd.blockchain</groupId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>crypto-pki</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.bouncycastle</groupId> | |||
<artifactId>bcprov-jdk15on</artifactId> | |||
<version>1.61</version> | |||
</dependency> | |||
<!-- https://mvnrepository.com/artifact/org.bouncycastle/bcpkix-jdk15on --> | |||
<dependency> | |||
<groupId>org.bouncycastle</groupId> | |||
<artifactId>bcpkix-jdk15on</artifactId> | |||
<version>1.61</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,20 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>crypto-sm</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,22 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>crypto</artifactId> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>crypto-framework</module> | |||
<module>crypto-classic</module> | |||
<module>crypto-sm</module> | |||
<!-- <module>crypto-jni-clib</module> --> | |||
<module>crypto-adv</module> | |||
<module>crypto-pki</module> | |||
</modules> | |||
</project> |
@@ -1,14 +0,0 @@ | |||
<?xml version="1.0" encoding="UTF-8"?> | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<parent> | |||
<artifactId>jdchain-root</artifactId> | |||
<groupId>com.jd.blockchain</groupId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<modelVersion>4.0.0</modelVersion> | |||
<artifactId>deployAsEnd</artifactId> | |||
</project> |
@@ -1,38 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>deployment</artifactId> | |||
<packaging>pom</packaging> | |||
<modules> | |||
<module>deployment-gateway</module> | |||
<module>deployment-peer</module> | |||
</modules> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,150 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>jdchain-root</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>gateway</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus-bftsmart</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus-mq</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-rpc</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>sdk-base</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>tools-keygen</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>data-explorer</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-web</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-classic</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-sm</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>commons-io</groupId> | |||
<artifactId>commons-io</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-web</artifactId> | |||
<exclusions> | |||
<exclusion> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-logging</artifactId> | |||
</exclusion> | |||
</exclusions> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-log4j2</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-starter-security</artifactId> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-configuration-processor</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework.boot</groupId> | |||
<artifactId>spring-boot-devtools</artifactId> | |||
<optional>true</optional> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-jar-plugin</artifactId> | |||
<configuration> | |||
<archive> | |||
<manifest> | |||
<mainClass>com.jd.blockchain.gateway.GatewayServerBooter</mainClass> | |||
<addClasspath>true</addClasspath> | |||
<classpathPrefix>.</classpathPrefix> | |||
<useUniqueVersions>false</useUniqueVersions> | |||
</manifest> | |||
</archive> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
</project> |
@@ -1,14 +0,0 @@ | |||
package com.jd.blockchain.gateway; | |||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; | |||
import org.springframework.boot.autoconfigure.SpringBootApplication; | |||
import org.springframework.boot.context.properties.EnableConfigurationProperties; | |||
import org.springframework.context.annotation.ComponentScan; | |||
@EnableAutoConfiguration | |||
@EnableConfigurationProperties | |||
@SpringBootApplication | |||
@ComponentScan | |||
public class GatewayConfiguration { | |||
} | |||
@@ -1,178 +0,0 @@ | |||
package com.jd.blockchain.gateway; | |||
import java.io.File; | |||
import java.io.InputStream; | |||
import java.net.URL; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import org.apache.commons.io.FileUtils; | |||
import org.springframework.boot.SpringApplication; | |||
import org.springframework.context.ConfigurableApplicationContext; | |||
import org.springframework.core.io.ClassPathResource; | |||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
import com.jd.blockchain.crypto.KeyGenUtils; | |||
import com.jd.blockchain.crypto.PrivKey; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.gateway.web.BlockBrowserController; | |||
import com.jd.blockchain.utils.ArgumentSet; | |||
import com.jd.blockchain.utils.ArgumentSet.ArgEntry; | |||
import com.jd.blockchain.utils.BaseConstant; | |||
import com.jd.blockchain.utils.ConsoleUtils; | |||
public class GatewayServerBooter { | |||
private static final String DEFAULT_GATEWAY_PROPS = "application-gw.properties"; | |||
// 当前参与方在初始化配置中的参与方列表的编号; | |||
private static final String HOST_ARG = "-c"; | |||
//sp;针对spring.config.location这个参数进行包装; | |||
private static final String SPRING_CF_LOCATION = BaseConstant.SPRING_CF_LOCATION; | |||
// 是否输出调试信息; | |||
private static final String DEBUG_OPT = "-debug"; | |||
public static void main(String[] args) { | |||
boolean debug = false; | |||
try { | |||
ArgumentSet arguments = ArgumentSet.resolve(args, ArgumentSet.setting().prefix(HOST_ARG, SPRING_CF_LOCATION).option(DEBUG_OPT)); | |||
debug = arguments.hasOption(DEBUG_OPT); | |||
ArgEntry argHost = arguments.getArg(HOST_ARG); | |||
String configFile = argHost == null ? null : argHost.getValue(); | |||
GatewayConfigProperties configProps; | |||
if (configFile == null) { | |||
ConsoleUtils.info("Load build-in default configuration ..."); | |||
ClassPathResource configResource = new ClassPathResource("gateway.conf"); | |||
try (InputStream in = configResource.getInputStream()) { | |||
configProps = GatewayConfigProperties.resolve(in); | |||
} | |||
} else { | |||
ConsoleUtils.info("Load configuration ..."); | |||
configProps = GatewayConfigProperties.resolve(argHost.getValue()); | |||
} | |||
//spring config location; | |||
String springConfigLocation=null; | |||
ArgumentSet.ArgEntry spConfigLocation = arguments.getArg(SPRING_CF_LOCATION); | |||
if (spConfigLocation != null) { | |||
springConfigLocation = spConfigLocation.getValue(); | |||
}else { | |||
//if no the config file, then should tip as follows. but it's not a good feeling, so we create it by inputStream; | |||
ConsoleUtils.info("no param:-sp, format: -sp /x/xx.properties, use the default application-gw.properties "); | |||
ClassPathResource configResource = new ClassPathResource(DEFAULT_GATEWAY_PROPS); | |||
InputStream in = configResource.getInputStream(); | |||
// 将文件写入至config目录下 | |||
String configPath = bootPath() + "config" + File.separator + DEFAULT_GATEWAY_PROPS; | |||
File targetFile = new File(configPath); | |||
// 先将原来文件删除再Copy | |||
if (targetFile.exists()) { | |||
FileUtils.forceDelete(targetFile); | |||
} | |||
FileUtils.copyInputStreamToFile(in, targetFile); | |||
springConfigLocation = "file:" + targetFile.getAbsolutePath(); | |||
} | |||
// 启动服务器; | |||
ConsoleUtils.info("Starting web server......"); | |||
GatewayServerBooter booter = new GatewayServerBooter(configProps,springConfigLocation); | |||
booter.start(); | |||
ConsoleUtils.info("Peer[%s] is connected success!", configProps.masterPeerAddress().toString()); | |||
} catch (Exception e) { | |||
ConsoleUtils.error("Error!! %s", e.getMessage()); | |||
if (debug) { | |||
e.printStackTrace(); | |||
} | |||
} | |||
} | |||
private volatile ConfigurableApplicationContext appCtx; | |||
private GatewayConfigProperties config; | |||
private AsymmetricKeypair defaultKeyPair; | |||
private String springConfigLocation; | |||
public GatewayServerBooter(GatewayConfigProperties config, String springConfigLocation) { | |||
this.config = config; | |||
this.springConfigLocation = springConfigLocation; | |||
String base58Pwd = config.keys().getDefault().getPrivKeyPassword(); | |||
if (base58Pwd == null || base58Pwd.length() == 0) { | |||
base58Pwd = KeyGenUtils.readPasswordString(); | |||
} | |||
// 加载密钥; | |||
PubKey pubKey = KeyGenUtils.decodePubKey(config.keys().getDefault().getPubKeyValue()); | |||
PrivKey privKey = null; | |||
String base58PrivKey = config.keys().getDefault().getPrivKeyValue(); | |||
if (base58PrivKey == null) { | |||
//注:GatewayConfigProperties 确保了 PrivKeyValue 和 PrivKeyPath 必有其一; | |||
privKey = KeyGenUtils.readPrivKey(config.keys().getDefault().getPrivKeyPath(), base58Pwd); | |||
} else { | |||
privKey = KeyGenUtils.decodePrivKey(base58PrivKey, base58Pwd); | |||
} | |||
defaultKeyPair = new AsymmetricKeypair(pubKey, privKey); | |||
} | |||
public synchronized void start() { | |||
if (this.appCtx != null) { | |||
throw new IllegalStateException("Gateway server is running already."); | |||
} | |||
this.appCtx = startServer(config.http().getHost(), config.http().getPort(), springConfigLocation, | |||
config.http().getContextPath()); | |||
ConsoleUtils.info("\r\n\r\nStart connecting to peer ...."); | |||
BlockBrowserController blockBrowserController = appCtx.getBean(BlockBrowserController.class); | |||
blockBrowserController.setDataRetrievalUrl(config.dataRetrievalUrl()); | |||
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()); | |||
} | |||
public synchronized void close() { | |||
if (this.appCtx == null) { | |||
return; | |||
} | |||
this.appCtx.close(); | |||
} | |||
private static ConfigurableApplicationContext startServer(String host, int port, String springConfigLocation, String contextPath) { | |||
List<String> argList = new ArrayList<String>(); | |||
argList.add(String.format("--server.address=%s", host)); | |||
argList.add(String.format("--server.port=%s", port)); | |||
if(springConfigLocation != null){ | |||
argList.add(String.format("--spring.config.location=%s", springConfigLocation)); | |||
} | |||
if (contextPath != null) { | |||
argList.add(String.format("--server.context-path=%s", contextPath)); | |||
} | |||
String[] args = argList.toArray(new String[argList.size()]); | |||
// 启动服务器; | |||
ConfigurableApplicationContext appCtx = SpringApplication.run(GatewayConfiguration.class, args); | |||
return appCtx; | |||
} | |||
private static String bootPath() throws Exception { | |||
URL url = GatewayServerBooter.class.getProtectionDomain().getCodeSource().getLocation(); | |||
String currPath = java.net.URLDecoder.decode(url.getPath(), "UTF-8"); | |||
// 处理打包至SpringBoot问题 | |||
if (currPath.contains("!/")) { | |||
currPath = currPath.substring(5, currPath.indexOf("!/")); | |||
} | |||
if (currPath.endsWith(".jar")) { | |||
currPath = currPath.substring(0, currPath.lastIndexOf("/") + 1); | |||
} | |||
System.out.printf("Current Project Boot Path = %s \r\n", currPath); | |||
return new File(currPath).getParent() + File.separator; | |||
} | |||
} |
@@ -1,17 +0,0 @@ | |||
package com.jd.blockchain.gateway.web; | |||
import org.springframework.context.annotation.Configuration; | |||
import org.springframework.security.config.annotation.web.builders.HttpSecurity; | |||
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; | |||
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; | |||
@Configuration | |||
@EnableWebSecurity | |||
public class GatewayWebSecurityConfigurer extends WebSecurityConfigurerAdapter { | |||
@Override | |||
protected void configure(HttpSecurity http) throws Exception { | |||
http.authorizeRequests().anyRequest().permitAll(); | |||
http.csrf().disable(); | |||
} | |||
} |
@@ -1,79 +0,0 @@ | |||
package com.jd.blockchain.gateway.web; | |||
import com.jd.blockchain.crypto.*; | |||
import com.jd.blockchain.gateway.service.GatewayInterceptService; | |||
import com.jd.blockchain.transaction.SignatureUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.springframework.beans.factory.annotation.Autowired; | |||
import org.springframework.web.bind.annotation.RequestBody; | |||
import org.springframework.web.bind.annotation.RequestMapping; | |||
import org.springframework.web.bind.annotation.RequestMethod; | |||
import org.springframework.web.bind.annotation.ResponseBody; | |||
import org.springframework.web.bind.annotation.RestController; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.gateway.PeerService; | |||
import com.jd.blockchain.ledger.DigitalSignature; | |||
import com.jd.blockchain.ledger.TransactionContent; | |||
import com.jd.blockchain.ledger.TransactionRequest; | |||
import com.jd.blockchain.ledger.TransactionResponse; | |||
import com.jd.blockchain.transaction.TransactionService; | |||
import com.jd.blockchain.utils.BusinessException; | |||
import com.jd.blockchain.web.converters.BinaryMessageConverter; | |||
/** | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@RestController | |||
public class TxProcessingController implements TransactionService { | |||
private static Logger LOGGER = LoggerFactory.getLogger(TxProcessingController.class); | |||
@Autowired | |||
private PeerService peerService; | |||
@Autowired | |||
private GatewayInterceptService interceptService; | |||
@RequestMapping(path = "rpc/tx", method = RequestMethod.POST, consumes = BinaryMessageConverter.CONTENT_TYPE_VALUE, produces = BinaryMessageConverter.CONTENT_TYPE_VALUE) | |||
@Override | |||
public @ResponseBody TransactionResponse process(@RequestBody TransactionRequest txRequest) { | |||
// 拦截请求进行校验 | |||
interceptService.intercept(txRequest); | |||
// 检查交易请求的信息是否完整; | |||
HashDigest ledgerHash = txRequest.getTransactionContent().getLedgerHash(); | |||
if (ledgerHash == null) { | |||
// 未指定交易的账本; | |||
throw new IllegalArgumentException("The TransactionRequest miss ledger hash!"); | |||
} | |||
// 预期的请求中不应该包含节点签名,首个节点签名应该由当前网关提供; | |||
if (txRequest.getNodeSignatures() != null && txRequest.getNodeSignatures().length > 0) { | |||
throw new IllegalArgumentException("Gateway cann't accept TransactionRequest with any NodeSignature!"); | |||
} | |||
// TODO:检查参与者的签名; | |||
DigitalSignature[] partiSigns = txRequest.getEndpointSignatures(); | |||
if (partiSigns == null || partiSigns.length == 0) { | |||
// 缺少参与者签名,则采用检查托管账户并进行托管签名;如果请求未包含托管账户,或者托管账户认证失败,则返回401错误; | |||
// TODO: 未实现! | |||
throw new IllegalStateException("Not implemented!"); | |||
} else { | |||
// 验证签名; | |||
StringBuilder signer = new StringBuilder(txRequest.getHash().toString()).append("->"); | |||
for (DigitalSignature sign : partiSigns) { | |||
signer.append(AddressEncoding.generateAddress(sign.getPubKey()).toBase58()).append(","); | |||
if (!SignatureUtils.verifySignature(txRequest.getTransactionContent(), sign.getDigest(), sign.getPubKey())) { | |||
throw new BusinessException("The validation of participant signatures fail!"); | |||
} | |||
} | |||
LOGGER.debug(signer.toString()); | |||
} | |||
// 注:转发前自动附加网关的签名并转发请求至共识节点;异步的处理方式 | |||
return peerService.getTransactionService().process(txRequest); | |||
} | |||
} |
@@ -1,31 +0,0 @@ | |||
#网关的HTTP服务地址; | |||
http.host=0.0.0.0 | |||
#网关的HTTP服务端口; | |||
http.port=8081 | |||
#网关的HTTP服务上下文路径,可选; | |||
#http.context-path= | |||
#共识节点的服务地址(与该网关节点连接的Peer节点的IP地址); | |||
peer.host=127.0.0.1 | |||
#共识节点的服务端口(与该网关节点连接的Peer节点的端口); | |||
peer.port=12000 | |||
#共识节点的服务是否启用安全证书; | |||
peer.secure=false | |||
#共识节点的服务提供解析器 | |||
#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider | |||
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 | |||
#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey-path= | |||
#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey=177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x | |||
#默认私钥的解码密码; | |||
keys.default.privkey-password=DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY |
@@ -1,31 +0,0 @@ | |||
package test.com.jd.blockchain.gateway.data; | |||
import java.lang.reflect.Type; | |||
import com.alibaba.fastjson.parser.DefaultJSONParser; | |||
import com.alibaba.fastjson.parser.JSONToken; | |||
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.utils.codec.Base58Utils; | |||
public class HashDigestDeserializer implements ObjectDeserializer{ | |||
public static final HashDigestDeserializer INSTANCE = new HashDigestDeserializer(); | |||
@SuppressWarnings("unchecked") | |||
@Override | |||
public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName) { | |||
if (type instanceof Class && HashDigest.class.isAssignableFrom((Class<?>) type)) { | |||
String base58Str = parser.parseObject(String.class); | |||
byte[] hashBytes = Base58Utils.decode(base58Str); | |||
return (T) new HashDigest(hashBytes); | |||
} | |||
return (T) parser.parse(fieldName); | |||
} | |||
@Override | |||
public int getFastMatchToken() { | |||
return JSONToken.LBRACE; | |||
} | |||
} |
@@ -1,27 +0,0 @@ | |||
package test.com.jd.blockchain.gateway.data; | |||
import java.io.IOException; | |||
import java.lang.reflect.Type; | |||
import com.alibaba.fastjson.serializer.JSONSerializer; | |||
import com.alibaba.fastjson.serializer.ObjectSerializer; | |||
import com.alibaba.fastjson.serializer.SerializeWriter; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
public class HashDigestSerializer implements ObjectSerializer { | |||
public static HashDigestSerializer INSTANCE = new HashDigestSerializer(); | |||
@Override | |||
public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) | |||
throws IOException { | |||
SerializeWriter out = serializer.out; | |||
if (object == null) { | |||
out.writeNull(); | |||
return; | |||
} | |||
HashDigest hash = (HashDigest) object; | |||
out.writeString(hash.toBase58()); | |||
} | |||
} |
@@ -1,128 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>ledger-core</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger-model</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>storage-service</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>binary-proto</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>storage-service</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>consensus-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>contract-jvm</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.springframework</groupId> | |||
<artifactId>spring-context</artifactId> | |||
</dependency> | |||
<!-- dependencies for test --> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-classic</artifactId> | |||
<version>${project.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-sm</artifactId> | |||
<version>${project.version}</version> | |||
<scope>test</scope> | |||
</dependency> | |||
<dependency> | |||
<groupId>org.mockito</groupId> | |||
<artifactId>mockito-core</artifactId> | |||
<scope>test</scope> | |||
</dependency> | |||
</dependencies> | |||
<build> | |||
<plugins> | |||
<plugin> | |||
<groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-deploy-plugin</artifactId> | |||
<version>2.8.2</version> | |||
<configuration> | |||
<skip>true</skip> | |||
</configuration> | |||
</plugin> | |||
<plugin> | |||
<groupId>org.sonatype.plugins</groupId> | |||
<artifactId>nexus-staging-maven-plugin</artifactId> | |||
<configuration> | |||
<skipNexusStagingDeployMojo>true</skipNexusStagingDeployMojo> | |||
</configuration> | |||
</plugin> | |||
</plugins> | |||
</build> | |||
<!--<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> | |||
<artifactId>maven-surefire-plugin</artifactId> <version>2.5</version> <configuration> | |||
<excludes> <exclude>**/TransactionBatchProcessorTest.java</exclude> </excludes> | |||
</configuration> </plugin> </plugins> </build> --> | |||
<!--<build> --> | |||
<!--<plugins> --> | |||
<!--<plugin> --> | |||
<!--<groupId>org.apache.maven.plugins</groupId> --> | |||
<!--<artifactId>maven-compiler-plugin</artifactId> --> | |||
<!--<version>3.1</version> --> | |||
<!--<configuration> --> | |||
<!--<source>1.8</source> --> | |||
<!--<target>1.8</target> --> | |||
<!--<encoding>UTF-8</encoding> --> | |||
<!--<compilerArgs> --> | |||
<!--<!–<arg>-verbose</arg>–> --> | |||
<!--<!–<arg>-Xlint:unchecked</arg>–> --> | |||
<!--<!–<arg>-Xlint:deprecation</arg>–> --> | |||
<!--<!–<arg>-bootclasspath</arg>–> --> | |||
<!--<!–<arg>${env.JAVA_HOME}/jre/lib/rt.jar</arg>–> --> | |||
<!--<arg>-extdirs</arg> --> | |||
<!--<arg>${project.basedir}/../contract/contract-libs;$JAVA_HOME/jre/lib/ext</arg> --> | |||
<!--</compilerArgs> --> | |||
<!--</configuration> --> | |||
<!--</plugin> --> | |||
<!--</plugins> --> | |||
<!--</build> --> | |||
</project> |
@@ -1,73 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.utils.Bytes; | |||
public class ContractAccount extends AccountDecorator implements ContractInfo { | |||
private static final String CONTRACT_INFO_PREFIX = "INFO" + LedgerConsts.KEY_SEPERATOR; | |||
private static final String CHAIN_CODE_KEY = "CHAIN-CODE"; | |||
public ContractAccount(CompositeAccount mklAccount) { | |||
super(mklAccount); | |||
} | |||
@Override | |||
public Bytes getAddress() { | |||
return getID().getAddress(); | |||
} | |||
@Override | |||
public PubKey getPubKey() { | |||
return getID().getPubKey(); | |||
} | |||
// public MerkleProof getChaincodeProof() { | |||
// return getHeaders().getProof(CHAIN_CODE_KEY); | |||
// } | |||
// | |||
// public MerkleProof getPropertyProof(Bytes key) { | |||
// return getHeaders().getProof(encodePropertyKey(key)); | |||
// } | |||
public long setChaincode(byte[] chaincode, long version) { | |||
TypedValue bytesValue = TypedValue.fromBytes(chaincode); | |||
return getHeaders().setValue(CHAIN_CODE_KEY, bytesValue, version); | |||
} | |||
public byte[] getChainCode() { | |||
return getHeaders().getValue(CHAIN_CODE_KEY).getBytes().toBytes(); | |||
} | |||
public byte[] getChainCode(long version) { | |||
return getHeaders().getValue(CHAIN_CODE_KEY, version).getBytes().toBytes(); | |||
} | |||
public long getChaincodeVersion() { | |||
return getHeaders().getVersion(CHAIN_CODE_KEY); | |||
} | |||
public long setProperty(String key, String value, long version) { | |||
TypedValue bytesValue = TypedValue.fromText(value); | |||
return getHeaders().setValue(encodePropertyKey(key), bytesValue, version); | |||
} | |||
public String getProperty(String key) { | |||
BytesValue bytesValue = getHeaders().getValue(encodePropertyKey(key)); | |||
return TypedValue.wrap(bytesValue).stringValue(); | |||
} | |||
public String getProperty(String key, long version) { | |||
BytesValue bytesValue = getHeaders().getValue(encodePropertyKey(key), version); | |||
return TypedValue.wrap(bytesValue).stringValue(); | |||
} | |||
private String encodePropertyKey(String key) { | |||
return CONTRACT_INFO_PREFIX.concat(key); | |||
} | |||
} |
@@ -1,131 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
import com.jd.blockchain.ledger.DigitalSignature; | |||
import com.jd.blockchain.ledger.MerkleProof; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.Transactional; | |||
public class ContractAccountSet implements Transactional, ContractAccountQuery { | |||
private MerkleAccountSet accountSet; | |||
public ContractAccountSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exStorage, | |||
VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { | |||
accountSet = new MerkleAccountSet(cryptoSetting, Bytes.fromString(prefix), exStorage, verStorage, accessPolicy); | |||
} | |||
public ContractAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, | |||
ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, | |||
AccountAccessPolicy accessPolicy) { | |||
accountSet = new MerkleAccountSet(dataRootHash, cryptoSetting, Bytes.fromString(prefix), exStorage, verStorage, | |||
readonly, accessPolicy); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getHeaders(int fromIndex, int count) { | |||
return accountSet.getHeaders(fromIndex, count); | |||
} | |||
public boolean isReadonly() { | |||
return accountSet.isReadonly(); | |||
} | |||
void setReadonly() { | |||
accountSet.setReadonly(); | |||
} | |||
@Override | |||
public HashDigest getRootHash() { | |||
return accountSet.getRootHash(); | |||
} | |||
/** | |||
* 返回合约总数; | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public long getTotal() { | |||
return accountSet.getTotal(); | |||
} | |||
@Override | |||
public MerkleProof getProof(Bytes address) { | |||
return accountSet.getProof(address); | |||
} | |||
@Override | |||
public boolean contains(Bytes address) { | |||
return accountSet.contains(address); | |||
} | |||
@Override | |||
public ContractAccount getAccount(Bytes address) { | |||
CompositeAccount accBase = accountSet.getAccount(address); | |||
return new ContractAccount(accBase); | |||
} | |||
@Override | |||
public ContractAccount getAccount(String address) { | |||
return getAccount(Bytes.fromBase58(address)); | |||
} | |||
@Override | |||
public ContractAccount getAccount(Bytes address, long version) { | |||
CompositeAccount accBase = accountSet.getAccount(address, version); | |||
return new ContractAccount(accBase); | |||
} | |||
/** | |||
* 部署一项新的合约链码; | |||
* | |||
* @param address 合约账户地址; | |||
* @param pubKey 合约账户公钥; | |||
* @param addressSignature 地址签名;合约账户的私钥对地址的签名; | |||
* @param chaincode 链码内容; | |||
* @return 合约账户; | |||
*/ | |||
public ContractAccount deploy(Bytes address, PubKey pubKey, DigitalSignature addressSignature, byte[] chaincode) { | |||
// TODO: 校验和记录合约地址签名; | |||
CompositeAccount accBase = accountSet.register(address, pubKey); | |||
ContractAccount contractAcc = new ContractAccount(accBase); | |||
contractAcc.setChaincode(chaincode, -1); | |||
return contractAcc; | |||
} | |||
/** | |||
* 更新指定账户的链码; | |||
* | |||
* @param address 合约账户地址; | |||
* @param chaincode 链码内容; | |||
* @param version 链码版本; | |||
* @return 返回链码的新版本号; | |||
*/ | |||
public long update(Bytes address, byte[] chaincode, long version) { | |||
CompositeAccount accBase = accountSet.getAccount(address); | |||
ContractAccount contractAcc = new ContractAccount(accBase); | |||
return contractAcc.setChaincode(chaincode, version); | |||
} | |||
@Override | |||
public boolean isUpdated() { | |||
return accountSet.isUpdated(); | |||
} | |||
@Override | |||
public void commit() { | |||
accountSet.commit(); | |||
} | |||
@Override | |||
public void cancel() { | |||
accountSet.cancel(); | |||
} | |||
} |
@@ -1,234 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
public class DataAccount extends AccountDecorator { | |||
public DataAccount(CompositeAccount mklAccount) { | |||
super(mklAccount); | |||
} | |||
// /** | |||
// * Create or update the value associated the specified key if the version | |||
// * checking is passed.<br> | |||
// * | |||
// * The value of the key will be updated only if it's latest version equals the | |||
// * specified version argument. <br> | |||
// * If the key doesn't exist, the version checking will be ignored, and key will | |||
// * be created with a new sequence number as id. <br> | |||
// * It also could specify the version argument to -1 to ignore the version | |||
// * checking. | |||
// * <p> | |||
// * If updating is performed, the version of the key increase by 1. <br> | |||
// * If creating is performed, the version of the key initialize by 0. <br> | |||
// * | |||
// * @param key The key of data; | |||
// * @param value The value of data; | |||
// * @param version The expected version of the key. | |||
// * @return The new version of the key. <br> | |||
// * If the key is new created success, then return 0; <br> | |||
// * If the key is updated success, then return the new version;<br> | |||
// * If this operation fail by version checking or other reason, then | |||
// * return -1; | |||
// */ | |||
// public long setBytes(Bytes key, BytesValue value, long version) { | |||
// return super.getDataset().setValue(key, value, version); | |||
// } | |||
// | |||
// /** | |||
// * Create or update the value associated the specified key if the version | |||
// * checking is passed.<br> | |||
// * | |||
// * The value of the key will be updated only if it's latest version equals the | |||
// * specified version argument. <br> | |||
// * If the key doesn't exist, the version checking will be ignored, and key will | |||
// * be created with a new sequence number as id. <br> | |||
// * It also could specify the version argument to -1 to ignore the version | |||
// * checking. | |||
// * <p> | |||
// * If updating is performed, the version of the key increase by 1. <br> | |||
// * If creating is performed, the version of the key initialize by 0. <br> | |||
// * | |||
// * @param key The key of data; | |||
// * @param value The value of data; | |||
// * @param version The expected version of the key. | |||
// * @return The new version of the key. <br> | |||
// * If the key is new created success, then return 0; <br> | |||
// * If the key is updated success, then return the new version;<br> | |||
// * If this operation fail by version checking or other reason, then | |||
// * return -1; | |||
// */ | |||
// public long setBytes(Bytes key, String value, long version) { | |||
// BytesValue bytesValue = TypedValue.fromText(value); | |||
// return baseAccount.setValue(key, bytesValue, version); | |||
// } | |||
// | |||
// /** | |||
// * Create or update the value associated the specified key if the version | |||
// * checking is passed.<br> | |||
// * | |||
// * The value of the key will be updated only if it's latest version equals the | |||
// * specified version argument. <br> | |||
// * If the key doesn't exist, the version checking will be ignored, and key will | |||
// * be created with a new sequence number as id. <br> | |||
// * It also could specify the version argument to -1 to ignore the version | |||
// * checking. | |||
// * <p> | |||
// * If updating is performed, the version of the key increase by 1. <br> | |||
// * If creating is performed, the version of the key initialize by 0. <br> | |||
// * | |||
// * @param key The key of data; | |||
// * @param value The value of data; | |||
// * @param version The expected version of the key. | |||
// * @return The new version of the key. <br> | |||
// * If the key is new created success, then return 0; <br> | |||
// * If the key is updated success, then return the new version;<br> | |||
// * If this operation fail by version checking or other reason, then | |||
// * return -1; | |||
// */ | |||
// public long setBytes(Bytes key, byte[] value, long version) { | |||
// BytesValue bytesValue = TypedValue.fromBytes(value); | |||
// return baseAccount.setValue(key, bytesValue, version); | |||
// } | |||
// | |||
// /** | |||
// * Return the latest version entry associated the specified key; If the key | |||
// * doesn't exist, then return -1; | |||
// * | |||
// * @param key | |||
// * @return | |||
// */ | |||
// public long getDataVersion(String key) { | |||
// return baseAccount.getVersion(Bytes.fromString(key)); | |||
// } | |||
// | |||
// /** | |||
// * Return the latest version entry associated the specified key; If the key | |||
// * doesn't exist, then return -1; | |||
// * | |||
// * @param key | |||
// * @return | |||
// */ | |||
// public long getDataVersion(Bytes key) { | |||
// return baseAccount.getVersion(key); | |||
// } | |||
// | |||
// /** | |||
// * return the latest version's value; | |||
// * | |||
// * @param key | |||
// * @return return null if not exist; | |||
// */ | |||
// public BytesValue getBytes(String key) { | |||
// return baseAccount.getValue(Bytes.fromString(key)); | |||
// } | |||
// | |||
// /** | |||
// * return the latest version's value; | |||
// * | |||
// * @param key | |||
// * @return return null if not exist; | |||
// */ | |||
// public BytesValue getBytes(Bytes key) { | |||
// return baseAccount.getValue(key); | |||
// } | |||
// | |||
// /** | |||
// * return the specified version's value; | |||
// * | |||
// * @param key | |||
// * @param version | |||
// * @return return null if not exist; | |||
// */ | |||
// public BytesValue getBytes(String key, long version) { | |||
// return baseAccount.getValue(Bytes.fromString(key), version); | |||
// } | |||
// | |||
// /** | |||
// * return the specified version's value; | |||
// * | |||
// * @param key | |||
// * @param version | |||
// * @return return null if not exist; | |||
// */ | |||
// public BytesValue getBytes(Bytes key, long version) { | |||
// return baseAccount.getValue(key, version); | |||
// } | |||
// /** | |||
// * @param key | |||
// * @param version | |||
// * @return | |||
// */ | |||
// public KVDataEntry getDataEntry(String key, long version) { | |||
// return getDataEntry(Bytes.fromString(key), version); | |||
// } | |||
// | |||
// /** | |||
// * @param key | |||
// * @param version | |||
// * @return | |||
// */ | |||
// public KVDataEntry getDataEntry(Bytes key, long version) { | |||
// BytesValue value = baseAccount.getValue(key, version); | |||
// if (value == null) { | |||
// return new KVDataObject(key.toUTF8String(), -1, null); | |||
// }else { | |||
// return new KVDataObject(key.toUTF8String(), version, value); | |||
// } | |||
// } | |||
// | |||
// /** | |||
// * return the specified index's KVDataEntry; | |||
// * | |||
// * @param fromIndex | |||
// * @param count | |||
// * @return return null if not exist; | |||
// */ | |||
// | |||
// public KVDataEntry[] getDataEntries(int fromIndex, int count) { | |||
// if (count == 0 || getDataEntriesTotalCount() == 0) { | |||
// return null; | |||
// } | |||
// | |||
// if (count == -1 || count > getDataEntriesTotalCount()) { | |||
// fromIndex = 0; | |||
// count = (int)getDataEntriesTotalCount(); | |||
// } | |||
// | |||
// if (fromIndex < 0 || fromIndex > getDataEntriesTotalCount() - 1) { | |||
// fromIndex = 0; | |||
// } | |||
// | |||
// KVDataEntry[] kvDataEntries = new KVDataEntry[count]; | |||
// byte[] value; | |||
// String key; | |||
// long ver; | |||
// for (int i = 0; i < count; i++) { | |||
// value = baseAccount.dataset.getValuesAtIndex(fromIndex); | |||
// key = baseAccount.dataset.getKeyAtIndex(fromIndex); | |||
// ver = baseAccount.dataset.getVersion(key); | |||
// BytesValue decodeData = BinaryProtocol.decode(value); | |||
// kvDataEntries[i] = new KVDataObject(key, ver, decodeData); | |||
// fromIndex++; | |||
// } | |||
// | |||
// return kvDataEntries; | |||
// } | |||
// | |||
// /** | |||
// * return the dataAccount's kv total count; | |||
// * | |||
// * @param | |||
// * @param | |||
// * @return return total count; | |||
// */ | |||
// public long getDataEntriesTotalCount() { | |||
// if(baseAccount == null){ | |||
// return 0; | |||
// } | |||
// return baseAccount.dataset.getDataCount(); | |||
// } | |||
} |
@@ -1,113 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
import com.jd.blockchain.ledger.DigitalSignature; | |||
import com.jd.blockchain.ledger.MerkleProof; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.Transactional; | |||
public class DataAccountSet implements Transactional, DataAccountQuery { | |||
private MerkleAccountSet accountSet; | |||
public DataAccountSet(CryptoSetting cryptoSetting, String prefix, ExPolicyKVStorage exStorage, | |||
VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { | |||
accountSet = new MerkleAccountSet(cryptoSetting, Bytes.fromString(prefix), exStorage, verStorage, accessPolicy); | |||
} | |||
public DataAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String prefix, | |||
ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, | |||
AccountAccessPolicy accessPolicy) { | |||
accountSet = new MerkleAccountSet(dataRootHash, cryptoSetting, Bytes.fromString(prefix), exStorage, verStorage, | |||
readonly, accessPolicy); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getHeaders(int fromIndex, int count) { | |||
return accountSet.getHeaders(fromIndex, count); | |||
} | |||
public boolean isReadonly() { | |||
return accountSet.isReadonly(); | |||
} | |||
void setReadonly() { | |||
accountSet.setReadonly(); | |||
} | |||
@Override | |||
public HashDigest getRootHash() { | |||
return accountSet.getRootHash(); | |||
} | |||
@Override | |||
public long getTotal() { | |||
return accountSet.getTotal(); | |||
} | |||
@Override | |||
public boolean contains(Bytes address) { | |||
return accountSet.contains(address); | |||
} | |||
/** | |||
* 返回账户的存在性证明; | |||
*/ | |||
@Override | |||
public MerkleProof getProof(Bytes address) { | |||
return accountSet.getProof(address); | |||
} | |||
public DataAccount register(Bytes address, PubKey pubKey, DigitalSignature addressSignature) { | |||
// TODO: 未实现对地址签名的校验和记录; | |||
CompositeAccount accBase = accountSet.register(address, pubKey); | |||
return new DataAccount(accBase); | |||
} | |||
@Override | |||
public DataAccount getAccount(String address) { | |||
return getAccount(Bytes.fromBase58(address)); | |||
} | |||
/** | |||
* 返回数据账户; <br> | |||
* 如果不存在,则返回 null; | |||
* | |||
* @param address | |||
* @return | |||
*/ | |||
@Override | |||
public DataAccount getAccount(Bytes address) { | |||
CompositeAccount accBase = accountSet.getAccount(address); | |||
if (accBase == null) { | |||
return null; | |||
} | |||
return new DataAccount(accBase); | |||
} | |||
@Override | |||
public DataAccount getAccount(Bytes address, long version) { | |||
CompositeAccount accBase = accountSet.getAccount(address, version); | |||
return new DataAccount(accBase); | |||
} | |||
@Override | |||
public boolean isUpdated() { | |||
return accountSet.isUpdated(); | |||
} | |||
@Override | |||
public void commit() { | |||
accountSet.commit(); | |||
} | |||
@Override | |||
public void cancel() { | |||
accountSet.cancel(); | |||
} | |||
} |
@@ -1,481 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.jd.blockchain.binaryproto.BinaryProtocol; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.Crypto; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.HashFunction; | |||
import com.jd.blockchain.ledger.LedgerAdminSettings; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
import com.jd.blockchain.ledger.LedgerInitSetting; | |||
import com.jd.blockchain.ledger.LedgerMetadata; | |||
import com.jd.blockchain.ledger.LedgerMetadata_V2; | |||
import com.jd.blockchain.ledger.LedgerSettings; | |||
import com.jd.blockchain.ledger.ParticipantNode; | |||
import com.jd.blockchain.ledger.RolePrivilegeSettings; | |||
import com.jd.blockchain.ledger.UserAuthorizationSettings; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.Transactional; | |||
public class LedgerAdminDataset implements Transactional, LedgerAdminDataQuery, LedgerAdminSettings { | |||
static { | |||
DataContractRegistry.register(LedgerMetadata.class); | |||
DataContractRegistry.register(LedgerMetadata_V2.class); | |||
} | |||
private static Logger LOGGER = LoggerFactory.getLogger(LedgerAdminDataset.class); | |||
public static final String LEDGER_META_PREFIX = "MTA" + LedgerConsts.KEY_SEPERATOR; | |||
public static final String LEDGER_PARTICIPANT_PREFIX = "PAR" + LedgerConsts.KEY_SEPERATOR; | |||
public static final String LEDGER_SETTING_PREFIX = "SET" + LedgerConsts.KEY_SEPERATOR; | |||
public static final String ROLE_PRIVILEGE_PREFIX = "RPV" + LedgerConsts.KEY_SEPERATOR; | |||
public static final String USER_ROLE_PREFIX = "URO" + LedgerConsts.KEY_SEPERATOR; | |||
private final Bytes metaPrefix; | |||
private final Bytes settingPrefix; | |||
private LedgerMetadata_V2 origMetadata; | |||
private LedgerMetadataInfo metadata; | |||
/** | |||
* 原来的账本设置; | |||
* | |||
* <br> | |||
* 对 LedgerMetadata 修改的新配置不能立即生效,需要达成共识后,在下一次区块计算中才生效; | |||
*/ | |||
private LedgerSettings previousSettings; | |||
private HashDigest previousSettingHash; | |||
/** | |||
* 账本的参与节点; | |||
*/ | |||
private ParticipantDataset participants; | |||
/** | |||
* “角色-权限”数据集; | |||
*/ | |||
private RolePrivilegeDataset rolePrivileges; | |||
/** | |||
* “用户-角色”数据集; | |||
*/ | |||
private UserRoleDataset userRoles; | |||
/** | |||
* 账本参数配置; | |||
*/ | |||
private LedgerSettings settings; | |||
private ExPolicyKVStorage storage; | |||
private HashDigest adminDataHash; | |||
private boolean readonly; | |||
private boolean updated; | |||
public HashDigest getHash() { | |||
return adminDataHash; | |||
} | |||
public boolean isReadonly() { | |||
return readonly; | |||
} | |||
void setReadonly() { | |||
this.readonly = true; | |||
} | |||
public LedgerSettings getPreviousSetting() { | |||
return previousSettings; | |||
} | |||
@Override | |||
public RolePrivilegeSettings getRolePrivileges() { | |||
return rolePrivileges; | |||
} | |||
@Override | |||
public UserAuthorizationSettings getAuthorizations() { | |||
return userRoles; | |||
} | |||
@Override | |||
public LedgerAdminSettings getAdminInfo() { | |||
return this; | |||
} | |||
/** | |||
* 初始化账本的管理账户; | |||
* | |||
* <br> | |||
* | |||
* 只在新建账本时调用此方法; | |||
* | |||
* @param ledgerSeed | |||
* @param settings | |||
* @param partiList | |||
* @param exPolicyStorage | |||
* @param versioningStorage | |||
*/ | |||
public LedgerAdminDataset(LedgerInitSetting initSetting, String keyPrefix, ExPolicyKVStorage exPolicyStorage, | |||
VersioningKVStorage versioningStorage) { | |||
this.metaPrefix = Bytes.fromString(keyPrefix + LEDGER_META_PREFIX); | |||
this.settingPrefix = Bytes.fromString(keyPrefix + LEDGER_SETTING_PREFIX); | |||
ParticipantNode[] parties = initSetting.getConsensusParticipants(); | |||
if (parties.length == 0) { | |||
throw new LedgerException("No participant!"); | |||
} | |||
// 初始化元数据; | |||
this.metadata = new LedgerMetadataInfo(); | |||
this.metadata.setSeed(initSetting.getLedgerSeed()); | |||
// 新配置; | |||
this.settings = new LedgerConfiguration(initSetting.getConsensusProvider(), initSetting.getConsensusSettings(), | |||
initSetting.getCryptoSetting()); | |||
this.previousSettings = new LedgerConfiguration(settings); | |||
this.previousSettingHash = null; | |||
this.adminDataHash = null; | |||
// 基于原配置初始化参与者列表; | |||
String partiPrefix = keyPrefix + LEDGER_PARTICIPANT_PREFIX; | |||
this.participants = new ParticipantDataset(previousSettings.getCryptoSetting(), partiPrefix, exPolicyStorage, | |||
versioningStorage); | |||
for (ParticipantNode p : parties) { | |||
this.participants.addConsensusParticipant(p); | |||
} | |||
String rolePrivilegePrefix = keyPrefix + ROLE_PRIVILEGE_PREFIX; | |||
this.rolePrivileges = new RolePrivilegeDataset(this.settings.getCryptoSetting(), rolePrivilegePrefix, | |||
exPolicyStorage, versioningStorage); | |||
String userRolePrefix = keyPrefix + USER_ROLE_PREFIX; | |||
this.userRoles = new UserRoleDataset(this.settings.getCryptoSetting(), userRolePrefix, exPolicyStorage, | |||
versioningStorage); | |||
// 初始化其它属性; | |||
this.storage = exPolicyStorage; | |||
this.readonly = false; | |||
} | |||
public LedgerAdminDataset(HashDigest adminAccountHash, String keyPrefix, ExPolicyKVStorage kvStorage, | |||
VersioningKVStorage versioningKVStorage, boolean readonly) { | |||
this.metaPrefix = Bytes.fromString(keyPrefix + LEDGER_META_PREFIX); | |||
this.settingPrefix = Bytes.fromString(keyPrefix + LEDGER_SETTING_PREFIX); | |||
this.storage = kvStorage; | |||
this.readonly = readonly; | |||
this.origMetadata = loadAndVerifyMetadata(adminAccountHash); | |||
this.metadata = new LedgerMetadataInfo(origMetadata); | |||
this.settings = loadAndVerifySettings(metadata.getSettingsHash()); | |||
// 复制记录一份配置作为上一个区块的原始配置,该实例仅供读取,不做修改,也不会回写到存储; | |||
this.previousSettings = new LedgerConfiguration(settings); | |||
this.previousSettingHash = metadata.getSettingsHash(); | |||
this.adminDataHash = adminAccountHash; | |||
String partiPrefix = keyPrefix + LEDGER_PARTICIPANT_PREFIX; | |||
this.participants = new ParticipantDataset(metadata.getParticipantsHash(), previousSettings.getCryptoSetting(), | |||
partiPrefix, kvStorage, versioningKVStorage, readonly); | |||
String rolePrivilegePrefix = keyPrefix + ROLE_PRIVILEGE_PREFIX; | |||
this.rolePrivileges = new RolePrivilegeDataset(metadata.getRolePrivilegesHash(), | |||
previousSettings.getCryptoSetting(), rolePrivilegePrefix, kvStorage, versioningKVStorage, readonly); | |||
String userRolePrefix = keyPrefix + USER_ROLE_PREFIX; | |||
this.userRoles = new UserRoleDataset(metadata.getUserRolesHash(), previousSettings.getCryptoSetting(), | |||
userRolePrefix, kvStorage, versioningKVStorage, readonly); | |||
} | |||
private LedgerSettings loadAndVerifySettings(HashDigest settingsHash) { | |||
if (settingsHash == null) { | |||
return null; | |||
} | |||
Bytes key = encodeSettingsKey(settingsHash); | |||
byte[] bytes = storage.get(key); | |||
HashFunction hashFunc = Crypto.getHashFunction(settingsHash.getAlgorithm()); | |||
if (!hashFunc.verify(settingsHash, bytes)) { | |||
String errorMsg = "Verification of the hash for ledger setting failed! --[HASH=" + key + "]"; | |||
LOGGER.error(errorMsg); | |||
throw new LedgerException(errorMsg); | |||
} | |||
return deserializeSettings(bytes); | |||
} | |||
private LedgerSettings deserializeSettings(byte[] bytes) { | |||
return BinaryProtocol.decode(bytes); | |||
} | |||
private byte[] serializeSetting(LedgerSettings setting) { | |||
return BinaryProtocol.encode(setting, LedgerSettings.class); | |||
} | |||
private LedgerMetadata_V2 loadAndVerifyMetadata(HashDigest adminAccountHash) { | |||
Bytes key = encodeMetadataKey(adminAccountHash); | |||
byte[] bytes = storage.get(key); | |||
HashFunction hashFunc = Crypto.getHashFunction(adminAccountHash.getAlgorithm()); | |||
if (!hashFunc.verify(adminAccountHash, bytes)) { | |||
String errorMsg = "Verification of the hash for ledger metadata failed! --[HASH=" + key + "]"; | |||
LOGGER.error(errorMsg); | |||
throw new LedgerException(errorMsg); | |||
} | |||
return deserializeMetadata(bytes); | |||
} | |||
private Bytes encodeSettingsKey(HashDigest settingsHash) { | |||
return settingPrefix.concat(settingsHash); | |||
} | |||
private Bytes encodeMetadataKey(HashDigest metadataHash) { | |||
// return LEDGER_META_PREFIX + metadataHash; | |||
// return metaPrefix + metadataHash; | |||
return metaPrefix.concat(metadataHash); | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.core.LedgerAdministration#getMetadata() | |||
*/ | |||
@Override | |||
public LedgerMetadata_V2 getMetadata() { | |||
return metadata; | |||
} | |||
// /** | |||
// * 返回原来的账本配置; | |||
// * | |||
// * <br> | |||
// * 此方法总是返回从上一个区块加载的账本配置,即时调用 {@link #setLedgerSetting(LedgerSettings)} 做出了新的更改; | |||
// * | |||
// * @return | |||
// */ | |||
// public LedgerSettings getPreviousSetting() { | |||
// return previousSettings; | |||
// } | |||
/** | |||
* 返回当前设置的账本配置; | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public LedgerSettings getSettings() { | |||
return settings; | |||
} | |||
/** | |||
* 更新账本配置; | |||
* | |||
* @param ledgerSetting | |||
*/ | |||
public void setLedgerSetting(LedgerSettings ledgerSetting) { | |||
if (readonly) { | |||
throw new IllegalArgumentException("This merkle dataset is readonly!"); | |||
} | |||
settings = ledgerSetting; | |||
updated = true; | |||
} | |||
@Override | |||
public long getParticipantCount() { | |||
return participants.getParticipantCount(); | |||
} | |||
@Override | |||
public ParticipantNode[] getParticipants() { | |||
return participants.getParticipants(); | |||
} | |||
@Override | |||
public ParticipantDataset getParticipantDataset() { | |||
return participants; | |||
} | |||
/** | |||
* 加入新的参与方; 如果指定的参与方已经存在,则引发 LedgerException 异常; | |||
* | |||
* @param participant | |||
*/ | |||
public void addParticipant(ParticipantNode participant) { | |||
participants.addConsensusParticipant(participant); | |||
} | |||
/** | |||
* 更新参与方的状态参数; | |||
* | |||
* @param participant | |||
*/ | |||
public void updateParticipant(ParticipantNode participant) { | |||
participants.updateConsensusParticipant(participant); | |||
} | |||
@Override | |||
public boolean isUpdated() { | |||
return updated || participants.isUpdated() || rolePrivileges.isUpdated() || userRoles.isUpdated(); | |||
} | |||
@Override | |||
public void commit() { | |||
if (!isUpdated()) { | |||
return; | |||
} | |||
// 计算并更新参与方集合的根哈希; | |||
participants.commit(); | |||
metadata.setParticipantsHash(participants.getRootHash()); | |||
// 计算并更新角色权限集合的根哈希; | |||
rolePrivileges.commit(); | |||
metadata.setRolePrivilegesHash(rolePrivileges.getRootHash()); | |||
// 计算并更新用户角色授权集合的根哈希; | |||
userRoles.commit(); | |||
metadata.setUserRolesHash(userRoles.getRootHash()); | |||
// 当前区块上下文的密码参数设置的哈希函数; | |||
HashFunction hashFunc = Crypto.getHashFunction(previousSettings.getCryptoSetting().getHashAlgorithm()); | |||
// 计算并更新参数配置的哈希; | |||
if (settings == null) { | |||
throw new LedgerException("Missing ledger settings!"); | |||
} | |||
byte[] settingsBytes = serializeSetting(settings); | |||
HashDigest settingsHash = hashFunc.hash(settingsBytes); | |||
metadata.setSettingsHash(settingsHash); | |||
if (previousSettingHash == null || !previousSettingHash.equals(settingsHash)) { | |||
Bytes settingsKey = encodeSettingsKey(settingsHash); | |||
boolean nx = storage.set(settingsKey, settingsBytes, ExPolicy.NOT_EXISTING); | |||
if (!nx) { | |||
String base58MetadataHash = settingsHash.toBase58(); | |||
// 有可能发生了并发写入冲突,不同的节点都向同一个存储服务器上写入数据; | |||
String errMsg = "Ledger metadata already exist! --[MetadataHash=" + base58MetadataHash + "]"; | |||
LOGGER.warn(errMsg); | |||
throw new LedgerException(errMsg); | |||
} | |||
} | |||
// 基于之前的密码配置来计算元数据的哈希; | |||
byte[] metadataBytes = serializeMetadata(metadata); | |||
HashDigest metadataHash = hashFunc.hash(metadataBytes); | |||
if (adminDataHash == null || !adminDataHash.equals(metadataHash)) { | |||
// update modify; | |||
// String base58MetadataHash = metadataHash.toBase58(); | |||
// String metadataKey = encodeMetadataKey(base58MetadataHash); | |||
Bytes metadataKey = encodeMetadataKey(metadataHash); | |||
boolean nx = storage.set(metadataKey, metadataBytes, ExPolicy.NOT_EXISTING); | |||
if (!nx) { | |||
String base58MetadataHash = metadataHash.toBase58(); | |||
// 有可能发生了并发写入冲突,不同的节点都向同一个存储服务器上写入数据; | |||
String errMsg = "Ledger metadata already exist! --[MetadataHash=" + base58MetadataHash + "]"; | |||
LOGGER.warn(errMsg); | |||
throw new LedgerException(errMsg); | |||
} | |||
adminDataHash = metadataHash; | |||
} | |||
updated = false; | |||
} | |||
private LedgerMetadata_V2 deserializeMetadata(byte[] bytes) { | |||
return BinaryProtocol.decode(bytes); | |||
} | |||
private byte[] serializeMetadata(LedgerMetadataInfo config) { | |||
return BinaryProtocol.encode(config, LedgerMetadata_V2.class); | |||
} | |||
@Override | |||
public void cancel() { | |||
if (!isUpdated()) { | |||
return; | |||
} | |||
participants.cancel(); | |||
metadata =origMetadata == null ? new LedgerMetadataInfo() : new LedgerMetadataInfo(origMetadata); | |||
} | |||
public static class LedgerMetadataInfo implements LedgerMetadata_V2 { | |||
private byte[] seed; | |||
// private LedgerSetting setting; | |||
private HashDigest participantsHash; | |||
private HashDigest settingsHash; | |||
private HashDigest rolePrivilegesHash; | |||
private HashDigest userRolesHash; | |||
public LedgerMetadataInfo() { | |||
} | |||
public LedgerMetadataInfo(LedgerMetadata_V2 metadata) { | |||
this.seed = metadata.getSeed(); | |||
this.participantsHash = metadata.getParticipantsHash(); | |||
this.settingsHash = metadata.getSettingsHash(); | |||
this.rolePrivilegesHash = metadata.getRolePrivilegesHash(); | |||
this.userRolesHash = metadata.getUserRolesHash(); | |||
} | |||
@Override | |||
public byte[] getSeed() { | |||
return seed; | |||
} | |||
@Override | |||
public HashDigest getSettingsHash() { | |||
return settingsHash; | |||
} | |||
@Override | |||
public HashDigest getParticipantsHash() { | |||
return participantsHash; | |||
} | |||
@Override | |||
public HashDigest getRolePrivilegesHash() { | |||
return rolePrivilegesHash; | |||
} | |||
@Override | |||
public HashDigest getUserRolesHash() { | |||
return userRolesHash; | |||
} | |||
public void setSeed(byte[] seed) { | |||
this.seed = seed; | |||
} | |||
public void setSettingsHash(HashDigest settingHash) { | |||
this.settingsHash = settingHash; | |||
} | |||
public void setParticipantsHash(HashDigest participantsHash) { | |||
this.participantsHash = participantsHash; | |||
} | |||
public void setRolePrivilegesHash(HashDigest rolePrivilegesHash) { | |||
this.rolePrivilegesHash = rolePrivilegesHash; | |||
} | |||
public void setUserRolesHash(HashDigest userRolesHash) { | |||
this.userRolesHash = userRolesHash; | |||
} | |||
} | |||
} |
@@ -1,432 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.jd.blockchain.contract.ContractException; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.ContractInfo; | |||
import com.jd.blockchain.ledger.TypedKVEntry; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.ledger.TypedKVData; | |||
import com.jd.blockchain.ledger.KVDataVO; | |||
import com.jd.blockchain.ledger.KVInfoVO; | |||
import com.jd.blockchain.ledger.LedgerAdminInfo; | |||
import com.jd.blockchain.ledger.LedgerBlock; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
import com.jd.blockchain.ledger.LedgerInfo; | |||
import com.jd.blockchain.ledger.LedgerMetadata; | |||
import com.jd.blockchain.ledger.LedgerTransaction; | |||
import com.jd.blockchain.ledger.ParticipantNode; | |||
import com.jd.blockchain.ledger.TransactionState; | |||
import com.jd.blockchain.ledger.UserInfo; | |||
import com.jd.blockchain.transaction.BlockchainQueryService; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.DataEntry; | |||
import com.jd.blockchain.utils.DataIterator; | |||
import com.jd.blockchain.utils.QueryUtil; | |||
public class LedgerQueryService implements BlockchainQueryService { | |||
private static final TypedKVEntry[] EMPTY_ENTRIES = new TypedKVEntry[0]; | |||
private HashDigest[] ledgerHashs; | |||
private LedgerQuery ledger; | |||
public LedgerQueryService(LedgerQuery ledger) { | |||
this.ledger = ledger; | |||
this.ledgerHashs = new HashDigest[] { ledger.getHash() }; | |||
} | |||
private void checkLedgerHash(HashDigest ledgerHash) { | |||
if (!ledgerHashs[0].equals(ledgerHash)) { | |||
throw new LedgerException("Unsupport cross chain query!"); | |||
} | |||
} | |||
@Override | |||
public HashDigest[] getLedgerHashs() { | |||
return ledgerHashs; | |||
} | |||
@Override | |||
public LedgerInfo getLedger(HashDigest ledgerHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerInfo ledgerInfo = new LedgerInfo(); | |||
ledgerInfo.setHash(ledger.getHash()); | |||
ledgerInfo.setLatestBlockHash(ledger.getLatestBlockHash()); | |||
ledgerInfo.setLatestBlockHeight(ledger.getLatestBlockHeight()); | |||
return ledgerInfo; | |||
} | |||
@Override | |||
public LedgerAdminInfo getLedgerAdminInfo(HashDigest ledgerHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
LedgerAdminInfo administration = ledger.getAdminInfo(block); | |||
return administration; | |||
} | |||
@Override | |||
public ParticipantNode[] getConsensusParticipants(HashDigest ledgerHash) { | |||
return getLedgerAdminInfo(ledgerHash).getParticipants(); | |||
} | |||
@Override | |||
public LedgerMetadata getLedgerMetadata(HashDigest ledgerHash) { | |||
return getLedgerAdminInfo(ledgerHash).getMetadata(); | |||
} | |||
@Override | |||
public LedgerBlock getBlock(HashDigest ledgerHash, long height) { | |||
checkLedgerHash(ledgerHash); | |||
return ledger.getBlock(height); | |||
} | |||
@Override | |||
public LedgerBlock getBlock(HashDigest ledgerHash, HashDigest blockHash) { | |||
checkLedgerHash(ledgerHash); | |||
return ledger.getBlock(blockHash); | |||
} | |||
@Override | |||
public long getTransactionCount(HashDigest ledgerHash, long height) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(height); | |||
TransactionQuery txset = ledger.getTransactionSet(block); | |||
return txset.getTotalCount(); | |||
} | |||
@Override | |||
public long getTransactionCount(HashDigest ledgerHash, HashDigest blockHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(blockHash); | |||
TransactionQuery txset = ledger.getTransactionSet(block); | |||
return txset.getTotalCount(); | |||
} | |||
@Override | |||
public long getTransactionTotalCount(HashDigest ledgerHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
TransactionQuery txset = ledger.getTransactionSet(block); | |||
return txset.getTotalCount(); | |||
} | |||
@Override | |||
public long getDataAccountCount(HashDigest ledgerHash, long height) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(height); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
return dataAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getDataAccountCount(HashDigest ledgerHash, HashDigest blockHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(blockHash); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
return dataAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getDataAccountTotalCount(HashDigest ledgerHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
return dataAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getUserCount(HashDigest ledgerHash, long height) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(height); | |||
UserAccountQuery userAccountSet = ledger.getUserAccountSet(block); | |||
return userAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getUserCount(HashDigest ledgerHash, HashDigest blockHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(blockHash); | |||
UserAccountQuery userAccountSet = ledger.getUserAccountSet(block); | |||
return userAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getUserTotalCount(HashDigest ledgerHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
UserAccountQuery userAccountSet = ledger.getUserAccountSet(block); | |||
return userAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getContractCount(HashDigest ledgerHash, long height) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(height); | |||
ContractAccountQuery contractAccountSet = ledger.getContractAccountSet(block); | |||
return contractAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getContractCount(HashDigest ledgerHash, HashDigest blockHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getBlock(blockHash); | |||
ContractAccountQuery contractAccountSet = ledger.getContractAccountSet(block); | |||
return contractAccountSet.getTotal(); | |||
} | |||
@Override | |||
public long getContractTotalCount(HashDigest ledgerHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
ContractAccountQuery contractAccountSet = ledger.getContractAccountSet(block); | |||
return contractAccountSet.getTotal(); | |||
} | |||
@Override | |||
public LedgerTransaction[] getTransactions(HashDigest ledgerHash, long height, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock ledgerBlock = ledger.getBlock(height); | |||
TransactionQuery transactionSet = ledger.getTransactionSet(ledgerBlock); | |||
int lastHeightTxTotalNums = 0; | |||
if (height > 0) { | |||
lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); | |||
} | |||
int currentHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); | |||
// 取当前高度的增量交易数,在增量交易里进行查找 | |||
int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; | |||
// | |||
// if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { | |||
// fromIndex = 0; | |||
// } | |||
// if (count == -1) { | |||
// fromIndex = 0; | |||
// count = currentHeightTxNums; | |||
// } | |||
// if (count > currentHeightTxNums) { | |||
// count = currentHeightTxNums - fromIndex; | |||
// } | |||
int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); | |||
return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0], indexAndCount[1]); | |||
} | |||
@Override | |||
public LedgerTransaction[] getTransactions(HashDigest ledgerHash, HashDigest blockHash, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock ledgerBlock = ledger.getBlock(blockHash); | |||
long height = ledgerBlock.getHeight(); | |||
TransactionQuery transactionSet = ledger.getTransactionSet(ledgerBlock); | |||
int lastHeightTxTotalNums = 0; | |||
if (height > 0) { | |||
lastHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height - 1)).getTotalCount(); | |||
} | |||
int currentHeightTxTotalNums = (int) ledger.getTransactionSet(ledger.getBlock(height)).getTotalCount(); | |||
// 取当前块hash的增量交易数,在增量交易里进行查找 | |||
int currentHeightTxNums = currentHeightTxTotalNums - lastHeightTxTotalNums; | |||
// if (fromIndex < 0 || fromIndex >= currentHeightTxNums) { | |||
// fromIndex = 0; | |||
// } | |||
// if (count == -1) { | |||
// fromIndex = 0; | |||
// count = currentHeightTxNums; | |||
// } | |||
// if (count > currentHeightTxNums) { | |||
// count = currentHeightTxNums - fromIndex; | |||
// } | |||
int indexAndCount[] = QueryUtil.calFromIndexAndCount(fromIndex, count, currentHeightTxNums); | |||
return transactionSet.getTxs(lastHeightTxTotalNums + indexAndCount[0], indexAndCount[1]); | |||
} | |||
@Override | |||
public LedgerTransaction getTransactionByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
TransactionQuery txset = ledger.getTransactionSet(block); | |||
return txset.get(contentHash); | |||
} | |||
@Override | |||
public TransactionState getTransactionStateByContentHash(HashDigest ledgerHash, HashDigest contentHash) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
TransactionQuery txset = ledger.getTransactionSet(block); | |||
return txset.getState(contentHash); | |||
} | |||
@Override | |||
public UserInfo getUser(HashDigest ledgerHash, String address) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
UserAccountQuery userAccountSet = ledger.getUserAccountSet(block); | |||
return userAccountSet.getAccount(address); | |||
} | |||
@Override | |||
public BlockchainIdentity getDataAccount(HashDigest ledgerHash, String address) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
return dataAccountSet.getAccount(Bytes.fromBase58(address)).getID(); | |||
} | |||
@Override | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, String... keys) { | |||
if (keys == null || keys.length == 0) { | |||
return EMPTY_ENTRIES; | |||
} | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
TypedKVEntry[] entries = new TypedKVEntry[keys.length]; | |||
long ver; | |||
for (int i = 0; i < entries.length; i++) { | |||
final String currKey = keys[i]; | |||
ver = dataAccount == null ? -1 : dataAccount.getDataset().getVersion(currKey); | |||
if (ver < 0) { | |||
entries[i] = new TypedKVData(currKey, -1, null); | |||
} else { | |||
BytesValue value = dataAccount.getDataset().getValue(currKey, ver); | |||
entries[i] = new TypedKVData(currKey, ver, value); | |||
} | |||
} | |||
return entries; | |||
} | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, KVInfoVO kvInfoVO) { | |||
// parse kvInfoVO; | |||
List<String> keyList = new ArrayList<>(); | |||
List<Long> versionList = new ArrayList<>(); | |||
if (kvInfoVO != null) { | |||
for (KVDataVO kvDataVO : kvInfoVO.getData()) { | |||
for (Long version : kvDataVO.getVersion()) { | |||
keyList.add(kvDataVO.getKey()); | |||
versionList.add(version); | |||
} | |||
} | |||
} | |||
String[] keys = keyList.toArray(new String[keyList.size()]); | |||
Long[] versions = versionList.toArray(new Long[versionList.size()]); | |||
if (keys == null || keys.length == 0) { | |||
return null; | |||
} | |||
if (versions == null || versions.length == 0) { | |||
return null; | |||
} | |||
if (keys.length != versions.length) { | |||
throw new ContractException("keys.length!=versions.length!"); | |||
} | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
TypedKVEntry[] entries = new TypedKVEntry[keys.length]; | |||
long ver = -1; | |||
for (int i = 0; i < entries.length; i++) { | |||
// ver = dataAccount.getDataVersion(Bytes.fromString(keys[i])); | |||
// dataAccount.getBytes(Bytes.fromString(keys[i]),1); | |||
ver = versions[i]; | |||
if (ver < 0) { | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
if (dataAccount.getDataset().getDataCount() == 0 | |||
|| dataAccount.getDataset().getValue(keys[i], ver) == null) { | |||
// is the address is not exist; the result is null; | |||
entries[i] = new TypedKVData(keys[i], -1, null); | |||
} else { | |||
BytesValue value = dataAccount.getDataset().getValue(keys[i], ver); | |||
entries[i] = new TypedKVData(keys[i], ver, value); | |||
} | |||
} | |||
} | |||
return entries; | |||
} | |||
@Override | |||
public TypedKVEntry[] getDataEntries(HashDigest ledgerHash, String address, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
int pages[] = QueryUtil.calFromIndexAndCount(fromIndex, count, (int) dataAccount.getDataset().getDataCount()); | |||
// return dataAccount.getDataset().getDataEntry(key, version).getDataEntries(pages[0], pages[1]); | |||
fromIndex = pages[0]; | |||
count = pages[1]; | |||
DataIterator<String, TypedValue> iterator = dataAccount.getDataset().iterator(); | |||
iterator.skip(fromIndex); | |||
DataEntry<String, TypedValue>[] dataEntries = iterator.next(count); | |||
TypedKVEntry[] typedKVEntries = ArrayUtils.castTo(dataEntries, TypedKVEntry.class, | |||
e -> e == null ? null : new TypedKVData(e.getKey(), e.getVersion(), e.getValue())); | |||
return typedKVEntries; | |||
} | |||
@Override | |||
public long getDataEntriesTotalCount(HashDigest ledgerHash, String address) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
DataAccount dataAccount = dataAccountSet.getAccount(Bytes.fromBase58(address)); | |||
return dataAccount.getDataset().getDataCount(); | |||
} | |||
@Override | |||
public ContractInfo getContract(HashDigest ledgerHash, String address) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
ContractAccountQuery contractAccountSet = ledger.getContractAccountSet(block); | |||
return contractAccountSet.getAccount(Bytes.fromBase58(address)); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getUsers(HashDigest ledgerHash, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
UserAccountQuery userAccountSet = ledger.getUserAccountSet(block); | |||
int pages[] = QueryUtil.calFromIndexAndCountDescend(fromIndex, count, (int) userAccountSet.getTotal()); | |||
return userAccountSet.getHeaders(pages[0], pages[1]); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getDataAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
DataAccountQuery dataAccountSet = ledger.getDataAccountSet(block); | |||
int pages[] = QueryUtil.calFromIndexAndCountDescend(fromIndex, count, (int) dataAccountSet.getTotal()); | |||
return dataAccountSet.getHeaders(pages[0], pages[1]); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getContractAccounts(HashDigest ledgerHash, int fromIndex, int count) { | |||
checkLedgerHash(ledgerHash); | |||
LedgerBlock block = ledger.getLatestBlock(); | |||
ContractAccountQuery contractAccountSet = ledger.getContractAccountSet(block); | |||
int pages[] = QueryUtil.calFromIndexAndCountDescend(fromIndex, count, (int) contractAccountSet.getTotal()); | |||
return contractAccountSet.getHeaders(pages[0], pages[1]); | |||
} | |||
} |
@@ -1,411 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import java.util.HashMap; | |||
import java.util.Map; | |||
import com.jd.blockchain.binaryproto.DataContractRegistry; | |||
import com.jd.blockchain.crypto.AddressEncoding; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.BlockchainIdentityData; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
import com.jd.blockchain.ledger.MerkleProof; | |||
import com.jd.blockchain.ledger.MerkleSnapshot; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.DataEntry; | |||
import com.jd.blockchain.utils.Transactional; | |||
public class MerkleAccountSet implements Transactional, MerkleProvable, AccountQuery<CompositeAccount> { | |||
static { | |||
DataContractRegistry.register(MerkleSnapshot.class); | |||
DataContractRegistry.register(BlockchainIdentity.class); | |||
} | |||
private final Bytes keyPrefix; | |||
/** | |||
* 账户根哈希的数据集; | |||
*/ | |||
private MerkleDataSet merkleDataset; | |||
/** | |||
* The cache of latest version accounts, including accounts getting by querying | |||
* and by new regiestering ; | |||
* | |||
*/ | |||
// TODO:未考虑大数据量时,由于缺少过期策略,会导致内存溢出的问题; | |||
private Map<Bytes, InnerMerkleAccount> latestAccountsCache = new HashMap<>(); | |||
private ExPolicyKVStorage baseExStorage; | |||
private VersioningKVStorage baseVerStorage; | |||
private CryptoSetting cryptoSetting; | |||
private volatile boolean updated; | |||
private AccountAccessPolicy accessPolicy; | |||
public boolean isReadonly() { | |||
return merkleDataset.isReadonly(); | |||
} | |||
void setReadonly() { | |||
merkleDataset.setReadonly(); | |||
} | |||
public MerkleAccountSet(CryptoSetting cryptoSetting, Bytes keyPrefix, ExPolicyKVStorage exStorage, | |||
VersioningKVStorage verStorage, AccountAccessPolicy accessPolicy) { | |||
this(null, cryptoSetting, keyPrefix, exStorage, verStorage, false, accessPolicy); | |||
} | |||
public MerkleAccountSet(HashDigest rootHash, CryptoSetting cryptoSetting, Bytes keyPrefix, | |||
ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, | |||
AccountAccessPolicy accessPolicy) { | |||
this.keyPrefix = keyPrefix; | |||
this.cryptoSetting = cryptoSetting; | |||
this.baseExStorage = exStorage; | |||
this.baseVerStorage = verStorage; | |||
this.merkleDataset = new MerkleDataSet(rootHash, cryptoSetting, keyPrefix, this.baseExStorage, | |||
this.baseVerStorage, readonly); | |||
this.accessPolicy = accessPolicy; | |||
} | |||
@Override | |||
public HashDigest getRootHash() { | |||
return merkleDataset.getRootHash(); | |||
} | |||
@Override | |||
public MerkleProof getProof(Bytes key) { | |||
return merkleDataset.getProof(key); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getHeaders(int fromIndex, int count) { | |||
DataEntry<Bytes, byte[]>[] results = merkleDataset.getLatestDataEntries(fromIndex, count); | |||
BlockchainIdentity[] ids = new BlockchainIdentity[results.length]; | |||
for (int i = 0; i < results.length; i++) { | |||
InnerMerkleAccount account = createAccount(results[i].getKey(), new HashDigest(results[i].getValue()), | |||
results[i].getVersion(), true); | |||
ids[i] = account.getID(); | |||
} | |||
return ids; | |||
} | |||
/** | |||
* 返回账户的总数量; | |||
* | |||
* @return | |||
*/ | |||
public long getTotal() { | |||
return merkleDataset.getDataCount(); | |||
} | |||
@Override | |||
public CompositeAccount getAccount(String address) { | |||
return getAccount(Bytes.fromBase58(address)); | |||
} | |||
/** | |||
* 返回最新版本的 Account; | |||
* | |||
* @param address | |||
* @return | |||
*/ | |||
@Override | |||
public CompositeAccount getAccount(Bytes address) { | |||
return this.getAccount(address, -1); | |||
} | |||
/** | |||
* 账户是否存在;<br> | |||
* | |||
* 如果指定的账户已经注册(通过 {@link #register(String, PubKey)} 方法),但尚未提交(通过 | |||
* {@link #commit()} 方法),此方法对该账户仍然返回 false; | |||
* | |||
* @param address | |||
* @return | |||
*/ | |||
public boolean contains(Bytes address) { | |||
InnerMerkleAccount acc = latestAccountsCache.get(address); | |||
if (acc != null) { | |||
// 无论是新注册未提交的,还是缓存已提交的账户实例,都认为是存在; | |||
return true; | |||
} | |||
long latestVersion = merkleDataset.getVersion(address); | |||
return latestVersion > -1; | |||
} | |||
/** | |||
* 返回指定账户的版本; <br> | |||
* 如果账户已经注册,则返回该账户的最新版本,值大于等于 0; <br> | |||
* 如果账户不存在,则返回 -1;<br> | |||
* 如果账户已经注册(通过 {@link #register(String, PubKey)} 方法),但尚未提交(通过 {@link #commit()} | |||
* 方法),则返回 -1; <br> | |||
* | |||
* @param address | |||
* @return | |||
*/ | |||
public long getVersion(Bytes address) { | |||
InnerMerkleAccount acc = latestAccountsCache.get(address); | |||
if (acc != null) { | |||
// 已注册尚未提交,也返回 -1; | |||
return acc.getVersion(); | |||
} | |||
return merkleDataset.getVersion(address); | |||
} | |||
/** | |||
* 返回指定版本的 Account; | |||
* | |||
* 只有最新版本的账户才能可写的,其它都是只读; | |||
* | |||
* @param address 账户地址; | |||
* @param version 账户版本;如果指定为 -1,则返回最新版本; | |||
* @return | |||
*/ | |||
public CompositeAccount getAccount(Bytes address, long version) { | |||
version = version < 0 ? -1 : version; | |||
InnerMerkleAccount acc = latestAccountsCache.get(address); | |||
if (acc != null && version == -1) { | |||
return acc; | |||
} else if (acc != null && acc.getVersion() == version) { | |||
return acc; | |||
} | |||
long latestVersion = merkleDataset.getVersion(address); | |||
if (latestVersion < 0) { | |||
// Not exist; | |||
return null; | |||
} | |||
if (version > latestVersion) { | |||
return null; | |||
} | |||
// 如果是不存在的,或者刚刚新增未提交的账户,则前面一步查询到的 latestVersion 小于 0, 代码不会执行到此; | |||
if (acc != null && acc.getVersion() != latestVersion) { | |||
// 当执行到此处时,并且缓冲列表中缓存了最新的版本, | |||
// 如果当前缓存的最新账户的版本和刚刚从存储中检索得到的最新版本不一致,可能存在外部的并发更新,这超出了系统设计的逻辑; | |||
// TODO:如果是今后扩展至集群方案时,这种不一致的原因可能是由其它集群节点实例执行了更新,这种情况下,最好是放弃旧缓存,并重新加载和缓存最新版本; | |||
// by huanghaiquan at 2018-9-2 23:03:00; | |||
throw new IllegalStateException("The latest version in cache is not equals the latest version in storage! " | |||
+ "Mybe some asynchronzing updating are performed out of current server."); | |||
} | |||
// Now, be sure that "acc == null", so get account from storage; | |||
// Set readonly for the old version account; | |||
boolean readonly = (version > -1 && version < latestVersion) || isReadonly(); | |||
long qVersion = version == -1 ? latestVersion : version; | |||
// load account from storage; | |||
acc = loadAccount(address, readonly, qVersion); | |||
if (acc == null) { | |||
return null; | |||
} | |||
if (!readonly) { | |||
// cache the latest version witch enable reading and writing; | |||
// readonly version of account not necessary to be cached; | |||
latestAccountsCache.put(address, acc); | |||
} | |||
return acc; | |||
} | |||
public CompositeAccount register(Bytes address, PubKey pubKey) { | |||
return register(new BlockchainIdentityData(address, pubKey)); | |||
} | |||
/** | |||
* 注册一个新账户; <br> | |||
* | |||
* 如果账户已经存在,则会引发 {@link LedgerException} 异常; <br> | |||
* | |||
* 如果指定的地址和公钥不匹配,则会引发 {@link LedgerException} 异常; | |||
* | |||
* @param address 区块链地址; | |||
* @param pubKey 公钥; | |||
* @return 注册成功的账户对象; | |||
*/ | |||
public CompositeAccount register(BlockchainIdentity accountId) { | |||
if (isReadonly()) { | |||
throw new IllegalArgumentException("This AccountSet is readonly!"); | |||
} | |||
Bytes address = accountId.getAddress(); | |||
PubKey pubKey = accountId.getPubKey(); | |||
verifyAddressEncoding(address, pubKey); | |||
InnerMerkleAccount cachedAcc = latestAccountsCache.get(address); | |||
if (cachedAcc != null) { | |||
if (cachedAcc.getVersion() < 0) { | |||
// 同一个新账户已经注册,但尚未提交,所以重复注册不会引起任何变化; | |||
return cachedAcc; | |||
} | |||
// 相同的账户已经存在; | |||
throw new LedgerException("The registering account already exist!"); | |||
} | |||
long version = merkleDataset.getVersion(address); | |||
if (version >= 0) { | |||
throw new LedgerException("The registering account already exist!"); | |||
} | |||
if (!accessPolicy.checkRegistering(address, pubKey)) { | |||
throw new LedgerException("Account Registering was rejected for the access policy!"); | |||
} | |||
Bytes prefix = keyPrefix.concat(address); | |||
InnerMerkleAccount acc = createInstance(accountId, cryptoSetting, prefix); | |||
latestAccountsCache.put(address, acc); | |||
updated = true; | |||
return acc; | |||
} | |||
private void verifyAddressEncoding(Bytes address, PubKey pubKey) { | |||
Bytes chAddress = AddressEncoding.generateAddress(pubKey); | |||
if (!chAddress.equals(address)) { | |||
throw new LedgerException("The registering Address mismatch the specified PubKey!"); | |||
} | |||
} | |||
private InnerMerkleAccount createInstance(BlockchainIdentity header, CryptoSetting cryptoSetting, Bytes keyPrefix) { | |||
return new InnerMerkleAccount(header, cryptoSetting, keyPrefix, baseExStorage, baseVerStorage); | |||
} | |||
/** | |||
* 加载指定版本的账户; | |||
* | |||
* @param address 账户地址; | |||
* @param readonly 是否只读; | |||
* @param version 账户的版本;大于等于 0 ; | |||
* @return | |||
*/ | |||
private InnerMerkleAccount loadAccount(Bytes address, boolean readonly, long version) { | |||
byte[] rootHashBytes = merkleDataset.getValue(address, version); | |||
if (rootHashBytes == null) { | |||
return null; | |||
} | |||
HashDigest rootHash = new HashDigest(rootHashBytes); | |||
return createAccount(address, rootHash, version, readonly); | |||
} | |||
private InnerMerkleAccount createAccount(Bytes address, HashDigest rootHash, long version, boolean readonly) { | |||
// prefix; | |||
Bytes prefix = keyPrefix.concat(address); | |||
return new InnerMerkleAccount(address, version, rootHash, cryptoSetting, prefix, baseExStorage, baseVerStorage, | |||
readonly); | |||
} | |||
// TODO:优化:区块链身份(地址+公钥)与其Merkle树根哈希分开独立存储; | |||
// 不必作为一个整块,避免状态数据写入时频繁重写公钥,尤其某些算法的公钥可能很大; | |||
/** | |||
* 保存账户的根哈希,返回账户的新版本; | |||
* | |||
* @param account | |||
* @return | |||
*/ | |||
private long saveAccount(InnerMerkleAccount account) { | |||
// 提交更改,更新哈希; | |||
account.commit(); | |||
return account.getVersion(); | |||
} | |||
@Override | |||
public boolean isUpdated() { | |||
return updated; | |||
} | |||
@Override | |||
public void commit() { | |||
if (!updated) { | |||
return; | |||
} | |||
try { | |||
for (InnerMerkleAccount acc : latestAccountsCache.values()) { | |||
// updated or new created; | |||
if (acc.isUpdated() || acc.getVersion() < 0) { | |||
saveAccount(acc); | |||
} | |||
} | |||
merkleDataset.commit(); | |||
} finally { | |||
updated = false; | |||
latestAccountsCache.clear(); | |||
} | |||
} | |||
@Override | |||
public void cancel() { | |||
if (!updated) { | |||
return; | |||
} | |||
Bytes[] addresses = new Bytes[latestAccountsCache.size()]; | |||
latestAccountsCache.keySet().toArray(addresses); | |||
for (Bytes address : addresses) { | |||
InnerMerkleAccount acc = latestAccountsCache.remove(address); | |||
// cancel; | |||
if (acc.isUpdated()) { | |||
acc.cancel(); | |||
} | |||
} | |||
updated = false; | |||
} | |||
/** | |||
* 内部实现的账户,监听和同步账户数据的变更; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
private class InnerMerkleAccount extends MerkleAccount { | |||
private long version; | |||
public InnerMerkleAccount(BlockchainIdentity accountID, CryptoSetting cryptoSetting, Bytes keyPrefix, | |||
ExPolicyKVStorage exStorage, VersioningKVStorage verStorage) { | |||
super(accountID, cryptoSetting, keyPrefix, exStorage, verStorage); | |||
this.version = -1; | |||
} | |||
public InnerMerkleAccount(Bytes address, long version, HashDigest dataRootHash, CryptoSetting cryptoSetting, | |||
Bytes keyPrefix, ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly) { | |||
super(address, dataRootHash, cryptoSetting, keyPrefix, exStorage, verStorage, readonly); | |||
this.version = version; | |||
} | |||
@Override | |||
protected void onUpdated(String key, TypedValue value, long expectedVersion, long newVersion) { | |||
updated = true; | |||
} | |||
@Override | |||
protected void onCommited(HashDigest previousRootHash, HashDigest newRootHash) { | |||
long newVersion = merkleDataset.setValue(this.getAddress(), newRootHash.toBytes(), version); | |||
if (newVersion < 0) { | |||
// Update fail; | |||
throw new LedgerException("Account updating fail! --[Address=" + this.getAddress() + "]"); | |||
} | |||
this.version = newVersion; | |||
} | |||
public long getVersion() { | |||
return version; | |||
} | |||
} | |||
} |
@@ -1,740 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
import com.jd.blockchain.ledger.MerkleDataNode; | |||
import com.jd.blockchain.ledger.MerkleProof; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage.ExPolicy; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.storage.service.utils.BufferedKVStorage; | |||
import com.jd.blockchain.storage.service.utils.VersioningKVData; | |||
import com.jd.blockchain.utils.ArrayUtils; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.DataEntry; | |||
import com.jd.blockchain.utils.DataIterator; | |||
import com.jd.blockchain.utils.Dataset; | |||
import com.jd.blockchain.utils.Transactional; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
/** | |||
* 对新的数据项按顺序递增进行编号的 Merkle 数据集; <br> | |||
* | |||
* 注:此实现不是线程安全的; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class MerkleDataSet implements Transactional, MerkleProvable, Dataset<Bytes, byte[]> { | |||
/** | |||
* 4 MB MaxSize of value; | |||
*/ | |||
public static final int MAX_SIZE_OF_VALUE = 4 * 1024 * 1024; | |||
public static final Bytes SN_PREFIX = Bytes.fromString("SN" + LedgerConsts.KEY_SEPERATOR); | |||
public static final Bytes DATA_PREFIX = Bytes.fromString("KV" + LedgerConsts.KEY_SEPERATOR); | |||
public static final Bytes MERKLE_TREE_PREFIX = Bytes.fromString("MKL" + LedgerConsts.KEY_SEPERATOR); | |||
private final Bytes snKeyPrefix; | |||
private final Bytes dataKeyPrefix; | |||
private final Bytes merkleKeyPrefix; | |||
@SuppressWarnings("unchecked") | |||
private static final DataEntry<Bytes, byte[]>[] EMPTY_ENTRIES = new DataEntry[0]; | |||
private BufferedKVStorage bufferedStorage; | |||
private VersioningKVStorage valueStorage; | |||
private ExPolicyKVStorage snStorage; | |||
private MerkleTree merkleTree; | |||
private SNGenerator snGenerator; | |||
private boolean readonly; | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.core.MerkleProvable#getRootHash() | |||
*/ | |||
@Override | |||
public HashDigest getRootHash() { | |||
return merkleTree.getRootHash(); | |||
} | |||
/** | |||
* 创建一个新的 MerkleDataSet; | |||
* | |||
* @param setting 密码设置; | |||
* @param exPolicyStorage 默克尔树的存储; | |||
* @param versioningStorage 数据的存储; | |||
*/ | |||
public MerkleDataSet(CryptoSetting setting, String keyPrefix, ExPolicyKVStorage exPolicyStorage, | |||
VersioningKVStorage versioningStorage) { | |||
this(setting, Bytes.fromString(keyPrefix), exPolicyStorage, versioningStorage); | |||
} | |||
/** | |||
* 创建一个新的 MerkleDataSet; | |||
* | |||
* @param setting 密码设置; | |||
* @param exPolicyStorage 默克尔树的存储; | |||
* @param versioningStorage 数据的存储; | |||
*/ | |||
public MerkleDataSet(CryptoSetting setting, Bytes keyPrefix, ExPolicyKVStorage exPolicyStorage, | |||
VersioningKVStorage versioningStorage) { | |||
// 缓冲对KV的写入; | |||
this.bufferedStorage = new BufferedKVStorage(exPolicyStorage, versioningStorage, false); | |||
// 把存储数据值、SN、Merkle节点的 key 分别加入独立的前缀,避免针对 key 的注入攻击; | |||
// this.valueStorage = PrefixAppender.prefix(DATA_PREFIX, (VersioningKVStorage) | |||
// bufferedStorage); | |||
// this.snStorage = PrefixAppender.prefix(SN_PREFIX, (ExPolicyKVStorage) | |||
// bufferedStorage); | |||
snKeyPrefix = keyPrefix.concat(SN_PREFIX); | |||
dataKeyPrefix = keyPrefix.concat(DATA_PREFIX); | |||
this.valueStorage = bufferedStorage; | |||
this.snStorage = bufferedStorage; | |||
// MerkleTree 本身是可缓冲的; | |||
// ExPolicyKVStorage merkleTreeStorage = | |||
// PrefixAppender.prefix(MERKLE_TREE_PREFIX, exPolicyStorage); | |||
merkleKeyPrefix = keyPrefix.concat(MERKLE_TREE_PREFIX); | |||
ExPolicyKVStorage merkleTreeStorage = exPolicyStorage; | |||
this.merkleTree = new MerkleTree(setting, merkleKeyPrefix, merkleTreeStorage); | |||
this.snGenerator = new MerkleSequenceSNGenerator(merkleTree); | |||
} | |||
/** | |||
* 从指定的 Merkle 根构建的 MerkleDataSet; | |||
* | |||
* @param dataStorage | |||
* @param defaultMerkleHashAlgorithm | |||
* @param verifyMerkleHashOnLoad | |||
* @param merkleTreeStorage | |||
* @param snGenerator | |||
*/ | |||
public MerkleDataSet(HashDigest merkleRootHash, CryptoSetting setting, String keyPrefix, | |||
ExPolicyKVStorage exPolicyStorage, VersioningKVStorage versioningStorage, boolean readonly) { | |||
this(merkleRootHash, setting, Bytes.fromString(keyPrefix), exPolicyStorage, versioningStorage, readonly); | |||
} | |||
/** | |||
* 从指定的 Merkle 根构建的 MerkleDataSet; | |||
* | |||
* @param dataStorage | |||
* @param defaultMerkleHashAlgorithm | |||
* @param verifyMerkleHashOnLoad | |||
* @param merkleTreeStorage | |||
* @param snGenerator | |||
*/ | |||
public MerkleDataSet(HashDigest merkleRootHash, CryptoSetting setting, Bytes keyPrefix, | |||
ExPolicyKVStorage exPolicyStorage, VersioningKVStorage versioningStorage, boolean readonly) { | |||
// 缓冲对KV的写入; | |||
this.bufferedStorage = new BufferedKVStorage(exPolicyStorage, versioningStorage, false); | |||
// 把存储数据值、SN、Merkle节点的 key 分别加入独立的前缀,避免针对 key 的注入攻击; | |||
// snKeyPrefix = Bytes.fromString(keyPrefix + SN_PREFIX); | |||
// dataKeyPrefix = Bytes.fromString(keyPrefix + DATA_PREFIX); | |||
snKeyPrefix = keyPrefix.concat(SN_PREFIX); | |||
dataKeyPrefix = keyPrefix.concat(DATA_PREFIX); | |||
this.valueStorage = bufferedStorage; | |||
this.snStorage = bufferedStorage; | |||
// MerkleTree 本身是可缓冲的; | |||
merkleKeyPrefix = keyPrefix.concat(MERKLE_TREE_PREFIX); | |||
ExPolicyKVStorage merkleTreeStorage = exPolicyStorage; | |||
this.merkleTree = new MerkleTree(merkleRootHash, setting, merkleKeyPrefix, merkleTreeStorage, readonly); | |||
this.snGenerator = new MerkleSequenceSNGenerator(merkleTree); | |||
this.readonly = readonly; | |||
} | |||
public boolean isReadonly() { | |||
return readonly; | |||
} | |||
void setReadonly() { | |||
this.readonly = true; | |||
} | |||
@Override | |||
public long getDataCount() { | |||
return merkleTree.getDataCount(); | |||
} | |||
/** | |||
* 返回理论上允许的最大数据索引; | |||
* | |||
* @return | |||
*/ | |||
public long getMaxIndex() { | |||
return merkleTree.getMaxSn(); | |||
} | |||
public byte[][] getLatestValues(long fromIndex, int count) { | |||
if (count > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
if (fromIndex < 0 || (fromIndex + count) > merkleTree.getDataCount()) { | |||
throw new IllegalArgumentException("Index out of bound!"); | |||
} | |||
byte[][] values = new byte[count][]; | |||
for (int i = 0; i < count; i++) { | |||
MerkleDataNode dataNode = merkleTree.getData(fromIndex + i); | |||
Bytes dataKey = encodeDataKey(dataNode.getKey()); | |||
values[i] = valueStorage.get(dataKey, dataNode.getVersion()); | |||
} | |||
return values; | |||
} | |||
public DataEntry<Bytes, byte[]>[] getLatestDataEntries(long fromIndex, int count) { | |||
if (count > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException("Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
if (fromIndex < 0 || (fromIndex + count) > merkleTree.getDataCount()) { | |||
throw new IllegalArgumentException("Index out of bound!"); | |||
} | |||
if (count == 0) { | |||
return EMPTY_ENTRIES; | |||
} | |||
@SuppressWarnings("unchecked") | |||
DataEntry<Bytes, byte[]>[] values = new DataEntry[count]; | |||
byte[] bytesValue; | |||
for (int i = 0; i < count; i++) { | |||
MerkleDataNode dataNode = merkleTree.getData(fromIndex + i); | |||
Bytes dataKey = encodeDataKey(dataNode.getKey()); | |||
bytesValue = valueStorage.get(dataKey, dataNode.getVersion()); | |||
values[i] = new VersioningKVData<Bytes, byte[]>(dataNode.getKey(), dataNode.getVersion(), bytesValue); | |||
} | |||
return values; | |||
} | |||
public DataEntry<Bytes, byte[]> getLatestDataEntry(long index) { | |||
if (index < 0 || index + 1 > merkleTree.getDataCount()) { | |||
throw new IllegalArgumentException("Index out of bound!"); | |||
} | |||
byte[] bytesValue; | |||
MerkleDataNode dataNode = merkleTree.getData(index); | |||
Bytes dataKey = encodeDataKey(dataNode.getKey()); | |||
bytesValue = valueStorage.get(dataKey, dataNode.getVersion()); | |||
DataEntry<Bytes, byte[]> entry = new VersioningKVData<Bytes, byte[]>(dataNode.getKey(), dataNode.getVersion(), | |||
bytesValue); | |||
return entry; | |||
} | |||
/** | |||
* get the data at the specific index; | |||
* | |||
* @param fromIndex | |||
* @return | |||
*/ | |||
public byte[] getValuesAtIndex(int fromIndex) { | |||
MerkleDataNode dataNode = merkleTree.getData(fromIndex); | |||
Bytes dataKey = encodeDataKey(dataNode.getKey()); | |||
return valueStorage.get(dataKey, dataNode.getVersion()); | |||
} | |||
/** | |||
* get the key at the specific index; | |||
* | |||
* @param fromIndex | |||
* @return | |||
*/ | |||
public String getKeyAtIndex(int fromIndex) { | |||
MerkleDataNode dataNode = merkleTree.getData(fromIndex); | |||
// TODO: 未去掉前缀; | |||
return dataNode.getKey().toUTF8String(); | |||
} | |||
// /** | |||
// * Create or update the value associated the specified key if the version | |||
// * checking is passed.<br> | |||
// * | |||
// * The value of the key will be updated only if it's latest version equals the | |||
// * specified version argument. <br> | |||
// * If the key doesn't exist, it will be created when the version arg was -1. | |||
// * <p> | |||
// * If updating is performed, the version of the key increase by 1. <br> | |||
// * If creating is performed, the version of the key initialize by 0. <br> | |||
// * | |||
// * @param key The key of data; | |||
// * @param value The value of data; | |||
// * @param version The expected latest version of the key. | |||
// * @return The new version of the key. <br> | |||
// * If the key is new created success, then return 0; <br> | |||
// * If the key is updated success, then return the new version;<br> | |||
// * If this operation fail by version checking or other reason, then | |||
// * return -1; | |||
// */ | |||
// @Override | |||
// public long setValue(String key, byte[] value, long version) { | |||
// return setValue(Bytes.fromString(key), value, version); | |||
// } | |||
/** | |||
* Create or update the value associated the specified key if the version | |||
* checking is passed.<br> | |||
* | |||
* The value of the key will be updated only if it's latest version equals the | |||
* specified version argument. <br> | |||
* If the key doesn't exist, it will be created when the version arg was -1. | |||
* <p> | |||
* If updating is performed, the version of the key increase by 1. <br> | |||
* If creating is performed, the version of the key initialize by 0. <br> | |||
* | |||
* @param key The key of data; | |||
* @param value The value of data; | |||
* @param version The expected latest version of the key. | |||
* @return The new version of the key. <br> | |||
* If the key is new created success, then return 0; <br> | |||
* If the key is updated success, then return the new version;<br> | |||
* If this operation fail by version checking or other reason, then | |||
* return -1; | |||
*/ | |||
@Override | |||
public long setValue(Bytes key, byte[] value, long version) { | |||
if (readonly) { | |||
throw new IllegalArgumentException("This merkle dataset is readonly!"); | |||
} | |||
if (value.length > MAX_SIZE_OF_VALUE) { | |||
throw new IllegalArgumentException( | |||
"The size of value is great than the max size[" + MAX_SIZE_OF_VALUE + "]!"); | |||
} | |||
Bytes dataKey = encodeDataKey(key); | |||
long latestVersion = valueStorage.getVersion(dataKey); | |||
if (version != latestVersion) { | |||
return -1; | |||
} | |||
// set into versioning kv storage before adding to merkle tree, in order to | |||
// check version confliction first; | |||
long sn; | |||
long newVersion; | |||
if (version < 0) { | |||
// creating ; | |||
sn = snGenerator.generate(key); | |||
newVersion = valueStorage.set(dataKey, value, -1); | |||
if (newVersion < 0) { | |||
return -1; | |||
} | |||
byte[] snBytes = BytesUtils.toBytes(sn); | |||
Bytes snKey = encodeSNKey(key); | |||
boolean nx = snStorage.set(snKey, snBytes, ExPolicy.NOT_EXISTING); | |||
if (!nx) { | |||
throw new LedgerException("SN already exist! --[KEY=" + key + "]"); | |||
} | |||
} else { | |||
// updating; | |||
// TODO: 未在当前实例的层面,实现对输入键-值的缓冲,而直接写入了存储,而 MerkleTree 在未调用 commit | |||
// 之前是缓冲的,这使得在存储层面的数据会不一致,而未来需要优化; | |||
newVersion = valueStorage.set(dataKey, value, version); | |||
if (newVersion < 0) { | |||
return -1; | |||
} | |||
sn = getSN(key); | |||
} | |||
// update merkle tree; | |||
merkleTree.setData(sn, key, newVersion, value); | |||
// TODO: 未在当前实例的层面,实现对输入键-值的缓冲,而直接写入了存储,而 MerkleTree 在未调用 commit | |||
// 之前是缓冲的,这使得在存储层面的数据会不一致,而未来需要优化; | |||
return newVersion; | |||
} | |||
private Bytes encodeSNKey(Bytes key) { | |||
return new Bytes(snKeyPrefix, key); | |||
} | |||
private Bytes encodeDataKey(Bytes key) { | |||
return new Bytes(dataKeyPrefix, key); | |||
} | |||
/** | |||
* 返回指定 key 对应的序号,如果不存在,则返回 -1; | |||
* | |||
* @param key | |||
* @return | |||
*/ | |||
private long getSN(Bytes key) { | |||
// SN-KEY index entry has never changed; | |||
Bytes snKey = encodeSNKey(key); | |||
byte[] snBytes = snStorage.get(snKey); | |||
if (snBytes == null) { | |||
// throw new IllegalStateException("Cann't found SN of key[" + key + "] from | |||
// data storage!"); | |||
return -1; | |||
} | |||
return BytesUtils.toLong(snBytes); | |||
} | |||
/** | |||
* 返回默克尔树中记录的指定键的版本,在由默克尔树表示的数据集的快照中,这是指定键的最新版本,<br> | |||
* 但该版本有可能小于实际存储的最新版本(由于后续追加的新修改被之后生成的快照维护); | |||
* | |||
* @param key | |||
* @return 返回指定的键的版本;如果不存在,则返回 -1; | |||
*/ | |||
private long getMerkleVersion(Bytes key) { | |||
long sn = getSN(key); | |||
if (sn < 0) { | |||
return -1; | |||
} | |||
MerkleDataNode mdn = merkleTree.getData(sn); | |||
if (mdn == null) { | |||
return -1; | |||
} | |||
return mdn.getVersion(); | |||
} | |||
// /** | |||
// * Return the specified version's value;<br> | |||
// * | |||
// * If the key with the specified version doesn't exist, then return null;<br> | |||
// * If the version is specified to -1, then return the latest version's value; | |||
// * | |||
// * @param key | |||
// * @param version | |||
// */ | |||
// @Override | |||
// public byte[] getValue(String key, long version) { | |||
// return getValue(Bytes.fromString(key), version); | |||
// } | |||
/** | |||
* Return the specified version's value;<br> | |||
* | |||
* If the key with the specified version doesn't exist, then return null;<br> | |||
* If the version is specified to -1, then return the latest version's value; | |||
* | |||
* @param key | |||
* @param version | |||
*/ | |||
@Override | |||
public byte[] getValue(Bytes key, long version) { | |||
long latestVersion = getMerkleVersion(key); | |||
if (latestVersion < 0 || version > latestVersion) { | |||
// key not exist, or the specified version is out of the latest version indexed | |||
// by the current merkletree; | |||
return null; | |||
} | |||
version = version < 0 ? latestVersion : version; | |||
Bytes dataKey = encodeDataKey(key); | |||
return valueStorage.get(dataKey, version); | |||
} | |||
// /** | |||
// * Return the latest version's value; | |||
// * | |||
// * @param key | |||
// * @return return null if not exist; | |||
// */ | |||
// @Override | |||
// public byte[] getValue(String key) { | |||
// return getValue(Bytes.fromString(key)); | |||
// } | |||
/** | |||
* Return the latest version's value; | |||
* | |||
* @param key | |||
* @return return null if not exist; | |||
*/ | |||
@Override | |||
public byte[] getValue(Bytes key) { | |||
long latestVersion = getMerkleVersion(key); | |||
if (latestVersion < 0) { | |||
return null; | |||
} | |||
Bytes dataKey = encodeDataKey(key); | |||
return valueStorage.get(dataKey, latestVersion); | |||
} | |||
// /** | |||
// * Return the latest version entry associated the specified key; If the key | |||
// * doesn't exist, then return -1; | |||
// * | |||
// * @param key | |||
// * @return | |||
// */ | |||
// @Override | |||
// public long getVersion(String key) { | |||
// return getMerkleVersion(Bytes.fromString(key)); | |||
// } | |||
/** | |||
* Return the latest version entry associated the specified key; If the key | |||
* doesn't exist, then return -1; | |||
* | |||
* @param key | |||
* @return | |||
*/ | |||
@Override | |||
public long getVersion(Bytes key) { | |||
return getMerkleVersion(key); | |||
} | |||
// @Override | |||
// public VersioningKVEntry<String, byte[]> getDataEntry(String key) { | |||
// return getDataEntry(key, -1); | |||
// } | |||
/** | |||
* | |||
* @param key | |||
* @return Null if the key doesn't exist! | |||
*/ | |||
@Override | |||
public DataEntry<Bytes, byte[]> getDataEntry(Bytes key) { | |||
return getDataEntry(key, -1); | |||
} | |||
// @Override | |||
// public VersioningKVEntry<String, byte[]> getDataEntry(String key, long version) { | |||
// Bytes keyBytes = Bytes.fromString(key); | |||
// long latestVersion = getMerkleVersion(keyBytes); | |||
// if (latestVersion < 0 || version > latestVersion) { | |||
// // key not exist, or the specified version is out of the latest version indexed | |||
// // by the current merkletree; | |||
// return null; | |||
// } | |||
// version = version < 0 ? latestVersion : version; | |||
// Bytes dataKey = encodeDataKey(keyBytes); | |||
// byte[] value = valueStorage.get(dataKey, version); | |||
// if (value == null) { | |||
// return null; | |||
// } | |||
// return new VersioningKVData<String, byte[]>(key, version, value); | |||
// } | |||
@Override | |||
public DataEntry<Bytes, byte[]> getDataEntry(Bytes key, long version) { | |||
long latestVersion = getMerkleVersion(key); | |||
if (latestVersion < 0 || version > latestVersion) { | |||
// key not exist, or the specified version is out of the latest version indexed | |||
// by the current merkletree; | |||
return null; | |||
} | |||
version = version < 0 ? latestVersion : version; | |||
Bytes dataKey = encodeDataKey(key); | |||
byte[] value = valueStorage.get(dataKey, version); | |||
if (value == null) { | |||
return null; | |||
} | |||
return new VersioningKVData<Bytes, byte[]>(key, version, value); | |||
} | |||
@Override | |||
public DataIterator<Bytes, byte[]> iterator() { | |||
return new AscDataInterator(getDataCount()); | |||
} | |||
@Override | |||
public DataIterator<Bytes, byte[]> iteratorDesc() { | |||
return new DescDataInterator(getDataCount()); | |||
} | |||
public MerkleDataEntry getMerkleEntry(Bytes key, long version) { | |||
DataEntry<Bytes, byte[]> dataEntry = getDataEntry(key, version); | |||
if (dataEntry == null) { | |||
return null; | |||
} | |||
MerkleProof proof = getProof(key); | |||
return new MerkleDataEntryWrapper(dataEntry, proof); | |||
} | |||
public MerkleDataEntry getMerkleEntry(Bytes key) { | |||
DataEntry<Bytes, byte[]> dataEntry = getDataEntry(key); | |||
if (dataEntry == null) { | |||
return null; | |||
} | |||
MerkleProof proof = getProof(key); | |||
return new MerkleDataEntryWrapper(dataEntry, proof); | |||
} | |||
public MerkleProof getProof(String key) { | |||
return getProof(Bytes.fromString(key)); | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.core.MerkleProvable#getProof(java.lang.String) | |||
*/ | |||
@Override | |||
public MerkleProof getProof(Bytes key) { | |||
long sn = getSN(key); | |||
if (sn < 0) { | |||
return null; | |||
} | |||
return merkleTree.getProof(sn); | |||
} | |||
/** | |||
* A wrapper for {@link DataEntry} and {@link MerkleProof}; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
private static class MerkleDataEntryWrapper implements MerkleDataEntry { | |||
private DataEntry<Bytes, byte[]> data; | |||
private MerkleProof proof; | |||
public MerkleDataEntryWrapper(DataEntry<Bytes, byte[]> data, MerkleProof proof) { | |||
this.data = data; | |||
this.proof = proof; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]> getData() { | |||
return data; | |||
} | |||
@Override | |||
public MerkleProof getProof() { | |||
return proof; | |||
} | |||
} | |||
@Override | |||
public boolean isUpdated() { | |||
return bufferedStorage.isUpdated() || merkleTree.isUpdated(); | |||
} | |||
@Override | |||
public void commit() { | |||
bufferedStorage.commit(); | |||
merkleTree.commit(); | |||
} | |||
@Override | |||
public void cancel() { | |||
bufferedStorage.cancel(); | |||
merkleTree.cancel(); | |||
snGenerator = new MerkleSequenceSNGenerator(merkleTree); | |||
} | |||
// ---------------------------------------------------------- | |||
private class AscDataInterator implements DataIterator<Bytes, byte[]> { | |||
private final long total; | |||
private long cursor = 0; | |||
public AscDataInterator(long total) { | |||
this.total = total; | |||
} | |||
@Override | |||
public void skip(long count) { | |||
cursor = nextCursor(count); | |||
} | |||
private long nextCursor(long skippingCount) { | |||
long c = cursor + skippingCount; | |||
return c > total ? total : c; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]> next() { | |||
if (hasNext()) { | |||
DataEntry<Bytes, byte[]> entry = getLatestDataEntry(cursor); | |||
cursor = nextCursor(1); | |||
return entry; | |||
} | |||
return null; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]>[] next(int count) { | |||
if (hasNext()) { | |||
long from = cursor; | |||
long nextCursor = nextCursor(count); | |||
long c = nextCursor - cursor; | |||
if (c > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException( | |||
"Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
DataEntry<Bytes, byte[]>[] entries = getLatestDataEntries(from, (int) c); | |||
cursor = nextCursor; | |||
return entries; | |||
} | |||
return EMPTY_ENTRIES; | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return cursor < total; | |||
} | |||
} | |||
private class DescDataInterator implements DataIterator<Bytes, byte[]> { | |||
private final long total; | |||
private long cursor; | |||
public DescDataInterator(long total) { | |||
this.total = total; | |||
this.cursor = total - 1; | |||
} | |||
@Override | |||
public void skip(long count) { | |||
cursor = nextCursor(count); | |||
} | |||
private long nextCursor(long skippingCount) { | |||
long c = cursor - skippingCount; | |||
return c < 0 ? -1 : c; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]> next() { | |||
if (hasNext()) { | |||
DataEntry<Bytes, byte[]> entry = getLatestDataEntry(cursor); | |||
cursor = nextCursor(1); | |||
return entry; | |||
} | |||
return null; | |||
} | |||
@Override | |||
public DataEntry<Bytes, byte[]>[] next(int count) { | |||
if (hasNext()) { | |||
long nextCursor = nextCursor(count); | |||
long from = nextCursor + 1; | |||
long c = cursor - nextCursor; | |||
if (c > LedgerConsts.MAX_LIST_COUNT) { | |||
throw new IllegalArgumentException( | |||
"Count exceed the upper limit[" + LedgerConsts.MAX_LIST_COUNT + "]!"); | |||
} | |||
DataEntry<Bytes, byte[]>[] entries = getLatestDataEntries(from, (int) c); | |||
// reverse; | |||
ArrayUtils.reverse(entries); | |||
cursor = nextCursor; | |||
return entries; | |||
} | |||
return EMPTY_ENTRIES; | |||
} | |||
@Override | |||
public boolean hasNext() { | |||
return cursor < total; | |||
} | |||
} | |||
} |
@@ -1,11 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
public class Node { | |||
public Node(){ | |||
} | |||
} |
@@ -1,73 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.ParticipantNode; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.ledger.ParticipantNodeState; | |||
/** | |||
* 参与方证书数据对象; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class ParticipantCertData implements ParticipantNode { | |||
private int id; | |||
private Bytes address; | |||
private String name; | |||
private PubKey pubKey; | |||
private ParticipantNodeState participantNodeState; | |||
public ParticipantCertData() { | |||
} | |||
public ParticipantCertData(ParticipantNode participantNode) { | |||
this.id = participantNode.getId(); | |||
this.address = participantNode.getAddress(); | |||
this.name = participantNode.getName(); | |||
this.pubKey = participantNode.getPubKey(); | |||
this.participantNodeState = participantNode.getParticipantNodeState(); | |||
} | |||
public ParticipantCertData(Bytes address, String name, PubKey pubKey, ParticipantNodeState participantNodeState) { | |||
this.address = address; | |||
this.name = name; | |||
this.pubKey = pubKey; | |||
this.participantNodeState = participantNodeState; | |||
} | |||
@Override | |||
public Bytes getAddress() { | |||
return address; | |||
} | |||
@Override | |||
public String getName() { | |||
return name; | |||
} | |||
@Override | |||
public PubKey getPubKey() { | |||
return pubKey; | |||
} | |||
@Override | |||
public int getId() { | |||
return id; | |||
} | |||
public void setId(int id) { | |||
this.id = id; | |||
} | |||
@Override | |||
public ParticipantNodeState getParticipantNodeState() { | |||
return participantNodeState; | |||
} | |||
public void setParticipantNodeState(ParticipantNodeState participantNodeState) { | |||
this.participantNodeState = participantNodeState; | |||
} | |||
} |
@@ -1,22 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.ledger.ParticipantNode; | |||
/** | |||
* @author hhq | |||
* @version 1.0 | |||
* @created 14-6��-2018 12:13:33 | |||
*/ | |||
public class Peer extends Node { | |||
public ParticipantNode m_Participant; | |||
public Peer(){ | |||
} | |||
public void finalize() throws Throwable { | |||
super.finalize(); | |||
} | |||
} |
@@ -1,508 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import java.util.*; | |||
import com.jd.blockchain.ledger.*; | |||
import com.jd.blockchain.utils.Bytes; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.ledger.core.TransactionRequestExtension.Credential; | |||
import com.jd.blockchain.service.TransactionBatchProcess; | |||
import com.jd.blockchain.service.TransactionBatchResult; | |||
import com.jd.blockchain.service.TransactionBatchResultHandle; | |||
import com.jd.blockchain.transaction.SignatureUtils; | |||
import com.jd.blockchain.transaction.TxBuilder; | |||
import com.jd.blockchain.transaction.TxResponseMessage; | |||
public class TransactionBatchProcessor implements TransactionBatchProcess { | |||
private static final Logger LOGGER = LoggerFactory.getLogger(TransactionBatchProcessor.class); | |||
private LedgerSecurityManager securityManager; | |||
private LedgerEditor newBlockEditor; | |||
private LedgerQuery ledger; | |||
private OperationHandleRegisteration handlesRegisteration; | |||
// 新创建的交易; | |||
private LedgerBlock block; | |||
private TransactionState globalResult; | |||
private List<TransactionResponse> responseList = new ArrayList<>(); | |||
private TransactionBatchResult batchResult; | |||
public byte[] getPrevLatestBlockHash() { | |||
return ledger.getLatestBlockHash().toBytes(); | |||
} | |||
public byte[] getGenisBlockHash() { | |||
return ledger.getBlockHash(0).toBytes(); | |||
} | |||
public long getPreLatestBlockHeight() { | |||
return ledger.getLatestBlockHeight(); | |||
} | |||
public HashDigest getLedgerHash() { | |||
return ledger.getHash(); | |||
} | |||
/** | |||
* @param newBlockEditor 新区块的数据编辑器; | |||
* @param newBlockEditor 账本查询器,只包含新区块的前一个区块的数据集;即未提交新区块之前的经过共识的账本最新数据集; | |||
* @param opHandles 操作处理对象注册表; | |||
*/ | |||
public TransactionBatchProcessor(LedgerSecurityManager securityManager, LedgerEditor newBlockEditor, | |||
LedgerQuery ledger, OperationHandleRegisteration opHandles) { | |||
this.securityManager = securityManager; | |||
this.newBlockEditor = newBlockEditor; | |||
this.ledger = ledger; | |||
this.handlesRegisteration = opHandles; | |||
} | |||
public TransactionBatchProcessor(LedgerRepository ledgerRepo, OperationHandleRegisteration handlesRegisteration) { | |||
this.ledger = ledgerRepo; | |||
this.handlesRegisteration = handlesRegisteration; | |||
LedgerBlock ledgerBlock = ledgerRepo.getLatestBlock(); | |||
LedgerDataQuery ledgerDataQuery = ledgerRepo.getLedgerData(ledgerBlock); | |||
LedgerAdminDataQuery previousAdminDataset = ledgerDataQuery.getAdminDataset(); | |||
this.securityManager = new LedgerSecurityManagerImpl(previousAdminDataset.getAdminInfo().getRolePrivileges(), | |||
previousAdminDataset.getAdminInfo().getAuthorizations(), previousAdminDataset.getParticipantDataset(), | |||
ledgerDataQuery.getUserAccountSet()); | |||
this.newBlockEditor = ledgerRepo.createNextBlock(); | |||
} | |||
public static TransactionBatchProcess create(LedgerRepository ledgerRepo, | |||
OperationHandleRegisteration handlesRegisteration) { | |||
LedgerBlock ledgerBlock = ledgerRepo.getLatestBlock(); | |||
LedgerEditor newBlockEditor = ledgerRepo.createNextBlock(); | |||
LedgerDataQuery previousBlockDataset = ledgerRepo.getLedgerData(ledgerBlock); | |||
LedgerAdminDataQuery previousAdminDataset = previousBlockDataset.getAdminDataset(); | |||
LedgerSecurityManager securityManager = new LedgerSecurityManagerImpl( | |||
previousAdminDataset.getAdminInfo().getRolePrivileges(), | |||
previousAdminDataset.getAdminInfo().getAuthorizations(), previousAdminDataset.getParticipantDataset(), | |||
previousBlockDataset.getUserAccountSet()); | |||
TransactionBatchProcessor processor = new TransactionBatchProcessor(securityManager, newBlockEditor, ledgerRepo, | |||
handlesRegisteration); | |||
return processor; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.jd.blockchain.ledger.core.impl.TransactionBatchProcess#schedule(com.jd. | |||
* blockchain.ledger.TransactionRequest) | |||
*/ | |||
@Override | |||
public TransactionResponse schedule(TransactionRequest request) { | |||
TransactionResponse resp; | |||
try { | |||
LOGGER.debug("Start handling transaction... --[BlockHeight={}][RequestHash={}][TxHash={}]", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||
TransactionRequestExtension reqExt = new TransactionRequestExtensionImpl(request); | |||
// 初始化交易的用户安全策略; | |||
// Set<Bytes> endPointAddresses = reqExt.getEndpointAddresses(); | |||
// int index = 0; | |||
// for (Bytes address : endPointAddresses) { | |||
// System.out.printf("EndPoint Sign Address %s[%s] -> %s \r\n", request.getHash(), index++, address.toBase58()); | |||
//// LOGGER.debug("EndPoint Sign Address {}[{}] -> {}", request.getHash(), index++, address.toBase58()); | |||
// } | |||
SecurityPolicy securityPolicy = securityManager.createSecurityPolicy(reqExt.getEndpointAddresses(), | |||
reqExt.getNodeAddresses()); | |||
SecurityContext.setContextUsersPolicy(securityPolicy); | |||
// 安全校验; | |||
checkSecurity(securityPolicy); | |||
// 验证交易请求; | |||
checkRequest(reqExt); | |||
// 创建交易上下文; | |||
// 此调用将会验证交易签名,验签失败将会抛出异常,同时,不记录签名错误的交易到链上; | |||
LedgerTransactionContext txCtx = newBlockEditor.newTransaction(request); | |||
// 处理交易; | |||
resp = handleTx(reqExt, txCtx); | |||
LOGGER.debug("Complete handling transaction. --[BlockHeight={}][RequestHash={}][TxHash={}]", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash()); | |||
} catch (IllegalTransactionException e) { | |||
// 抛弃发生处理异常的交易请求; | |||
resp = discard(request, e.getTxState()); | |||
LOGGER.error(String.format( | |||
"Ignore transaction caused by IllegalTransactionException! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
} catch (BlockRollbackException e) { | |||
// 发生区块级别的处理异常,向上重新抛出异常进行处理,整个区块可能被丢弃; | |||
resp = discard(request, e.getState()); | |||
LOGGER.error(String.format( | |||
"Ignore transaction caused by BlockRollbackException! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
throw e; | |||
} catch (Exception e) { | |||
// 抛弃发生处理异常的交易请求; | |||
resp = discard(request, TransactionState.SYSTEM_ERROR); | |||
LOGGER.error(String.format( | |||
"Ignore transaction caused by the system exception! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
} finally { | |||
// 清空交易的用户安全策略; | |||
SecurityContext.removeContextUsersPolicy(); | |||
} | |||
responseList.add(resp); | |||
return resp; | |||
} | |||
/** | |||
* 执行安全验证; | |||
*/ | |||
private void checkSecurity(SecurityPolicy securityPolicy) { | |||
// 验证节点和终端身份的合法性; | |||
// 多重身份签署的必须全部身份都合法; | |||
securityPolicy.checkEndpointValidity(MultiIDsPolicy.ALL); | |||
securityPolicy.checkNodeValidity(MultiIDsPolicy.ALL); | |||
// 验证参与方节点是否具有核准交易的权限; | |||
securityPolicy.checkNodePermission(LedgerPermission.APPROVE_TX, MultiIDsPolicy.AT_LEAST_ONE); | |||
} | |||
private void checkRequest(TransactionRequestExtension reqExt) { | |||
// TODO: 把验签和创建交易并行化; | |||
checkTxContentHash(reqExt); | |||
checkEndpointSignatures(reqExt); | |||
checkNodeSignatures(reqExt); | |||
} | |||
private void checkTxContentHash(TransactionRequestExtension requestExt) { | |||
TransactionContent txContent = requestExt.getTransactionContent(); | |||
if (!TxBuilder.verifyTxContentHash(txContent, txContent.getHash())) { | |||
// 由于哈希校验失败,引发IllegalTransactionException,使外部调用抛弃此交易请求; | |||
throw new IllegalTransactionException( | |||
"Wrong transaction content hash! --[TxHash=" + requestExt.getTransactionContent().getHash() + "]!", | |||
TransactionState.IGNORED_BY_WRONG_CONTENT_SIGNATURE); | |||
} | |||
} | |||
private void checkNodeSignatures(TransactionRequestExtension request) { | |||
TransactionContent txContent = request.getTransactionContent(); | |||
Collection<Credential> nodes = request.getNodes(); | |||
if (nodes != null) { | |||
for (Credential node : nodes) { | |||
if (!SignatureUtils.verifyHashSignature(txContent.getHash(), node.getSignature().getDigest(), | |||
node.getPubKey())) { | |||
// 由于签名校验失败,引发IllegalTransactionException,使外部调用抛弃此交易请求; | |||
throw new IllegalTransactionException( | |||
String.format("Wrong transaction node signature! --[Tx Hash=%s][Node Signer=%s]!", | |||
request.getTransactionContent().getHash(), node.getAddress()), | |||
TransactionState.IGNORED_BY_WRONG_CONTENT_SIGNATURE); | |||
} | |||
} | |||
} | |||
} | |||
private void checkEndpointSignatures(TransactionRequestExtension request) { | |||
TransactionContent txContent = request.getTransactionContent(); | |||
Collection<Credential> endpoints = request.getEndpoints(); | |||
if (endpoints != null) { | |||
for (Credential endpoint : endpoints) { | |||
if (!SignatureUtils.verifyHashSignature(txContent.getHash(), endpoint.getSignature().getDigest(), | |||
endpoint.getPubKey())) { | |||
// 由于签名校验失败,引发IllegalTransactionException,使外部调用抛弃此交易请求; | |||
throw new IllegalTransactionException( | |||
String.format("Wrong transaction endpoint signature! --[Tx Hash=%s][Endpoint Signer=%s]!", | |||
request.getTransactionContent().getHash(), endpoint.getAddress()), | |||
TransactionState.IGNORED_BY_WRONG_CONTENT_SIGNATURE); | |||
} | |||
} | |||
} | |||
} | |||
/** | |||
* 处理交易;<br> | |||
* | |||
* 此方法会处理所有的异常,以不同结果的 {@link TransactionResponse} 返回; | |||
* | |||
* @param request | |||
* @param txCtx | |||
* @return | |||
*/ | |||
private TransactionResponse handleTx(TransactionRequestExtension request, LedgerTransactionContext txCtx) { | |||
TransactionState result; | |||
List<OperationResult> operationResults = new ArrayList<>(); | |||
try { | |||
LedgerDataset dataset = txCtx.getDataset(); | |||
// 执行操作; | |||
Operation[] ops = request.getTransactionContent().getOperations(); | |||
OperationHandleContext handleContext = new OperationHandleContext() { | |||
@Override | |||
public void handle(Operation operation) { | |||
// assert; Instance of operation are one of User related operations or | |||
// DataAccount related operations; | |||
OperationHandle hdl = handlesRegisteration.getHandle(operation.getClass()); | |||
hdl.process(operation, dataset, request, ledger, this); | |||
} | |||
}; | |||
OperationHandle opHandle; | |||
int opIndex = 0; | |||
for (Operation op : ops) { | |||
opHandle = handlesRegisteration.getHandle(op.getClass()); | |||
BytesValue opResult = opHandle.process(op, dataset, request, ledger, handleContext); | |||
if (opResult != null) { | |||
operationResults.add(new OperationResultData(opIndex, opResult)); | |||
} | |||
opIndex++; | |||
} | |||
// 提交交易(事务); | |||
result = TransactionState.SUCCESS; | |||
txCtx.commit(result, operationResults); | |||
} catch (TransactionRollbackException e) { | |||
result = TransactionState.IGNORED_BY_TX_FULL_ROLLBACK; | |||
txCtx.rollback(); | |||
LOGGER.error(String.format( | |||
"Transaction was full rolled back! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
} catch (BlockRollbackException e) { | |||
// rollback all the block; | |||
// TODO: handle the BlockRollbackException in detail; | |||
result = TransactionState.IGNORED_BY_BLOCK_FULL_ROLLBACK; | |||
txCtx.rollback(); | |||
LOGGER.error( | |||
String.format("Transaction was rolled back! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), | |||
request.getTransactionContent().getHash(), e.getMessage()), | |||
e); | |||
// 重新抛出由上层错误处理; | |||
throw e; | |||
} catch (LedgerException e) { | |||
// TODO: 识别更详细的异常类型以及执行对应的处理; | |||
result = TransactionState.LEDGER_ERROR; | |||
if (e instanceof DataAccountDoesNotExistException) { | |||
result = TransactionState.DATA_ACCOUNT_DOES_NOT_EXIST; | |||
} else if (e instanceof UserDoesNotExistException) { | |||
result = TransactionState.USER_DOES_NOT_EXIST; | |||
} else if (e instanceof ContractDoesNotExistException) { | |||
result = TransactionState.CONTRACT_DOES_NOT_EXIST; | |||
} else if (e instanceof ParticipantDoesNotExistException) { | |||
result = TransactionState.PARTICIPANT_DOES_NOT_EXIST; | |||
} else if (e instanceof DataVersionConflictException) { | |||
result = TransactionState.DATA_VERSION_CONFLICT; | |||
} | |||
txCtx.discardAndCommit(result, operationResults); | |||
LOGGER.error(String.format( | |||
"Due to ledger exception, the data changes resulting from transaction execution will be rolled back and the results of the transaction will be committed! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
} catch (LedgerSecurityException e) { | |||
// TODO: 识别更详细的异常类型以及执行对应的处理; | |||
result = TransactionState.REJECTED_BY_SECURITY_POLICY; | |||
txCtx.discardAndCommit(result, operationResults); | |||
LOGGER.error(String.format( | |||
"Due to ledger security exception, the data changes resulting from transaction execution will be rolled back and the results of the transaction will be committed! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
} catch (Exception e) { | |||
result = TransactionState.SYSTEM_ERROR; | |||
txCtx.discardAndCommit(TransactionState.SYSTEM_ERROR, operationResults); | |||
LOGGER.error(String.format( | |||
"Due to system exception, the data changes resulting from transaction execution will be rolled back and the results of the transaction will be committed! --[BlockHeight=%s][RequestHash=%s][TxHash=%s] --%s", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
e.getMessage()), e); | |||
} | |||
TxResponseHandle resp = new TxResponseHandle(request, result); | |||
if (!operationResults.isEmpty()) { | |||
OperationResult[] operationResultArray = new OperationResult[operationResults.size()]; | |||
resp.setOperationResults(operationResults.toArray(operationResultArray)); | |||
} | |||
return resp; | |||
} | |||
/** | |||
* 直接丢弃交易; | |||
* | |||
* @param request | |||
* @param txState | |||
* @return 丢弃交易的回复;只包含原始请求中的交易内容哈希和交易被丢弃的原因,而不包含区块信息; | |||
*/ | |||
private TransactionResponse discard(TransactionRequest request, TransactionState txState) { | |||
// 丢弃交易的回复;只返回请求的交易内容哈希和交易被丢弃的原因, | |||
TxResponseMessage resp = new TxResponseMessage(request.getTransactionContent().getHash()); | |||
resp.setExecutionState(txState); | |||
LOGGER.error("Discard transaction request! --[BlockHeight={}][RequestHash={}][TxHash={}][ResponseState={}]", | |||
newBlockEditor.getBlockHeight(), request.getHash(), request.getTransactionContent().getHash(), | |||
resp.getExecutionState()); | |||
return resp; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.core.impl.TransactionBatchProcess#prepare() | |||
*/ | |||
@Override | |||
public TransactionBatchResultHandle prepare() { | |||
if (batchResult != null) { | |||
throw new IllegalStateException("Batch result has already been prepared or canceled!"); | |||
} | |||
this.block = newBlockEditor.prepare(); | |||
this.batchResult = new TransactionBatchResultHandleImpl(); | |||
return (TransactionBatchResultHandle) batchResult; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see | |||
* com.jd.blockchain.ledger.core.impl.TransactionBatchProcess#cancel(com.jd. | |||
* blockchain.ledger.ExecutionState) | |||
*/ | |||
@Override | |||
public TransactionBatchResult cancel(TransactionState errorResult) { | |||
if (batchResult != null) { | |||
throw new IllegalStateException("Batch result has already been prepared or canceled!"); | |||
} | |||
cancelInError(errorResult); | |||
batchResult = new TransactionBatchResultImpl(); | |||
return batchResult; | |||
} | |||
@Override | |||
public long blockHeight() { | |||
// if (block != null) { | |||
// return block.getHeight(); | |||
// } | |||
// return 0; | |||
return ledger.getLatestBlockHeight(); | |||
} | |||
private void commitSuccess() { | |||
newBlockEditor.commit(); | |||
onCommitted(); | |||
} | |||
private void cancelInError(TransactionState errorResult) { | |||
if (errorResult == TransactionState.SUCCESS) { | |||
throw new IllegalArgumentException("Cann't cancel by an success result!"); | |||
} | |||
newBlockEditor.cancel(); | |||
this.globalResult = errorResult; | |||
onCanceled(); | |||
} | |||
/** | |||
* 模板事件方法:交易已提交; | |||
*/ | |||
protected void onCommitted() { | |||
} | |||
/** | |||
* 模板事件方法:交易已取消; | |||
*/ | |||
protected void onCanceled() { | |||
} | |||
private class TxResponseHandle implements TransactionResponse { | |||
private TransactionRequest request; | |||
private TransactionState result; | |||
private OperationResult[] operationResults; | |||
public TxResponseHandle(TransactionRequest request, TransactionState result) { | |||
this.request = request; | |||
this.result = result; | |||
} | |||
@Override | |||
public HashDigest getContentHash() { | |||
return request.getTransactionContent().getHash(); | |||
} | |||
@Override | |||
public TransactionState getExecutionState() { | |||
return result; | |||
} | |||
@Override | |||
public HashDigest getBlockHash() { | |||
return block == null ? null : block.getHash(); | |||
} | |||
@Override | |||
public long getBlockHeight() { | |||
return block == null ? -1 : block.getHeight(); | |||
} | |||
@Override | |||
public boolean isSuccess() { | |||
return globalResult == null ? result == TransactionState.SUCCESS : globalResult == TransactionState.SUCCESS; | |||
} | |||
@Override | |||
public OperationResult[] getOperationResults() { | |||
return operationResults; | |||
} | |||
public void setOperationResults(OperationResult[] operationResults) { | |||
this.operationResults = operationResults; | |||
} | |||
} | |||
private class TransactionBatchResultImpl implements TransactionBatchResult { | |||
@Override | |||
public LedgerBlock getBlock() { | |||
return block; | |||
} | |||
@Override | |||
public Iterator<TransactionResponse> getResponses() { | |||
return responseList.iterator(); | |||
} | |||
} | |||
private class TransactionBatchResultHandleImpl extends TransactionBatchResultImpl | |||
implements TransactionBatchResultHandle { | |||
@Override | |||
public void commit() { | |||
commitSuccess(); | |||
} | |||
@Override | |||
public void cancel(TransactionState errorResult) { | |||
cancelInError(errorResult); | |||
} | |||
} | |||
} |
@@ -1,85 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BytesValue; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
import com.jd.blockchain.ledger.TypedValue; | |||
import com.jd.blockchain.ledger.UserInfo; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
* 用户账户; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class UserAccount extends AccountDecorator implements UserInfo { // implements UserInfo { | |||
private static final String USER_INFO_PREFIX = "PROP" + LedgerConsts.KEY_SEPERATOR; | |||
private static final String DATA_PUB_KEY = "DATA-PUBKEY"; | |||
public UserAccount(CompositeAccount baseAccount) { | |||
super(baseAccount); | |||
} | |||
private PubKey dataPubKey; | |||
@Override | |||
public Bytes getAddress() { | |||
return getID().getAddress(); | |||
} | |||
@Override | |||
public PubKey getPubKey() { | |||
return getID().getPubKey(); | |||
} | |||
@Override | |||
public PubKey getDataPubKey() { | |||
if (dataPubKey == null) { | |||
BytesValue pkBytes = getHeaders().getValue(DATA_PUB_KEY); | |||
if (pkBytes == null) { | |||
return null; | |||
} | |||
dataPubKey = new PubKey(pkBytes.getBytes().toBytes()); | |||
} | |||
return dataPubKey; | |||
} | |||
public void setDataPubKey(PubKey pubKey) { | |||
long version = getHeaders().getVersion(DATA_PUB_KEY); | |||
setDataPubKey(pubKey, version); | |||
} | |||
public void setDataPubKey(PubKey pubKey, long version) { | |||
TypedValue value = TypedValue.fromPubKey(dataPubKey); | |||
long newVersion = getHeaders().setValue(DATA_PUB_KEY, value, version); | |||
if (newVersion > -1) { | |||
dataPubKey = pubKey; | |||
} else { | |||
throw new LedgerException("Data public key was updated failed!"); | |||
} | |||
} | |||
public long setProperty(String key, String value, long version) { | |||
return getHeaders().setValue(encodePropertyKey(key), TypedValue.fromText(value), version); | |||
} | |||
public String getProperty(String key) { | |||
BytesValue value = getHeaders().getValue(encodePropertyKey(key)); | |||
return value == null ? null : value.getBytes().toUTF8String(); | |||
} | |||
public String getProperty(String key, long version) { | |||
BytesValue value = getHeaders().getValue(encodePropertyKey(key), version); | |||
return value == null ? null : value.getBytes().toUTF8String(); | |||
} | |||
private String encodePropertyKey(String key) { | |||
return USER_INFO_PREFIX+key; | |||
} | |||
} |
@@ -1,121 +0,0 @@ | |||
package com.jd.blockchain.ledger.core; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.ledger.BlockchainIdentity; | |||
import com.jd.blockchain.ledger.CryptoSetting; | |||
import com.jd.blockchain.ledger.LedgerException; | |||
import com.jd.blockchain.ledger.MerkleProof; | |||
import com.jd.blockchain.storage.service.ExPolicyKVStorage; | |||
import com.jd.blockchain.storage.service.VersioningKVStorage; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.Transactional; | |||
/** | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class UserAccountSet implements Transactional, UserAccountQuery { | |||
private MerkleAccountSet accountSet; | |||
public UserAccountSet(CryptoSetting cryptoSetting, String keyPrefix, ExPolicyKVStorage simpleStorage, | |||
VersioningKVStorage versioningStorage, AccountAccessPolicy accessPolicy) { | |||
accountSet = new MerkleAccountSet(cryptoSetting, Bytes.fromString(keyPrefix), simpleStorage, versioningStorage, | |||
accessPolicy); | |||
} | |||
public UserAccountSet(HashDigest dataRootHash, CryptoSetting cryptoSetting, String keyPrefix, | |||
ExPolicyKVStorage exStorage, VersioningKVStorage verStorage, boolean readonly, | |||
AccountAccessPolicy accessPolicy) { | |||
accountSet = new MerkleAccountSet(dataRootHash, cryptoSetting, Bytes.fromString(keyPrefix), exStorage, | |||
verStorage, readonly, accessPolicy); | |||
} | |||
@Override | |||
public BlockchainIdentity[] getHeaders(int fromIndex, int count) { | |||
return accountSet.getHeaders(fromIndex, count); | |||
} | |||
/** | |||
* 返回用户总数; | |||
* | |||
* @return | |||
*/ | |||
@Override | |||
public long getTotal() { | |||
return accountSet.getTotal(); | |||
} | |||
public boolean isReadonly() { | |||
return accountSet.isReadonly(); | |||
} | |||
void setReadonly() { | |||
accountSet.setReadonly(); | |||
} | |||
@Override | |||
public HashDigest getRootHash() { | |||
return accountSet.getRootHash(); | |||
} | |||
@Override | |||
public MerkleProof getProof(Bytes key) { | |||
return accountSet.getProof(key); | |||
} | |||
@Override | |||
public UserAccount getAccount(String address) { | |||
return getAccount(Bytes.fromBase58(address)); | |||
} | |||
@Override | |||
public UserAccount getAccount(Bytes address) { | |||
CompositeAccount baseAccount = accountSet.getAccount(address); | |||
return new UserAccount(baseAccount); | |||
} | |||
@Override | |||
public boolean contains(Bytes address) { | |||
return accountSet.contains(address); | |||
} | |||
@Override | |||
public UserAccount getAccount(Bytes address, long version) { | |||
CompositeAccount baseAccount = accountSet.getAccount(address, version); | |||
return new UserAccount(baseAccount); | |||
} | |||
/** | |||
* 注册一个新用户; <br> | |||
* | |||
* 如果用户已经存在,则会引发 {@link LedgerException} 异常; <br> | |||
* | |||
* 如果指定的地址和公钥不匹配,则会引发 {@link LedgerException} 异常; | |||
* | |||
* @param address 区块链地址; | |||
* @param pubKey 公钥; | |||
* @return 注册成功的用户对象; | |||
*/ | |||
public UserAccount register(Bytes address, PubKey pubKey) { | |||
CompositeAccount baseAccount = accountSet.register(address, pubKey); | |||
return new UserAccount(baseAccount); | |||
} | |||
@Override | |||
public boolean isUpdated() { | |||
return accountSet.isUpdated(); | |||
} | |||
@Override | |||
public void commit() { | |||
accountSet.commit(); | |||
} | |||
@Override | |||
public void cancel() { | |||
accountSet.cancel(); | |||
} | |||
} |
@@ -1,43 +0,0 @@ | |||
<project xmlns="http://maven.apache.org/POM/4.0.0" | |||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> | |||
<modelVersion>4.0.0</modelVersion> | |||
<parent> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>ledger</artifactId> | |||
<version>1.1.4.RELEASE</version> | |||
</parent> | |||
<artifactId>ledger-model</artifactId> | |||
<dependencies> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-common</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>utils-web</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>binary-proto</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-framework</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>crypto-classic</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
</dependencies> | |||
</project> |
@@ -1,14 +0,0 @@ | |||
package com.jd.blockchain.contract; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
@Target({ ElementType.TYPE }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface Contract { | |||
String name() default ""; | |||
} |
@@ -1,14 +0,0 @@ | |||
package com.jd.blockchain.contract; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
@Target({ ElementType.METHOD }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface ContractEvent { | |||
String name() default ""; | |||
} |
@@ -1,15 +0,0 @@ | |||
package com.jd.blockchain.contract; | |||
import java.lang.annotation.ElementType; | |||
import java.lang.annotation.Retention; | |||
import java.lang.annotation.RetentionPolicy; | |||
import java.lang.annotation.Target; | |||
@Target({ ElementType.METHOD }) | |||
@Retention(RetentionPolicy.RUNTIME) | |||
public @interface EventHandle { | |||
} |
@@ -1,33 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
public class BlockRollbackException extends LedgerException { | |||
private static final long serialVersionUID = 3583192000738807503L; | |||
private TransactionState state; | |||
public BlockRollbackException(String message) { | |||
this(TransactionState.SYSTEM_ERROR, message); | |||
} | |||
public BlockRollbackException(TransactionState state, String message) { | |||
super(message); | |||
assert TransactionState.SUCCESS != state; | |||
this.state = state; | |||
} | |||
public BlockRollbackException(String message, Throwable cause) { | |||
this(TransactionState.SYSTEM_ERROR, message, cause); | |||
} | |||
public BlockRollbackException(TransactionState state, String message, Throwable cause) { | |||
super(message, cause); | |||
assert TransactionState.SUCCESS != state; | |||
this.state = state; | |||
} | |||
public TransactionState getState() { | |||
return state; | |||
} | |||
} |
@@ -1,83 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
/** | |||
* 区块链事件类型;<p> | |||
* | |||
* 每一种事件类型都包含一个事件码;<p> | |||
* | |||
* 在一次事件消息中,可以包含多种事件,而且事件之间具有嵌套关系;<br> | |||
* | |||
* 例如:<p> | |||
* | |||
* 一个区块生成事件 {@link #BLOCK_GENERATED} 含了交易提交事件 | |||
* {@link #TRANSACTION_COMMITED};<p> | |||
* | |||
* 交易提交事件 {@link #TRANSACTION_COMMITED} 必然包含账户更新事件 {@link #ACCOUNT_UPDATED};<p> | |||
* | |||
* 更进一步,账户更新事件 {@link #ACCOUNT_UPDATED} 也必然包含了权限更新事件 | |||
* {@link #PRIVILEGE_UPDATED}、负载数据更新事件 {@link #PAYLOAD_UPDATED} | |||
* 、合约脚本更新事件{@link #SCRIPT_UPDATED} 、合约脚本执行事件{@link #SCRIPT_INVOKED} 这4种事件中的一种或者多种事件;<p> | |||
* | |||
* 这种嵌套关系,表现在事件的编码中是子事件码的比特位中包含了上级事件码; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public enum BlockchainEventType { | |||
/** | |||
* 生成新区块;<br> | |||
* | |||
* 事件码:1 (0x01) | |||
* | |||
*/ | |||
BLOCK_GENERATED(1), | |||
/** | |||
* 成功提交新交易;<br> | |||
* | |||
* 事件码:3 (0x03) | |||
*/ | |||
TRANSACTION_COMMITED(3), | |||
/** | |||
* 账户的版本已更新;<br> | |||
* | |||
* 事件码:259 (0x103) | |||
*/ | |||
ACCOUNT_UPDATED(259), | |||
/** | |||
* 账户权限已被更新;<br> | |||
* | |||
* 事件码:65795 (0x10103) | |||
*/ | |||
PRIVILEGE_UPDATED(65795), | |||
/** | |||
* 账户负载数据已被更新;<br> | |||
* | |||
* 事件码:131331 (0x20103) | |||
*/ | |||
PAYLOAD_UPDATED(131331), | |||
/** | |||
* 合约脚本已被更新;<br> | |||
* | |||
* 事件码:262403 (0x40103) | |||
*/ | |||
SCRIPT_UPDATED(262403), | |||
/** | |||
* 合约脚本已被调用;<br> | |||
* | |||
* 事件码:524547 (0x80103) | |||
*/ | |||
SCRIPT_INVOKED(524547); | |||
public final int CODE; | |||
private BlockchainEventType(int code) { | |||
this.CODE = code; | |||
} | |||
} |
@@ -1,216 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import java.io.ByteArrayInputStream; | |||
import java.io.ByteArrayOutputStream; | |||
import java.io.Externalizable; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.io.ObjectInput; | |||
import java.io.ObjectOutput; | |||
import java.io.OutputStream; | |||
import java.util.ArrayList; | |||
import java.util.List; | |||
import com.jd.blockchain.crypto.AddressEncoding; | |||
import com.jd.blockchain.crypto.CryptoAlgorithm; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.utils.Bytes; | |||
import com.jd.blockchain.utils.io.ByteArray; | |||
import com.jd.blockchain.utils.io.BytesEncoding; | |||
import com.jd.blockchain.utils.io.BytesReader; | |||
import com.jd.blockchain.utils.io.BytesUtils; | |||
import com.jd.blockchain.utils.io.BytesWriter; | |||
import com.jd.blockchain.utils.io.RuntimeIOException; | |||
/** | |||
* 区块链身份; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class BlockchainIdentityData implements BytesWriter, BytesReader, Externalizable, BlockchainIdentity { | |||
private Bytes address; | |||
private PubKey pubKey; | |||
private BlockchainIdentityData() { | |||
} | |||
public BlockchainIdentityData(PubKey pubKey) { | |||
this.pubKey = pubKey; | |||
this.address = AddressEncoding.generateAddress(pubKey); | |||
} | |||
public BlockchainIdentityData(CryptoAlgorithm algorithm, ByteArray pubKeyBytes) { | |||
this.pubKey = new PubKey(algorithm, pubKeyBytes.bytes()); | |||
this.address = AddressEncoding.generateAddress(pubKey); | |||
} | |||
public BlockchainIdentityData(Bytes address, PubKey pubKey) { | |||
if (!verifyAddress(address, pubKey)) { | |||
throw new IllegalArgumentException("Blockchain address is mismatch with the pub-key!"); | |||
} | |||
this.address = address; | |||
this.pubKey = pubKey; | |||
} | |||
public static boolean verifyAddress(Bytes address, PubKey pubKey) { | |||
Bytes addr = AddressEncoding.generateAddress(pubKey); | |||
return addr.equals(address); | |||
} | |||
@Override | |||
public void resolvFrom(InputStream in) throws IOException { | |||
Bytes addr = AddressEncoding.readAddress(in); | |||
byte[] value = BytesEncoding.readInShort(in); | |||
PubKey pk = new PubKey(value); | |||
this.address = addr; | |||
this.pubKey = pk; | |||
} | |||
@Override | |||
public void writeTo(OutputStream out) throws IOException { | |||
AddressEncoding.writeAddress(address, out); | |||
BytesEncoding.writeInShort(pubKey.toBytes(), out); | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.BlockchainIdentity#getAddress() | |||
*/ | |||
@Override | |||
public Bytes getAddress() { | |||
return address; | |||
} | |||
/* | |||
* (non-Javadoc) | |||
* | |||
* @see com.jd.blockchain.ledger.BlockchainIdentity#getPubKey() | |||
*/ | |||
@Override | |||
public PubKey getPubKey() { | |||
return pubKey; | |||
} | |||
public static BlockchainIdentity resolveFrom(ByteArray bytes) { | |||
try { | |||
BlockchainIdentityData id = new BlockchainIdentityData(); | |||
id.resolvFrom(bytes.asInputStream()); | |||
return id; | |||
} catch (IOException e) { | |||
throw new RuntimeIOException(e.getMessage(), e); | |||
} | |||
} | |||
public static ByteArray toBytes(List<BlockchainIdentityData> identities) { | |||
try { | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
BytesUtils.writeInt(identities.size(), out); | |||
for (BlockchainIdentityData identity : identities) { | |||
identity.writeTo(out); | |||
} | |||
return ByteArray.wrap(out.toByteArray()); | |||
} catch (IOException e) { | |||
throw new RuntimeIOException(e.getMessage(), e); | |||
} | |||
} | |||
public static List<BlockchainIdentityData> resolveIdentitiesFrom(ByteArray bytes) { | |||
try { | |||
InputStream in = bytes.asInputStream(); | |||
int identitiesLen = BytesUtils.readInt(in); | |||
List<BlockchainIdentityData> identities = new ArrayList<>(); | |||
for (int i = 0; i < identitiesLen; i++) { | |||
BlockchainIdentityData id = new BlockchainIdentityData(); | |||
id.resolvFrom(in); | |||
identities.add(id); | |||
} | |||
return identities; | |||
} catch (IOException e) { | |||
throw new RuntimeIOException(e.getMessage(), e); | |||
} | |||
} | |||
public ByteArray toBytes() { | |||
try { | |||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | |||
writeTo(out); | |||
return ByteArray.wrap(out.toByteArray()); | |||
} catch (IOException e) { | |||
throw new RuntimeIOException(e.getMessage(), e); | |||
} | |||
} | |||
@Override | |||
public int hashCode() { | |||
return address.hashCode(); | |||
} | |||
@Override | |||
public boolean equals(Object other) { | |||
if (this == other) { | |||
return true; | |||
} | |||
if (!(other instanceof BlockchainIdentityData)) { | |||
return false; | |||
} | |||
BlockchainIdentity identity = (BlockchainIdentity) other; | |||
if (!getAddress().equals(identity.getAddress())) { | |||
return false; | |||
} | |||
return pubKey.equals(identity.getPubKey()); | |||
} | |||
/** | |||
* The object implements the writeExternal method to save its contents by | |||
* calling the methods of DataOutput for its primitive values or calling the | |||
* writeObject method of ObjectOutput for objects, strings, and arrays. | |||
* | |||
* @param out | |||
* the stream to write the object to | |||
* @throws IOException | |||
* Includes any I/O exceptions that may occur | |||
* @serialData Overriding methods should use this tag to describe the data | |||
* layout of this Externalizable object. List the sequence of | |||
* element types and, if possible, relate the element to a | |||
* public/protected field and/or method of this Externalizable | |||
* class. | |||
*/ | |||
@Override | |||
public void writeExternal(ObjectOutput out) throws IOException { | |||
ByteArrayOutputStream os = new ByteArrayOutputStream(); | |||
writeTo(os); | |||
byte[] bts = os.toByteArray(); | |||
out.writeInt(bts.length); | |||
out.write(bts); | |||
} | |||
/** | |||
* The object implements the readExternal method to restore its contents by | |||
* calling the methods of DataInput for primitive types and readObject for | |||
* objects, strings and arrays. The readExternal method must read the values in | |||
* the same sequence and with the same types as were written by writeExternal. | |||
* | |||
* @param in | |||
* the stream to read data from in order to restore the object | |||
* @throws IOException | |||
* if I/O errors occur | |||
* @throws ClassNotFoundException | |||
* If the class for an object being restored cannot be found. | |||
*/ | |||
@Override | |||
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { | |||
int len = in.readInt(); | |||
byte[] bts = new byte[len]; | |||
in.readFully(bts); | |||
this.resolvFrom(new ByteArrayInputStream(bts)); | |||
} | |||
} |
@@ -1,40 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.crypto.CryptoAlgorithm; | |||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
import com.jd.blockchain.crypto.Crypto; | |||
import com.jd.blockchain.crypto.SignatureFunction; | |||
/** | |||
* 区块链密钥生成器; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class BlockchainKeyGenerator { | |||
public static final String DEFAULT_ALGORITHM = "ED25519"; | |||
private BlockchainKeyGenerator() { | |||
} | |||
public static BlockchainKeyGenerator getInstance() { | |||
return new BlockchainKeyGenerator(); | |||
} | |||
public BlockchainKeypair generate() { | |||
return generate(DEFAULT_ALGORITHM); | |||
} | |||
public BlockchainKeypair generate(String algorithmName) { | |||
CryptoAlgorithm algorithm = Crypto.getAlgorithm(algorithmName); | |||
return generate(algorithm); | |||
} | |||
public BlockchainKeypair generate(CryptoAlgorithm signatureAlgorithm) { | |||
SignatureFunction signFunc = Crypto.getSignatureFunction(signatureAlgorithm); | |||
AsymmetricKeypair cryptoKeyPair = signFunc.generateKeypair(); | |||
return new BlockchainKeypair(cryptoKeyPair.getPubKey(), cryptoKeyPair.getPrivKey()); | |||
} | |||
} |
@@ -1,46 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.crypto.AsymmetricKeypair; | |||
import com.jd.blockchain.crypto.PrivKey; | |||
import com.jd.blockchain.crypto.PubKey; | |||
import com.jd.blockchain.utils.Bytes; | |||
/** | |||
* 区块链密钥对; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
public class BlockchainKeypair extends AsymmetricKeypair { | |||
private BlockchainIdentity id; | |||
// public BlockchainKeyPair(CryptoAlgorithm algorithm, ByteArray pubKeyBytes, ByteArray privKeyBytes) { | |||
// this.id = new BlockchainIdentity(algorithm, pubKeyBytes); | |||
// privKey = new PrivKey(algorithm, privKeyBytes.bytes()); | |||
// } | |||
public BlockchainKeypair(String address, PubKey pubKey, PrivKey privKey) { | |||
super(pubKey, privKey); | |||
if (pubKey.getAlgorithm() != privKey.getAlgorithm()) { | |||
throw new IllegalArgumentException("The PublicKey's algorithm is different from the PrivateKey's!"); | |||
} | |||
this.id = new BlockchainIdentityData(Bytes.fromBase58(address), pubKey); | |||
} | |||
public BlockchainKeypair(PubKey pubKey, PrivKey privKey) { | |||
super(pubKey, privKey); | |||
if (pubKey.getAlgorithm() != privKey.getAlgorithm()) { | |||
throw new IllegalArgumentException("The PublicKey's algorithm is different from the PrivateKey's!"); | |||
} | |||
this.id = new BlockchainIdentityData(pubKey); | |||
} | |||
public Bytes getAddress() { | |||
return id.getAddress(); | |||
} | |||
public BlockchainIdentity getIdentity() { | |||
return id; | |||
} | |||
} |
@@ -1,23 +0,0 @@ | |||
//package com.jd.blockchain.ledger; | |||
// | |||
//import com.jd.blockchain.ledger.data.AccountUpdateOperationBuilder; | |||
// | |||
///** | |||
// * 合约代码部署操作; | |||
// * | |||
// * @author huanghaiquan | |||
// * | |||
// */ | |||
//public interface CodeDeployOperation extends AccountUpdateOperationBuilder { | |||
// | |||
// /** | |||
// * 修改脚本; | |||
// * | |||
// * @param code | |||
// * 合约代码; | |||
// * @param codeVersion | |||
// * 预期的当前的代码的版本;如果指定为 -1,则不进行版本检查; | |||
// */ | |||
// void set(BlockchainIdentity id, String code, long codeVersion); | |||
// | |||
//} |
@@ -1,31 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
@DataContract(code= DataCodes.TX_OP_CONTRACT_DEPLOY) | |||
public interface ContractCodeDeployOperation extends Operation { | |||
@DataField(order=2, refContract = true) | |||
BlockchainIdentity getContractID(); | |||
@DataField(order=3, primitiveType=PrimitiveType.BYTES) | |||
byte[] getChainCode(); | |||
/** | |||
* 地址签名; | |||
* | |||
* <br> | |||
* 这是合约账户身份 ({@link #getContractID()}) 使用对应的私钥对地址做出的签名; | |||
* <br> | |||
* 在注册时将校验此签名与账户地址、公钥是否相匹配,以此保证只有私钥的持有者才能注册相应的合约账户,确保合约账户的唯一性; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=4, refContract = true) | |||
DigitalSignature getAddressSignature(); | |||
} |
@@ -1,32 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
import com.jd.blockchain.utils.Bytes; | |||
@DataContract(code= DataCodes.TX_OP_DATA_ACC_SET) | |||
public interface DataAccountKVSetOperation extends Operation { | |||
@DataField(order=2, primitiveType=PrimitiveType.BYTES) | |||
Bytes getAccountAddress(); | |||
@DataField(order=3, list=true, refContract=true) | |||
KVWriteEntry[] getWriteSet(); | |||
@DataContract(code=DataCodes.TX_OP_DATA_ACC_SET_KV) | |||
public static interface KVWriteEntry{ | |||
@DataField(order=1, primitiveType=PrimitiveType.TEXT) | |||
String getKey(); | |||
@DataField(order=2, refContract = true) | |||
BytesValue getValue(); | |||
@DataField(order=3, primitiveType=PrimitiveType.INT64) | |||
long getExpectedVersion(); | |||
} | |||
} |
@@ -1,26 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.consts.DataCodes; | |||
@DataContract(code= DataCodes.TX_OP_DATA_ACC_REG) | |||
public interface DataAccountRegisterOperation extends Operation { | |||
@DataField(order=1, refContract = true) | |||
BlockchainIdentity getAccountID(); | |||
/** | |||
* 地址签名; | |||
* | |||
* <br> | |||
* 这是账户身份 ({@link #getAccountID()}) 使用对应的私钥对地址做出的签名; | |||
* <br> | |||
* 在注册时将校验此签名与账户地址、公钥是否相匹配,以此保证只有私钥的持有者才能注册数据账户,确保数据账户的唯一性; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=2, refContract = true) | |||
DigitalSignature getAddressSignature(); | |||
} |
@@ -1,185 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.BaseType; | |||
import com.jd.blockchain.binaryproto.EnumContract; | |||
import com.jd.blockchain.binaryproto.EnumField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
/** | |||
* 键值操作的数据类型; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@EnumContract(code = DataCodes.ENUM_TYPE_BYTES_VALUE_TYPE) | |||
public enum DataType { | |||
/** | |||
* 空; | |||
*/ | |||
NIL(PrimitiveType.NIL.CODE), | |||
/** | |||
* 布尔型; | |||
*/ | |||
BOOLEAN(PrimitiveType.BOOLEAN.CODE), | |||
/** | |||
* 数值型: | |||
*/ | |||
INT8(PrimitiveType.INT8.CODE), | |||
INT16(PrimitiveType.INT16.CODE), | |||
INT32(PrimitiveType.INT32.CODE), | |||
INT64(PrimitiveType.INT64.CODE), | |||
/** | |||
* 文本数据; | |||
*/ | |||
TEXT(PrimitiveType.TEXT.CODE), | |||
/** | |||
* 二进制数据; | |||
*/ | |||
BYTES(PrimitiveType.BYTES.CODE), | |||
/** | |||
* 时间戳; | |||
*/ | |||
TIMESTAMP((byte) (BaseType.INTEGER | 0x08)), | |||
/** | |||
* 文本数据; | |||
*/ | |||
JSON((byte) (BaseType.TEXT | 0x01)), | |||
/** | |||
* 文本数据; | |||
*/ | |||
XML((byte) (BaseType.TEXT | 0x02)), | |||
/** | |||
* 大整数; | |||
*/ | |||
BIG_INT((byte) (BaseType.BYTES | 0x01)), | |||
/** | |||
* 图片; | |||
*/ | |||
IMG((byte) (BaseType.BYTES | 0x02)), | |||
/** | |||
* 视频; | |||
*/ | |||
VIDEO((byte) (BaseType.BYTES | 0x03)), | |||
/** | |||
* 位置坐标; | |||
*/ | |||
LOCATION((byte) (BaseType.BYTES | 0x04)), | |||
/** | |||
* 公钥; | |||
*/ | |||
PUB_KEY((byte) (BaseType.BYTES | 0x05)), | |||
/** | |||
* 签名摘要; | |||
*/ | |||
SIGNATURE_DIGEST((byte) (BaseType.BYTES | 0x06)), | |||
/** | |||
* 哈希摘要; | |||
*/ | |||
HASH_DIGEST((byte) (BaseType.BYTES | 0x07)), | |||
/** | |||
* 加密数据; | |||
*/ | |||
ENCRYPTED_DATA((byte) (BaseType.BYTES | 0x08)), | |||
/** | |||
* DataContract 数据; | |||
*/ | |||
DATA_CONTRACT((byte) (BaseType.EXT | 0x01)); | |||
public static final boolean BOOLEAN_DEFAULT_VALUE = false; | |||
public static final byte INT8_DEFAULT_VALUE = 0; | |||
public static final short INT16_DEFAULT_VALUE = 0; | |||
public static final int INT32_DEFAULT_VALUE = 0; | |||
public static final long INT64_DEFAULT_VALUE = 0; | |||
@EnumField(type = PrimitiveType.INT8) | |||
public final byte CODE; | |||
private DataType(byte code) { | |||
this.CODE = code; | |||
} | |||
/** | |||
* 是否表示“文本类型”或“文本衍生类型”; | |||
* | |||
* @return | |||
*/ | |||
public boolean isText() { | |||
return BaseType.TEXT == (BaseType.TEXT & CODE); | |||
} | |||
/** | |||
* 是否表示“字节类型”或“字节衍生类型”; | |||
* | |||
* @return | |||
*/ | |||
public boolean isBytes() { | |||
return BaseType.BYTES == (BaseType.BYTES & CODE); | |||
} | |||
/** | |||
* 是否表示“整数类型”或“整数衍生类型”; | |||
* | |||
* @return | |||
*/ | |||
public boolean isInteger() { | |||
return BaseType.INTEGER == (BaseType.INTEGER & CODE); | |||
} | |||
/** | |||
* 是否表示“布尔类型”; | |||
* | |||
* @return | |||
*/ | |||
public boolean isBoolean() { | |||
return BaseType.BOOLEAN == (BaseType.BOOLEAN & CODE); | |||
} | |||
/** | |||
* 是否表示“扩展类型”; | |||
* | |||
* @return | |||
*/ | |||
public boolean isExt() { | |||
return BaseType.EXT == (BaseType.EXT & CODE); | |||
} | |||
public static DataType valueOf(byte code) { | |||
for (DataType dataType : DataType.values()) { | |||
if (dataType.CODE == code) { | |||
return dataType; | |||
} | |||
} | |||
throw new IllegalArgumentException("Code [" + code + "] not supported by BytesValueType enum!"); | |||
} | |||
} |
@@ -1,27 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
public class DataVersionConflictException extends LedgerException { | |||
private static final long serialVersionUID = 3583192000738807503L; | |||
private TransactionState state; | |||
public DataVersionConflictException() { | |||
this(TransactionState.DATA_VERSION_CONFLICT, null); | |||
} | |||
public DataVersionConflictException(String message) { | |||
this(TransactionState.DATA_VERSION_CONFLICT, message); | |||
} | |||
private DataVersionConflictException(TransactionState state, String message) { | |||
super(message); | |||
assert TransactionState.SUCCESS != state; | |||
this.state = state; | |||
} | |||
public TransactionState getState() { | |||
return state; | |||
} | |||
} |
@@ -1,15 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.consts.DataCodes; | |||
/** | |||
* 数字签名; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@DataContract(code= DataCodes.DIGITALSIGNATURE) | |||
public interface DigitalSignature extends DigitalSignatureBody { | |||
} |
@@ -1,30 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.binaryproto.PrimitiveType; | |||
import com.jd.blockchain.consts.DataCodes; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
@DataContract(code= DataCodes.REQUEST_ENDPOINT) | |||
public interface EndpointRequest { | |||
@DataField(order=1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getHash(); | |||
/** | |||
* 交易内容; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=2, refContract=true) | |||
TransactionContent getTransactionContent(); | |||
/** | |||
* 终端用户的签名列表; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=3, list=true, refContract=true) | |||
DigitalSignature[] getEndpointSignatures(); | |||
} |
@@ -1,46 +0,0 @@ | |||
//package com.jd.blockchain.ledger; | |||
// | |||
///** | |||
// * Hash 算法的代码常量; | |||
// * | |||
// * @author zhaoming9 | |||
// * | |||
// */ | |||
//public enum HashAlgorithm { | |||
// | |||
// RIPE160((byte) 1), | |||
// | |||
// SHA256((byte) 2), | |||
// | |||
// SM3((byte) 4); | |||
// | |||
// public final byte CODE; | |||
// | |||
// private HashAlgorithm(byte algorithm) { | |||
// CODE = algorithm; | |||
// } | |||
// | |||
// public byte getAlgorithm() { | |||
// return CODE; | |||
// } | |||
// | |||
// public static HashAlgorithm valueOf(byte algorithm) { | |||
// for (HashAlgorithm hashAlgorithm : HashAlgorithm.values()) { | |||
// if (hashAlgorithm.CODE == algorithm) { | |||
// return hashAlgorithm; | |||
// } | |||
// } | |||
// throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); | |||
// } | |||
// | |||
// public static void checkHashAlgorithm(HashAlgorithm algorithm) { | |||
// switch (algorithm) { | |||
// case RIPE160: | |||
// break; | |||
// case SHA256: | |||
// break; | |||
// default: | |||
// throw new IllegalArgumentException("Unsupported hash algorithm [" + algorithm + "]!"); | |||
// } | |||
// } | |||
//} |
@@ -1,37 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.consts.DataCodes; | |||
import com.jd.blockchain.crypto.HashDigest; | |||
/** | |||
* HashObject 表示以“哈希值”作为唯一标识的对象; | |||
* | |||
* @author huanghaiquan | |||
* | |||
*/ | |||
@DataContract(code= DataCodes.HASH_OBJECT) | |||
public interface HashObject { | |||
/** | |||
* 哈希值; | |||
* | |||
* @return | |||
*/ | |||
//no need annotation | |||
HashDigest getHash(); | |||
// /** | |||
// * 哈希算法; | |||
// * | |||
// * @return | |||
// */ | |||
// HashAlgorithm getHashAlgorithm(); | |||
// /** | |||
// * 进行哈希运算的数据; | |||
// * @return | |||
// */ | |||
// ByteArray getHashData(); | |||
} |
@@ -1,59 +0,0 @@ | |||
//package com.jd.blockchain.ledger; | |||
// | |||
//import my.utils.io.ByteArray; | |||
// | |||
//import java.io.Serializable; | |||
// | |||
///** | |||
// * Ledger 账本;<br> | |||
// * | |||
// * 账本只是一个逻辑上的对象,它是对一条区块的hash链的归纳抽象,而在存储上并没有具体的存在形式,而是具体化为由一个特定的创世区块作为开端的区块 hash | |||
// * 链;<br> | |||
// * | |||
// * 账本的唯一标识也是其创世区块(GenisisBlock)的 hash;<br> | |||
// * | |||
// * @author huanghaiquan | |||
// * | |||
// */ | |||
//public interface Ledger extends Serializable { | |||
// | |||
// /** | |||
// * 账本的 hash; <br> | |||
// * | |||
// * 同时也是账本的唯一,等同于其创世区块(GenisisBlock)的 hash {@link GenesisBlock#getBlockHash()}; | |||
// * | |||
// * @return | |||
// */ | |||
// ByteArray getLedgerHash(); | |||
// | |||
// /** | |||
// * 账本结构版本;<br> | |||
// * | |||
// * 等同于 {@link Block#getLedgerVersion()}; | |||
// * | |||
// * @return | |||
// */ | |||
// long getLedgerVersion(); | |||
// | |||
// /** | |||
// * 由随机数构成的该账本的创世序列; | |||
// * | |||
// * @return | |||
// */ | |||
// ByteArray getGenesisKey(); | |||
// | |||
// /** | |||
// * 当前最新区块的 hash; | |||
// * | |||
// * @return | |||
// */ | |||
// ByteArray getBlockHash(); | |||
// | |||
// /** | |||
// * 账本的区块高度; | |||
// * | |||
// * @return | |||
// */ | |||
// long getBlockHeight(); | |||
// | |||
//} |
@@ -1,323 +0,0 @@ | |||
//package com.jd.blockchain.ledger; | |||
// | |||
//import com.jd.blockchain.ledger.data.HashEncoding; | |||
// | |||
//import my.utils.io.ByteArray; | |||
//import my.utils.io.BytesEncoding; | |||
//import my.utils.io.BytesReader; | |||
//import my.utils.io.BytesUtils; | |||
//import my.utils.io.BytesWriter; | |||
//import my.utils.io.NumberMask; | |||
// | |||
//import java.io.IOException; | |||
//import java.io.InputStream; | |||
//import java.io.OutputStream; | |||
//import java.util.Objects; | |||
// | |||
///** | |||
// * Ledger 实现 | |||
// * | |||
// * @author zhaoming9 | |||
// */ | |||
//public class LedgerImpl implements Ledger, BytesWriter, BytesReader { | |||
// | |||
// private HashAlgorithm ledgerHashAlgorithm = HashAlgorithm.SHA256; // 账本hash算法 | |||
// private ByteArray ledgerHash = ByteArray.EMPTY; // 账本hash | |||
// | |||
// private long blockHeight = 0; // 账本当前高度 | |||
// private long blockVersion = 0; // 账本当前版本 | |||
// | |||
// private HashAlgorithm currentBlockHashAlgorithm = HashAlgorithm.SHA256; // 账本当前区块hash算法 | |||
// private ByteArray currentBlockHash = ByteArray.EMPTY; // 账本当前区块hash | |||
// | |||
// private HashAlgorithm previousBlockHashAlgorithm = HashAlgorithm.SHA256; // 账本前一区块hash算法 | |||
// private ByteArray previousBlockHash = ByteArray.EMPTY; // 账本前一区块hash | |||
// | |||
// private ByteArray accountRoot = ByteArray.EMPTY; // account mpt root hash | |||
// private long accountCount; // 账户数量 | |||
// private long txTotalCount; // 交易数量 | |||
// | |||
// private ByteArray genesisKey=ByteArray.EMPTY; // 创世块随机序列 | |||
// | |||
// public LedgerImpl() { | |||
// } | |||
// | |||
// /** | |||
// * 初始化一个新的账本; | |||
// * @param genesisKey | |||
// */ | |||
// public LedgerImpl(ByteArray genesisKey) { | |||
// this.genesisKey = genesisKey; | |||
// } | |||
// | |||
// /** | |||
// * @param ledgerHashAlgorithm | |||
// * @param ledgerHash | |||
// * @param height | |||
// * @param version | |||
// * @param currentBlockHashAlgorithm | |||
// * @param currentBlockHash | |||
// * @param previousBlockHashAlgorithm | |||
// * @param previousBlockHash | |||
// * @param accountRoot | |||
// * @param accountCount | |||
// * @param txTotalCount | |||
// * @param genesisKey | |||
// */ | |||
// private LedgerImpl(HashAlgorithm ledgerHashAlgorithm, ByteArray ledgerHash, long height, long version, | |||
// HashAlgorithm currentBlockHashAlgorithm, ByteArray currentBlockHash, | |||
// HashAlgorithm previousBlockHashAlgorithm, ByteArray previousBlockHash, | |||
// ByteArray accountRoot, long accountCount, long txTotalCount, ByteArray genesisKey) { | |||
// this.ledgerHashAlgorithm = ledgerHashAlgorithm; | |||
// this.ledgerHash = ledgerHash; | |||
// this.blockHeight = height; | |||
// this.blockVersion = version; | |||
// this.currentBlockHashAlgorithm = currentBlockHashAlgorithm; | |||
// this.currentBlockHash = currentBlockHash; | |||
// this.previousBlockHashAlgorithm = previousBlockHashAlgorithm; | |||
// this.previousBlockHash = previousBlockHash; | |||
// this.accountRoot = accountRoot; | |||
// this.accountCount = accountCount; | |||
// this.txTotalCount = txTotalCount; | |||
// this.genesisKey = genesisKey; | |||
// } | |||
// | |||
// public LedgerImpl(ByteArray ledgerHash, long blockHeight, long blockVersion, ByteArray currentBlockHash, | |||
// ByteArray previousBlockHash, ByteArray accountRoot, long accountCount, long txTotalCount, ByteArray genesisKey) { | |||
// this(HashAlgorithm.SHA256, ledgerHash, blockHeight, blockVersion, HashAlgorithm.SHA256, currentBlockHash, | |||
// HashAlgorithm.SHA256, previousBlockHash, accountRoot, accountCount, txTotalCount, genesisKey); | |||
// } | |||
// | |||
// public LedgerImpl(LedgerImpl ledger) { | |||
// this(ledger.getLedgerHashAlgorithm(), ledger.getLedgerHash(), ledger.getBlockHeight(), ledger.getBlockVersion(), | |||
// ledger.getCurrentBlockHashAlgorithm(), ledger.getCurrentBlockHash(), | |||
// ledger.getPreviousBlockHashAlgorithm(), ledger.getPreviousBlockHash(), | |||
// ledger.getAccountRoot(), ledger.getAccountCount(), ledger.getTxTotalCount(),ledger.getGenesisKey()); | |||
// } | |||
// | |||
// public LedgerImpl nextLedger(ByteArray nextBlockHash, ByteArray accountRoot, long newAccountCnt, long newTxCnt) { | |||
// LedgerImpl nextLedger = new LedgerImpl(this); | |||
// nextLedger.blockHeight+=1; | |||
// nextLedger.previousBlockHash = nextLedger.currentBlockHash; | |||
// nextLedger.currentBlockHash = nextBlockHash; | |||
// nextLedger.accountRoot = accountRoot; | |||
// nextLedger.accountCount += newAccountCnt; | |||
// nextLedger.txTotalCount += newTxCnt; | |||
// | |||
// return nextLedger; | |||
// } | |||
// | |||
// /** | |||
// * 账本的 hash; <br> | |||
// * <p> | |||
// * 同时也是账本的唯一,等同于其创世区块(GenisisBlock)的 hash | |||
// * | |||
// * @return | |||
// */ | |||
// @Override | |||
// public ByteArray getLedgerHash() { | |||
// return ledgerHash; | |||
// } | |||
// | |||
// /** | |||
// * 由随机数构成的该账本的创世序列; | |||
// * | |||
// * @return | |||
// */ | |||
// @Override | |||
// public ByteArray getGenesisKey() { | |||
// return genesisKey; | |||
// } | |||
// | |||
// /** | |||
// * 当前最新区块的 hash; | |||
// * | |||
// * @return | |||
// */ | |||
// @Override | |||
// public ByteArray getBlockHash() { | |||
// return currentBlockHash; | |||
// } | |||
// | |||
// public HashAlgorithm getBlockHashAlgorithm() { | |||
// return currentBlockHashAlgorithm; | |||
// } | |||
// | |||
// /** | |||
// * 账本的区块高度; | |||
// * | |||
// * @return | |||
// */ | |||
// @Override | |||
// public long getBlockHeight() { | |||
// return blockHeight; | |||
// } | |||
// | |||
// @Override | |||
// public void resolvFrom(InputStream in) throws IOException { | |||
// HashAlgorithm ledgerHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); | |||
// HashAlgorithm.checkHashAlgorithm(ledgerHashAlgorithm); | |||
// ByteArray ledgerHash = HashEncoding.read(in); | |||
// | |||
// long height = BytesUtils.readLong(in); | |||
// long version = BytesUtils.readLong(in); | |||
// | |||
// HashAlgorithm currentBlockHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); | |||
// HashAlgorithm.checkHashAlgorithm(currentBlockHashAlgorithm); | |||
// ByteArray currentBlockHash = HashEncoding.read(in); | |||
// | |||
// HashAlgorithm previousBlockHashAlgorithm = HashAlgorithm.valueOf(BytesUtils.readByte(in)); | |||
// HashAlgorithm.checkHashAlgorithm(previousBlockHashAlgorithm); | |||
// ByteArray previousBlockHash = HashEncoding.read(in); | |||
// | |||
// ByteArray accountHash = HashEncoding.read(in); | |||
// long accountCount = BytesUtils.readLong(in); | |||
// long txTotalCount = BytesUtils.readLong(in); | |||
// ByteArray key = BytesEncoding.readAsByteArray(NumberMask.SHORT, in); | |||
// | |||
// this.ledgerHashAlgorithm = ledgerHashAlgorithm; | |||
// this.ledgerHash = ledgerHash; | |||
// this.blockHeight = height; | |||
// this.blockVersion = version; | |||
// this.currentBlockHashAlgorithm = currentBlockHashAlgorithm; | |||
// this.currentBlockHash = currentBlockHash; | |||
// this.previousBlockHashAlgorithm = previousBlockHashAlgorithm; | |||
// this.previousBlockHash = previousBlockHash; | |||
// this.accountRoot = accountHash; | |||
// this.accountCount = accountCount; | |||
// this.txTotalCount = txTotalCount; | |||
// this.genesisKey = key; | |||
// } | |||
// | |||
// @Override | |||
// public void writeTo(OutputStream out) throws IOException { | |||
// BytesUtils.writeByte(ledgerHashAlgorithm.getAlgorithm(), out); | |||
// HashEncoding.write(ledgerHash, out); | |||
// | |||
// BytesUtils.writeLong(blockHeight, out); | |||
// BytesUtils.writeLong(blockVersion, out); | |||
// | |||
// BytesUtils.writeByte(currentBlockHashAlgorithm.getAlgorithm(), out); | |||
// HashEncoding.write(currentBlockHash, out); | |||
// | |||
// BytesUtils.writeByte(previousBlockHashAlgorithm.getAlgorithm(), out); | |||
// HashEncoding.write(previousBlockHash, out); | |||
// | |||
// HashEncoding.write(accountRoot, out); | |||
// BytesUtils.writeLong(accountCount, out); | |||
// BytesUtils.writeLong(txTotalCount, out); | |||
// BytesEncoding.write(genesisKey, NumberMask.SHORT, out); | |||
// } | |||
// | |||
// public HashAlgorithm getLedgerHashAlgorithm() { | |||
// return ledgerHashAlgorithm; | |||
// } | |||
// | |||
// public void setLedgerHashAlgorithm(HashAlgorithm ledgerHashAlgorithm) { | |||
// this.ledgerHashAlgorithm = ledgerHashAlgorithm; | |||
// } | |||
// | |||
// public void setLedgerHash(ByteArray ledgerHash) { | |||
// this.ledgerHash = ledgerHash; | |||
// } | |||
// | |||
// public void setBlockHeight(long blockHeight) { | |||
// this.blockHeight = blockHeight; | |||
// } | |||
// | |||
// public HashAlgorithm getCurrentBlockHashAlgorithm() { | |||
// return currentBlockHashAlgorithm; | |||
// } | |||
// | |||
// public void setCurrentBlockHashAlgorithm(HashAlgorithm currentBlockHashAlgorithm) { | |||
// this.currentBlockHashAlgorithm = currentBlockHashAlgorithm; | |||
// } | |||
// | |||
// public long getBlockVersion() { | |||
// return blockVersion; | |||
// } | |||
// | |||
// public void setBlockVersion(long blockVersion) { | |||
// this.blockVersion = blockVersion; | |||
// } | |||
// | |||
// public void setGenesisKey(ByteArray genesisKey) { | |||
// this.genesisKey = genesisKey; | |||
// } | |||
// | |||
// public ByteArray getCurrentBlockHash() { | |||
// return currentBlockHash; | |||
// } | |||
// | |||
// public void setCurrentBlockHash(ByteArray currentBlockHash) { | |||
// this.currentBlockHash = currentBlockHash; | |||
// } | |||
// | |||
// public HashAlgorithm getPreviousBlockHashAlgorithm() { | |||
// return previousBlockHashAlgorithm; | |||
// } | |||
// | |||
// public void setPreviousBlockHashAlgorithm(HashAlgorithm previousBlockHashAlgorithm) { | |||
// this.previousBlockHashAlgorithm = previousBlockHashAlgorithm; | |||
// } | |||
// | |||
// public ByteArray getAccountRoot() { | |||
// return accountRoot; | |||
// } | |||
// | |||
// public void setAccountRoot(ByteArray accountRoot) { | |||
// this.accountRoot = accountRoot; | |||
// } | |||
// | |||
// public long getAccountCount() { | |||
// return accountCount; | |||
// } | |||
// | |||
// public void setAccountCount(long accountCount) { | |||
// this.accountCount = accountCount; | |||
// } | |||
// | |||
// public long getTxTotalCount() { | |||
// return txTotalCount; | |||
// } | |||
// | |||
// public void setTxTotalCount(long txTotalCount) { | |||
// this.txTotalCount = txTotalCount; | |||
// } | |||
// | |||
// public ByteArray getPreviousBlockHash() { | |||
// return previousBlockHash; | |||
// } | |||
// | |||
// public void setPreviousBlockHash(ByteArray previousBlockHash) { | |||
// this.previousBlockHash = previousBlockHash; | |||
// } | |||
// | |||
// @Override | |||
// public boolean equals(Object o) { | |||
// if (this == o) return true; | |||
// if (!(o instanceof LedgerImpl)) return false; | |||
// LedgerImpl ledger = (LedgerImpl) o; | |||
// return getBlockHeight() == ledger.getBlockHeight() && | |||
// getBlockVersion() == ledger.getBlockVersion() && | |||
// getLedgerHashAlgorithm() == ledger.getLedgerHashAlgorithm() && | |||
// Objects.equals(getLedgerHash(), ledger.getLedgerHash()) && | |||
// getCurrentBlockHashAlgorithm() == ledger.getCurrentBlockHashAlgorithm() && | |||
// Objects.equals(getCurrentBlockHash(), ledger.getCurrentBlockHash()) && | |||
// getPreviousBlockHashAlgorithm() == ledger.getPreviousBlockHashAlgorithm() && | |||
// Objects.equals(getPreviousBlockHash(), ledger.getPreviousBlockHash()) && | |||
// Objects.equals(getGenesisKey(), ledger.getGenesisKey()); | |||
// } | |||
// | |||
// @Override | |||
// public int hashCode() { | |||
// | |||
// return Objects.hash(getLedgerHashAlgorithm(), getLedgerHash(), getBlockHeight(), getBlockVersion(), getCurrentBlockHashAlgorithm(), getCurrentBlockHash(), getPreviousBlockHashAlgorithm(), getPreviousBlockHash(), getGenesisKey()); | |||
// } | |||
// | |||
// @Override | |||
// public long getLedgerVersion() { | |||
// // TODO Auto-generated method stub | |||
// return 0; | |||
// } | |||
//} |
@@ -1,13 +0,0 @@ | |||
package com.jd.blockchain.ledger; | |||
import com.jd.blockchain.binaryproto.DataContract; | |||
import com.jd.blockchain.binaryproto.DataField; | |||
import com.jd.blockchain.consts.DataCodes; | |||
@DataContract(code= DataCodes.TX_OP_LEDGER_INIT) | |||
public interface LedgerInitOperation extends Operation{ | |||
@DataField(order=1, refContract=true) | |||
LedgerInitSetting getInitSetting(); | |||
} |