Browse Source

Merge branch 'release/1.3.0'

* release/1.3.0: (65 commits)
  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
  udpate;
  jira 433 , consensus settings update op data contract unregisted
  ...

# Conflicts:
#	deploy/deploy-gateway/pom.xml
#	deploy/deploy-kvdb/pom.xml
#	deploy/deploy-peer/pom.xml
#	deploy/pom.xml
#	libs/bft-smart
#	samples/pom.xml
tags/1.3.0^0
huanghaiquan 4 years ago
parent
commit
6dcec58070
23 changed files with 1767 additions and 221 deletions
  1. +1
    -1
      core
  2. +4
    -4
      deploy/deploy-gateway/pom.xml
  3. +16
    -8
      deploy/deploy-gateway/src/main/resources/config/gateway.conf
  4. +485
    -0
      deploy/deploy-gateway/src/main/resources/docs/api_doc_cn_1.4.MD
  5. +361
    -0
      deploy/deploy-gateway/src/main/resources/docs/code_example.MD
  6. +436
    -0
      deploy/deploy-gateway/src/main/resources/docs/design_contract-dev-manual.md
  7. +8
    -8
      deploy/deploy-kvdb/pom.xml
  8. +9
    -9
      deploy/deploy-peer/pom.xml
  9. +4
    -4
      deploy/deploy-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java
  10. +0
    -29
      deploy/deploy-peer/src/main/resources/config/init/ledger.init
  11. +133
    -22
      deploy/deploy-peer/src/main/resources/docs/安装部署.MD
  12. +4
    -3
      deploy/pom.xml
  13. +1
    -1
      explorer
  14. +1
    -1
      framework
  15. +1
    -1
      libs/bft-smart
  16. +2
    -2
      samples/pom.xml
  17. +69
    -0
      samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_ActiveParticipant.java
  18. +85
    -0
      samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegistParticipant.java
  19. +96
    -0
      samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Event_Demo.java
  20. +47
    -0
      samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_RegistParticipant_Demo.java
  21. +3
    -14
      samples/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Participant_Regist_Test_.java
  22. +0
    -113
      samples/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Participant_State_Update_Test_.java
  23. +1
    -1
      test

+ 1
- 1
core

@@ -1 +1 @@
Subproject commit 116d8708ec43c9bc6684f8d87d217dd979a8efff
Subproject commit ec842b88f4ea19ffa8f9a00f31317e5e4dc5218b

+ 4
- 4
deploy/deploy-gateway/pom.xml View File

@@ -5,10 +5,10 @@
<parent>
<groupId>com.jd.blockchain</groupId>
<artifactId>deploy-root</artifactId>
<version>1.2.1.RELEASE</version>
<version>1.3.0.RELEASE</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>

+ 16
- 8
deploy/deploy-gateway/src/main/resources/config/gateway.conf View File

@@ -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=

+ 485
- 0
deploy/deploy-gateway/src/main/resources/docs/api_doc_cn_1.4.MD View File

@@ -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|账本权限集|

+ 361
- 0
deploy/deploy-gateway/src/main/resources/docs/code_example.MD View File

@@ -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();
```

+ 436
- 0
deploy/deploy-gateway/src/main/resources/docs/design_contract-dev-manual.md View File

@@ -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>
```

+ 8
- 8
deploy/deploy-kvdb/pom.xml View File

@@ -5,7 +5,7 @@
<parent>
<artifactId>deploy-root</artifactId>
<groupId>com.jd.blockchain</groupId>
<version>1.2.1.RELEASE</version>
<version>1.3.0.RELEASE</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@@ -17,37 +17,37 @@
<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>kvdb-protocol</artifactId>
<version>${project.version}</version>
<version>${kvdb.version}</version>
</dependency>

<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>kvdb-engine</artifactId>
<version>${project.version}</version>
<version>${kvdb.version}</version>
</dependency>

<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>kvdb-server</artifactId>
<version>${project.version}</version>
<version>${kvdb.version}</version>
</dependency>

<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>kvdb-cli</artifactId>
<version>${project.version}</version>
<version>${kvdb.version}</version>
</dependency>

<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>kvdb-benchmark</artifactId>
<version>${project.version}</version>
<version>${kvdb.version}</version>
</dependency>

<dependency>
<groupId>com.jd.blockchain</groupId>
<artifactId>kvdb-client</artifactId>
<version>${project.version}</version>
<version>${kvdb.version}</version>
</dependency>
</dependencies>

@@ -96,4 +96,4 @@
</plugin>
</plugins>
</build>
</project>
</project>

+ 9
- 9
deploy/deploy-peer/pom.xml View File

@@ -5,7 +5,7 @@
<parent>
<groupId>com.jd.blockchain</groupId>
<artifactId>deploy-root</artifactId>
<version>1.2.1.RELEASE</version>
<version>1.3.0.RELEASE</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>

+ 4
- 4
deploy/deploy-peer/src/main/java/com/jd/blockchain/boot/peer/PeerBooter.java View File

@@ -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
*
*/


+ 0
- 29
deploy/deploy-peer/src/main/resources/config/init/ledger.init View File

@@ -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个参与方的账本初始服务的端口;


+ 133
- 22
deploy/deploy-peer/src/main/resources/docs/安装部署.MD View File

@@ -1,6 +1,6 @@
# JDChain安装部署指南

本部署指南基于JDChain1.0.0.RELEASE版本来构建。
本部署指南基于JDChain1.3.0.RELEASE版本来构建。
## 1. 部署环境
### 1.1 系统部署结构
![部署结构](imgs/structure.png)
@@ -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节点进行通信。


+ 4
- 3
deploy/pom.xml View File

@@ -9,11 +9,12 @@
<relativePath>../project/parent</relativePath>
</parent>
<artifactId>deploy-root</artifactId>
<version>1.2.1.RELEASE</version>
<version>1.3.0.RELEASE</version>
<packaging>pom</packaging>

<properties>
<core.version>1.2.1.RELEASE</core.version>
<core.version>1.3.0.RELEASE</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
explorer

@@ -1 +1 @@
Subproject commit 95c6dd67e11745ce010ff3555e0a724a6ff86e5a
Subproject commit 7baa88521acd1043fa03408a693c0d0a2546ad93

+ 1
- 1
framework

@@ -1 +1 @@
Subproject commit 8a15a21672afe69b8570875a6670e4fb2cbf6bdc
Subproject commit b5e7aec41425d85f6374c8bf2a604c69b6adad83

+ 1
- 1
libs/bft-smart

@@ -1 +1 @@
Subproject commit 8e6dd2fe6fe3c94e7c6534263f0cc622c4132fd8
Subproject commit 26482cf43365583e5d303544ddf05d44a2ad4a41

+ 2
- 2
samples/pom.xml View File

@@ -11,11 +11,11 @@

<groupId>com.jd.blockchain</groupId>
<artifactId>jdchain-samples</artifactId>
<version>1.2.1.RELEASE</version>
<version>1.2.0.RELEASE</version>
<packaging>pom</packaging>

<properties>
<framework.version>1.2.1.RELEASE</framework.version>
<framework.version>1.3.0.RELEASE</framework.version>
</properties>

<modules>


+ 69
- 0
samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_ActiveParticipant.java View File

@@ -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!");
}
}
}

+ 85
- 0
samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDKDemo_RegistParticipant.java View File

@@ -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();
}


}

+ 96
- 0
samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_Event_Demo.java View File

@@ -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();
}

}

+ 47
- 0
samples/sdk-samples/src/main/java/com/jd/blockchain/sdk/samples/SDK_RegistParticipant_Demo.java View File

@@ -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());

}
}

+ 3
- 14
samples/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Participant_Regist_Test_.java View File

@@ -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();


+ 0
- 113
samples/sdk-samples/src/test/java/test/com/jd/blockchain/sdk/test/SDK_GateWay_Participant_State_Update_Test_.java View File

@@ -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
- 1
test

@@ -1 +1 @@
Subproject commit 16bbed2578c7862f3992f0cd8f08c6886880c9e7
Subproject commit f5057486324d0daf2f8ef9f223a04537d03edede

Loading…
Cancel
Save