@@ -1,80 +0,0 @@ | |||
## 区块 | |||
采用`BFT-SMaRt`共识协议,即时出块,单个区块交易数限制默认为`2000`(`bftsmart.config`中参数`system.totalordermulticast.maxbatchsize`) | |||
### 结构 | |||
- `LedgerBlock`: | |||
```java | |||
@DataContract(code = DataCodes.BLOCK) | |||
public interface LedgerBlock extends BlockBody { | |||
/** | |||
* 区块哈希; | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getHash(); | |||
} | |||
``` | |||
- `BlockBody`: | |||
```java | |||
@DataContract(code= DataCodes.BLOCK_BODY) | |||
public interface BlockBody extends LedgerDataSnapshot{ | |||
// 上一个区块哈希 | |||
@DataField(order=2, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getPreviousHash(); | |||
// 账本哈希 | |||
@DataField(order=3, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getLedgerHash(); | |||
// 区块高度 | |||
@DataField(order=4, primitiveType= PrimitiveType.INT64) | |||
long getHeight(); | |||
// 交易数据集哈希 | |||
@DataField(order=5, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getTransactionSetHash(); | |||
// 区块时间戳,毫秒 | |||
@DataField(order=6, primitiveType = PrimitiveType.INT64) | |||
long getTimestamp(); | |||
} | |||
``` | |||
- `LedgerDataSnapshot`: | |||
```java | |||
@DataContract(code=DataCodes.DATA_SNAPSHOT) | |||
public interface LedgerDataSnapshot { | |||
// 管理数据集哈希 | |||
@DataField(order=1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getAdminAccountHash(); | |||
// 用户集哈希 | |||
@DataField(order=2, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getUserAccountSetHash(); | |||
// 数据账户集哈希 | |||
@DataField(order=3, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getDataAccountSetHash(); | |||
// 合约集哈希 | |||
@DataField(order=4, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getContractAccountSetHash(); | |||
// 系统事件集哈希 | |||
@DataField(order=5, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getSystemEventSetHash(); | |||
// 用户事件集哈希 | |||
@DataField(order=6, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getUserEventSetHash(); | |||
} | |||
``` |
@@ -1,150 +0,0 @@ | |||
## 证书 | |||
`JD Chain`身份认证支持两种模式:`KEYPAIR`(默认)/`CA`,即公私钥对和证书。 | |||
证书模式采用`X.509`标准的数字证书作为用户标识,证书字段中,附加组织和角色等信息。 | |||
`JD Chain`使用`jdchain-cli`/`openssl`生成的自签名证书,也支持使用`CFCA`等国家认可的第三方`CA`颁发的外部证书。 | |||
`JD Chain` `CA`支持`RSA 2048`/`ECDSA P-256`/`SM2 SM3WithSM2`/`ED25519`四种签名算法。 | |||
### 类别 | |||
`JD Chain`证书体系分`ROOT`,`CA`,`PEER`,`GW`,`USER`几个类别。 | |||
使用证书`Subject`中`OU`字段区分。 | |||
#### ROOT | |||
根证书,可用于签发证书及账本初始化时作为账本证书。 | |||
#### CA | |||
中间证书,可用于签发证书及账本初始化时作为账本证书。 | |||
#### PEER | |||
共识节点证书,注册参与方时需要提供`PEER`类型证书。 | |||
#### GW | |||
网关证书,网关所配置公私钥对应账户信息在链上必须存储有`GW`类型的证书。 | |||
#### USER | |||
普通用户证书 | |||
### 实现 | |||
`JD Chain`证书使用链上存储方式。主要存储于两个地方: | |||
- 元数据区,存储账本初始化时配置的根证书列表 | |||
- 用户账户头部,存储用户注册时提供的证书 | |||
> 根证书支持列表,即支持多个参与机构使用不同的证书链,且根证书可使用`ROOT`证书,也可使用`CA`(中间)证书,但节点/网关/用户证书必须由配置在`JD Chain`根证书列表中的证书直接签出。 | |||
#### 账本初始化 | |||
*`ledger.init`* | |||
较`KEYPAIR`模式有如下修改: | |||
1. `identity-mode` | |||
```properties | |||
identity-mode=CA | |||
``` | |||
`identity-mode`身份认证模式,`KEYPAIR`(默认)/`CA` | |||
2. `root-ca-path` | |||
```properties | |||
root-ca-path=/**/ledger1.crt,/**/ledger2.crt | |||
``` | |||
`root-ca-path`根证书列表,使用`ROOT`或者`CA`类型证书,多个根证书使用半角逗号分割。初始化完成后,证书信息会上链存储,通过[2.7 获取账本初始化配置信息](api.md#27-获取账本初始化配置信息)可查。 | |||
3. `cons_parti.*.ca-path` | |||
**CA 模式参与方需要增加配置网关信息,网关节点IP和端口不需要填写** | |||
节点公钥配置改为证书地址: | |||
```properties | |||
// KEYPAIR | |||
// cons_parti.0.pubkey-path= | |||
// cons_parti.0.pubkey= | |||
// CA | |||
cons_parti.0.ca-path=/**/peer0.crt | |||
``` | |||
*`local.conf`* | |||
较`KEYPAIR`模式有如下修改: | |||
```properties | |||
#当前参与方的公钥,用于非证书模式 | |||
# local.parti.pubkey= | |||
#当前参与方的证书信息,用于证书模式 | |||
local.parti.ca-path= | |||
#当前参与方的私钥文件,PEM格式,用于证书模式 | |||
local.parti.privkey-path= | |||
``` | |||
#### 节点运行 | |||
节点启动和运行时会校验证书类型,时间有效性以及是否由某个根证书签出等,一旦校验失败会阻止网关接入,不再对外服务。 | |||
#### 网关接入 | |||
*gateway.conf* | |||
```properties | |||
#默认公钥的内容(Base58编码数据),非CA模式下必填; | |||
keys.default.pubkey= | |||
#默认网关证书路径(X509,PEM),CA模式下必填; | |||
keys.default.ca-path=/home/imuge/jd/nodes/peer0/config/keys/gw1.crt | |||
#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey-path=/home/imuge/jd/nodes/peer0/config/keys/gw1.key | |||
#默认私钥的内容(加密的Base58编码数据);在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey= | |||
``` | |||
网关接入网络需要配置`GW`类型证书及对应的私钥信息,证书类型必须是`GW`。 | |||
网关接入时会做如下认证: | |||
- 证书类型包含`GW` | |||
- 根证书列表存在类型正确且有效证书 | |||
- 网关证书由根证书列表中某个证书签出(此证书类型正确且有效) | |||
#### 交易认证 | |||
交易时使用证书持有者私钥签名,交易内容不包含签名用户证书信息。 | |||
交易执行前会校验所有账本根证书,签名终端用户和节点用户的证书类型及有效性。 | |||
> 请务必在证书到期前更新证书有效期 | |||
#### 证书更新 | |||
1. 根证书 | |||
`SDK`方式: | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
txTemp.metaInfo().ca(X509Utils.resolveCertificate("*.crt")); | |||
``` | |||
命令行方式:[更新账本根证书](cli/tx.md#更新账本根证书) | |||
2. 节点/网关/普通用户证书 | |||
> 在`JD Chain`中,共识节点,网关配置的接入账户和普通用户本质都是用户账户类型,它们对应的证书管理方式一致。 | |||
`SDK`方式: | |||
```java | |||
txTemp.user("user address").ca(X509Utils.resolveCertificate("*.crt")); | |||
``` | |||
命令行方式:[更新用户证书](cli/tx.md#更新用户证书) | |||
### 证书生成 | |||
使用`jdchain-cli`提供的[keys](cli/keys.md)和[ca](cli/ca.md)指令工具创建公私钥对以及签发证书。 | |||
其中[ca-test](cli/ca.md#生成测试证书)可一键生成账本初始化所需的所有证书外加可用的普通用户证书。 |
@@ -1,248 +0,0 @@ | |||
### 证书管理 | |||
`jdchain-cli`提供**`ED25519`,`RSA`,`ECDSA`,`SM2`**密钥算法的证书签发工具:[证书列表](#证书列表),[显示证书](#显示证书),[CSR](#CSR),[CRT](#CRT),[更新证书](#更新证书),[生成测试证书](#生成测试证书) | |||
> 目前支持创建`ED25519`,RSA`,`ECDSA`,`SM2`四种签名算法,请使用对应算法的公私钥 | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca -h | |||
Usage: jdchain-cli ca [-hV] [--pretty] [--home=<path>] [COMMAND] | |||
List, create, update certificates. | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
Default: ../ | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
Commands: | |||
list List all the certificates. | |||
show Show certificate. | |||
csr Create certificate signing request. | |||
crt Create new certificate. | |||
renew Update validity period. | |||
test Create certificates for a testnet. | |||
help Displays help information about the specified command | |||
``` | |||
- `home`,指定密钥和证书存储相关目录,`${home}/config/keys` | |||
#### 证书列表 | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca list -h | |||
List all the certificates. | |||
Usage: jdchain-cli ca list [-hV] [--pretty] [--home=<path>] | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys list | |||
NAME ALGORITHM ADDRESS PUBKEY | |||
``` | |||
- `NAME`,名称 | |||
- `ALGORITHM`,算法 | |||
- `ADDRESS`,地址 | |||
- `PUBKEY`,公钥 | |||
#### 显示证书 | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca show -h | |||
Show certificate. | |||
Usage: jdchain-cli ca show [-hV] [--pretty] [--home=<path>] -n=<name> | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the certificate | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,证书名称 | |||
如显示`${home}/config/keys`下名为`G1`的证书信息: | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca show -n G1 | |||
./jdchain-cli.sh ca show -n G1 | |||
NAME ALGORITHM TYPE ROLE PUBKEY | |||
G1 SM2 ROLE-TODO [GW] SFZ6LjGKVz6wdU4G9PAraojyzCYPJ1BXAg1XBwSPCMC6Ug6u5oom5zcLPUzWtz42aCp9PLGXpHweBjSu3EW2aDzsa4JoT | |||
[0] Version: 3 | |||
SerialNumber: 440724497 | |||
IssuerDN: O=JDT,OU=ROOT,C=CN,ST=BJ,L=BJ,CN=ROOT,E=imuge@jd.com | |||
Start Date: Fri Sep 03 16:43:01 GMT+08:00 2021 | |||
Final Date: Thu May 30 16:43:01 GMT+08:00 2024 | |||
SubjectDN: O=JDT,OU=GW,C=CN,ST=BJ,L=BJ,CN=G1,E=imuge@jd.com | |||
Public Key: EC Public Key [c0:b9:58:d1:35:3d:a9:bc:1d:85:2a:ea:bf:57:80:39:e9:f6:57:6d] | |||
X: 67e4a4afe0a5beb1e5fb6e915314a9ed94b74f449cc4f50314ff78ecf62ba786 | |||
Y: 2d5c233bfcd582f0c1098dbe4f1319db074fcf00023fdc9f3461a8d01488d9f2 | |||
Signature Algorithm: SM3WITHSM2 | |||
Signature: 3046022100b70107554a723ec96569bbb23c65cb | |||
ac6d7934f47722aa50f18a5e9ca3a978b9022100 | |||
9b68e5f3bd14bf103248c8516c493e5e1d9a872c | |||
39841c3704686ca85311bac0 | |||
``` | |||
#### CSR | |||
生成证书请求文件 | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca csr -h | |||
Create certificate signing request. | |||
Usage: jdchain-cli ca csr [-hV] [--pretty] [--home=<path>] [-n=<name>] | |||
[--priv=<privPath>] [--pub=<pubPath>] | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--pretty Pretty json print | |||
--priv=<privPath> Path of the private key file | |||
--pub=<pubPath> Path of the public key file | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,密钥对名称,创建公私钥请参照[keys](keys.md)文档说明 | |||
如使用`${home}/config/keys`下名为`ROOT`的公私钥信息创建`CSR`: | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca csr -n ROOT | |||
// 选择证书角色,输入对应数字即可,多个角色使用半角逗号相隔 | |||
input certificate roles (0 for ROOT, 1 for CA, 2 for PEER, 3 for GW, 4 for USER. multi values use ',' split): | |||
> 1 | |||
input country: | |||
> CN | |||
input locality: | |||
> BJ | |||
input province: | |||
> BJ | |||
input organization name: | |||
> JDT | |||
input email address: | |||
> imuge@jd.com | |||
// 输入ROOT私钥密码 | |||
input password of the key: | |||
> 1 | |||
create [${home}/config/keys/ROOT.csr] success | |||
``` | |||
成功后会创建`${home}/config/keys/ROOT.csr`文件。 | |||
#### CRT | |||
签发证书: | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca crt -h | |||
Create new certificate. | |||
Usage: jdchain-cli ca crt [-hV] [--pretty] [--csr=<csrPath>] --days=<days> | |||
[--home=<path>] [--issuer-crt=<issuerCrtPath>] | |||
[--issuer-name=<issuerName>] | |||
[--issuer-priv=<issuerPrivPath>] [-n=<name>] | |||
--csr=<csrPath> Path of the certificate signing request file | |||
--days=<days> Days of certificate validity | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--issuer-crt=<issuerCrtPath> | |||
Path of the issuer certificate file | |||
--issuer-name=<issuerName> | |||
Name of the issuer key | |||
--issuer-priv=<issuerPrivPath> | |||
Path of the issuer private key file | |||
-n, --name=<name> Name of the certificate signing request file | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,`CSR`文件名,不为空时要求在`${home}/config/keys`目录下存在`${name.csr}`文件 | |||
- `csr`,`CSR`文件路径,与`name`二选一,优先使用`name`参数 | |||
- `days`,证书有效天数,当前签发时间开始计算 | |||
- `issuer-name`,签发者公私钥对名称,不为空时需要`${home}/config/keys`目录下至少存在`${issuer-name}.priv`,`${issuer-name}.crt` | |||
- `issuer-crt`,签发者证书文件 | |||
- `issuer-priv`,签发者私钥文件 | |||
> `issuer-name`为空时,`issuer-crt`和`issuer-priv`必须同时提供 | |||
如使用`${home}/config/keys`下名为`ROOT`签发自签名证书: | |||
```bash | |||
./jdchain-cli.sh ca crt -n CA --issuer-name ROOT --days 1000 | |||
// 输入签发者私钥密码 | |||
input password of the issuer: | |||
> 1 | |||
create [${home}/config/keys/ROOT.crt] success | |||
``` | |||
#### 更新证书 | |||
仅可更新证书有效天数 | |||
```bash | |||
Update validity period. | |||
Usage: jdchain-cli ca renew [-hV] [--pretty] [--crt=<crtPath>] --days=<days> | |||
[--home=<path>] [--issuer-crt=<issuerCrtPath>] | |||
[--issuer-name=<issuerName>] | |||
[--issuer-priv=<issuerPrivPath>] [-n=<name>] | |||
--crt=<crtPath> File of the certificate | |||
--days=<days> Days of certificate validity | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--issuer-crt=<issuerCrtPath> | |||
Path of the issuer certificate file | |||
--issuer-name=<issuerName> | |||
Name of the issuer key | |||
--issuer-priv=<issuerPrivPath> | |||
Path of the issuer private key file | |||
-n, --name=<name> Name of the certificate | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,`CRT`文件名,不为空时要求在`${home}/config/keys`目录下存在`${name.crt}`文件 | |||
- `crt`,`CRT`文件路径,与`name`二选一,优先使用`name`参数 | |||
- `days`,证书有效天数,当前签发时间开始计算 | |||
- `issuer-name`,签发者公私钥对名称,不为空时需要`${home}/config/keys`目录下至少存在`${issuer-name}.priv`,`${issuer-name}.crt` | |||
- `issuer-crt`,签发者证书文件 | |||
- `issuer-priv`,签发者私钥文件 | |||
> `issuer-name`为空时,`issuer-crt`和`issuer-priv`必须同时提供 | |||
如更新`${home}/config/keys`下名为`ROOT`证书有效期: | |||
```bash | |||
./jdchain-cli.sh ca crt -n ROOT --issuer-name ROOT --days 2000 | |||
input password of the issuer: | |||
> 1 | |||
renew [${home}/config/keys/ROOT.crt] success success | |||
``` | |||
#### 生成测试证书 | |||
一键生成可用于初始化`JD Chain`网络及使用需要的证书 | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca test -h | |||
Create certificates for a testnet. | |||
Usage: jdchain-cli ca test [-hV] [--pretty] [-a=<algorithm>] | |||
--country=<country> --email=<email> [--gws=<gws>] | |||
[--home=<path>] --locality=<locality> | |||
[--nodes=<nodes>] --org=<organization> | |||
[-p=<password>] --province=<province> | |||
[--users=<users>] | |||
-a, --algorithm=<algorithm> | |||
Crypto algorithm | |||
--country=<country> Country | |||
--email=<email> Email address | |||
--gws=<gws> Gateway size | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--locality=<locality> Locality | |||
--nodes=<nodes> Node size | |||
--org=<organization> Organization name | |||
-p, --password=<password> Password of the key | |||
--pretty Pretty json print | |||
--province=<province> Province | |||
--users=<users> Available user size | |||
-V, --version Print version information and exit. | |||
``` | |||
- `algorithm`,签名算法,默认`ED25519`,仅支持传入`ED25519`, `RSA`,`ECDSA`,`SM2`之一 | |||
- `nodes`,共识节点个数,生成`nodes`个`PEER`类型的证书,可用于节点使用。默认:`4` | |||
- `gws`,网关节点个数,生成`gws`个`GW`类型的证书,可用于网关使用。默认:`1` | |||
- `users`,用户个数,生成`users`可个可用于普通用户使用的证书。默认:`10` | |||
如创建基于`SM2`签名算法的一个`ROOT`类型证书,`4`个节点证书,`1`个网关证书,`10`个用户证书: | |||
```bash | |||
:bin$ ./jdchain-cli.sh ca test --org JDT --country CN --locality BJ --province BJ --email jdchain@jd.com | |||
input private key password: | |||
// 输入操作过程中生成的私钥加密密码 | |||
> 1 | |||
create test certificates in [${home}/config/keys] success | |||
``` |
@@ -1,146 +0,0 @@ | |||
### 密钥管理 | |||
`jdchain-cli`提供基于本地目录的密钥管理:[密钥对列表](#密钥对列表),[显示密钥对](#显示密钥对),[添加密钥对](#添加密钥对),[更新私钥密码](#更新私钥密码),[删除密钥对](#删除密钥对) | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys -h | |||
Usage: git status [<options>...] [--] [<pathspec>...] | |||
List, create, update or delete keypairs. | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
Default: ../ | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
Commands: | |||
list List all the keypairs. | |||
show Show keypair. | |||
add Create a new keypair. | |||
update Update privkey password. | |||
delete Delete keypair. | |||
help Displays help information about the specified command | |||
``` | |||
#### 密钥对列表 | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys list -h | |||
List all the keypairs. | |||
Usage: jdchain-cli keys list [-hV] [--pretty] [--home=<path>] | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `home`,指定密钥存储相关目录,`${home}/config/keys` | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys list | |||
NAME ALGORITHM ADDRESS PUBKEY | |||
``` | |||
- `NAME`,名称 | |||
- `ALGORITHM`,算法 | |||
- `ADDRESS`,地址 | |||
- `PUBKEY`,公钥 | |||
#### 显示密钥对 | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys show -h | |||
Show the keypair. | |||
Usage: jdchain-cli keys show [-hV] [--pretty] [--home=<path>] -n=<name> | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,密钥对名称 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys show -n k1 | |||
input the password: | |||
> 1 | |||
NAME ALGORITHM ADDRESS PUBKEY PRIVKEY PASSWORD | |||
k1 ED25519 LdeNwzRRuF33BNkyzbMuzKV3zFNGMrYBsRXvm 7VeRPc4QsYJX7qpzHBBJTzwvvmXXFVvP1MwmdU7WCBv9Uvc5 177gk2XHAsWRMXyHLLcJsig2jvXWpgo4ZVg2HYgGaiXauAZqPcnsETNeLUeRShw2BKgHVbN 8EjkXVSTxMFjCvNNsTo8RBMDEVQmk7gYkW4SCDuvdsBG | |||
``` | |||
会显示`k1`所有信息 | |||
#### 添加密钥对 | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys add -h | |||
Create a new keypair. | |||
Usage: jdchain-cli keys add [-hV] [--pretty] [-a=<algorithm>] [--home=<path>] | |||
-n=<name> | |||
-a, --algorithm=<algorithm> | |||
Crypto algorithm | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--pretty Pretty json print | |||
-p, --password=<password> | |||
Password of the key | |||
-V, --version Print version information and exit. | |||
``` | |||
- `a`/`algorithm`,密钥算法:`SM2`,`ED25519`等,默认`ED25519` | |||
- `name`,密钥对名称 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys add -n k1 | |||
please input password: > | |||
// 输入私钥密码 | |||
1 | |||
NAME ALGORITHM ADDRESS PUBKEY | |||
k1 ED25519 LdeP1iczD3zpmcayKAxTfSywict9y2r6Jpq6n 7VeRBamwPeMb7jzTNg3Ap2DscBiy3QE3PK5NqBvv9tUjQVk4 | |||
``` | |||
#### 更新私钥密码 | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys update -h | |||
Update privkey password. | |||
Usage: jdchain-cli keys update [-hV] [--pretty] [--home=<path>] -n=<name> | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,密钥对名称 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys update -n k1 | |||
input the current password: | |||
// 输入当前密码 | |||
> 1 | |||
input new password: | |||
// 输入新密码 | |||
> 2 | |||
NAME ALGORITHM ADDRESS PUBKEY | |||
k1 ED25519 LdeP1iczD3zpmcayKAxTfSywict9y2r6Jpq6n 7VeRBamwPeMb7jzTNg3Ap2DscBiy3QE3PK5NqBvv9tUjQVk4 | |||
``` | |||
#### 删除密钥对 | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys delete -h | |||
Delete keypair. | |||
Usage: jdchain-cli keys delete [-hV] [--pretty] [--home=<path>] -n=<name> | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,密钥对名称 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh keys delete -n k1 | |||
input the current password: > | |||
// 输入当前密码 | |||
2 | |||
[k1] deleted | |||
``` |
@@ -1,219 +0,0 @@ | |||
### 共识节点变更 | |||
借助`BFT-SMaRt`共识提供的Reconfig操作元语,`JD Chain`实现了在不停机的情况下快速更新共识网络拓扑,实现添加共识节点,移除共识节点,更新共识信息 等功能。 | |||
```bash | |||
:bin$ ./jdchain-cli.sh participant -h | |||
Usage: jdchain-cli participant [-hV] [--pretty] [--home=<path>] [COMMAND] | |||
Add, update or delete participant. | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
Default: ../ | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
Commands: | |||
register Register new participant. | |||
active Active participant. | |||
update Update participant. | |||
inactive Inactive participant. | |||
help Displays help information about the specified command | |||
``` | |||
- `register` [注册新节点](#注册新节点) | |||
- `active` [激活节点](#激活节点) | |||
- `active` [更新节点](#更新节点) | |||
- `inactive` [移除节点](#移除节点) | |||
#### 注册新节点 | |||
```bash | |||
:bin$ ./jdchain-cli.sh participant register -h | |||
Register new participant. | |||
Usage: jdchain-cli participant register [-hV] [--ca-mode] [--pretty] | |||
[--crt=<caPath>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
[-n=<name>] | |||
--participant-name=<participantName> | |||
[--pubkey=<pubkey>] | |||
--ca-mode Register with CA | |||
--crt=<caPath> File of the X509 certificate | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--participant-name=<participantName> | |||
Name of the participant | |||
--pretty Pretty json print | |||
--pubkey=<pubkey> Pubkey of the user | |||
-V, --version Print version information and exit. | |||
``` | |||
- `participant-name`,新节点名称 | |||
- `ca-mode`,身份认证模式是否为证书(`CA`)模式,默认`false` | |||
- `name`,当`ca-mode`为`true`时会读取本地`${home}/config/keys/${name}.crt`文件,反之读取`${home}/config/keys/${name}.pub` | |||
- `crt`,证书文件路径 | |||
- `pubkey`,`Base58`编码公钥信息,仅在非`ca-mode`情况下使用 | |||
注册新节点: | |||
```bash | |||
:bin$ ./jdchain-cli.sh participant register --participant-name node4 --name node4 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
// 选择账本 | |||
> 0 | |||
// 选择此交易签名用户(必须是链上存在的用户,且有相应操作权限) | |||
select keypair to sign tx, input the index: | |||
0 k1 LdeNq3862vtUCeptww1T5mVvLbAeppYqVNdqD | |||
1 1627618939 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
2 node4 LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T | |||
1 | |||
input password of the key: | |||
> 1 | |||
register participant: [LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T] | |||
``` | |||
成功在账本`j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg`中注册了新的节点`LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T` | |||
可通过[共识节点列表](query.md#共识节点列表)查看新的账本列表: | |||
```bash | |||
:bin$ ./jdchain-cli.sh query participants | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
[{"address":"LdeNwsiuo7n6HULWhNKc87PBXJXAfGKFon9RE","id":2,"name":"2","participantNodeState":"CONSENSUS","pubKey":"7VeRFF1ednwhrFoe5cngKwPUJ2N4iFKD9Jt53GxSCc1MmPQ6"},{"address":"LdeNiXZbsBCsTc2ZGp1PGBX81aUxPekhwEwmY","id":1,"name":"1","participantNodeState":"CONSENSUS","pubKey":"7VeREmuT4fF9yRPEMbSSaNLKbLa3qoTpfGHRgwpnSWUn5tqW"},{"address":"LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T","id":4,"name":"node4","participantNodeState":"READY","pubKey":"7VeRKiWHcHjNoYH9kJk2fxoJxgBrstVJ7bHRecKewJAKcvUD"},{"address":"LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw","id":0,"name":"0","participantNodeState":"CONSENSUS","pubKey":"7VeRJpb2XX8XKAaC7G5zQg9DbgKM8gmLhUBtGFmerFbhJTZn"},{"address":"LdeP2ji8PR1DPsLt5NoFeiBnhpckrLHgCJge6","id":3,"name":"3","participantNodeState":"CONSENSUS","pubKey":"7VeRGE4V9MR7HgAqTrkxGvJvaaKRZ3fAjHUjYzpNBGcjfAvr"}] | |||
register participant: [LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T] | |||
``` | |||
可以看出`node4`注册成功,地址为`LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T` | |||
#### 激活节点 | |||
激活节点前请正确配置并启动新节点,以下以刚注册成功的`node4`为例 | |||
1. 配置`node4` | |||
解压`peer`压缩包,复制待加入账本其他节点中数据最新的节点数据库数据,然后修改`config/ledger-binding.conf`: | |||
```bash | |||
# Base58编码的账本哈希 | |||
ledger.bindings=<账本hash> | |||
# 账本名字,与账本相关的其他其他peer保持一致 | |||
binding.<账本HASH>.name=<节点名称> | |||
# peer4的名名称,与[向现有共识网络注册新的参与方]操作中保持一致 | |||
binding.<账本hash>.parti.name=<节点名称> | |||
# peer4的用户地址 | |||
binding.<账本hash>.parti.address=<peer4的用户地址> | |||
# 新参与方base58编码的私钥,与[向现有共识网络注册新的参与方]操作中保持一致 | |||
binding.<账本hash>.parti.pk=<新参与方base58编码的私钥> | |||
# 新参与方base58编码的私钥读取密码,与[向现有共识网络注册新的参与方]操作中保持一致 | |||
binding.<账本hash>.parti.pwd=<新参与方base58编码的私钥读取密码> | |||
# 新参与方对应的账本数据库连接uri,即peer4的账本数据库存放位置,参照其他peer修改,不可与其他peer混用 | |||
binding.<账本hash>.db.uri=<账本数据库连接> | |||
``` | |||
2. 启动`node4` | |||
| **一定注意在启动新参与方节点进程之前确保完成了账本数据库的复制工作** | |||
执行`peer4`中`bin`目录下`peer-startup.sh`脚本启动启动新参与方`peer4`节点进程: | |||
```bash | |||
./peer-startup.sh | |||
``` | |||
3. 激活新节点 | |||
```bash | |||
:bin$ ./jdchain-cli.sh participant active -h | |||
Active participant. | |||
Usage: jdchain-cli participant active [-hV] [--pretty] [--shutdown] | |||
--consensus-port=<consensusPort> | |||
[--home=<path>] --host=<host> | |||
--ledger=<ledger> --port=<port> | |||
--syn-host=<synHost> --syn-port=<synPort> | |||
--consensus-port=<consensusPort> | |||
Set the participant consensus port. | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--host=<host> Set the participant host. | |||
--ledger=<ledger> Set the ledger. | |||
--port=<port> Set the participant service port. | |||
--pretty Pretty json print | |||
--shutdown Restart the node server. | |||
--syn-host=<synHost> Set synchronization participant host. | |||
--syn-port=<synPort> Set synchronization participant port. | |||
-V, --version Print version information and exit. | |||
``` | |||
- `ledger`,账本哈希 | |||
- `host`,新节点地址 | |||
- `port`,新节点服务端口 | |||
- `consensus-port`,新节点共识端口 | |||
- `syn-host`,数据同步节点地址 | |||
- `syn-port`,数据同步节点服务端口 | |||
在账本`j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg`中激活`node4`(以步骤2中启动的服务地址和端口为`127.0.0.1`和`7084`例),共识端口设置为`10088`,同步节点地址和端口为`127.0.0.1`和`7080`为例: | |||
```bash | |||
:bin$./jdchain-cli.sh participant active --ledger j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg --host 127.0.0.1 --port 7084 --consensus-port 10088 --syn-host 127.0.0.1 --syn-port 7080 | |||
participant activated | |||
``` | |||
成功后可通过[共识节点列表](query.md#共识节点列表)查询最新共识节点列表状态,`node4`为`CONSENSUS` | |||
#### 更新节点 | |||
通过[激活节点](#激活节点)操作除了激活新增的节点外,还可以动态修改已经处于激活状态的共识节点的`IP`和`共识端口`信息,从而实现本机的共识端口变更,不同机器之间进行`账本迁移`。 | |||
| **在进行节点信息变更时,要求暂停向共识网络中发起新的业务数据上链请求** | |||
1. 变更共识端口 | |||
| **操作前请确保变更到的端口未被占用** | |||
如将`node4`共识端口由`10088`修改为`10188`,操作指令如下: | |||
```bash | |||
:bin$./jdchain-cli.sh participant update --ledger j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg --host 127.0.0.1 --port 7084 --consensus-port 10188 --syn-host 127.0.0.1 --syn-port 7080 | |||
participant updated | |||
``` | |||
指令成功执行后,`peer1`的共识端口将自动变更为`10188` | |||
2. 账本迁移 | |||
账本迁移指将一台机器(`IP`)上的共识节点迁移到另一台机器(`IP`)上,主要操作流程如下: | |||
| **操作前请确保变更到的端口未被占用** | |||
如将`node4`共识`IP`由`127.0.0.1`修改为`192.168.1.100`(另一台机器),操作指令如下: | |||
```bash | |||
:bin$./jdchain-cli.sh participant update --ledger j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg --host 192.168.1.100 --port 7084 --consensus-port 10188 --syn-host 127.0.0.1 --syn-port 7080 -shutdown | |||
participant updated | |||
``` | |||
**特别注意**:`-shutdown`会停止当前运行的当前账本共识服务,为必填选项,否则可能将导致整个网络需要重启。 | |||
#### 移除节点 | |||
``` | |||
:bin$ ./jdchain-cli.sh participant inactive -h | |||
Inactive participant. | |||
Usage: jdchain-cli participant inactive [-hV] [--pretty] --address=<address> | |||
[--home=<path>] --host=<host> | |||
--ledger=<ledger> --port=<port> | |||
--syn-host=<synHost> | |||
--syn-port=<synPort> | |||
--address=<address> Set the participant address. | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--host=<host> Set the participant host. | |||
--ledger=<ledger> Set the ledger. | |||
--port=<port> Set the participant service port. | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `ledger`,账本哈希 | |||
- `address`,待移除节点共识端口 | |||
- `host`,待移除节点服务地址 | |||
- `port`,待移除节点服务端口 | |||
如移除`node4`: | |||
```bash | |||
:bin$ ./jdchain-cli.sh participant inactive --ledger j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg --address LdeNwG6ECEGz57o2ufhwSbnW4C35TvPqANK7T --host 127.0.0.1 --port 7084 | |||
participant inactivated | |||
``` | |||
成功后可通过[共识节点列表](query.md#共识节点列表)查询最新共识节点列表状态,`node4`为`DEACTIVATED` |
@@ -1,872 +0,0 @@ | |||
### 交易 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx -h | |||
Usage: jdchain-cli tx [-hV] [--pretty] [--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] [COMMAND] | |||
Build, sign or send transaction. | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
Default: ../ | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
Commands: | |||
root-ca Update ledger root certificates. | |||
user-register Register new user. | |||
user-ca Update user certificate. | |||
user-state Update user(certificate) state. | |||
role Create or config role. | |||
authorization User role authorization. | |||
data-account-register Register new data account. | |||
data-account-permission Update data account permission. | |||
kv Set key-value. | |||
event-account-register Register event account. | |||
event-account-permission Update event account permission. | |||
event Publish event. | |||
event-listen Subscribe event. | |||
contract-deploy Deploy or update contract. | |||
contract-permission Update contract permission. | |||
contract Call contract method. | |||
contract-state Update contract state. | |||
sign Sign transaction. | |||
send Send transaction. | |||
help Displays help information about the specified | |||
command | |||
``` | |||
参数: | |||
- `export`,导出交易到指定位置,用于离线交易相关命令 | |||
- `gw-host`,网关服务地址,默认`127.0.0.1` | |||
- `gw-port`,网关服务端口,默认`8080` | |||
- `home`,指定密钥存储相关目录,`${home}/config/keys` | |||
命令: | |||
- `root-ca`,[更新账本根证书](#更新账本根证书) | |||
- `user-register`,[注册用户](#注册用户) | |||
- `user-ca`,[更新用户证书](#更新用户证书) | |||
- `user-state`,[更新用户(证书)状态](#更新用户(证书)状态) | |||
- `role`,[角色管理](#角色管理) | |||
- `authorization`,[权限配置](#权限配置) | |||
- `data-account-register`,[注册数据账户](#注册数据账户) | |||
- `data-account-permission`,[修改数据账户权限](#修改数据账户权限) | |||
- `kv`,[KV设值](#KV设值) | |||
- `event-account-register`,[注册事件账户](#注册事件账户) | |||
- `event-account-permission`,[修改事件账户权限](#修改事件账户权限) | |||
- `event`,[发布事件](#发布事件) | |||
- `event-listen`,[监听事件](#监听事件) | |||
- `contract-deploy`,[部署合约](#部署合约) | |||
- `contract-permission`,[修改合约权限](#修改合约权限) | |||
- `contract`,[合约调用](#合约调用) | |||
- `contract-state`,[更新合约状态](#更新合约状态) | |||
- `sign`,[离线交易签名](#离线交易签名) | |||
- `send`,[离线交易发送](#离线交易发送) | |||
#### 更新账本根证书 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx root-ca -h | |||
Update ledger root certificates. | |||
Usage: jdchain-cli tx ledger-ca-update [-hV] [--pretty] --crt=<caPath> | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
--crt=<caPath> File of the X509 certificate | |||
--operation Operation for this certificate. Optional values: ADD,UPDATE,REMOVE | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `crt`,证书文件路径 | |||
- `operation`,操作类型:`ADD`,`UPDATE`,`REMOVE` | |||
如: | |||
```bash | |||
:bin$ $ ./jdchain-cli.sh tx root-ca --crt /home/imuge/jd/nodes/peer0/config/keys/ledger.crt --operation UPDATE | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5pFrMigE47t6TobQJXsztnoeA29H31v1vHHF1wqCp4rzi | |||
// 选择账本,当前网关服务只有上面一个可用账本 | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W | |||
// 选择链上已存在且有注册用户权限的用户所对应的公私钥对,用于交易签名 | |||
> 0 | |||
input password of the key: | |||
// 输入签名私钥密码 | |||
> 1 | |||
ledger ca: [7VeRBQ9jpsgNXje2NYXU5MhyGKVRj462RtkJ8f6FNL1oxYbX](pubkey) updated | |||
``` | |||
会更新链上公钥为`7VeRBQ9jpsgNXje2NYXU5MhyGKVRj462RtkJ8f6FNL1oxYbX`的账本证书信息。 | |||
#### 注册用户 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx user-register -h | |||
Register new user. | |||
Usage: jdchain-cli tx user-register [-hV] [--ca-mode] [--pretty] | |||
[--crt=<caPath>] [--export=<export>] | |||
[--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] [-n=<name>] | |||
[--pubkey=<pubkey>] | |||
--ca-mode Register with CA | |||
--crt=<caPath> File of the X509 certificate | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
-n, --name=<name> Name of the key | |||
--pretty Pretty json print | |||
--pubkey=<pubkey> Pubkey of the user | |||
-V, --version Print version information and exit. | |||
``` | |||
- `ca-mode`,身份认证模式是否为证书(`CA`)模式,默认`false` | |||
- `name`,当`ca-mode`为`true`时会读取本地`${home}/config/keys/${name}.crt`文件,反之读取`${home}/config/keys/${name}.pub` | |||
- `crt`,证书文件路径 | |||
- `pubkey`,`Base58`编码公钥信息,仅在非`ca-mode`情况下使用 | |||
从`${home}/config/keys`目录下密钥对选择密钥注册到网关服务对应的区块链网络。 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx user-register -name k1 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
// 选择账本,当前网关服务只有上面一个可用账本 | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
// 选择链上已存在且有注册用户权限的用户所对应的公私钥对,用于交易签名 | |||
> 0 | |||
input password of the key: | |||
// 输入签名私钥密码 | |||
> 1 | |||
register user: [LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC] | |||
``` | |||
会在链上注册地址为`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`的用户账户信息。 | |||
#### 更新用户证书 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx user-ca -h | |||
Update user certificate. | |||
Usage: jdchain-cli tx user-ca [-hV] [--pretty] [--crt=<caPath>] | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
--crt=<caPath> File of the X509 certificate | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `crt`,证书文件路径 | |||
如: | |||
```bash | |||
:bin$ $ ./jdchain-cli.sh tx user-ca --crt /home/imuge/jd/nodes/peer0/config/keys/peer0.crt | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5pFrMigE47t6TobQJXsztnoeA29H31v1vHHF1wqCp4rzi | |||
// 选择账本,当前网关服务只有上面一个可用账本 | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W | |||
// 选择链上已存在且有注册用户权限的用户所对应的公私钥对,用于交易签名 | |||
> 0 | |||
input password of the key: | |||
// 输入签名私钥密码 | |||
> 1 | |||
user: [LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W] ca updated | |||
``` | |||
会更新链上地址为`LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W`的用户证书信息。 | |||
#### 更新用户(证书)状态 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx user-state -h | |||
Update user(certificate) state. | |||
Usage: jdchain-cli tx user-state [-hV] [--pretty] --address=<address> | |||
[--export=<export>] | |||
[--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
--state=<state> | |||
--address=<address> User address | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
--state=<state> User state,Optional values: FREEZE,NORMAL,REVOKE | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,用户地址 | |||
- `state`,用户状态,可选值:FREEZE,NORMAL,REVOKE | |||
如冻结用户`LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W`: | |||
```bash | |||
:bin$ $ ./jdchain-cli.sh tx user-state --address LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W --state FREEZE | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5pFrMigE47t6TobQJXsztnoeA29H31v1vHHF1wqCp4rzi | |||
// 选择账本,当前网关服务只有上面一个可用账本 | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W | |||
// 选择链上已存在且有注册用户权限的用户所对应的公私钥对,用于交易签名 | |||
> 0 | |||
input password of the key: | |||
// 输入签名私钥密码 | |||
> 1 | |||
user: [LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W] revoked | |||
``` | |||
会冻结链上地址为`LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W`的用户(证书),此用户无法再接入使用此网络。 | |||
#### 角色管理 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx role -h | |||
Create or config role. | |||
Usage: jdchain-cli tx role [-hV] [--pretty] [--export=<export>] | |||
[--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] --name=<role> | |||
[--disable-ledger-perms=<disableLedgerPerms>[, | |||
<disableLedgerPerms>...]]... | |||
[--disable-transaction-perms=<disableTransactionPerms | |||
>[,<disableTransactionPerms>...]]... | |||
[--enable-ledger-perms=<enableLedgerPerms>[, | |||
<enableLedgerPerms>...]]... | |||
[--enable-transaction-perms=<enableTransactionPerms> | |||
[,<enableTransactionPerms>...]]... | |||
--disable-ledger-perms=<disableLedgerPerms>[,<disableLedgerPerms>...] | |||
Disable ledger permissions | |||
--disable-transaction-perms=<disableTransactionPerms>[, | |||
<disableTransactionPerms>...] | |||
Disable transaction permissions | |||
--enable-ledger-perms=<enableLedgerPerms>[,<enableLedgerPerms>...] | |||
Enable ledger permissions | |||
--enable-transaction-perms=<enableTransactionPerms>[, | |||
<enableTransactionPerms>...] | |||
Enable transaction permissions | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--name=<role> Role name | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `name`,角色名称,不存在则创建 | |||
- `disable-ledger-perms`,禁用的账本权限列表,半角逗号分割 | |||
- `disable-transaction-perms`,禁用的交易权限列表,半角逗号分割 | |||
- `enable-ledger-perms`,的账本权限列表,半角逗号分割 | |||
- `enable-transaction-perms`,禁用的交易权限列表,半角逗号分割 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx role --name ROLE1 --enable-ledger-perms REGISTER_USER,REGISTER_DATA_ACCOUNT --enable-transaction-perms DIRECT_OPERATION,CONTRACT_OPERATION | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
// 选择账本 | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
// 选择签名账户 | |||
> 0 | |||
input password of the key: | |||
// 输入签名账户私钥密码 | |||
> 1 | |||
Role config success! | |||
``` | |||
#### 权限配置 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx authorization -h | |||
User role authorization. | |||
Usage: jdchain-cli tx authorization [-hV] [--pretty] --address=<address> | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
[--policy=<policy>] | |||
[--authorize=<authorizeRoles>[, | |||
<authorizeRoles>...]]... | |||
[--unauthorize=<unauthorizeRoles>[, | |||
<unauthorizeRoles>...]]... | |||
--address=<address> User address | |||
--authorize=<authorizeRoles>[,<authorizeRoles>...] | |||
Authorize roles | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--policy=<policy> Role policy | |||
--pretty Pretty json print | |||
--unauthorize=<unauthorizeRoles>[,<unauthorizeRoles>...] | |||
Unauthorize roles | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,用户地址 | |||
- `authorize`,赋予角色列表,半角逗号分割 | |||
- `unauthorize`,移除角色列表,半角逗号分割 | |||
- `policy`,角色策略,`UNION`/`INTERSECT`,默认`UNION`合并所有角色权限 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx authorization --address LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC --authorize ROLE1 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
Authorization config success! | |||
``` | |||
#### 注册数据账户 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx data-account-register -h | |||
Register new data account. | |||
Usage: jdchain-cli tx data-account-register [-hV] [--pretty] | |||
[--export=<export>] [--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] [--pubkey=<pubkey>] | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
--pubkey=<pubkey> The pubkey of the exist data account | |||
-V, --version Print version information and exit. | |||
``` | |||
- `pubkey`,待注册数据账户公钥 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx data-account-register --pubkey 7VeRFk4ANQHjWjAmAoL7492fuykTpXujihJeAgbXT2J9H9Yk | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
register data account: [LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC] | |||
``` | |||
会在链上注册地址为`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`的数据账户信息。 | |||
#### 修改数据账户权限 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx data-account-permission -h | |||
Update data account permission. | |||
Usage: jdchain-cli tx data-account-permission [-hV] [--pretty] | |||
[--address=<address>] [--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] [--mode=<mode>] [--role=<role>] | |||
--address=<address> Address of the data account | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--mode=<mode> Mode value of the data account | |||
--pretty Pretty json print | |||
--role=<role> Role of the data account | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,数据账户地址 | |||
- `role`,数据账户所属角色 | |||
- `mode`,数据账户权限值 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx data-account-permission --address LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC --role ROLE1 --mode 777 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
update data account: [LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC] permission | |||
``` | |||
将修改数据账户`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`所属角色为`ROLE1`,权限值为`777`(所有用户可读可写)。 | |||
#### KV设值 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx kv -h | |||
Set key-value. | |||
Usage: jdchain-cli tx kv [-hV] [--pretty] --address=<address> | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] --key=<key> | |||
--value=<value> [--ver=<version>] | |||
--address=<address> Data account address | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--key=<key> Key to set | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
--value=<value> Value to set | |||
--ver=<version> Version of the key-value | |||
``` | |||
- `address`,数据账户地址 | |||
- `key`,键 | |||
- `value`,值 | |||
- `ver`,版本 | |||
如向账户地址`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`写入`k1`:`v1`:`-1`键值对数据: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx kv --address LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC --key k1 --value v1 --ver -1 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
set kv success | |||
``` | |||
#### 注册事件账户 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event-account-register -h | |||
Register event account. | |||
Usage: jdchain-cli tx event-account-register [-hV] [--pretty] | |||
[--export=<export>] [--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] [--pubkey=<pubkey>] | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
--pubkey=<pubkey> The pubkey of the exist event account | |||
-V, --version Print version information and exit. | |||
``` | |||
- `pubkey`,待注册事件账户私钥 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event-account-register --pubkey 7VeRFk4ANQHjWjAmAoL7492fuykTpXujihJeAgbXT2J9H9Yk | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
register event account: [LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC] | |||
``` | |||
会在链上注册地址为`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`的事件账户信息。 | |||
#### 修改事件账户权限 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event-account-permission -h | |||
Update event account permission. | |||
Usage: jdchain-cli tx event-account-permission [-hV] [--pretty] | |||
[--address=<address>] [--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] [--mode=<mode>] [--role=<role>] | |||
--address=<address> Address of the event account | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--mode=<mode> Mode value of the event account | |||
--pretty Pretty json print | |||
--role=<role> Role of the event account | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,事件账户地址 | |||
- `role`,事件账户所属角色 | |||
- `mode`,事件账户权限值 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event-account-permission --address LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC --role ROLE1 --mode 777 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
update event account: [LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC] permission | |||
``` | |||
将修改事件账户`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`所属角色为`ROLE1`,权限值为`777`(所有用户可读可写)。 | |||
#### 发布事件 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event -h | |||
Publish event. | |||
Usage: jdchain-cli tx event [-hV] [--pretty] --address=<address> | |||
--content=<value> [--export=<export>] | |||
[--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] [--sequence=<sequence>] | |||
--name=<name> | |||
--address=<address> Contract address | |||
--content=<value> Event content | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
--sequence=<sequence> Sequence of the event | |||
--name=<name> Event name | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,事件账户地址 | |||
- `name`,事件名 | |||
- `content`,事件内容 | |||
- `sequence`,事件序号 | |||
如向账户地址`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`发布事件`n1`:`c1`:`-1`: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event --address LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC --name n1 --content c1 --sequence -1 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
event publish success | |||
``` | |||
#### 监听事件 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event-listen -h | |||
Subscribe event. | |||
Usage: jdchain-cli tx event-listen [-hV] [--pretty] [--address=<address>] | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
--name=<name> [--sequence=<sequence>] | |||
--address=<address> Event address | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--name=<name> Event name | |||
--pretty Pretty json print | |||
--sequence=<sequence> Sequence of the event | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,事件账户地址,不传则表示监听系统事件 | |||
- `name`,事件名,系统事件目前仅支持:`new_block_created` | |||
- `sequence`,起始监听序号 | |||
如监听系统新区块事件: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx event-listen --name new_block_created --sequence 0 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5mXXoNsmh6qadnWLjxFMXobyNGsXT1PmTNzXiHyiYMxoP | |||
> 0 | |||
# 会打印新区块事件:区块高度:最新区块高度 | |||
New block:0:12 | |||
New block:1:12 | |||
New block:2:12 | |||
``` | |||
#### 部署合约 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract-deploy -h | |||
Deploy or update contract. | |||
Usage: jdchain-cli tx contract-deploy [-hV] [--pretty] --car=<car> | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
[--pubkey=<pubkey>] | |||
--car=<car> The car file path | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
--pubkey=<pubkey> The pubkey of the exist contract | |||
-V, --version Print version information and exit. | |||
``` | |||
- `pubkey`,合约公钥,更新合约时使用 | |||
- `car`,合约`car`文件 | |||
如将`contract-samples-1.5.0.RELEASE.car`文件中的合约部署上链: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract-deploy --car /home/imuge/Desktop/jdchain-cli/1.5.0/contract-samples-1.5.0.RELEASE.car | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
deploy contract: [LdeNyF6jdNry5iCqmHdAFTQPvC8UkbJ9avoXH] | |||
``` | |||
合约地址:`LdeNyF6jdNry5iCqmHdAFTQPvC8UkbJ9avoXH` | |||
#### 修改合约权限 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract-permission -h | |||
Update contract permission. | |||
Usage: jdchain-cli tx contract-permission [-hV] [--pretty] | |||
[--address=<address>] [--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] [--mode=<mode>] [--role=<role>] | |||
--address=<address> Address of the contract | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--mode=<mode> Mode value of the contract | |||
--pretty Pretty json print | |||
--role=<role> Role of the contract | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,合约地址 | |||
- `role`,合约所属角色 | |||
- `mode`,合约权限值 | |||
如: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract-permission --address LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC --role ROLE1 --mode 777 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
update contract: [LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC] permission | |||
``` | |||
将修改合约`LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC`所属角色为`ROLE1`,权限值为`777`(所有用户可读可写)。 | |||
#### 合约调用 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract -h | |||
Call contract method. | |||
Usage: jdchain-cli tx contract [-hV] [--pretty] --address=<address> | |||
[--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] | |||
--method=<method> [--args=<args>[,<args>...]]... | |||
--address=<address> Contract address | |||
--args=<args>[,<args>...] | |||
Method arguments | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--method=<method> Contract method | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,合约地址 | |||
- `method`,合约方法 | |||
- `args`,合约参数,半角逗号分割,命令行中会将所有参数处理为字符串,非字符串参数方法调用暂无法通过命令行工具调用 | |||
如调用合约`LdeNyF6jdNry5iCqmHdAFTQPvC8UkbJ9avoXH`中`registerUser`方法,传参`ed386a148fcb48b281b325f66103c805`: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract --address LdeNyF6jdNry5iCqmHdAFTQPvC8UkbJ9avoXH --method registerUser --args ed386a148fcb48b281b325f66103c805 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
call contract success | |||
return string: LdeNqvSjL4izfpMNsGpQiBpTBse4g6qLxZ6j5 | |||
``` | |||
调用成功并返回了字符串:`LdeNqvSjL4izfpMNsGpQiBpTBse4g6qLxZ6j5` | |||
#### 更新合约状态 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract-state -h | |||
Update contract state. | |||
Usage: jdchain-cli tx contract-state [-hV] [--pretty] | |||
--address=<address> [--export=<export>] [--gw-host=<gwHost>] | |||
[--gw-port=<gwPort>] [--home=<path>] --state=<state> | |||
--address=<address> Contract address | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
--state=<state> Contract state,Optional values: FREEZE,NORMAL, | |||
REVOKE | |||
-V, --version Print version information and exit. | |||
``` | |||
- `address`,合约地址 | |||
- `state`,合约状态,可选值:FREEZE,NORMAL,REVOKE | |||
如冻结合约`LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W`: | |||
```bash | |||
:bin$ $ ./jdchain-cli.sh tx contract-state --address LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W --state FREEZE | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5pFrMigE47t6TobQJXsztnoeA29H31v1vHHF1wqCp4rzi | |||
// 选择账本,当前网关服务只有上面一个可用账本 | |||
> 0 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W | |||
// 选择链上已存在且有注册用户权限的用户所对应的公私钥对,用于交易签名 | |||
> 0 | |||
input password of the key: | |||
// 输入签名私钥密码 | |||
> 1 | |||
contract: [LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W] revoked | |||
``` | |||
会冻结链上地址为`LdeNpEmyh5DMwbAwamxNaiJgMVGn6aTtQDA5W`的合约,此合约不能再被调用。 | |||
#### 离线交易签名 | |||
1. 离线交易 | |||
执行以上所有交易时,若`export`参数不为空,则会执行交易写入本地操作,而非签名并发送交易 | |||
如构造合约调用操作交易并保存到本地: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx contract --address LdeNyF6jdNry5iCqmHdAFTQPvC8UkbJ9avoXH --method registerUser --args ed386a148fcb48b281b325f66103c810 --export /txs | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
export transaction success: /txs/j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3 | |||
``` | |||
交易内容会被序列化保存在`/txs/j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3`中,其中`j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3`是该交易哈希。 | |||
2. 离线签名 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx sign -h | |||
Sign transaction. | |||
Usage: jdchain-cli tx sign [-hV] [--pretty] [--export=<export>] | |||
[--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] [--tx=<txFile>] | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--tx=<txFile> Local transaction file | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `tx`,离线交易路径 | |||
如对步骤1中创建的离线交易添加终端用户(此用户需是链上存在的且有相关权限的用户)签名: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx sign --tx /txs/j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3 | |||
select keypair to sign tx: | |||
INDEX KEY ADDRESS | |||
0 peer0 LdeNyibeafrAQXgHjBxgQxoLbna6hL4BcXZiw | |||
1 k1 LdeNwQWabrf6WSjZ35saFo52MfQFhVKvm11aC | |||
> 0 | |||
input password of the key: | |||
> 1 | |||
Sign transaction success! | |||
``` | |||
#### 离线交易发送 | |||
发送本地已经签名完成的交易 | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx send -h | |||
Send transaction. | |||
Usage: jdchain-cli tx send [-hV] [--pretty] [--export=<export>] | |||
[--gw-host=<gwHost>] [--gw-port=<gwPort>] | |||
[--home=<path>] [--tx=<txFile>] | |||
--export=<export> Transaction export directory | |||
--gw-host=<gwHost> Set the gateway host. Default: 127.0.0.1 | |||
--gw-port=<gwPort> Set the gateway port. Default: 8080 | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--tx=<txFile> Local transaction file | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
``` | |||
- `tx`,离线交易路径 | |||
如发送`/txs/j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3`中包含的交易数据: | |||
```bash | |||
:bin$ ./jdchain-cli.sh tx send --tx /home/imuge/Desktop/jdchain-cli/1.5.0/txs/j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3 | |||
select ledger, input the index: | |||
INDEX LEDGER | |||
0 j5sB3sVTFgTqTYzo7KtQjBLSy8YQGPpJpvQZaW9Eqk46dg | |||
> 0 | |||
Send transaction success: j5xR8ty8YbujTYKNRshmbfMYsL4jfe3yRUtMparmeHppd3 | |||
``` |
@@ -1,362 +0,0 @@ | |||
## 配置 | |||
### ledger.init | |||
```properties | |||
#账本的种子;一段16进制字符,最长可以包含64个字符;可以用字符“-”分隔,以便更容易读取; | |||
ledger.seed=932dfe23-fe23232f-283f32fa-dd32aa76-8322ca2f-56236cda-7136b322-cb323ffe | |||
#账本的描述名称;此属性不参与共识,仅仅在当前参与方的本地节点用于描述用途; | |||
ledger.name= | |||
#身份认证模式:KEYPAIR/CA,默认KEYPAIR即公私钥对模式 | |||
identity-mode=KEYPAIR | |||
#账本根证书路径,identity-mode 为 CA 时,此选项不能为空,支持多个,半角逗号相隔 | |||
root-ca-path= | |||
#声明的账本创建时间;格式为 “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 | |||
#账本数据底层结构,分为:MERKLE_TREE, KV两种,默认MERKLE_TREE | |||
ledger.data.structure=MERKLE_TREE | |||
#----------------------------------------------- | |||
# 初始的角色名称列表;可选项; | |||
# 角色名称不区分大小写,最长不超过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=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= | |||
#第1个参与方的证书路径,identity-mode 为 CA 时,此选项不能为空 | |||
cons_parti.0.ca-path= | |||
#第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个参与方的账本初始服务的端口; | |||
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个参与方的证书路径,identity-mode 为 CA 时,此选项不能为空 | |||
cons_parti.1.ca-path= | |||
#第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= | |||
#第1个参与方的证书路径,identity-mode 为 CA 时,此选项不能为空 | |||
cons_parti.2.ca-path= | |||
#第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= | |||
#第1个参与方的证书路径,identity-mode 为 CA 时,此选项不能为空 | |||
cons_parti.3.ca-path= | |||
#第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 | |||
``` | |||
- `ledger.data.structure`,账本数据底层结构。 | |||
> `MERKLE_TREE`:区块内各类根哈希和具体数据之间的锚定关系通过复杂的多叉默克尔树来实现,账本数据库内存储默克尔树构建过程中的所有中间节点状态,对交易的任何恶意篡改都涉及到对所有默克尔树中间节点状态的修改,篡改代价极大,安全性高;但复杂的数据结构以及对于KV数据的膨胀存储严重影响了整个系统的性能;适用于对安全性要求较高,而对性能要求不高的业务场景; | |||
> `KV`:在不改变交易处理流程的情况下,简化了区块内根哈希与数据锚定的默克尔树结构,仅通过通用的二叉默克尔树来保证账本数据的不可篡改,计算默克尔根哈希的中间节点状态不上链,降低了`KV`数据存储带来的读写膨胀,在效率与安全性上进行了权衡,折中。安全性上与`MERKLE_TREE`类型相比有所降低,适用于对性能要求较高的安全组织联盟中。`KV`类型还可保证数据的顺序性。 | |||
### local.conf | |||
```properties | |||
#当前参与方的 id,与ledger.init文件中cons_parti.id一致,默认从0开始 | |||
local.parti.id=0 | |||
#当前参与方的公钥,用于非证书模式 | |||
local.parti.pubkey= | |||
#当前参与方的证书信息,用于证书模式 | |||
local.parti.ca-path= | |||
#当前参与方的私钥(密文编码) | |||
local.parti.privkey= | |||
#当前参与方的私钥文件,PEM格式,用于证书模式 | |||
local.parti.privkey-path= | |||
#当前参与方的私钥解密密钥(原始口令的一次哈希,Base58格式),如果不设置,则启动过程中需要从控制台输入; | |||
local.parti.pwd= | |||
#账本初始化完成后生成的"账本绑定配置文件"的输出目录 | |||
#推荐使用绝对路径,相对路径以当前文件(local.conf)所在目录为基准 | |||
ledger.binding.out=../ | |||
#账本数据库的连接字符 | |||
#rocksdb数据库连接格式:rocksdb://{path},例如:rocksdb:///export/App08/peer/rocks.db/rocksdb0.db | |||
#redis数据库连接格式:redis://{ip}:{prot}/{db},例如:redis://127.0.0.1:6379/0 | |||
#kvdb数据库连接格式:kvdb://{ip}:{prot}/{db},例如:kvdb://127.0.0.1:7078/test | |||
ledger.db.uri= | |||
#账本数据库的连接口令 | |||
ledger.db.pwd= | |||
``` | |||
### bftsmart.config | |||
```properties | |||
############################################ | |||
###### #Consensus Participant0 ###### | |||
############################################ | |||
system.server.0.network.host=127.0.0.1 | |||
system.server.0.network.port=16000 | |||
system.server.0.network.secure=false | |||
############################################ | |||
###### #Consensus Participant1 ###### | |||
############################################ | |||
system.server.1.network.host=127.0.0.1 | |||
system.server.1.network.port=16010 | |||
system.server.1.network.secure=false | |||
############################################ | |||
###### #Consensus Participant2 ###### | |||
############################################ | |||
system.server.2.network.host=127.0.0.1 | |||
system.server.2.network.port=16020 | |||
system.server.2.network.secure=false | |||
############################################ | |||
###### #Consensus Participant3 ###### | |||
############################################ | |||
system.server.3.network.host=127.0.0.1 | |||
system.server.3.network.port=16030 | |||
system.server.3.network.secure=false | |||
############################################ | |||
####### Communication Configurations ####### | |||
############################################ | |||
#HMAC algorithm used to authenticate messages between processes (HmacMD5 is the default value) | |||
#This parameter is not currently being used | |||
#system.authentication.hmacAlgorithm = HmacSHA1 | |||
#Specify if the communication system should use a thread to send data (true or false) | |||
#system.communication.useSenderThread = true //unused property; | |||
#Force all processes to use the same public/private keys pair and secret key. This is useful when deploying experiments | |||
#and benchmarks, but must not be used in production systems. | |||
system.communication.defaultkeys = true | |||
############################################ | |||
### Replication Algorithm Configurations ### | |||
############################################ | |||
#Number of servers in the group | |||
system.servers.num = 4 | |||
#Maximum number of faulty replicas | |||
system.servers.f = 1 | |||
#Timeout to asking for a client request | |||
#system.totalordermulticast.timeout = 60000 | |||
#Allowable time tolerance range(millisecond) | |||
system.totalordermulticast.timeTolerance = 3000000 | |||
#Maximum batch size (in number of messages) | |||
system.totalordermulticast.maxbatchsize = 2000 | |||
#Number of nonces (for non-determinism actions) generated | |||
system.totalordermulticast.nonces = 10 | |||
#if verification of leader-generated timestamps are increasing | |||
#it can only be used on systems in which the network clocks | |||
#are synchronized | |||
system.totalordermulticast.verifyTimestamps = false | |||
#Quantity of messages that can be stored in the receive queue of the communication system | |||
system.communication.inQueueSize = 500000 | |||
# Quantity of messages that can be stored in the send queue of each replica | |||
system.communication.outQueueSize = 500000 | |||
#The time interval for retrying to send message after connection failure. In milliseconds; | |||
system.communication.send.retryInterval=2000 | |||
#The number of retries to send message after connection failure. | |||
system.communication.send.retryCount=100 | |||
#Set to 1 if SMaRt should use signatures, set to 0 if otherwise | |||
system.communication.useSignatures = 0 | |||
#Set to 1 if SMaRt should use MAC's, set to 0 if otherwise | |||
system.communication.useMACs = 1 | |||
#Set to 1 if SMaRt should use the standard output to display debug messages, set to 0 if otherwise | |||
system.debug = 0 | |||
#Print information about the replica when it is shutdown | |||
system.shutdownhook = true | |||
############################################ | |||
###### State Transfer Configurations ####### | |||
############################################ | |||
#Activate the state transfer protocol ('true' to activate, 'false' to de-activate) | |||
system.totalordermulticast.state_transfer = true | |||
#Maximum ahead-of-time message not discarded | |||
system.totalordermulticast.highMark = 10000 | |||
#Maximum ahead-of-time message not discarded when the replica is still on EID 0 (after which the state transfer is triggered) | |||
system.totalordermulticast.revival_highMark = 10 | |||
#Number of ahead-of-time messages necessary to trigger the state transfer after a request timeout occurs | |||
system.totalordermulticast.timeout_highMark = 200 | |||
############################################ | |||
###### Log and Checkpoint Configurations ### | |||
############################################ | |||
system.totalordermulticast.log = true | |||
system.totalordermulticast.log_parallel = false | |||
system.totalordermulticast.log_to_disk = true | |||
system.totalordermulticast.sync_log = false | |||
#Period at which BFT-SMaRt requests the state to the application (for the state transfer state protocol) | |||
system.totalordermulticast.checkpoint_period = 1000 | |||
system.totalordermulticast.global_checkpoint_period = 120000 | |||
system.totalordermulticast.checkpoint_to_disk = false | |||
system.totalordermulticast.sync_ckp = false | |||
############################################ | |||
###### Reconfiguration Configurations ###### | |||
############################################ | |||
#Replicas ID for the initial view, separated by a comma. | |||
# The number of replicas in this parameter should be equal to that specified in 'system.servers.num' | |||
system.initial.view = 0,1,2,3 | |||
#The ID of the trust third party (TTP) | |||
system.ttp.id = 2001 | |||
#This sets if the system will function in Byzantine or crash-only mode. Set to "true" to support Byzantine faults | |||
system.bft = true | |||
#Custom View Storage; | |||
#view.storage.handler=bftsmart.reconfiguration.views.DefaultViewStorage | |||
# 延迟打包时间,单位毫秒,默认50。此参数对性能有影响,实际环境调整此参数可提高整体性能。 | |||
#system.epoch.delay=50 | |||
``` |
@@ -1,549 +0,0 @@ | |||
# 智能合约 | |||
### 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.6.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. 黑名单调用限制(具体黑名单可查看配置文件),需要注意的是,黑名单分析策略会递归分析类实现的接口和父类,也就是说调用一个实现了指定黑名单接口的类也是不允许的; | |||
目前设置的黑名单如下: | |||
```conf | |||
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; | |||
} | |||
} | |||
``` | |||
**账本数据可见范围**: | |||
`ContractEventContext`中`getUncommittedLedger`方法可访问执行中的未提交区块数据,此方法的合理使用可以解决客户并发调用合约方法涉及数据版本/事件序列冲突的问题。 | |||
```java | |||
/** | |||
* 当前包含未提交区块数据账本查询上下文; | |||
*/ | |||
LedgerQueryService getUncommittedLedger(); | |||
``` | |||
`ContractEventContext`中`getLedger`方法访问的是链上已提交的最新区块数据,不包含未提交交易,所以存在未提交交易中多个合约方法调用操作间数据不可见,导致并发时数据版本等冲突问题。 | |||
```java | |||
/** | |||
* 账本操作上下文; | |||
*/ | |||
LedgerContext getLedger(); | |||
``` | |||
合约方法中对账本的操作通过调用`LedgerContext`中相关方法,可参照[示例合约](https://github.com/blockchain-jd-com/jdchain/tree/master/samples/contract-samples/src/main/java/com/jdchain/samples/contract) | |||
#### 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>7VeRMpXVeTY4cqPogUHeNoZNk86CGAejBh9Xbd5ndFZXNFj3</pubKey> | |||
</ContractAddress> | |||
<!-- 合约部署交易的签名账户;该账户必须具备合约部署的权限; --> | |||
<signer> | |||
<pubKey>7VeRLdGtSz1Y91gjLTqEdnkotzUfaAqdap3xw6fQ1yKHkvVq</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= 7VeRBsHM2nsGwP8b2ufRxz36hhNtSqjKTquzoa4WVKWty5sD \ | |||
-DcontractAddress= LdeNt7sEmTirh9PmE7axKvA2txTrbB9kxz6KB \ | |||
-DsignerPubKey=7VeRLdGtSz1Y91gjLTqEdnkotzUfaAqdap3xw6fQ1yKHkvVq \ | |||
-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>7VeRLdGtSz1Y91gjLTqEdnkotzUfaAqdap3xw6fQ1yKHkvVq</pubKey> | |||
<privKey>177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x</privKey> | |||
<privKeyPwd>DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY</privKeyPwd> | |||
</signer> | |||
</deployment> | |||
</configuration> | |||
</plugin> | |||
``` | |||
### 5. 合约SDK | |||
除上述使用 maven 命令方式部署合约外,JD Chain SDK 提供了 Java 和 Go 语言的合约部署/升级,合约调用等方法。 | |||
以下以 Java SDK 为例讲述主要步骤,完整代码参照[JD Chain Samples](https://github.com/blockchain-jd-com/jdchain/tree/master/samples)合约部分。 | |||
#### 5.1 合约部署 | |||
```java | |||
// 新建交易 | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 生成合约账户 | |||
BlockchainKeypair contractAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("合约地址:" + contractAccount.getAddress()); | |||
// 部署合约 | |||
txTemp.contracts().deploy(contractAccount.getIdentity(), FileUtils.readBytes("src/main/resources/contract-samples-1.4.2.RELEASE.car")); | |||
``` | |||
#### 5.2 合约升级 | |||
```java | |||
// 新建交易 | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 解析合约身份信息 | |||
BlockchainIdentity contractIdentity = new BlockchainIdentityData(KeyGenUtils.decodePubKey("7VeRCfSaoBW3uRuvTqVb26PYTNwvQ1iZ5HBY92YKpEVN7Qht")); | |||
System.out.println("合约地址:" + contractIdentity.getAddress()); | |||
// 指定合约地址,升级合约,如合约地址不存在会创建该合约账户 | |||
txTemp.contracts().deploy(contractIdentity, FileUtils.readBytes("src/main/resources/contract-samples-1.4.2.RELEASE.car")); | |||
``` | |||
#### 5.3 合约调用 | |||
5.3.1 动态代理方式 | |||
基于动态代理方式合约调用,需要依赖合约接口 | |||
```java | |||
// 新建交易 | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 一次交易中可调用多个(多次调用)合约方法 | |||
// 调用合约的 registerUser 方法 | |||
SampleContract sampleContract = txTemp.contract("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye", SampleContract.class); | |||
GenericValueHolder<String> userAddress = ContractReturnValue.decode(sampleContract.registerUser(UUID.randomUUID().toString())); | |||
// 准备交易 | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
// 交易签名 | |||
ptx.sign(adminKey); | |||
// 提交交易 | |||
TransactionResponse response = ptx.commit(); | |||
Assert.assertTrue(response.isSuccess()); | |||
// 获取返回值 | |||
System.out.println(userAddress.get()); | |||
``` | |||
5.3.2 非动态代理方式 | |||
不需要依赖合约接口及实现,传入参数构造合约调用操作 | |||
```java | |||
// 新建交易 | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
ContractEventSendOperationBuilder builder = txTemp.contract(); | |||
// 一次交易中可调用多个(多次调用)合约方法 | |||
// 调用合约的 registerUser 方法,传入合约地址,合约方法名,合约方法参数列表 | |||
builder.send("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye", "registerUser", | |||
new BytesDataList(new TypedValue[]{ | |||
TypedValue.fromText(UUID.randomUUID().toString()) | |||
}) | |||
); | |||
// 准备交易 | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
// 交易签名 | |||
ptx.sign(adminKey); | |||
// 提交交易 | |||
TransactionResponse response = ptx.commit(); | |||
Assert.assertTrue(response.isSuccess()); | |||
Assert.assertEquals(1, response.getOperationResults().length); | |||
// 解析合约方法调用返回值 | |||
for (int i = 0; i < response.getOperationResults().length; i++) { | |||
BytesValue content = response.getOperationResults()[i].getResult(); | |||
switch (content.getType()) { | |||
case TEXT: | |||
System.out.println(content.getBytes().toUTF8String()); | |||
break; | |||
case INT64: | |||
System.out.println(BytesUtils.toLong(content.getBytes().toBytes())); | |||
break; | |||
case BOOLEAN: | |||
System.out.println(BytesUtils.toBoolean(content.getBytes().toBytes()[0])); | |||
break; | |||
default: // byte[], Bytes | |||
System.out.println(content.getBytes().toBase58()); | |||
break; | |||
} | |||
} | |||
``` |
@@ -1,63 +0,0 @@ | |||
## 数据账户 | |||
`JD Chain`存放`KV`数据的数据结构。 | |||
### 1. 基本概念 | |||
可类比传统数据库的表的概念,上层应用需要保存的应用数据最终都应表现为`KV`类型数据写入到数据账户中。 | |||
写入`KV`数据之前,需要创建或使用已存在数据账户。 | |||
一个账本可以创建无限多个数据账户,一个数据账户中可以写入无限多个`KV`数据。 | |||
`KV`数据有`Version`(数据版本)的概念,以`KEY`作为唯一标识,`VALUE`的更新写入需要提供当前该`KEY`的最高版本,`KEY`不存在时为`-1`。 | |||
### 2. SDK | |||
以下只描述主要步骤,完整示例代码可参照[JD Chain Samples](samples.md)数据账户部分。 | |||
#### 2.1 注册数据账户 | |||
创建数据账户: | |||
```java | |||
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("数据账户地址:" + dataAccount.getAddress()); | |||
// 注册数据账户 | |||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||
``` | |||
从已存在数据账户公钥恢复: | |||
```java | |||
PubKey pubKey = KeyGenUtils.decodePubKey("7VeRLdGtSz1Y91gjLTqEdnkotzUfaAqdap3xw6fQ1yKHkvVq"); | |||
BlockchainIdentity dataAccountIdentity = new BlockchainIdentityData(pubKey); | |||
System.out.println("数据账户地址:" + dataAccountIdentity.getAddress()); | |||
// 注册数据账户 | |||
txTemp.dataAccounts().register(dataAccountIdentity); | |||
``` | |||
#### 2.2 写入数据 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
txTemp.dataAccount("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye") | |||
.setText("key1", "value1", -1) | |||
.setText("key1", "value1", 0) | |||
.setInt64("key2", 1, -1) | |||
.setJSON("key3", "{}", -1) | |||
.setBytes("key4", Bytes.fromInt(2), -1); | |||
``` | |||
支持写入数据类型: | |||
- `setText`,字符类型 | |||
- `setInt64`,长整型 | |||
- `setJSON`,`JSON`串 | |||
- `setBytes`,字节数组 | |||
- `setTimestamp`,时间戳,长整型 | |||
- `setXML`,`XML`文本 | |||
- `setImage`,图片字节数据 | |||
> 本质上仅支持`String/Long/[]byte`这三种数据类型,`JSON/XML/Image/Timestamp`等起标识作用,用于扩展差异化数据展示等场景需求 | |||
`setText("key1", "value1", -1)`中第三个参数即为数据版本,需要传入`JD Chain`网络中当前`key1`的最高数据版本,首次写入时传入`-1`。 |
@@ -1,94 +0,0 @@ | |||
## 账户级别权限 | |||
数据账户,事件账户以及合约账户数据权限设计。 | |||
**数据读取完全开放**,本文档讨论全新变更仅对数据写入和合约调用生效。 | |||
### 权限定义 | |||
类似`linux`文件权限,使用用10位数据表示账户数据权限信息: | |||
`0 123 456 789` | |||
- `0`: 数据集或者合约, `-`或`c` | |||
- `123`: 所有者列表, `read(-/r)`, `write(-/w)` 以及 `execute(-/x)` | |||
- `456`: 所属角色, `read(-/r)`, `write(-/w)` 以及 `execute(-/x)` | |||
- `789`: 其他用户, `read(-/r)`, `write(-/w)` 以及 `execute(-/x)` | |||
> 当前实现数据账户仅对`write`权限更新有效;事件账户仅对`write`权限更新有效;合约仅对`execute`权限更新有效。 | |||
### 实现 | |||
权限数据存储与数据集头信息中,`SecurityPolicy`中增加: | |||
```java | |||
// 查询/写入/执行 权限校验 | |||
void checkDataPermission(DataPermission permission, DataPermissionType permissionType) throws LedgerSecurityException; | |||
// 账户创建者校验,只有创建者才能修改数据权限 | |||
void checkDataOwners(DataPermission permission, MultiIDsPolicy midPolicy) throws LedgerSecurityException; | |||
``` | |||
在数据写入/合约方法调用前进行权限校验 | |||
数据账户,事件账户,合约账户均实现`PermissionAccount`接口: | |||
```java | |||
public interface PermissionAccount { | |||
DataPermission getPermission(); | |||
void setPermission(DataPermission permission); | |||
void setModeBits(AccountModeBits modeBits); | |||
void setRole(String role); | |||
} | |||
``` | |||
增加`AccountPermissionSetOperation`账户数据权限设置操作及其处理逻辑 | |||
### SDK | |||
统一使用风格 | |||
#### 数据账户 | |||
```java | |||
txTemp.dataAccount("LdeNrUrMGxkG1R5mDNwrUvkFdRdD91xH1Pcvd") | |||
.permission() // 创建权限修改操作构造器 | |||
.mode(777) // 设置权限值,与 linux chmod 操作类似 | |||
.role("ADMIN"); // 设置账户数据所属角色 | |||
``` | |||
#### 事件账户 | |||
```java | |||
txTemp.eventAccount("LdeNrUrMGxkG1R5mDNwrUvkFdRdD91xH1Pcvd") | |||
.permission() // 创建权限修改操作构造器 | |||
.mode(777) // 设置权限值,与 linux chmod 操作类似 | |||
.role("ADMIN"); // 设置账户数据所属角色 | |||
``` | |||
#### 合约账户 | |||
```java | |||
txTemp.contract("LdeNrUrMGxkG1R5mDNwrUvkFdRdD91xH1Pcvd") | |||
.permission() // 创建权限修改操作构造器 | |||
.mode(777) // 设置权限值,与 linux chmod 操作类似 | |||
.role("ADMIN"); // 设置账户数据所属角色 | |||
``` | |||
### JD Chain Cli | |||
#### 数据账户 | |||
[更新数据账户权限](cli/tx.md#修改数据账户权限) | |||
#### 事件账户 | |||
[更新数据账户权限](cli/tx.md#修改事件账户权限) | |||
#### 合约账户 | |||
[更新合约权限](cli/tx.md#修改合约权限) |
@@ -1,185 +0,0 @@ | |||
## 事件 | |||
`JD Chain`账本中设计了事件数据集,用以存储事件账户,事件数据。 | |||
`事件账户`与`用户`,`数据账户`,`合约账户`是相互独立的,所承载的数据也是相互隔离的。 | |||
`JD Chain`事件分为两类:[系统事件](#系统事件),[用户事件](#用户事件) | |||
`JD Chain SDK` 针对事件数据集开发了[事件发布](#事件发布),[事件监听](#事件监听)实现。 | |||
### 1. 系统事件 | |||
系统运行期间产生的事件,目前仅定义了`新区块产生`这一个: | |||
```java | |||
/** | |||
* 系统事件类型 | |||
*/ | |||
public enum SystemEvent { | |||
// 新区块 | |||
NEW_BLOCK_CREATED("new_block_created"); | |||
} | |||
``` | |||
### 2. 用户事件 | |||
用户自定义事件类型,需要创建`事件账户`,用户自定义事件名 | |||
`事件账户`数量没有限制,一个事件账户内`Topic`(事件名)数量没有限制 | |||
同一个`事件账户`的同一个`Topic`所指向的`Content`(事件内容)有`Sequence`(事件序号)的概念,以`Topic`作为唯一标识,同一个`Topic`的更新发布需要提供当前该`Topic`的最高序号,`Topic`不存在时为`-1`。 | |||
### 3. 事件结构 | |||
```java | |||
/** | |||
* 事件; | |||
* | |||
*/ | |||
@DataContract(code = DataCodes.EVENT_MESSAGE) | |||
public interface Event { | |||
/** | |||
* 事件名; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.TEXT) | |||
String getName(); | |||
/** | |||
* 事件序号; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 2, primitiveType = PrimitiveType.INT64) | |||
long getSequence(); | |||
/** | |||
* 事件内容; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order=3, refContract = true) | |||
BytesValue getContent(); | |||
/** | |||
* 产生事件的交易哈希; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 4, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getTransactionSource(); | |||
/** | |||
* 产生事件的合约地址; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 5, primitiveType = PrimitiveType.TEXT) | |||
String getContractSource(); | |||
/** | |||
* 产生事件的区块高度 | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 6, primitiveType = PrimitiveType.INT64) | |||
long getBlockHeight(); | |||
/** | |||
* 事件账户地址,系统事件此字段为空 | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 7, primitiveType = PrimitiveType.BYTES) | |||
Bytes getEventAccount(); | |||
} | |||
``` | |||
### 4. SDK | |||
`JD Chain`事件监听是`SDK`端以`拉`的方式实现,消息可重复消费,需要使用者自行保存消费位置。 | |||
以下只描述主要步骤,完整示例代码可参照[JD Chain Samples](samples.md)事件相关部分。 | |||
#### 4.1 生成事件账户 | |||
> 用户事件才有事件账户 | |||
```java | |||
// 新建交易 | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 生成事件账户 | |||
BlockchainKeypair eventAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("事件账户地址:" + eventAccount.getAddress()); | |||
// 注册事件账户 | |||
txTemp.eventAccounts().register(eventAccount.getIdentity()); | |||
``` | |||
#### 4.2 事件发布 | |||
> 系统事件由系统运行期间自动产生,用户事件可通过SDK发布 | |||
```java | |||
// 新建交易 | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
txTemp.eventAccount(Bytes.fromBase58("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye")) | |||
.publish("topic1", "content1", -1) | |||
.publish("topic1", "content2", 0) | |||
.publish("topic1", "content3", 1) | |||
.publish("topic2", "content", -1) | |||
.publish("topic3", 1, -1) | |||
.publish("topic4", Bytes.fromInt(1), -1); | |||
``` | |||
支持发布数据类型: | |||
- `Text`,字符类型 | |||
- `Int64`,长整型 | |||
- `JSON`,`JSON`串 | |||
- `Bytes`,字节数组 | |||
- `Timestamp`,时间戳,长整型 | |||
- `XML`,`XML`文本 | |||
- `Image`,图片字节数据 | |||
> 本质上仅支持`String/Long/[]byte`这三种数据类型,`JSON/XML/Image/Timestamp`等起标识作用,用于扩展差异化数据展示等场景需求 | |||
`publish("topic1", "content1", -1)`中第三个参数即为事件序号,需要传入`JD Chain`网络中当前`topic1`的最高序号,首次写入时传入`-1`。 | |||
#### 4.3 事件监听 | |||
- 监听系统事件 | |||
```java | |||
// 目前仅有新区快产生事件 | |||
blockchainService.monitorSystemEvent(ledger, | |||
SystemEvent.NEW_BLOCK_CREATED, 0, (eventMessages, eventContext) -> { | |||
for (Event eventMessage : eventMessages) { | |||
// content中存放的是当前链上最新高度 | |||
System.out.println("New block:" + eventMessage.getSequence() + ":" + BytesUtils.toLong(eventMessage.getContent().getBytes().toBytes())); | |||
} | |||
}); | |||
``` | |||
- 监听用户事件: | |||
```java | |||
blockchainService.monitorUserEvent(ledger, "LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye", "sample-event", 0, (eventMessage, eventContext) -> { | |||
BytesValue content = eventMessage.getContent(); | |||
switch (content.getType()) { | |||
case TEXT: | |||
case XML: | |||
case JSON: | |||
System.out.println(eventMessage.getName() + ":" + eventMessage.getSequence() + ":" + content.getBytes().toUTF8String()); | |||
break; | |||
case INT64: | |||
case TIMESTAMP: | |||
System.out.println(eventMessage.getName() + ":" + eventMessage.getSequence() + ":" + BytesUtils.toLong(content.getBytes().toBytes())); | |||
break; | |||
default: // byte[], Bytes | |||
System.out.println(eventMessage.getName() + ":" + eventMessage.getSequence() + ":" + new String(content.getBytes().toBytes())); | |||
break; | |||
} | |||
}); | |||
``` |
@@ -1,57 +0,0 @@ | |||
## 网关服务 | |||
`JD Chain`的网关服务是应用的接入层。 | |||
终端接入是`JD Chain`网关的基本功能,在确认终端身份的同时提供连接节点、转发消息和隔离共识节点与客户端等服务。网关确认客户端的合法身份,接收并验证交易;网关根据初始配置文件与对应的共识节点建立连接,并转发交易数据。 | |||
### 1. 配置 | |||
网关配置在文件`gateway.conf`中: | |||
```properties | |||
#网关的HTTP服务地址; | |||
http.host=0.0.0.0 | |||
#网关的HTTP服务端口; | |||
http.port=8080 | |||
#网关的HTTP服务上下文路径,可选; | |||
#http.context-path= | |||
#共识节点的服务地址(与该网关节点连接的Peer节点的IP地址); | |||
peer.host=127.0.0.1 | |||
#共识节点的服务端口(与该网关节点连接的Peer节点的端口,即在Peer节点的peer-startup.sh中定义的端口); | |||
peer.port=7080 | |||
#共识节点的服务是否启用安全证书; | |||
peer.secure=false | |||
#账本节点拓扑信息落盘,默认false。配置为true后重启网关节点时将直接使用存储中的拓扑连接; | |||
topology.store=false | |||
#是否开启动态感知,默认true,即根据初始共识节点获取整个共识网络拓扑连接,网关保持对所有共识节点连接,发送交易和查询会自动选择区块高度最新的节点; | |||
topology.aware=true | |||
#共识节点的服务提供解析器 | |||
#BftSmart共识Provider:com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
#简单消息共识Provider:com.jd.blockchain.consensus.mq.MsgQueueConsensusProvider | |||
peer.providers=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
#数据检索服务对应URL,格式:http://{ip}:{port},例如:http://127.0.0.1:10001 | |||
#若该值不配置或配置不正确,则浏览器模糊查询部分无法正常显示 | |||
data.retrieval.url= | |||
schema.retrieval.url= | |||
#默认公钥的内容(Base58编码数据),非CA模式下必填; | |||
keys.default.pubkey= | |||
#默认网关证书路径(X509,PEM),CA模式下必填; | |||
keys.default.ca-path= | |||
#默认私钥的路径;在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey-path= | |||
#默认私钥的内容;在 pk-path 和 pk 之间必须设置其一; | |||
keys.default.privkey= | |||
#默认私钥的解码密码; | |||
keys.default.privkey-password= | |||
``` | |||
其中: | |||
- `data.retrieval.url`与`schema.retrieval.url`分别与[Argus(高级检索)](https://github.com/blockchain-jd-com/jdchain-indexer)中的[区块链基础数据检索服务](https://github.com/blockchain-jd-com/jdchain-indexer#%E5%90%AF%E5%8A%A8%E5%8C%BA%E5%9D%97%E9%93%BE%E5%9F%BA%E7%A1%80%E6%95%B0%E6%8D%AE%E7%B4%A2%E5%BC%95%E6%A3%80%E7%B4%A2%E6%9C%8D%E5%8A%A1)和[`Schema`服务](https://github.com/blockchain-jd-com/jdchain-indexer#%E5%90%AF%E5%8A%A8value%E7%B4%A2%E5%BC%95%E6%9C%8D%E5%8A%A1)相对应,只有启动并正确配置了`Argus`,`JD Chain`浏览器才能正常使用搜索功能。 | |||
- `keys.default`几个参数代表接入`JD Chain`网络的身份信息,只有处于非停用(`DEACTIVATED`)状态的参与方身份才可作为此处的配置。 |
@@ -1,15 +0,0 @@ | |||
# 高级检索 | |||
## Argus | |||
穿透式检索Argus提供JD Chain区块链基础数据索引,自定义键值索引服务 | |||
源码已开源,请参照[Argus](https://github.com/blockchain-jd-com/jdchain-indexer)首页说明安装使用。 | |||
## JD Chain + Argus | |||
修改JD Chain网关配置文件`gateway.conf`中: | |||
- `data.retrieval.url` 对应`Argus`中区块链基础数据检索服务 | |||
- `schema.retrieval.url` 对应`Argus`中`Schema`服务 | |||
重启网关后可在区块连浏览器中使用搜索功能 |
@@ -1,32 +0,0 @@ | |||
## JD Chain Cli | |||
JD Chain 命令行工具集,提供密钥管理,实时交易,链上信息查询,离线交易,共识节点变更等操作。 | |||
```bash | |||
:bin$ ./jdchain-cli.sh -h | |||
Usage: jdchain-cli [-hV] [--pretty] [--home=<path>] [COMMAND] | |||
JDChain Cli is a convenient tool to manage jdchain keys, sign and send | |||
transactions to jdchain network, query data from jdchain network. | |||
-h, --help Show this help message and exit. | |||
--home=<path> Set the home directory. | |||
--pretty Pretty json print | |||
-V, --version Print version information and exit. | |||
Commands: | |||
The most commonly used commands are: | |||
keys List, create, update or delete keypairs. | |||
ca List, create, update certificates. | |||
tx Build, sign or send transaction. | |||
query Query commands. | |||
participant Add, update or delete participant. | |||
help Displays help information about the specified command | |||
See 'jdchain-cli help <command>' to read about a specific subcommand or concept. | |||
``` | |||
- `keys` [密钥管理](cli/keys.md) | |||
- `tx` [交易](cli/tx.md) | |||
- `ca` [证书](cli/ca.md) | |||
- `query` [链上信息查询](cli/query.md) | |||
- `participant` [共识节点变更](cli/participant.md) |
@@ -1,365 +0,0 @@ | |||
## RocksDB as a server | |||
### 1.简介 | |||
`KVDB`是一个简单的`NoSQL`数据库,支持简单的“键值”读写操作。 | |||
`KVDB`包装了`RocksDB`作为数据库引擎,实现了单机部署和集群部署。 | |||
`KVDB`的集群是一个分布式的分片服务集群,每个分片节点是一个`KVDB`的数据库服务实例,采用对等模式部署,没有主节点。 | |||
### 2.安装 | |||
> java 版本>= 1.8 | |||
下载源代码,执行: | |||
```bash | |||
mvn clean package | |||
``` | |||
`kvdb-server`模块`target`下会生成`kvdb-***.zip`,解压缩安装包,结构如下: | |||
```bash | |||
bin # 脚本文件目录 | |||
start # 启动本机的数据库服务的脚本文件 | |||
stop # 停止本机的数据库服务的脚本文件 | |||
kvdb-cli # 连接本机的数据库服务控制台的脚本文件 | |||
kvdb-benchmark # 基准测试 | |||
conf # 配置文件目录 | |||
kvdb.conf # 本机的数据库服务配置 | |||
cluster.conf # 数据库集群的配置 | |||
libs # 数据库服务的代码库目录 | |||
system # 系统数据目录 | |||
dblist # 文件记录了本数据库服务实例装载的数据库列表 | |||
pid # 文件记录了本数据库服务实例的运行进程ID;当服务实例运行时创建,服务实例退出后删除 | |||
``` | |||
### 3.部署配置 | |||
#### 3.1 `kvdb.conf` | |||
```bash | |||
# 数据库服务的本机监听地址; | |||
server.host=0.0.0.0 | |||
# 数据库服务的本机监听端口; | |||
server.port=7078 | |||
# 管理控制台的端口; | |||
# 注:管理控制台总是绑定到环回地址 127.0.0.1,只允许本机访问; | |||
manager.port=7060 | |||
# 数据库实例默认的根目录 | |||
dbs.rootdir=/usr/kvdb/dbs | |||
# 数据库实例默认的本地分区数 | |||
dbs.partitions=4 | |||
# 是否禁用WAL | |||
wal.disable=false | |||
# WAL刷新机制,-1跟随系统,0实时刷新,n(n>0)秒刷新一次 | |||
wal.flush=1 | |||
``` | |||
> `WAL`写入成功,但`RocksDB`写入失败,服务器将在后续所有数据库读写操作前根据`WAL`进行重试操作,直至成功。 | |||
#### 3.2 `cluster.conf` | |||
```bash | |||
# 数据库集群的分片数,每一个分片都赋予唯一的编号,分片编号最小为 0,所有分片的编号必须连续递增 | |||
# ‘<name>’表示集群名称,只允许用字母、数字、下划线组成; | |||
cluster.<name>.partitions=3 | |||
# 数据库集群 ‘<name>’ 的第 1 个分片的数据库实例地址(URL格式); | |||
cluster.<name>.0=kvdb://<host>:<port>/<dbname> | |||
# 数据库集群 ‘<name>’ 的第 2 个分片的数据库实例地址(URL格式); | |||
cluster.<name>.1=kvdb://<host>:<port>/<dbname> | |||
# 数据库集群 ‘<name>’ 的第 3 个分片的数据库实例地址(URL格式); | |||
cluster.<name>.2=kvdb://<host>:<port>/<dbname> | |||
# 指定多个不同的集群 | |||
#cluster.<name1>.partitions=3 | |||
... | |||
``` | |||
> 一个数据库实例只能加入唯一的集群;一旦加入集群,则不能再被客户端以单库连接方式访问,对该库实例的连接自动转为集群连接。集群中配置的数据库必须在`dblist`中已存在且设置为`true` | |||
> 所有`host`使用明确的可通畅连接的地址,不同服务器节点中同一集群配置必须完全一致 | |||
#### 3.3 `dblist` | |||
```bash | |||
# 是否激活数据库 ‘<name>’ ;如果为`true`,则创建或加载该数据库并提供访问; | |||
# ‘<name>’表示数据库名称,只允许用字母、数字、下划线组成; | |||
# db.<name>.enable=true | |||
# 数据库 <name> 的根目录;如果未配置,则从默认目录加载(由`conf/kvdb.conf`指定了默认配置${dbs.rootdir}/<name>); | |||
# db.test.rootdir= | |||
# 数据库 <name> 的本地分区数;如果未配置,则采用`conf/kvdb.conf`中指定的默认配置${dbs.partitions} | |||
# db.test.partitions= | |||
``` | |||
> `dblist`中配置的数据库会在`kvdb-server`启动时自动创建。在`kvdb-server`启动完成后由管理工具执行`create database <name>`创建数据库,创建成功的数据库会追加到`dblist`中。使用`kvdb-cli`命令行工具执行数据库实例更新操作。 | |||
#### 3.4 日志 | |||
1. `kvdb-server` | |||
修改`bin`目录下,`start.sh`文件: | |||
```bash | |||
LOG_SET="-Dlogging.path="$HOME/logs" -Dlogging.level=ERROR" | |||
``` | |||
默认日志路径:程序解压缩后主目录下`logs`目录 | |||
默认日志等级:`ERROR` | |||
可配置日志等级:`ALL`,`TRACE`,`DEBUG`,`INFO`,`WARN`,`ERROR`,`FATAL`,`OFF` | |||
2. `kvdb-cli` | |||
修改`bin`目录下,`kvdb-cli.sh`文件: | |||
```bash | |||
LOG_SET="-Dlogging.path="$HOME/logs" -Dlogging.level.root=error" | |||
``` | |||
默认日志路径:程序解压缩后主目录下`logs`目录 | |||
默认日志等级:`error` | |||
#### 3.5 启动 | |||
```bash | |||
./start.sh | |||
``` | |||
#### 停止 | |||
```bash | |||
./stop.sh | |||
``` | |||
### 4.SDK | |||
1. 依赖 | |||
```maven | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>kvdb-client</artifactId> | |||
<version>${project.version}</version> | |||
</dependency> | |||
``` | |||
2. 创建客户端连接 | |||
```java | |||
// `host`、`port`为服务器地址和端口,`database`为数据库名称 | |||
KVDBClient client = new KVDBClient("kvdb://<host>:<port>/<database>"); | |||
# KVDBClient client = new KVDBClient(new KVDBURI("kvdb://<host>:<port>/<database>")); | |||
# KVDBClient client = new KVDBClient(new ClientConfig(host, port, db)); | |||
``` | |||
> 对于集群的连接,客户端将开启`WAL`,在`WAL`写入成功,部分服务器失败的情况下,客户端在执行后续所有操作前都将根据`WAL`日志执行重试操作,直至成功。 | |||
3. 操作 | |||
```java | |||
/** | |||
* 存在性查询 | |||
* | |||
* @param key | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean exists(Bytes key) throws KVDBException; | |||
/** | |||
* 存在性查询,支持多个键值 | |||
* | |||
* @param keys | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean[] exists(Bytes... keys) throws KVDBException; | |||
/** | |||
* 键值获取 | |||
* | |||
* @param key | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
Bytes get(Bytes key) throws KVDBException; | |||
/** | |||
* 键值获取,支持多个键值 | |||
* | |||
* @param keys | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
Bytes[] get(Bytes... keys) throws KVDBException; | |||
/** | |||
* 设置键值对 | |||
* | |||
* @param key | |||
* @param value | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean put(Bytes key, Bytes value) throws KVDBException; | |||
/** | |||
* 设置键值对 | |||
* | |||
* @param key | |||
* @param value | |||
* @param aSync | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean put(Bytes key, Bytes value, boolean aSync) throws KVDBException; | |||
/** | |||
* 开启批处理 | |||
* | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean batchBegin() throws KVDBException; | |||
/** | |||
* 取消批处理 | |||
* | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean batchAbort() throws KVDBException; | |||
/** | |||
* 提交批处理,服务器掉线重连后会丢失未提交批处理数据 | |||
* <p> | |||
* 未提交的`batch`对其他客户端连接不可见。 | |||
* | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean batchCommit() throws KVDBException; | |||
/** | |||
* 提交批处理 | |||
* | |||
* @param size 此次批处理操作去重后的key数量 | |||
* @return | |||
* @throws KVDBException | |||
*/ | |||
boolean batchCommit(long size) throws KVDBException; | |||
/** | |||
* 关闭 | |||
*/ | |||
void close(); | |||
``` | |||
### 5.管理工具 | |||
`kvdb-cli`是基于[`SDK`](#SDK)的命令行工具实现: | |||
```bash | |||
./kvdb-cli.sh -h <kvdb server host> -p <kvdb server port> -db <database> -t <time out in milliseconds> -bs <buffer size> -k <keep alive> | |||
``` | |||
参数说明: | |||
- `-h` 服务器地址。选填,默认`localhost` | |||
- `-p` 管理端口。选填,默认`7060` | |||
- `-db` 数据库。选填 | |||
- `-t` 超时时间,毫秒。选填,默认`60000 ms` | |||
- `-bs` 发送/接收缓冲区大小。选填,默认`1048576` | |||
- `-k` 保持连接。选填,默认`true` | |||
所有支持指令操作: | |||
```bash | |||
localhost:7060>help | |||
AVAILABLE COMMANDS | |||
Built-In Commands | |||
clear: Clear the shell screen. | |||
exit, quit: Exit the shell. | |||
help: Display help about available commands. | |||
stacktrace: Display the full stacktrace of the last error. | |||
KVDB Commands | |||
batch abort: 取消批处理 | |||
batch begin: 开启批处理 | |||
batch commit: 提交批处理 | |||
cluster info: 服务器集群配置信息 | |||
create database: 创建数据库实例,仅在当前连接的`kvdb`服务器创建数据库实例,集群数据库创建只能通过修改`cluster.conf`进行配置。 | |||
disable database: 关闭数据库实例,加入集群的实例不可修改 | |||
drop database: 删除数据库实例,加入集群的实例不可修改 | |||
enable database: 开放数据库实例,加入集群的实例不可修改 | |||
exists: 检查存在性 | |||
get: 获取键值 | |||
put, set: 设置键值 | |||
show databases: 展示数据库实例列表 | |||
status: 当前数据库信息 | |||
use: 切换数据库 | |||
``` | |||
指令帮助(以`put`为例): | |||
```bash | |||
localhost:7060>help put | |||
NAME | |||
put - Set a key-value | |||
SYNOPSYS | |||
put [--key] string [--value] string | |||
OPTIONS | |||
--key string | |||
[Mandatory] | |||
--value string | |||
[Mandatory] | |||
ALSO KNOWN AS | |||
set | |||
``` | |||
示例(可执行如下指令插入数据): | |||
```bash | |||
localhost:7060>put k v | |||
localhost:7060>set k v | |||
localhost:7060>put --key k --value v | |||
localhost:7060>set --key k --value v | |||
``` | |||
### 6.Benchmark | |||
`kvdb-sever`性能测试工具,简单的数据插入测试。 | |||
```bash | |||
./kvdb-benchmark.sh -h <kvdb server host> -p <kvdb server port> -db <database> -c <time out in milliseconds> -n <request times> -b <buffer size> -k <keep alive> | |||
``` | |||
参数说明: | |||
- `-h` 服务器地址。选填,默认`localhost` | |||
- `-p` 端口。选填,默认`7078` | |||
- `-db` 数据库。必填 | |||
- `-c` 客户端数量。选填,默认`20` | |||
- `-n` 请求数量。选填,默认`100000` | |||
- `-ds` 键/值字节数。选填,默认`16` | |||
- `-b` 是否使用批处理。选填,默认`false` | |||
- `-bs` 一次批处理键值对数。选填,默认`100` | |||
- `-k` 保持连接。选填,默认`true` | |||
示例: | |||
```bash | |||
./kvdb-benchmark.sh -db test1 -c 1 -n 4000000 -b true -bs 1000 -ds 8 | |||
requests:4000000, clients:1, batch:true, batch_size:1000, kv_data_size:8bytes, times:54014ms, tps:74054.874662 | |||
``` | |||
其中: | |||
- `requests` 请求数量 | |||
- `clients` 并发数 | |||
- `batch` 是否开启批量模式 | |||
- `batch_size` 一次批处理键值对数 | |||
- `kv_data_size` 键/值字节数 | |||
- `times` 总耗时 | |||
- `tps` TPS | |||
可根据`tps`值,调整`kvdb.conf`中`dbs.partitions`数值,以寻找最优服务器配置。 |
@@ -1,19 +0,0 @@ | |||
## 日志 | |||
`JD Chain`使用`Log4j2`配置日志输出 | |||
### Gateway | |||
网关日志默认配置文件为`config/log4j2-gw.xml`,可修改启动脚本(`bin/startup.sh`)中`-Djdchain.log=$APP_HOME/logs`和`-Dlogging.config=file:$APP_HOME/config/log4j2-gw.xml`两个选项修改日志配置。 | |||
启动过程中会有少量控制台输出`bin/peer.out`,注意观察启动时是否有错误信息。 | |||
运行中日志根据默认配置会输出到`logs`目录中。 | |||
### Peer | |||
节点日志默认配置文件为`config/log4j2-peer.xml`,可修改启动脚本(`bin/peer-startup.sh`)中`-Djdchain.log=$APP_HOME/logs`和`-Dlogging.config=file:$APP_HOME/config/log4j2-peer.xml`两个选项修改日志配置。 | |||
启动过程中会有少量控制台输出`bin/peer.out`,注意观察启动时是否有错误信息。 | |||
运行中日志根据默认配置会输出到`logs`目录中。 |
@@ -1,253 +0,0 @@ | |||
## 共识节点变更 | |||
借助`BFT-SMaRT`共识提供的`Reconfig`操作元语,`JD Chain`实现了在不停机的情况下快速更新共识网络拓扑,实现[添加共识节点](#1添加共识节点),[移除共识节点](#2移除共识节点),[更新共识信息](#3更新共识信息) 等功能。 | |||
**共识节点相关操作错误极容易导致整个网络不可用,甚至无法恢复,操作前请做好数据备份,务必谨慎操作,确保所有环境和指令正确** | |||
以下操作说明均以在部署好的如下单机四节点环境操作为例: | |||
- `ledger` `j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP` | |||
- `peer0` 启动端口 `7080`,共识端口`10080` | |||
- `peer1` 启动端口 `7081`,共识端口`10081` | |||
- `peer2` 启动端口 `7082`,共识端口`10082` | |||
- `peer3` 启动端口 `7083`,共识端口`10083` | |||
- `网关` 服务端口 `8080` | |||
### 1.添加共识节点 | |||
#### 1.1 生成身份信息 | |||
解压`peer`的`zip`包作为新的参与方节点`peer4`。使用`bin`目录下`keygen.sh`脚本生成公私钥信息: | |||
```bash | |||
$ ./keygen.sh -n new-node | |||
# 输入私钥密码 | |||
Input password: | |||
# 是否保存Base58编码后的私钥密码信息 | |||
Do you want to save encode password to file? Please input y or n ...y | |||
``` | |||
执行完成后会在`peer4`中`config/keys`目录下生成`new-node.priv`/`new-node.pub`/`new-node.pwd`文件,分别保存公钥/私钥/私钥密码信息,用作新增节点的身份信息。 | |||
#### 1.2 注册新节点 | |||
`peer4`中`bin`目录下提供了`reg-parti.sh`注册参与方脚本: | |||
```bash | |||
./reg-parti.sh -ledger <账本HASH> -pub <新节点公钥> -priv <新节点私钥> -pass <新节点私钥密码> -name <新节点名称> -existpub <链上已存在用户公钥> -existpriv <链上已存在用户私钥> -existpass <链上已存在用户私钥密码> -host <网关IP> -port <网关端口> | |||
``` | |||
参数: | |||
- `ledger`,指定要注册新参与方的 用户手册账本,`Base58`编码的字符串,必填 | |||
- `pub`,步骤1中产生的用户公钥信息,`Base58`编码的字符串,必填 | |||
- `priv`,步骤1中产生的用户私钥信息,`Base58`编码的字符串,必填 | |||
- `pass`,步骤1中产生的用户私钥密码, `Base58`编码的字符串,必填 | |||
- `name`,新参与方的名字,字符串,必填 | |||
- `existpub`,账本中已注册的任意用户的公钥信息,`Base58`编码的字符串,必填 | |||
- `existpriv`,账本中已注册的任意用户的私钥信息,`Base58`编码的字符串,用于对客户端提交的交易进行终端签名,必填 | |||
- `existpass`,账本中已注册的任意用户的私钥密码信息,`Base58`编码的字符串,必填 | |||
- `host`,客户端接入网关`IP`,必填 | |||
- `port`,客户端接入网关`Port`,必填 | |||
- `debug`,开启`DEBUG`模式 ,默认为`false`,可选 | |||
示例: | |||
```bash | |||
./reg-parti.sh -ledger j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP -pub 3snPdw7i7PjLAfhTx22uU4C9oGRFpHAcFkt3wHNVSuQgodE616H45H -priv 177gjyyCaYGiTvjRQbxAZwUDc8SjriRwgWwAwZLuzCvuixJ9AbcZx2FWTMP1XoUigRqUJmD -pass 8EjkXVSTxMFjCvNNsTo8RBMDEVQmk7gYkW4SCDuvdsBG -name 4 -existpub 3snPdw7i7PjWHy3hV5yw7JqMKfNqZdotSNzqJJHpnKmLCeJMd383jd -existpriv 177gjzjdwHqADjfC6yLqECZcEK8HPM8GAvjsQZrDiPNyLUGgqQPQ8zqjFBcV6TsKitmfFFV -existpass 8EjkXVSTxMFjCvNNsTo8RBMDEVQmk7gYkW4SCDuvdsBG -host 127.0.0.1 -port 8080 | |||
``` | |||
执行成功后将打印成功信息,并显示`peer4`的用户地址信息。 | |||
#### 1.3 确定复制节点 | |||
查询每个共识节点的账本信息: | |||
```bash | |||
curl http://<ip>:<port>/ledgers/<ledgerHash> | |||
``` | |||
> 其中`ip`和`port`为各个`peer`的`IP`地址和启动端口 | |||
选出具有最新区块数据的共识节点作为复制节点。 | |||
#### 1.4 构建新节点 | |||
修改`peer4`目录下以下文件: | |||
1. `config/ledger-binding.conf` | |||
```bash | |||
# Base58编码的账本哈希 | |||
ledger.bindings=<账本hash> | |||
# 账本名字,与账本相关的其他其他peer保持一致 | |||
binding.<账本HASH>.name=<节点名称> | |||
# peer4的名名称,与[向现有共识网络注册新的参与方]操作中保持一致 | |||
binding.<账本hash>.parti.name=<节点名称> | |||
# peer4的用户地址 | |||
binding.<账本hash>.parti.address=<peer4的用户地址> | |||
# 新参与方base58编码的私钥,与[向现有共识网络注册新的参与方]操作中保持一致 | |||
binding.<账本hash>.parti.pk=<新参与方base58编码的私钥> | |||
# 新参与方base58编码的私钥读取密码,与[向现有共识网络注册新的参与方]操作中保持一致 | |||
binding.<账本hash>.parti.pwd=<新参与方base58编码的私钥读取密码> | |||
# 新参与方对应的账本数据库连接uri,即peer4的账本数据库存放位置,参照其他peer修改,不可与其他peer混用 | |||
binding.<账本hash>.db.uri=<账本数据库连接> | |||
``` | |||
2. `bin/peer-startup.sh` | |||
对于脚本中`PROC_INFO`变量的`-p` 参数修改为`peer4`的`http`启动端口 | |||
3. 账本数据复制 | |||
步骤[确定复制节点](#确定复制节点)中选出了复制节点,以`peer0`为例,从`peer0`的`ledger-binding.conf`文件可知`peer0`的账本数据库存放地址,如:`rocksdb:///home/jdchain/peer0/rocksdb`,复制此目录所有数据到`peer4` `config/ledger-binding.conf`中`binding.<账本hash>.db.uri`指定的位置 | |||
> **除了数据库目录,切不可将其他节点所有内容直接复制使用**,因为像`runtime`等目录中会保存节点差异化的相关运行数据。 | |||
#### 1.5 启动新节点 | |||
> **一定注意在启动新参与方节点进程之前确保完成了账本数据库的复制工作** | |||
执行`peer4`中`bin`目录下`peer-startup.sh`脚本启动启动新参与方`peer4`节点进程: | |||
```bash | |||
./peer-startup.sh | |||
``` | |||
#### 1.6 激活新节点 | |||
> **在进行新参与方节点的激活操作时,要求暂停向共识网络中发起新的业务数据** | |||
`peer`包中`bin`目录下提供了`active-parti.sh`激活参与方脚本: | |||
```bash | |||
./active-parti.sh -ledger <账本哈希> -httphost <新节点IP> -httpport <新节点启动端口> -consensushost <新节点共识IP> -consensusport <新节点共识端口> -synchost <数据同步节点IP> -syncport <数据同步节点端口> -debug -shutdown | |||
``` | |||
参数: | |||
- `ledger`,指定要注册新参与方的账本,`Base58`编码的字符串,必填 | |||
- `httphost`,新参与方`IP`地址,必填 | |||
- `httpport`,新参与方启动端口,必填 | |||
- `consensushost`,新参与方的共识`IP`地址,必填(**务必确保该端口未被占用**) | |||
- `synchost`,新参与方进行账本数据复制的节点`IP`地址,必填 | |||
- `syncport`, 新参与方进行账本数据复制的节点的启动端口,,必填 | |||
- `debug`,开启`DEBUG`模式 ,默认为`false`,可选 | |||
示例: | |||
```bash | |||
./active-parti.sh -ledger j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP -httphost 127.0.0.1 -httpport 7084 -consensushost 127.0.0.1 -consensusport 10088 -synchost 127.0.0.1 -syncport 7080 | |||
``` | |||
#### 1.7 查询节点状态 | |||
查询网关接口,获取节点状态信息: | |||
```bash | |||
curl http://<网关ip>:<网关port>/ledgers/<账本hash>/participants | |||
``` | |||
节点状态为`CONSENSUS`,说明添加成功。 | |||
### 2.移除共识节点 | |||
> **在多于4个共识节点的共识网络中,才允许进行节点的移除操作** | |||
> **在进行节点的移除操作时,要求暂停向共识网络中发起新的业务数据上链请求** | |||
#### 2.1 确定复制节点 | |||
查询每个共识节点的账本信息: | |||
```bash | |||
curl http://<ip>:<port>/ledgers/<ledgerHash> | |||
``` | |||
> 其中`ip`和`port`为各个`peer`的`IP`地址和启动端口 | |||
选出具有最新区块数据的共识节点作为复制节点。 | |||
#### 2.2 移除节点 | |||
`peer`包中`bin`目录下提供了`deactive-parti.sh`移除节点脚本: | |||
```bash | |||
./reg-parti.sh -ledger <账本HASH> -participantAddress <待移除节点地址> -httphost <待移除节点的http启动IP地址> -httpport <待移除节点的http启动Port> -synchost <数据同步节点IP> -syncport <数据同步节点端口> -debug | |||
``` | |||
参数: | |||
- `ledger`,指定要注册新参与方的账本,`Base58`编码的字符串,必填 | |||
- `participantAddress`, 要移除的参与方用户地址,`Base58`编码的字符串,必填; | |||
- `httphost`,待移除参与方`IP`地址,必填 | |||
- `httpport`,待移除参与方启动端口,必填 | |||
- `synchost`,待移除参与方进行账本数据复制的节点`IP`地址,必填 | |||
- `syncport`,待移除参与方进行账本数据复制的节点的启动端口, 必填 | |||
- `debug`,开启`DEBUG`模式 ,默认为`false`,可选 | |||
示例: | |||
```bash | |||
./deactive-parti.sh -ledger j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP -participantAddress LdeNnjhSDx9nxE5hoyzzbU7paWQhP4MAfpYiC -httphost 127.0.0.1 -httpport 7084 -synchost 127.0.0.1 -syncport 7080 | |||
``` | |||
执行此操作可将`peer4`从该共识网络中移除,不再参与共识服务。 | |||
#### 2.3 查询节点状态 | |||
查询网关接口,获取参与方状态信息: | |||
```bash | |||
curl http://<网关ip>:<网关port>/ledgers/<账本hash>/participants | |||
``` | |||
参与方状态为非`CONSENSUS`,说明操作成功。 | |||
### 3.更新共识信息 | |||
通过[激活节点](#6. 激活新节点)操作除了激活新增的节点外,还可以动态修改已经处于激活状态的共识节点的`IP`和`共识端口`信息,从而实现本机的共识端口变更,不同机器之间进行`账本迁移`。 | |||
> **在进行节点信息变更时,要求暂停向共识网络中发起新的业务数据上链请求** | |||
#### 3.1 变更共识端口 | |||
> **操作前请确保变更到的端口未被占用** | |||
如将`peer1`共识端口由`10082`修改为`10182`,操作指令如下: | |||
```bash | |||
./active-parti.sh -ledger j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP -httphost 127.0.0.1 -httpport 7081 -consensushost 127.0.0.1 -consensusport 10182 -synchost 127.0.0.1 -syncport 7080 | |||
``` | |||
指令成功执行后,`peer1`的共识端口将自动变更为`10182`。 | |||
#### 3.2 账本迁移 | |||
账本迁移指将一台机器(`IP`)上的共识节点迁移到另一台机器(`IP`)上,主要操作流程如下: | |||
> **操作前请确保变更到的端口未被占用** | |||
1. 修改共识信息 | |||
如将`peer2`中账本`j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP`的共识`IP`由`127.0.0.1`修改为`192.168.1.100`(另一台机器),操作指令如下: | |||
```bash | |||
./active-parti.sh -ledger j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP -httphost 127.0.0.1 -httpport 7082 -consensushost 192.168.1.100 -consensusport 10084 -synchost 127.0.0.1 -syncport 7080 -shutdown | |||
``` | |||
**特别注意**:`-shutdown`为必填选项,否则将导致整个网络需要重启。 | |||
指令成功执行后,`127.0.0.1`上账本`j5m4yF1uyxaMwwBWKaqJqyHkKViXs8LGe9ChWvPs1CqdjP`节点将不再参与此账本的共识服务。 | |||
2. 迁移节点数据 | |||
拷贝步骤1中`127.0.0.1`上与移出账本相关的所有数据(包括`rocksdb`,`runtime`等等)到`191.168.1.100`上一致的部署目录。 | |||
请修改`127.0.0.1`上`peer2/config/ledger-binding.conf`文件,去除移出的账本配置。若此节点上仅此一个账本,可关闭本节点进程。 | |||
修改`191.168.1.100`上`peer2/config/ledger-binding.conf`文件,保留移出的账本配置。 | |||
3. 启动节点 | |||
在`191.168.1.100`上执行`peer2/bin`目录下`peer-startup.sh`脚本启动节点。 | |||
查询网关接口: | |||
```bash | |||
curl http://<网关ip>:<网关port>/ledgers/<账本hash>/settings | |||
``` | |||
查看账本各共识节点的相关信息。 |
@@ -1,56 +0,0 @@ | |||
### JD Chain 源代码库 | |||
#### project | |||
- URL:git@github.com:blockchain-jd-com/jdchain-project.git | |||
- 说明:公共的父项目,定义公共的依赖 | |||
#### framework | |||
- URL:git@github.com:blockchain-jd-com/jdchain-framework.git | |||
- 说明:框架源码库,定义公共数据类型、框架、模块组件接口、`SDK`、`SPI`、工具 | |||
#### core | |||
- URL:git@github.com:blockchain-jd-com/jdchain-core.git | |||
- 说明:模块组件实现的源码库 | |||
#### explorer | |||
- URL:git@github.com:blockchain-jd-com/explorer.git | |||
- 说明:相关产品的前端模块的源码库 | |||
#### bft-smart | |||
- URL:git@github.com:blockchain-jd-com/bftsmart.git | |||
- 说明:`BFT-SMaRt` 共识算法的源码库 | |||
#### explorer | |||
- URL:git@github.com:blockchain-jd-com/explorer.git | |||
- 说明:区块链浏览器/管理工具前端代码 | |||
#### binary-proto | |||
- URL:git@github.com:blockchain-jd-com/binary-proto.git | |||
- 说明:自研序列化/反序列化框架 | |||
#### utils | |||
- URL:git@github.com:blockchain-jd-com/utils.git | |||
- 说明:工具类库 | |||
#### httpservice | |||
- URL:git@github.com:blockchain-jd-com/httpservice.git | |||
- 说明:`HTTP` `RPC` 服务框架 | |||
#### kvdb | |||
- URL:git@github.com:blockchain-jd-com/kvdb.git | |||
- 说明:基于`RocksDB` `JNI`方式实现可独立部署,支持`KV`读写的数据库服务 | |||
#### test | |||
- URL:git@github.com:blockchain-jd-com/jdchain-test.git | |||
- 说明:集成测试用例的源码库 |
@@ -1,52 +0,0 @@ | |||
以下四种方式均可初始化/运行`JD Chain`网络,组网过程有难易,需要开发者细心操作,操作过程中遇到问题,可随时与我们联系。 | |||
### 1. 官方完整步骤 | |||
`JD Chain`官网提供了[安装部署](http://ledger.jd.com/setup.html)详细介绍,较为繁琐,但是其他便捷组网方法的基础。 | |||
### 2. 管理工具 | |||
`JD Chain`提供了基于界面操作的网络初始化启动工具,相关脚本为`manager-startup.sh`和`manager-shutdown.sh`。 | |||
送上操作视频:http://storage.jd.com/jd.block.chain/init-jdchain-by-manager-tool.mp4 | |||
### 3. 基于内存的四节点网络 | |||
1. 克隆[ JD Chain主项目](https://github.com/blockchain-jd-com/jdchain)源码,并切换到对应版本分支 | |||
请查阅**主项目首页介绍**,里面有子项目代码拉取,项目编译打包的介绍。代码根路径下执行: | |||
```bash | |||
build/build.sh --update --skipTests | |||
``` | |||
即可完成所有子项目代码拉取,完成编译打包 | |||
2. 运行[Samples](https://github.com/blockchain-jd-com/jdchain/tree/master/samples)模块下代码 | |||
参照[Samples介绍](https://github.com/blockchain-jd-com/jdchain/tree/master/samples) | |||
运行`sdk-samples`里的`TestNet`类`main`方法即可启动基于内存的四节点+单网关区块链网络环境,浏览器地址为`http://localhost:11000`。 | |||
`sdk-samples`中测试用例默认基于`TestNet`启动的网络环境配置,都可直接运行。覆盖绝大多数交易类型提交,交易查询。 | |||
### 4. 基于安装包和部署脚本 | |||
1. 下载`JD Chain`安装包 | |||
安装包获取途径: | |||
- 下载编译[`JD Chain`源码](https://github.com/blockchain-jd-com/jdchain),参照首页说明进行编译打包。 | |||
- 访问[JD Chain官网](http://ledger.jd.com/downloadapps.html)下载,版本更新可能不及源码快。 | |||
2. 脚本初始化 | |||
复制[testnet.sh](scripts/testnet.sh)脚本,保存到本地,设置可运行权限 | |||
> 脚本仅在特定的`linux`环境下测试通过,不同系统环境可能存在`shell`语句或者依赖差异,请酌情修改 | |||
> 此脚本可一键生成多节点,多账本,目前还相当粗糙,仅当抛砖引玉~ | |||
将`jdchain-peer-*.RELEASE.zip`,`jdchain-gateway-*.RELEASE.zip`压缩包以及`testnet.sh`脚本放置同一目录下。 | |||
直接运行`testnet.sh`便可自动初始化默认四节点+单网关的环境,同时生成一键启动(`start.sh`)和关闭(`shutdown.sh`)的脚本。 | |||
运行`start.sh`便可启动测试网络,参照[JD Chain Samples](https://github.com/blockchain-jd-com/jdchain/tree/master/samples)介绍,配置好网络环境参数,即可快速上手`JD Chain SDK`使用。 |
@@ -1,45 +0,0 @@ | |||
## 示例代码 | |||
克隆[JD Chain主项目](https://github.com/blockchain-jd-com/jdchain),切换到指定分支。 | |||
[Samples](https://github.com/blockchain-jd-com/jdchain/tree/master/samples) 中提供了 `用户`,`数据账户`,`合约`,`事件`,`查询API`相关使用。 | |||
### 依赖 TestNet | |||
`Samples`项目中提供了基于内存的四节点加单网关的网络环境初始化和启动方式(`TestNet`类),若需要此运行环境,请执行: | |||
```bash | |||
build/build.sh --update --skipTests | |||
``` | |||
将项目子项目及依赖都更新到指定分支对应版本,并完成编译打包。 | |||
执行`TestNet`类`main`即可启动测试网络,网络成功启动后可执行`sdk-samples`中所有测试用例。 | |||
### 不依赖 TestNet | |||
对于已有`JD Chain`测试网络,不使用`TestNet`的情况,开发者可以只导入`Samples`项目,并删除`TestNet`相关的包和类,去除`pom.xml`中以下依赖: | |||
```xml | |||
<!--以下依赖用于 com.jdchain.samples.Network 中四节点网路环境初始化和启动 --> | |||
<dependency> | |||
<groupId>org.reflections</groupId> | |||
<artifactId>reflections</artifactId> | |||
<version>0.9.12</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>tools-initializer</artifactId> | |||
<version>${framework.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>peer</artifactId> | |||
<version>${framework.version}</version> | |||
</dependency> | |||
<dependency> | |||
<groupId>com.jd.blockchain</groupId> | |||
<artifactId>gateway</artifactId> | |||
<version>${framework.version}</version> | |||
</dependency> | |||
``` | |||
修改`resources`中关于网络的相关参数,即可运行所有测试用例。 |
@@ -1,278 +0,0 @@ | |||
```bash | |||
#!/bin/bash | |||
HOME=$(cd `dirname $0`; pwd) | |||
echo "" > nohup.out | |||
ps -ef|grep '$HOME'|grep -v grep|cut -c 9-15|xargs kill -9 | |||
echo "Start jdchain Initialing in $HOME" | |||
## parameters | |||
# 节点数量 | |||
NODE_SIZE=4 | |||
# 允许错误节点个数 | |||
FAULT_SIZE=1 | |||
# 数据库URI | |||
DB_URI="" | |||
# peer API server start port | |||
PEER_PORT=7080 | |||
# consus start port | |||
CONSUS_PORT=10080 | |||
# DB password | |||
DB_PWD="" | |||
# ledger size | |||
LEDGER_SIZE=1 | |||
while getopts ":N:f:d:P:p:w:L:" opt | |||
do | |||
case $opt in | |||
N) | |||
NODE_SIZE=$OPTARG | |||
;; | |||
f) | |||
FAULT_SIZE=$OPTARG | |||
;; | |||
d) | |||
DB_URI=$OPTARG | |||
;; | |||
P) | |||
PEER_PORT=$OPTARG | |||
;; | |||
p) | |||
CONSUS_PORT=$OPTARG | |||
;; | |||
w) | |||
DB_PWD=$OPTARG | |||
;; | |||
L) | |||
LEDGER_SIZE=$OPTARG | |||
;; | |||
?) | |||
echo "unknow parameter" | |||
esac | |||
done | |||
# clear old data | |||
rm -rf peer* | |||
rm -rf gw* | |||
####################################### init peers files | |||
KEY_NAME=`date '+%s'` | |||
i=0 | |||
while(( $i<$NODE_SIZE )) | |||
do | |||
# uzip peer and gateway files | |||
unzip -oq jdchain-peer-* -d peer$i | |||
chmod 777 peer$i/bin/* | |||
# generate key | |||
mkdir peer$i/config/keys | |||
if [ -f "keygen" ];then | |||
chmod +x keygen | |||
./keygen $KEY_NAME peer$i/config/keys "WprD3WiAi5S6Z1BSDlvUkhBVhcBiaxf"$i | |||
else | |||
cd peer$i/bin/ | |||
expect -c " | |||
set timeout 10 | |||
spawn ./keygen.sh -n $KEY_NAME | |||
expect \"*Input password:\" | |||
send \"1\r\" | |||
expect \"*input y or n *\" | |||
send \"y\r\" | |||
expect eof | |||
" | |||
cd $HOME | |||
fi | |||
IDs[$i]=$i | |||
PUBKs[$i]=$(cat peer$i/config/keys/$KEY_NAME.pub) | |||
PRIVs[$i]=$(cat peer$i/config/keys/$KEY_NAME.priv) | |||
PWDs[$i]=$(cat peer$i/config/keys/$KEY_NAME.pwd) | |||
let "i++" | |||
done | |||
# init peer-startup.sh | |||
i=0 | |||
while(( $i<$NODE_SIZE )) | |||
do | |||
sed -ri "s#7080#$PEER_PORT#g" peer$i/bin/peer-startup.sh | |||
let "PEER_PORT++" | |||
let "i++" | |||
done | |||
####################################### init gateway | |||
unzip -oq jdchain-gateway-* -d gw | |||
chmod 777 gw/bin/* | |||
sed -ri "s#peer.port=7080#peer.port=$((PEER_PORT-NODE_SIZE))#g" gw/config/gateway.conf | |||
sed -ri "s#keys.default.pubkey=#keys.default.pubkey=${PUBKs[0]}#g" gw/config/gateway.conf | |||
sed -ri "s#keys.default.privkey=#keys.default.privkey=${PRIVs[0]}#g" gw/config/gateway.conf | |||
sed -ri "s#keys.default.privkey-password=#keys.default.privkey-password=${PWDs[0]}#g" gw/config/gateway.conf | |||
###################################### generate start and shutdown files | |||
echo "#!/bin/bash" > start.sh | |||
i=0 | |||
while(( $i<$NODE_SIZE )) | |||
do | |||
echo " | |||
nohup ./peer$i/bin/peer-startup.sh & " >> start.sh | |||
chmod 777 start.sh | |||
let "i++" | |||
done | |||
echo " | |||
sleep 20 | |||
nohup ./gw/bin/startup.sh &" >> start.sh | |||
echo "#!/bin/bash | |||
ps -ef|grep '$HOME'|grep -v grep|cut -c 9-15|xargs kill -9 | |||
" > shutdown.sh | |||
chmod 777 shutdown.sh | |||
###################################### ledger init | |||
k=0 | |||
while(( $k<$LEDGER_SIZE )) | |||
do | |||
echo "初始化账本 "$k | |||
seed=`date +%s%N | md5sum |cut -c 1-32` | |||
i=0 | |||
while(( $i<$NODE_SIZE )) | |||
do | |||
#### init local.conf | |||
sed -ri "s/local.parti.id(.*)/local.parti.id=$i/g" peer$i/config/init/local.conf | |||
sed -ri "s/local.parti.pubkey(.*)/local.parti.pubkey=${PUBKs[$i]}/g" peer$i/config/init/local.conf | |||
sed -ri "s/local.parti.privkey(.*)/local.parti.privkey=${PRIVs[$i]}/g" peer$i/config/init/local.conf | |||
sed -ri "s/local.parti.pwd(.*)/local.parti.pwd=${PWDs[$i]}/g" peer$i/config/init/local.conf | |||
if [ -z $DB_URI ] | |||
then | |||
sed -ri "s#ledger.db.uri(.*)#ledger.db.uri=rocksdb://$HOME/peer$i/rocksdb$k#g" peer$i/config/init/local.conf | |||
else | |||
sed -ri "s#ledger.db.uri(.*)#ledger.db.uri=$DB_URI/$i#g" peer$i/config/init/local.conf | |||
fi | |||
sed -ri "s#ledger.db.pwd(.*)#ledger.db.pwd=$DB_PWD#g" peer$i/config/init/local.conf | |||
#### init ledger.init | |||
echo " | |||
ledger.seed=$seed | |||
ledger.name=ledger$k | |||
created-time=$(date +"%Y-%m-%d %H:%M:%S").000+0800 | |||
consensus.service-provider=com.jd.blockchain.consensus.bftsmart.BftsmartConsensusProvider | |||
consensus.conf=$HOME/peer$i/config/init/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.count=$NODE_SIZE" > peer$i/config/init/ledger.init | |||
j=0 | |||
port=11010 | |||
while(( $j<$NODE_SIZE )) | |||
do | |||
echo " | |||
cons_parti.$j.name=$j | |||
cons_parti.$j.pubkey=${PUBKs[$j]} | |||
cons_parti.$j.initializer.host=127.0.0.1 | |||
cons_parti.$j.initializer.port=$port | |||
cons_parti.$j.initializer.secure=false" >> peer$i/config/init/ledger.init | |||
let "j++" | |||
let "port++" | |||
done | |||
#### init bftsmart.config | |||
echo "" > peer$i/config/init/bftsmart.config | |||
j=0 | |||
port=$((CONSUS_PORT+k*100)) | |||
while(( $j<$NODE_SIZE )) | |||
do | |||
echo " | |||
system.server.$j.network.host=127.0.0.1 | |||
system.server.$j.network.port=$port | |||
system.server.$j.network.secure=false" >> peer$i/config/init/bftsmart.config | |||
let "j++" | |||
let "port++" | |||
let "port++" | |||
done | |||
echo " | |||
system.communication.useSenderThread = true | |||
system.communication.defaultkeys = true | |||
system.servers.num = $NODE_SIZE | |||
system.servers.f = $FAULT_SIZE | |||
system.totalordermulticast.timeout = 60000 | |||
system.totalordermulticast.timeTolerance = 3000000 | |||
system.totalordermulticast.maxbatchsize = 2000 | |||
system.totalordermulticast.nonces = 10 | |||
system.totalordermulticast.verifyTimestamps = false | |||
system.communication.inQueueSize = 500000 | |||
system.communication.outQueueSize = 500000 | |||
system.communication.send.retryInterval = 2000 | |||
system.communication.send.retryCount = 100 | |||
system.communication.useSignatures = 0 | |||
system.communication.useMACs = 1 | |||
system.debug = 0 | |||
system.shutdownhook = true | |||
system.totalordermulticast.state_transfer = true | |||
system.totalordermulticast.highMark = 10000 | |||
system.totalordermulticast.revival_highMark = 10 | |||
system.totalordermulticast.timeout_highMark = 200 | |||
system.totalordermulticast.log = true | |||
system.totalordermulticast.log_parallel = false | |||
system.totalordermulticast.log_to_disk = true | |||
system.totalordermulticast.sync_log = false | |||
system.totalordermulticast.checkpoint_period = 1000 | |||
system.totalordermulticast.global_checkpoint_period = 120000 | |||
system.totalordermulticast.checkpoint_to_disk = false | |||
system.totalordermulticast.sync_ckp = false | |||
system.initial.view = $( IFS=$','; echo "${IDs[*]}" ) | |||
system.ttp.id = 2001 | |||
system.bft = true" >> peer$i/config/init/bftsmart.config | |||
let "i++" | |||
done | |||
echo "" > nohup.out | |||
### start ledger init | |||
i=0 | |||
while(( $i<$NODE_SIZE )) | |||
do | |||
nohup expect -c " | |||
set timeout 180 | |||
spawn peer$i/bin/ledger-init.sh | |||
expect \"*Any key to continue...*\" | |||
send \"1\r\" | |||
expect \"*Press any key to quit. *\" | |||
send \"quit\r\" | |||
expect eof | |||
" & | |||
let "i++" | |||
done | |||
tail -f nohup.out| while read line | |||
nSize=0 | |||
do | |||
if [[ $line =~ "Update Ledger binding configuration success!" ]] | |||
then | |||
let "nSize++" | |||
if [[ $line == $NODE_SIZE ]] | |||
then | |||
echo -e ".\c" | |||
else | |||
echo "" | |||
echo "账本 "$k" 初始化完成" | |||
break | |||
fi | |||
else | |||
echo -e ".\c" | |||
fi | |||
done | |||
let "k++" | |||
sleep 1 | |||
done | |||
``` |
@@ -1,295 +0,0 @@ | |||
## SDK | |||
`JD Chain`提供了`Java`和`Go`版本的`SDK`,此处以`Java`为例,`Go`版本参照[framework-go](https://github.com/blockchain-jd-com/framework-go) | |||
`SDK`可执行示例代码参照[JD Chain Samples](https://github.com/blockchain-jd-com/jdchain/tree/master/samples) | |||
### 1. 连接网关 | |||
通过`GatewayServiceFactory`中静态方法连接网关: | |||
```java | |||
static GatewayServiceFactory connect(NetworkAddress gatewayAddress) | |||
static GatewayServiceFactory connect(NetworkAddress gatewayAddress, BlockchainKeypair userKey) | |||
static GatewayServiceFactory connect(String gatewayHost, int gatewayPort, boolean secure) | |||
static GatewayServiceFactory connect(String gatewayHost, int gatewayPort, boolean secure, BlockchainKeypair userKey) | |||
``` | |||
其中`BlockchainKeypair userKey`参数用于自动签署终端用户签名,可不传,不传`userKey`时需要在提交交易前调用签名操作,添加终端账户签名信息。 | |||
通过: | |||
```java | |||
BlockchainService blockchainService = GatewayServiceFactory.connect(gatewayHost, gatewayPort, false).getBlockchainService(); | |||
``` | |||
创建区块连服务,常规情况`BlockchainService`**单例**即可。 | |||
### 2. 调用过程 | |||
#### 2.1 操作 | |||
1. 新建交易: | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
``` | |||
2. 操作: | |||
```java | |||
BlockchainKeypair user = BlockchainKeyGenerator.getInstance().generate(); | |||
// 注册用户 | |||
txTemp.users().register(user.getIdentity()); | |||
``` | |||
> 一笔交易中可以包含多个操作,所有操作类型参照[交易](transaction.md)文档。 | |||
3. 准备交易 | |||
```java | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
``` | |||
4. 终端用户签名 | |||
```java | |||
ptx.sign(userKey); | |||
``` | |||
> 若[连接网关](#1-连接网关)中传入了用户身份信息,且用户身份具备交易中包含的所有操作[权限](user.md),此处签名操作可省略。 | |||
5. 提交交易 | |||
```java | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
#### 2.2 查询 | |||
`BlockchainService`中包含了所有链上数据的查询方法,直接使用即可: | |||
```java | |||
LedgerInfo ledgerInfo = blockchainService.getLedger(ledger); | |||
``` | |||
### 3. 操作类型 | |||
按功能模块划分:`用户操作`,`数据账户操作`,`事件操作`,`合约操作`,`查询`。 | |||
#### 3.1 用户操作 | |||
用户/角色/权限相关说明参照[用户](user.md)文档说明。 | |||
1. 注册用户 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 生成用户 | |||
BlockchainKeypair user = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("用户地址:" + user.getAddress()); | |||
// 注册用户 | |||
txTemp.users().register(user.getIdentity()); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
2. 角色赋权 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 创建角色 MANAGER ,并设置可以写数据账户,能执行交易 | |||
txTemp.security().roles().configure("MANAGER") | |||
.enable(LedgerPermission.WRITE_DATA_ACCOUNT) | |||
.enable(TransactionPermission.DIRECT_OPERATION); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
3. 用户赋权 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 赋予用户 user MANAGER 角色,取消 ADMIN 角色,设置多角色策略策略为合并策略 | |||
txTemp.security().authorziations().forUser(user.getAddress()).authorize("MANAGER").unauthorize("ADMIN").setPolicy(RolesPolicy.UNION); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
#### 3.2 数据账户操作 | |||
1. 创建数据账户 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 生成数据账户 | |||
BlockchainKeypair dataAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("数据账户地址:" + dataAccount.getAddress()); | |||
// 注册数据账户 | |||
txTemp.dataAccounts().register(dataAccount.getIdentity()); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
2. 写KV | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// expVersion是针对此key的插入更新操作次数严格递增,初始为-1 | |||
txTemp.dataAccount(Bytes.fromBase58("LdeNscE3MP9a1vgyVUg9LgxQx6yzkUEUS65Rn")).setInt64("key2", 1024, -1); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
#### 3.3 事件操作 | |||
1. 创建事件账户 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 生成事件账户 | |||
BlockchainKeypair eventAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("事件账户地址:" + eventAccount.getAddress()); | |||
// 注册事件账户 | |||
txTemp.eventAccounts().register(eventAccount.getIdentity()); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
2. 发布事件 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// sequence是针对此消息name的序列严格递增,初始为-1,可通过查询事件名下最新事件获取序列参数 | |||
txTemp.eventAccount(Bytes.fromBase58("LdeNiAPuZ5tpYZVrrbELJNjqdvB51PBpNd8QA")).publish("topic", "content", -1); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
3. 监听事件 | |||
```java | |||
// 监听系统事件,目前仅有新区快产生事件 | |||
blockchainService.monitorSystemEvent(ledger, | |||
SystemEvent.NEW_BLOCK_CREATED, 0, (eventMessages, eventContext) -> { | |||
for (Event eventMessage : eventMessages) { | |||
// content中存放的是当前链上最新高度 | |||
System.out.println("New block:" + eventMessage.getSequence() + ":" + BytesUtils.toLong(eventMessage.getContent().getBytes().toBytes())); | |||
} | |||
}); | |||
// 监听用户自定义事件 | |||
blockchainService.monitorUserEvent(ledger, "LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye", "sample-event", 0, (eventMessage, eventContext) -> { | |||
BytesValue content = eventMessage.getContent(); | |||
switch (content.getType()) { | |||
case TEXT: | |||
case XML: | |||
case JSON: | |||
System.out.println(eventMessage.getName() + ":" + eventMessage.getSequence() + ":" + content.getBytes().toUTF8String()); | |||
break; | |||
case INT64: | |||
case TIMESTAMP: | |||
System.out.println(eventMessage.getName() + ":" + eventMessage.getSequence() + ":" + BytesUtils.toLong(content.getBytes().toBytes())); | |||
break; | |||
default: // byte[], Bytes | |||
System.out.println(eventMessage.getName() + ":" + eventMessage.getSequence() + ":" + content.getBytes().toBase58()); | |||
break; | |||
} | |||
}); | |||
``` | |||
#### 3.4 合约操作 | |||
1. 部署合约 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 生成合约账户 | |||
BlockchainKeypair contractAccount = BlockchainKeyGenerator.getInstance().generate(); | |||
System.out.println("合约地址:" + contractAccount.getAddress()); | |||
// 部署合约 | |||
txTemp.contracts().deploy(contractAccount.getIdentity(), FileUtils.readBytes("src/main/resources/contract-samples-1.4.2.RELEASE.car")); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
2. 升级合约 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 解析已存在的合约身份信息 | |||
BlockchainIdentity contractIdentity = new BlockchainIdentityData(KeyGenUtils.decodePubKey("7VeRCfSaoBW3uRuvTqVb26PYTNwvQ1iZ5HBY92YKpEVN7Qht")); | |||
System.out.println("合约地址:" + contractIdentity.getAddress()); | |||
// 部署合约 | |||
txTemp.contracts().deploy(contractIdentity, FileUtils.readBytes("src/main/resources/contract-samples-1.4.2.RELEASE.car")); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
``` | |||
3. 调用合约 | |||
基于动态代理方式合约调用,需要依赖合约接口: | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 一次交易中可调用多个(多次调用)合约方法 | |||
// 调用合约的 registerUser 方法 | |||
SampleContract sampleContract = txTemp.contract("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye", SampleContract.class); | |||
GenericValueHolder<String> userAddress = ContractReturnValue.decode(sampleContract.registerUser(UUID.randomUUID().toString())); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
Assert.assertTrue(response.isSuccess()); | |||
// 获取返回值 | |||
System.out.println(userAddress.get()); | |||
``` | |||
非动态代理方式合约调用,不需要依赖合约接口及实现: | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
ContractEventSendOperationBuilder builder = txTemp.contract(); | |||
// 一次交易中可调用多个(多次调用)合约方法 | |||
// 调用合约的 registerUser 方法,传入合约地址,合约方法名,合约方法参数列表 | |||
builder.send("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye", "registerUser", | |||
new BytesDataList(new TypedValue[]{ | |||
TypedValue.fromText(UUID.randomUUID().toString()) | |||
}) | |||
); | |||
PreparedTransaction ptx = txTemp.prepare(); | |||
ptx.sign(adminKey); | |||
TransactionResponse response = ptx.commit(); | |||
Assert.assertTrue(response.isSuccess()); | |||
// 解析合约方法调用返回值 | |||
for (int i = 0; i < response.getOperationResults().length; i++) { | |||
BytesValue content = response.getOperationResults()[i].getResult(); | |||
switch (content.getType()) { | |||
case TEXT: | |||
System.out.println(content.getBytes().toUTF8String()); | |||
break; | |||
case INT64: | |||
System.out.println(BytesUtils.toLong(content.getBytes().toBytes())); | |||
break; | |||
case BOOLEAN: | |||
System.out.println(BytesUtils.toBoolean(content.getBytes().toBytes()[0])); | |||
break; | |||
default: // byte[], Bytes | |||
System.out.println(content.getBytes().toBase58()); | |||
break; | |||
} | |||
} | |||
``` | |||
#### 3.5 查询 | |||
与[网关 API](api.md)所提供查询一致,参照[Query Samples](https://github.com/blockchain-jd-com/jdchain/blob/master/samples/sdk-samples/src/test/java/com/jdchain/samples/sdk/QuerySample.java) |
@@ -1,200 +0,0 @@ | |||
## 架构设计 | |||
### 总体目标 | |||
区块链是一种新型分布式架构,以密码学和分布式技术为核心,无需借助“第三方” 就能在多个业务方之间进行安全、可信、直接的信息和价值交换。在这种点对点的信息和价值的交换中,区块链起到了“协议”的作用。基于这一视角,`JD Chain`的目标是实现一个面向企业应用场景的通用区块链框架系统,能够作为企业级基础设施,为业务创新提供高效、灵活和安全的解决方案。 | |||
### 设计原则 | |||
设计原则是系统设计和实现的第一价值观,从根本上指导技术产品的发展方向。京东区块链在技术规划和系统架构设计上,遵循以下设计原则: | |||
 | |||
1. 面向业务 | |||
“企业级区块链”的目标定位决定了系统的功能设计必须要从实际的业务场景出发,分析抽象不同业务领域的共性需求。京东的区块链应用实践案例涉及包括金融、供应链、电子存证、医疗、政务、公益慈善等众多领域,从中获得丰富的应用实践经验,这能够为京东区块链获得良好通用性提供设计输入和业务验证。 | |||
2. 模块化 | |||
企业应用场景的多样性和复杂性要求系统有良好的扩展性,遵循模块化的设计原则,可以在确保系统核心逻辑稳定的同时,对外提供最小的扩展边界,实现系统的高内聚低耦合。 | |||
3. 安全可审计 | |||
区块链的可信任需要在系统设计和实现上遵循安全原则,数据可审计原则,以及满足不同地区和场景的标准与合规要求,保障信息处理可满足机密性、完整性、可控性、可用性和不可否认性等要求。 | |||
4. 简洁与效率 | |||
简洁即高效,从设计到编码都力求遵循这一原则。采用简洁的系统模型可以提升易用性并降低分布式系统的实现风险。此外,在追求提升系统性能的同时,也注重提升应用开发和方案落地的效能。 | |||
5. 标准化 | |||
区块链作为一种点对点的信息和价值交换的“桥梁”,通过定义一套标准的操作接口和数据结构,能够提升多方业务对接的效率,降低应用落地的复杂度。遵循标准化原则,要求在系统设计时数据模型及操作模型独立于系统实现,让数据“系于链却独于链”,可在链下被独立地验证和运用,更好地支持企业进行数据治理,提升区块链系统的灵活性和通用性。 | |||
### 设计核心 | |||
区块链的核心可以归结为两点:运用密码算法保障信息的完整性与不可否认性;在上述基础上,运用共识协议使信息复制保存到不同业务方,实现多方对信息共同背书。基于这两点可定义出区块链的5个核心部分:密码算法、共识协议、数据账本模型、数据存储、`API`(应用编程接口Application Programing Interface,以下简称`API`)。围绕总体设计原则,`JD Chain`的设计思路如下: | |||
1. 密码算法 | |||
密码算法的选择需要满足安全和合规的要求,同时面临源自实际业务场景的多样性要求。`JD Chain`在密码方面的关键任务是设计可插拔的密码框架,定义标准的`SPI`(服务提供者接口Service Provider Interface, 以下简称`SPI`)。系统默认支持国密算法以满足合规要求。基于密码SPI可以快速适配其它的密码算法实现,支持多密码体系。`JD Chain`将提供具有隐私保护功能密码算法和安全协议,来满足具体应用与业务的需求。 | |||
2. 共识协议 | |||
共识协议的核心任务是保障区块链网络中有效节点的状态一致性。另外在选择共识协议时,还需要考虑业务场景中的安全性要求、时效性要求和节点规模等诸多因素。`JD Chain`在共识协议方面的关键任务是设计可插拔的共识框架,解耦共识协议与数据账本模型,定义标准的共识协议`SPI`,以满足业务场景的多样化需求。 | |||
3. 数据账本模型 | |||
数据账本的核心任务是对数据进行有效地组织和管理,因此,需要定义数据的结构和数据处理的操作模型。`JD Chain`的数据账本模型以“键值”结构来组织业务数据,定义标准的读写操作,记录数据变更历史,维护数据完整性与不可否认性,管理数据的存在性证明。 | |||
4. 数据存储 | |||
数据存储的核心任务是把数据账本高效地读写到持久化介质中。`JD Chain`把数据账本模型映射为“键值”结构,为数据的存储提供更好的伸缩性。另外,还定义了标准的持久化服务`SPI`,能够适配不同的数据库引擎,更好地复用企业现有的IT基础设施,满足企业的多样化需求。 | |||
5. API | |||
`JD Chain`的`API`设计需要提供标准化的操作接口,考虑通讯协议和编程语言的广泛性,支持端到端的离线密码计算,向企业提供更安全可信和易用的编程接口。 | |||
### 功能模块 | |||
JD Chain按功能层次分为4个部分:网关服务、共识服务、数据账本和工具包。 | |||
1. 网关服务 | |||
`JD Chain`的网关服务是应用的接入层,提供终端接入、私钥托管、安全隐私和协议转换等功能。 | |||
终端接入是`JD Chain`网关的基本功能,在确认终端身份的同时提供连接节点、转发消息和隔离共识节点与客户端等服务。网关确认客户端的合法身份,接收并验证交易;网关根据初始配置文件与对应的共识节点建立连接,并转发交易数据。 | |||
私钥托管功能使共识节点可以将私钥等秘密信息以密文的形式托管在网关内,为有权限的共识节点提供私钥恢复、签名生成等服务。 | |||
安全隐私,一方面是网关借助具有隐私保护功能的密码算法和协议,来进行隐藏端到端身份信息,脱敏处理数据信息,防止无权限客户端访问数据信息等操作;另一方面,网关的隔离作用使外部实体无法干预内部共识过程,保证共识和业务之间的独立性。 | |||
网关中的协议转换功能提供了轻量化的HTTP Restful Service,能够适配区块链节点的`API`,实现各节点在不同协议之间的互操作。 | |||
数据浏览功能提供对链上数据可视化展示的能力。 | |||
2. 共识服务 | |||
共识服务是`JD Chain`的核心实现层,包括共识网络、身份管理、安全权限、交易处理、智能合约和数据检索等功能,来保证各节点间账本信息的一致性。 | |||
`JD Chain`的共识网络采用多种可插拔共识协议,并加以优化,来提供确定性交易执行、拜占庭容错和动态调整节点等功能,进而满足企业级应用场景需求。按照模块化的设计思路,将共识协议的各阶段进行封装,抽象出可扩展的接口,方便节点调用。共识节点之间使用`P2P`网络作为传输通道来执行共识协议。 | |||
身份管理功能使`JD Chain`网络能够通过公钥信息来辨识并认证节点,为访问控制、权限管理提供基础身份服务。 | |||
安全权限指的是,根据具体应用和业务场景,为节点设置多种权限形式,实现指定的安全管理,契合应用和业务场景。 | |||
交易处理是共识节点根据具体的协议来对交易信息进行排序、验证、共识和结块等处理操作,使全局共享相同的账本信息的功能。 | |||
智能合约是`JD Chain`中一种能够自动执行的链上编码逻辑,来更改账本和账户的状态信息。合约内容包括业务逻辑、节点的准入退出和系统配置的变更等。此外,`JD Chain`采用相应的合约引擎来保证智能合约能够安全高效地执行,降低开发难度并增加扩展性。开发者可以使用该合约引擎进行开发和测试,并通过接口进行部署和调用。 | |||
数据检索能够为协助节点检索接口,来查询区块、交易、合约、账本等相关信息。 | |||
3. 数据账本 | |||
数据账本为各参与方提供区块链底层服务功能,包括区块、账户、配置和存储等。 | |||
区块是`JD Chain`账本主要组成部分,包含交易信息和交易执行状态的数据快照哈希值,但不存储具体的交易操作和状态数据。`JD Chain`将账本状态和合约进行分离,并约束合约对账本状态的访问,来实现数据与逻辑分离,提供无状态逻辑抽象。 | |||
`JD Chain`通过细化账户分类、分级分类授权的方式,对区块链系统中的账户进行管理,达到逻辑清晰化、隔离业务和保护相关数据内容的目的。 | |||
配置文件包括密钥信息,存储信息以及共享的参与者身份信息等内容,使`JD Chain`系统中各节点能够执行诸如连接其他节点、验证信息、存储并更新账本等操作。 | |||
存储格式采用简洁的`KV`数据类型, 使用较为成熟的`NoSQL`数据库来实现账本的持久化存储,使区块链系统能够支持海量交易。 | |||
4. 工具包 | |||
节点可以使用`JD Chain`中提供的工具包获取上述三个层级的功能服务,并响应相关应用和业务。工具包贯穿整个区块链系统,使用者只需调用特定的接口即可使用对应工具。工具包包括数据管理、开发包(`SDK`)、安装部署和服务监控等。 | |||
上述三个功能层级都有对应的开发包,以接口形式提供给使用者,这些开发包包括密码算法、智能合约、数据检索的`SPI`等。 | |||
数据管理是对数据信息进行管理操作的工具包,这些管理操作包括备份、转移、导出、校验、回溯,以及多链情况下的数据合并、拆分等操作。 | |||
安装部署类工具包括密钥生成、数据存储等辅助功能,帮助各节点执行区块链系统。 | |||
服务监控工具能够帮组使用者获取即时吞吐量、节点状态、数据内容等系统运行信息,实现运维管理和实时监控。 | |||
### 部署模型 | |||
在企业的实际使用过程中,应用场景随着业务的不同往往是千差万别,不同的场景下如何选择部署模型,如何进行部署,往往是每个企业都会面临的实际问题。面对复杂多样的应用场景,`JD Chain`从易用性方面考虑,为企业应用提供了一套行之有效的部署模型解决方案。 | |||
 | |||
`JD Chain`通过节点实现信息之间的交互,不同类型的节点可以在同一物理服务器上部署运行。`JD Chain`中定义了三种不同类型的节点: | |||
客户端节点(`Client`):通过`JD Chain`的`SDK`进行区块链操作的上层应用; | |||
网关节点(`Gateway`):提供网关服务的节点,用于连接客户端和共识节点; | |||
共识节点(`Peer`):共识协议参与方,会产生一致性账本。 | |||
不同企业规模的应用,部署方案会有较大区别,`JD Chain`根据实际应用的不同规模,提供了面向中小型企业和大型企业的两种不同部署模型。 | |||
1. 中小企业应用部署模型 | |||
`JD Chain`在不同的应用环境中可采用不同的部署模型。它的最简部署模型是`JD Chain`可正常运行的最低配置,在硬件条件满足的情况下,可以支持亿级交易,通常用于`Demo`实验或小型应用。 | |||
最简部署模型需要部署一个客户端节点、一个网关节点和多个共识节点(共识节点数量依赖于共识算法),如下所示: | |||
 | |||
此外,`JD Chain`提供了数据服务功能作为可选项,通过安装数据服务组件可以进行数据的检索、汇总等功能。数据服务组件与共识节点部署在相同或不同服务器均可,由此最简部署可演化为如下图所示模型: | |||
 | |||
随着应用级别的提升,对数据存储需求越来越大,每个共识节点可采用数据库集群的存储方式实现存储的平行化扩展。在这种方式下,可支持交易级别达到十亿(乃至更多),如下图所示: | |||
 | |||
在某些中型(乃至大型)实际应用中,共识节点会由不同的业务方安装部署,从安全性和扩展性角度考虑,更推荐使用共识节点集群: | |||
 | |||
2. 大型企业应用部署模型 | |||
在一些大型的企业应用中,实际的业务方关系、应用场景将非常复杂。在这类应用中,可能由很多不同类型的参与方、不同类型的终端接入区块链网络,甚至这些终端可以从任意授权的网关节点接入,它们之间也会存在各种复杂的逻辑关系。 下图是一个比较常见的大型企业应用部署模型,在这个模型中涉及到多种类型的参与方,不同的终端,不同的接入方式。 | |||
 | |||
### 交易流程 | |||
本文档描述了溯源数据状态转换及共享的交易机制。该方案包括四个参与方,`A`、`B`、`C`、`D`。他们每个参与方都有一部分基础溯源数据,换句话说,每个参与方对应了溯源数据的不同流程,但是有序的流程。基本的流程可以暂时认为包括:生产、加工、运输、收货。 | |||
 | |||
#### 准备工作 | |||
1. 用户准备 | |||
每个参与方在实际的`JD Chain`使用过程中都对应了一个用户,参与方之间实际是通过对应用户进行交易处理。 | |||
`JD Chain`利用用户的公私钥来标识用户,使用者可通过公私钥工具(`keygen.sh`) 生成一个用户: | |||
 | |||
2. 账本初始化 | |||
所有参与方根据安装部署脚本达成共识,建立一条专门用户溯源数据的状态转换和数据共享的链(一个账本)。 | |||
四个参与方分别对应了整个溯源的四个环节,分别按照`A -> B -> C -> D`顺序进行数据状态变更及数据更新,每个参与方依赖于前一个参与方的数据状态对其进行更新和共享。 | |||
#### 交易过程 | |||
每个参与方在实际处理过程都需要使用`Client`作为交易处理的载体,每个`Client`中都有该参与方在该链对应的用户。 | |||
以参与方A为示例对交流过程进行说明。每个参与方都是在其他参与方交易基础上进行数据共享和状态转换,这得益于`K-V-Version`的存储方式,可以将状态以`Version`的方式标记。 | |||
1. 启动交易 | |||
参与方A通过`Client`发送A所持有的生产数据。`Client`为实际业务系统,通过调用`JD Chain`提供的`SDK`(目前支持`Java`)将某溯源产品的生产数据封装为交易。该交易附有业务系统的签名信息,`SDK`将该交易封装成正确的请求格式,通过`Http`将其发送至`Gateway`节点。 | |||
 | |||
2. 验证交易 | |||
`Gateway`节点收到`Client`发送的交易后,首先对交易进行验证(包括交易的格式及签名信息),验证通过后`Gateway`节点会在原交易基础上附加自己的签名信息,然后将交易发送至`Peer`节点群进行共识。 | |||
 | |||
3. 交易共识 | |||
Peer节点群使用BFTSmart共识算法对提交的交易进行共识,该算法需要节点数为:`N = 3f + 1`(其中`N`为节点总数,`f`为可支持作恶节点数),因此共识过程至少需要`4`个节点参与。 | |||
`Peer`节点群对交易共识成功后会输出排序后的交易列表,该交易列表对于每个`Peer`节点而言是平等的,换句话说,每个`Peer`节点都会接收到完全一致的按顺序的交易列表。 | |||
 | |||
4. 交易应答 | |||
`Peer`节点收到排序后交易后对其进行检查(包括合法性验证、验签等),检查通过后会根据实际结块规则生成区块并更新账本。然后将结块的信息作为应答发送至`Gateway`节点,进而发送至`Client`节点。 | |||
 | |||
#### 时序图 | |||
一种事务流程如下: | |||
1. `Client`调用`SDK`生成对应的交易请求,并通过`SDK`将该交易发送至`Gateway`(`HTTP`协议)。 | |||
2. `Gateway`会对交易进行合法性验证,并进行验签,然后使用自身节点的用户进行签名,再将交易以自定义协议(`TCP`)的方式发送至`Peer`节点群进行共识。 | |||
3. `Peer`节点群接收连续的交易并进行共识,共识通过后会输出排序后的交易列表至每个`Peer`节点。 | |||
4. 每个`Peer`节点对接收的交易列表进行合法性检查、验签,验证通过后会执行对应的交易并根据结块规则生成区块,更新账本。完成后会将应答(含结块信息)发送至对应`Gateway`,进而发送至`Client`。 | |||
 |
@@ -1,478 +0,0 @@ | |||
## 交易 | |||
### 1. 请求 | |||
> `JD Chain`限制单笔交易数据大小不得超过 `4M` | |||
```java | |||
/** | |||
* 交易请求; | |||
*/ | |||
@DataContract(code= DataCodes.TX_REQUEST) | |||
public interface TransactionRequest { | |||
/** | |||
* 交易哈希; | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getTransactionHash(); | |||
/** | |||
* 交易内容; | |||
*/ | |||
@DataField(order = 2, refContract = true) | |||
TransactionContent getTransactionContent(); | |||
/** | |||
* 终端用户的签名列表; | |||
*/ | |||
@DataField(order = 3, list = true, refContract = true) | |||
DigitalSignature[] getEndpointSignatures(); | |||
/** | |||
* 接入交易的节点的签名; | |||
*/ | |||
@DataField(order=4, list=true, refContract=true) | |||
DigitalSignature[] getNodeSignatures(); | |||
} | |||
``` | |||
交易内容: | |||
```java | |||
/** | |||
* 交易内容; | |||
*/ | |||
@DataContract(code = DataCodes.TX_CONTENT) | |||
public interface TransactionContent { | |||
/** | |||
* 执行交易的账本地址; | |||
* | |||
* 注:除了账本的创世交易之外,任何交易的账本地址都不允许为 null; | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getLedgerHash(); | |||
/** | |||
* 操作列表; | |||
*/ | |||
@DataField(order = 2, list = true, refContract = true, genericContract = true) | |||
Operation[] getOperations(); | |||
/** | |||
* 生成交易的时间;<br> | |||
* 以毫秒为单位,表示距离 1970-1-1 00:00:00 (UTC) 的毫秒数;<br> | |||
*/ | |||
@DataField(order = 3, primitiveType = PrimitiveType.INT64) | |||
long getTimestamp(); | |||
} | |||
``` | |||
### 2. 操作 | |||
`Operation`接口实现类均为`JD Chain`交易中支持的操作类型 | |||
#### 2.1 共识信息变更 | |||
```java | |||
@DataContract(code= DataCodes.TX_OP_CONSENSUS_SETTINGS_UPDATE) | |||
public interface ConsensusSettingsUpdateOperation extends Operation{ | |||
/** | |||
* 配置列表 | |||
*/ | |||
@DataField(order = 0, primitiveType = PrimitiveType.BYTES, list = true) | |||
Property[] getProperties(); | |||
} | |||
``` | |||
#### 2.2 合约部署 | |||
```java | |||
@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(); | |||
/** | |||
* 合约版本 | |||
*/ | |||
@DataField(order=5, primitiveType=PrimitiveType.INT64) | |||
long getChainCodeVersion(); | |||
} | |||
``` | |||
#### 2.3 合约调用 | |||
```java | |||
@DataContract(code = DataCodes.TX_OP_CONTRACT_EVENT_SEND) | |||
public interface ContractEventSendOperation extends Operation { | |||
/** | |||
* 合约地址; | |||
*/ | |||
@DataField(order = 2, primitiveType = PrimitiveType.BYTES) | |||
Bytes getContractAddress(); | |||
/** | |||
* 合约方法名; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 3, primitiveType = PrimitiveType.TEXT) | |||
String getEvent(); | |||
/** | |||
* 合约方法调用参数; | |||
* | |||
* @return | |||
*/ | |||
@DataField(order = 4, refContract = true) | |||
BytesValueList getArgs(); | |||
/** | |||
* 合约版本; | |||
*/ | |||
@DataField(order = 5, primitiveType = PrimitiveType.INT64) | |||
long getVersion(); | |||
} | |||
``` | |||
#### 2.4 注册数据账户 | |||
```java | |||
@DataContract(code= DataCodes.TX_OP_DATA_ACC_REG) | |||
public interface DataAccountRegisterOperation extends Operation { | |||
/** | |||
* 数据账户信息; | |||
*/ | |||
@DataField(order=1, refContract = true) | |||
BlockchainIdentity getAccountID(); | |||
} | |||
``` | |||
#### 2.5 写KV操作 | |||
```java | |||
@DataContract(code= DataCodes.TX_OP_DATA_ACC_SET) | |||
public interface DataAccountKVSetOperation extends Operation { | |||
/** | |||
* 数据账户 | |||
*/ | |||
@DataField(order=2, primitiveType=PrimitiveType.BYTES) | |||
Bytes getAccountAddress(); | |||
/** | |||
* KV列表 | |||
*/ | |||
@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(); | |||
} | |||
} | |||
``` | |||
#### 2.6 注册事件账户 | |||
```java | |||
@DataContract(code = DataCodes.TX_OP_EVENT_ACC_REG) | |||
public interface EventAccountRegisterOperation extends Operation { | |||
@DataField(order = 2, refContract = true) | |||
BlockchainIdentity getEventAccountID(); | |||
} | |||
``` | |||
#### 2.7 发布事件 | |||
```java | |||
@DataContract(code = DataCodes.TX_OP_EVENT_PUBLISH) | |||
public interface EventPublishOperation extends Operation { | |||
/** | |||
* 事件地址 | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
Bytes getEventAddress(); | |||
/** | |||
* 事件列表 | |||
*/ | |||
@DataField(order = 2, list = true, refContract = true) | |||
EventEntry[] getEvents(); | |||
@DataContract(code = DataCodes.TX_OP_EVENT_PUBLISH_ENTITY) | |||
interface EventEntry { | |||
@DataField(order = 1, primitiveType = PrimitiveType.TEXT) | |||
String getName(); | |||
@DataField(order = 2, refContract = true) | |||
BytesValue getContent(); | |||
@DataField(order = 3, primitiveType = PrimitiveType.INT64) | |||
long getSequence(); | |||
} | |||
} | |||
``` | |||
#### 2.8 账本初始化 | |||
```java | |||
@DataContract(code= DataCodes.TX_OP_LEDGER_INIT) | |||
public interface LedgerInitOperation extends Operation{ | |||
/** | |||
* 账本初始化配置 | |||
*/ | |||
@DataField(order=1, refContract=true) | |||
LedgerInitSetting getInitSetting(); | |||
} | |||
``` | |||
#### 2.9 参与方状态变更 | |||
```java | |||
@DataContract(code= DataCodes.TX_OP_PARTICIPANT_STATE_UPDATE) | |||
public interface ParticipantStateUpdateOperation extends Operation { | |||
/** | |||
* 参与方身份 | |||
*/ | |||
@DataField(order = 0, refContract = true) | |||
BlockchainIdentity getParticipantID(); | |||
/** | |||
* 新状态 | |||
*/ | |||
@DataField(order = 1, refEnum = true) | |||
ParticipantNodeState getState(); | |||
} | |||
``` | |||
#### 2.10 角色赋权 | |||
```java | |||
@DataContract(code = DataCodes.TX_OP_ROLE_CONFIGURE) | |||
public interface RolesConfigureOperation extends Operation { | |||
/** | |||
* 角色权限列表 | |||
*/ | |||
@DataField(order = 2, refContract = true, list = true) | |||
RolePrivilegeEntry[] getRoles(); | |||
@DataContract(code = DataCodes.TX_OP_ROLE_CONFIGURE_ENTRY) | |||
public static interface RolePrivilegeEntry { | |||
/** | |||
* 角色名 | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.TEXT) | |||
String getRoleName(); | |||
/** | |||
* 开启的账本权限列表 | |||
*/ | |||
@DataField(order = 2, refEnum = true, list = true) | |||
LedgerPermission[] getEnableLedgerPermissions(); | |||
/** | |||
* 关闭的账本权限列表 | |||
*/ | |||
@DataField(order = 3, refEnum = true, list = true) | |||
LedgerPermission[] getDisableLedgerPermissions(); | |||
/** | |||
* 开启的交易权限列表 | |||
*/ | |||
@DataField(order = 4, refEnum = true, list = true) | |||
TransactionPermission[] getEnableTransactionPermissions(); | |||
/** | |||
* 关闭的交易权限列表 | |||
*/ | |||
@DataField(order = 5, refEnum = true, list = true) | |||
TransactionPermission[] getDisableTransactionPermissions(); | |||
} | |||
} | |||
``` | |||
#### 2.11 用户赋权 | |||
```java | |||
@DataContract(code = DataCodes.TX_OP_USER_ROLES_AUTHORIZE) | |||
public interface UserAuthorizeOperation extends Operation { | |||
/** | |||
* 用户角色列表 | |||
*/ | |||
@DataField(order = 2, refContract = true, list = true) | |||
UserRolesEntry[] getUserRolesAuthorizations(); | |||
@DataContract(code = DataCodes.TX_OP_USER_ROLE_AUTHORIZE_ENTRY) | |||
public static interface UserRolesEntry { | |||
/** | |||
* 用户地址; | |||
*/ | |||
@DataField(order = 0, primitiveType = PrimitiveType.BYTES, list = true) | |||
Bytes[] getUserAddresses(); | |||
/** | |||
* 要更新的多角色权限策略; | |||
*/ | |||
@DataField(order = 2, refEnum = true) | |||
RolesPolicy getPolicy(); | |||
/** | |||
* 授权的角色清单; | |||
*/ | |||
@DataField(order = 3, primitiveType = PrimitiveType.TEXT, list = true) | |||
String[] getAuthorizedRoles(); | |||
/** | |||
* 取消授权的角色清单; | |||
*/ | |||
@DataField(order = 4, primitiveType = PrimitiveType.TEXT, list = true) | |||
String[] getUnauthorizedRoles(); | |||
} | |||
} | |||
``` | |||
#### 2.12 注册用户 | |||
```java | |||
@DataContract(code = DataCodes.TX_OP_USER_REG) | |||
public interface UserRegisterOperation extends Operation { | |||
/** | |||
* 用户身份信息 | |||
*/ | |||
@DataField(order = 2, refContract = true) | |||
BlockchainIdentity getUserID(); | |||
} | |||
``` | |||
### 3. 结果 | |||
交易执行结果数据结构如下: | |||
```java | |||
@DataContract(code = DataCodes.TX_RESULT) | |||
public interface TransactionResult { | |||
/** | |||
* 交易哈希; | |||
*/ | |||
@DataField(order = 1, primitiveType = PrimitiveType.BYTES) | |||
HashDigest getTransactionHash(); | |||
/** | |||
* 交易被包含的区块高度; | |||
*/ | |||
@DataField(order = 2, primitiveType = PrimitiveType.INT64) | |||
long getBlockHeight(); | |||
/** | |||
* 交易的执行结果; | |||
*/ | |||
@DataField(order = 3, refEnum = true) | |||
TransactionState getExecutionState(); | |||
/** | |||
* 交易中操作的返回结果;顺序与操作列表的顺序一致; | |||
*/ | |||
@DataField(order = 4, list = true, refContract = true) | |||
OperationResult[] getOperationResults(); | |||
/** | |||
* 账本数据快照; | |||
*/ | |||
@DataField(order = 5, refContract = true) | |||
LedgerDataSnapshot getDataSnapshot(); | |||
} | |||
``` | |||
### 4. 查询 | |||
`SDK`查询交易详情数据使用`LedgerTransaction`接口实现 | |||
#### 4.1 结构 | |||
```java | |||
@DataContract(code = DataCodes.TX_RECORD) | |||
public interface LedgerTransaction { | |||
/** | |||
* 交易请求; | |||
*/ | |||
@DataField(order = 1, refContract = true) | |||
TransactionRequest getRequest(); | |||
/** | |||
* 交易结果; | |||
*/ | |||
@DataField(order = 2, refContract = true) | |||
TransactionResult getResult(); | |||
} | |||
``` | |||
#### 4.2 解析 | |||
- 成功/失败: | |||
```java | |||
getResult().getExecutionState(); | |||
``` | |||
`TransactionState.SUCCESS`为成功,其他失败。 | |||
- 操作解析 | |||
```java | |||
for(Operation operation : tx.getRequest().getTransactionContent().getOperations()) { | |||
// 注册用户 | |||
if(operation instanceof UserRegisterOperation) { | |||
UserRegisterOperation userRegisterOperation = (UserRegisterOperation) operation; | |||
// ... | |||
// 注册数据账户 | |||
} else if(operation instanceof DataAccountRegisterOperation) { | |||
DataAccountRegisterOperation dataAccountRegisterOperation = (DataAccountRegisterOperation) operation; | |||
// ... | |||
} // ... | |||
} | |||
``` | |||
上诉仅以注册用户/注册数据账户为例,其他操作类型嗯参照[所有操作类型](#2-操作)进行解析。 |
@@ -1,129 +0,0 @@ | |||
## 用户 | |||
`JD Chain`实现了基于角色和权限的用户账户管理体系。 | |||
### 1. 用户 | |||
可类比传统数据库的用户概念,`JD Chain`用户是接入`JD Chain`网络的必要身份,本质上由一对公私钥对标识,公钥和地址信息记录在账本用户数据集中。 | |||
### 2. 角色 | |||
角色名称不区分大小写,最长不超过20个字符,多个角色名称之间用半角的逗点`,`分隔 | |||
系统会预置一个默认角色`DEFAULT`,所有未指定角色的用户都以赋予该角色的权限,若初始化时未配置默认角色的权限,则为默认角色分配所有权限; | |||
#### 2.1 多角色策略 | |||
表示如何处理一个对象被赋予多个角色时的综合权限,在`RolesPolicy`枚举中定义: | |||
```java | |||
public enum RolesPolicy { | |||
// 合并权限,综合权限是所有角色权限的并集,即任何一个角色的权限都被继承 | |||
UNION((byte) 0), | |||
// 交叉权限,综合权限是所有角色权限的交集,即只有全部角色共同拥有的权限才会被继承 | |||
INTERSECT((byte) 1); | |||
} | |||
``` | |||
### 3. 权限 | |||
`JD Chain`权限设计分为两类:账本权限,交易权限。 | |||
#### 3.1 账本权限 | |||
账本相关的权限,这些权限属于全局性的 | |||
- `CONFIGURE_ROLES`配置角色 | |||
- `AUTHORIZE_USER_ROLES`授权用户角色 | |||
- `APPROVE_TX`参与方核准交易,如果不具备此项权限,则无法作为节点签署由终端提交的交易 | |||
- `REGISTER_PARTICIPANT`注册参与方 | |||
- `REGISTER_USER`注册用户 | |||
- `REGISTER_EVENT_ACCOUNT`注册事件账户 | |||
- `WRITE_EVENT_ACCOUNT`发布事件 | |||
- `REGISTER_DATA_ACCOUNT`注册数据账户 | |||
- `WRITE_DATA_ACCOUNT`写入数据账户 | |||
- `REGISTER_CONTRACT`注册合约 | |||
- `UPGRADE_CONTRACT`升级合约 | |||
- `UPDATE_USER_STATE`更新用户(证书)状态 | |||
- `UPDATE_ROOT_CA`更新账本根证书 | |||
- `UPDATE_USER_CA`更新用户(证书)状态 | |||
- `UPDATE_CONTRACT_STATE`更新合约状态 | |||
#### 3.2 交易权限 | |||
一个用户可以发起的交易类型 | |||
- `DIRECT_OPERATION`交易中包含指令操作 | |||
- `CONTRACT_OPERATION`交易中包含合约操作 | |||
### 4. 控制逻辑 | |||
`JD Chain`[交易](transaction.md)执行前会验证交易的签名信息,签名主要包含**节点签名**和**终端用户签名**。 | |||
#### 4.1 节点身份验证 | |||
> 网关提交交易前,会使用网关配置文件中配置的公私钥信息所代表的节点用户,自动添加签名到节点签名列表中。 | |||
`JD Chain`运行时网络执行交易前,要求节点签名用户至少有一个具有`LedgerPermission.APPROVE_TX`权限。 | |||
#### 4.2 终端用户验证 | |||
提交交易前,要求添加终端用户签名信息。 | |||
执行到具体操作前会校验相应账本/交易权限,策略都是至少有一个终端用户包含操作权限。 | |||
例如:创建用户账户操作执行前会校验所有终端用户签名中的用户**至少有一个**包含`LedgerPermission.REGISTER_USER`权限。 | |||
### 5. SDK | |||
可在`ledger.init`中配置好角色权限,在组网成功后所配置的角色和权限信息会写入到相关账本中。 | |||
也可以通过以下相关`SDK`代码向运行中`JD Chain`网络执行用户相关操作: | |||
#### 5.1 注册用户 | |||
默认使用`ed25519`编码 | |||
创建公私钥对: | |||
```java | |||
BlockchainKeypair user = BlockchainKeyGenerator.getInstance().generate(); | |||
``` | |||
从已存在的公私钥恢复: | |||
```java | |||
PubKey pubKey = KeyGenUtils.decodePubKey("7VeRLdGtSz1Y91gjLTqEdnkotzUfaAqdap3xw6fQ1yKHkvVq"); | |||
PrivKey privKey = KeyGenUtils.decodePrivKey("177gjzHTznYdPgWqZrH43W3yp37onm74wYXT4v9FukpCHBrhRysBBZh7Pzdo5AMRyQGJD7x", "DYu3G8aGTMBW1WrTw76zxQJQU4DHLw9MLyy7peG4LKkY"); | |||
BlockchainKeypair user = new BlockchainKeypair(pubKey, privKey); | |||
``` | |||
注册用户: | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
txTemp.users().register(user.getIdentity()); | |||
``` | |||
#### 5.2 创建角色 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 创建角色 MANAGER ,并设置可以写数据账户,能执行交易 | |||
txTemp.security().roles().configure("MANAGER") | |||
.enable(LedgerPermission.WRITE_DATA_ACCOUNT) | |||
.disable(LedgerPermission.REGISTER_USER) | |||
.enable(TransactionPermission.DIRECT_OPERATION); | |||
``` | |||
#### 5.3 用户赋权 | |||
```java | |||
TransactionTemplate txTemp = blockchainService.newTransaction(ledger); | |||
// 给用户设置 MANAGER 角色权限 | |||
txTemp.security().authorziations().forUser(Bytes.fromBase58("LdeNr7H1CUbqe3kWjwPwiqHcmd86zEQz2VRye")).authorize("MANAGER").unauthorize("DEFAULT").setPolicy(RolesPolicy.UNION); | |||
// 或者 | |||
txTemp.security().authorziations().forUser(user.getIdentity()).authorize("MANAGER").unauthorize("DEFAULT").setPolicy(RolesPolicy.UNION); | |||
``` |