动机

使用node-sdk进行了简单交易,有一些问题没有解决。

  1. cryptogen模块生成的证书如何给node-sdk进行使用。
  2. node-sdk中的主要模块包含哪些可用函数,可以完成哪些工作。

架构设计

sdk在fabric中扮演的角色:

  • 通过节点的gRPC协议访问安装在peer节点上的链码
  • 将peer返回的背书,封装成交易,发送给orderer,作排序
  • 分为以下三大模块:
  • fabric-ca-client
  • fabric-client
  • fabric-network

fabric-ca-client

与fabric-ca 模块交互,主要负责证书管理,如向ca申请证书 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
// 镜像
// curl -sL https://deb.nodesource.com/setup_10.x | sudo -E bash -
// nodejs
// sudo apt-get install -y nodejs
// node -v # v10.14.1
// express
// https://www.cnblogs.com/wgl0126/p/9290157.html

// 导入相关模块和类
// FabricCAServices:
// FileSystemWallet:以文件方式管理证书的类
// X509WalletMixin:证书x509处理类
// fs:文件信息读取类
// path:路径信息处理类
const FabricCAServices = require('fabric-ca-client');
const {FileSystemWallet, X509WalletMixin} = require('fabric-network');
const fs = require('fs');
const path = require('path');

// 连接文件路径
const ccpPath = path.resolve(__dirname, 'connection', 'connection-org1.json');
// 对连接文件进行异步读取
const ccpJSON = fs.readFileSync(ccpPath, 'utf8');
// json解析
const ccp = JSON.parse(ccpJSON);

async function main() {
try {
// 域名为ca.own.example.com的ca:相关信息
const caInfo = ccp.certificateAuthorities['ca.own.example.com'];
const caTLSCACerts = caInfo.tlsCACerts.pem;

// 创建FabricCAServices对象
// caInfo.url:域名
// trustedRoots:TLS通信证书证书
// verify:是否进行验证
// caInfo.caName:ca名称
const ca = new FabricCAServices(caInfo.url, { trustedRoots: caTLSCACerts, verify: false }, caInfo.caName);

// 创建文件系统管理证书
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);

// 检查admin是否存在
const adminExists = await wallet.exists('admin');
if (adminExists) {
console.log('An identity for the admin user "admin" already exists in the wallet');
return;
}

// 登记信心
const enrollment = await ca.enroll({ enrollmentID: 'admin', enrollmentSecret: 'adminpw' });
// 生成X509格式的证书(组织编号、证书、私钥)
const identity = X509WalletMixin.createIdentity('OwnMSP', enrollment.certificate, enrollment.key.toBytes());
// 导入文件系统
await wallet.import('admin', identity);
console.log('Successfully enrolled admin user "admin" and imported it into the wallet');
} catch (error) {
console.error(`Failed to enroll admin user "admin": ${error}`);
process.exit(1);
}
}

main();

fabric-client

负责与fabric网络交互,如发送提案,发送交易监听区块等,试用场景偏向与特殊业务定制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 创建身份标识
let client = new Client()
let keyPem = fs.readFileSync('.msp/keystore/user-key.pem','utf-8')
let certPem = fs.readFileSync('./msp/signcerts/user-cert.pem','utf-8')
let user = await client.createUser({ //创建User对象
username: 'user', //用户名称
mspid: 'Org1MSP', //所属MSP的ID
cryptoContent: {
privateKeyPEM: keyPem, //用户私钥
signedCertPEM: certPem //用户证书
},
skipPersistence: true //不计入缓存
})
client.setUserContext(user,true) //设置为client的当前身份

// 创建通道对象,并添加peer与orderer
let channel = client.newChannel('mychannel')
channel.addPeer(client.newPeer('grpc://127.0.0.1:7051'))
channel.addOrderer(client.newOrderer('grpc://127.0.0.1:7050'))

// 发送invoke交易
// 构建请求参数
let req = {
chaincodeId: 'mycc',
fcn: 'invoke',
args: ['10'],
txId: client.newTransactionID() // 创建交易id
}
// 获取背书
let prsp = await channel.sendTransactionProposal(req)
// 提交交易
let rsp = await channel.sendTransaction({
proposalResponses: prsp[0],
proposal: prsp[1]
})

fabric-network

负责与fabric网络交互,如发送交易,监听区块等,是fabric-client的封装,适用场景偏向普通业务直接集成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// 导入相关对象
// FileSystemWallet:以文件方式管理证书的类
// Gateway:网关对象
// path:路径处理对象
const { FileSystemWallet, Gateway } = require('fabric-network');
const path = require('path');

// 连接文件所在路径
const ccpPath = path.resolve(__dirname, 'connection', 'connection-org1.json');

// 异步函数
async function main() {
try {
// 证书存储路径
const walletPath = path.join(process.cwd(), 'wallet');
// 证书管理对象
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);

// 检查文件系统中是否存在lable为admin的一套证书
const userExists = await wallet.exists('admin');
if (!userExists) {
console.log('An identity for the admin "admin" does not exist in the wallet');
return;
}

// 创建网关对象
const gateway = new Gateway();
// 连接到连接文件所说明的Fabric网络
await gateway.connect(ccpPath, { wallet, identity: 'admin', discovery: { enabled: true, asLocalhost: false } });
// 连接到通道名称为channelss的网络,返回网络对象network
const network = await gateway.getNetwork('channelss');
// 获取名称为cc的智能合约
const contract = network.getContract('cc');

// 仓单申请
// peer chaincode invoke -n mycc -c '{"Args":["warehousecreate","001","20201-1-6","sugar","edible","3000kg","20000","50000000","8","IBM","xiaozhao","0771-4919611","123456789","Facebook","xiaohai","0771-4919611","147258369","s1",""]}' -C myc
// 提交仓单申请
var result = await contract.submitTransaction("warehousecreate","001","20201-1-6","sugar","edible","3000kg","20000","50000000","8","IBM",
"xiaozhao","0771-4919611","123456789","Facebook","xiaohai","0771-4919611","147258369","s1","");
console.log(`contract.submitTransaction('warehousecreate', "001", "20201-1-6", "sugar", "edible", "3000kg", "20000",
"50000000", "8", "IBM", "xiaozhao", "0771-4919611", "123456789", "Facebook", "xiaohai", "0771-4919611",
"987654321", "147258369", "s1", "")`);
console.log(byteToString(result));

// 网关对象断开连接
await gateway.disconnect();
} catch (error) {
console.error(`${error}`);
process.exit(1);
}
}
// 解析返回字节的数据
function byteToString(arr)
{
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
main();

connection-org1.json*

连接文件,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
{
"name": "kafka",
"version": "1.0.0",
"client": {
"organization": "Org1MSP",
"connection": {
"timeout": {
"peer": {
"endorser": "300"
}
}
}
},
"organizations": {
"Org1": {
"mspid": "Org1MSP",
"peers": [
"peer0.org1.example.com",
"peer1.org1.example.com"
]
},
"Org2": {
"mspid": "Org2MSP",
"peers": [
"peer0.org2.example.com",
"peer1.org2.example.com"
]
},
"OrdererOrg": {
"mspid": "OrdererMSP",
"peers": [
"orderer0.example.com",
"orderer1.example.com",
"orderer2.example.com"
]
}
},
"orderers": {
"orderer0.example.com": {
"url": "grpcs://orderer0.example.com:7050",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICMjCCAdigAwIBAgIRALMhPZa6UqilJuPaGa+b/AgwCgYIKoZIzj0EAwIwYzEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nFDASBgNVBAoTC2V4YW1wbGUuY29tMRowGAYDVQQDExF0bHNjYS5leGFtcGxlLmNv\nbTAeFw0yMDA5MTgwMjMwMDBaFw0zMDA5MTYwMjMwMDBaMGMxCzAJBgNVBAYTAkNO\nMQ4wDAYDVQQIEwVIZU5hbjESMBAGA1UEBxMJWmhlbmdaaG91MRQwEgYDVQQKEwtl\neGFtcGxlLmNvbTEaMBgGA1UEAxMRdGxzY2EuZXhhbXBsZS5jb20wWTATBgcqhkjO\nPQIBBggqhkjOPQMBBwNCAATARxJ7STXZAWvgbtJAcHfTIEuZG86wcCESYx9P0VDL\nCGs545MDlOGxxvjHwldr1wW2lQtLSsligNt44Ft/BXn5o20wazAOBgNVHQ8BAf8E\nBAMCAaYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQF\nMAMBAf8wKQYDVR0OBCIEIOvcFJuXX+akCLbnwCOqKmQLWJnAFbgdPXLJH8KRxmCQ\nMAoGCCqGSM49BAMCA0gAMEUCIQCKqPOfBrjJByYbjicTBe9pytAk7ONntgAjJFSL\nZirdhwIgfNhGQ9zsOMgugwnAjIKR2wiHWviDhq5y/DDe2p1dAYg=\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "orderer0.example.com",
"hostnameOverride": "orderer0.example.com"
}
},
"orderer1.example.com": {
"url": "grpcs://orderer1.example.com:7050",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICMjCCAdigAwIBAgIRALMhPZa6UqilJuPaGa+b/AgwCgYIKoZIzj0EAwIwYzEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nFDASBgNVBAoTC2V4YW1wbGUuY29tMRowGAYDVQQDExF0bHNjYS5leGFtcGxlLmNv\nbTAeFw0yMDA5MTgwMjMwMDBaFw0zMDA5MTYwMjMwMDBaMGMxCzAJBgNVBAYTAkNO\nMQ4wDAYDVQQIEwVIZU5hbjESMBAGA1UEBxMJWmhlbmdaaG91MRQwEgYDVQQKEwtl\neGFtcGxlLmNvbTEaMBgGA1UEAxMRdGxzY2EuZXhhbXBsZS5jb20wWTATBgcqhkjO\nPQIBBggqhkjOPQMBBwNCAATARxJ7STXZAWvgbtJAcHfTIEuZG86wcCESYx9P0VDL\nCGs545MDlOGxxvjHwldr1wW2lQtLSsligNt44Ft/BXn5o20wazAOBgNVHQ8BAf8E\nBAMCAaYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQF\nMAMBAf8wKQYDVR0OBCIEIOvcFJuXX+akCLbnwCOqKmQLWJnAFbgdPXLJH8KRxmCQ\nMAoGCCqGSM49BAMCA0gAMEUCIQCKqPOfBrjJByYbjicTBe9pytAk7ONntgAjJFSL\nZirdhwIgfNhGQ9zsOMgugwnAjIKR2wiHWviDhq5y/DDe2p1dAYg=\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "orderer1.example.com",
"hostnameOverride": "orderer1.example.com"
}
},
"orderer2.example.com": {
"url": "grpcs://orderer2.example.com:7050",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICMjCCAdigAwIBAgIRALMhPZa6UqilJuPaGa+b/AgwCgYIKoZIzj0EAwIwYzEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nFDASBgNVBAoTC2V4YW1wbGUuY29tMRowGAYDVQQDExF0bHNjYS5leGFtcGxlLmNv\nbTAeFw0yMDA5MTgwMjMwMDBaFw0zMDA5MTYwMjMwMDBaMGMxCzAJBgNVBAYTAkNO\nMQ4wDAYDVQQIEwVIZU5hbjESMBAGA1UEBxMJWmhlbmdaaG91MRQwEgYDVQQKEwtl\neGFtcGxlLmNvbTEaMBgGA1UEAxMRdGxzY2EuZXhhbXBsZS5jb20wWTATBgcqhkjO\nPQIBBggqhkjOPQMBBwNCAATARxJ7STXZAWvgbtJAcHfTIEuZG86wcCESYx9P0VDL\nCGs545MDlOGxxvjHwldr1wW2lQtLSsligNt44Ft/BXn5o20wazAOBgNVHQ8BAf8E\nBAMCAaYwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA8GA1UdEwEB/wQF\nMAMBAf8wKQYDVR0OBCIEIOvcFJuXX+akCLbnwCOqKmQLWJnAFbgdPXLJH8KRxmCQ\nMAoGCCqGSM49BAMCA0gAMEUCIQCKqPOfBrjJByYbjicTBe9pytAk7ONntgAjJFSL\nZirdhwIgfNhGQ9zsOMgugwnAjIKR2wiHWviDhq5y/DDe2p1dAYg=\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "orderer2.example.com",
"hostnameOverride": "orderer2.example.com"
}
}
},
"peers": {
"peer0.org1.example.com": {
"url": "grpcs://peer0.org1.example.com:7051",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICRTCCAeygAwIBAgIRAKsB3hyP3C4vImu1v5v6rn0wCgYIKoZIzj0EAwIwbTEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRsc2NhLm9yZzEu\nZXhhbXBsZS5jb20wHhcNMjAwOTE4MDIzMDAwWhcNMzAwOTE2MDIzMDAwWjBtMQsw\nCQYDVQQGEwJDTjEOMAwGA1UECBMFSGVOYW4xEjAQBgNVBAcTCVpoZW5nWmhvdTEZ\nMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxzY2Eub3JnMS5l\neGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOSlikRSI3U4PT5t\nDWdiCr1UKqGBzlmsW8oVd1MRkp1GjwyX897iw2oNXiticKal5sVxPdRD645GzuNK\njZ5+3XajbTBrMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYI\nKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgVIfqiPN7rD0cv7vP\npH+hzY6N20nnlLtD4KiSz3FlC3AwCgYIKoZIzj0EAwIDRwAwRAIgOA7gzAI6xdO9\nBBK/CKJQNjBMA6/5F+JNKgj4YlcAb6YCIBP39FwkwwA854CmUvBSMPyiCfdGrcgs\nbNup+aXeu9xQ\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "peer0.org1.example.com",
"hostnameOverride": "peer0.org1.example.com"
}
},
"peer1.org1.example.com": {
"url": "grpcs://peer1.org1.example.com:7051",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICRTCCAeygAwIBAgIRAKsB3hyP3C4vImu1v5v6rn0wCgYIKoZIzj0EAwIwbTEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nGTAXBgNVBAoTEG9yZzEuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRsc2NhLm9yZzEu\nZXhhbXBsZS5jb20wHhcNMjAwOTE4MDIzMDAwWhcNMzAwOTE2MDIzMDAwWjBtMQsw\nCQYDVQQGEwJDTjEOMAwGA1UECBMFSGVOYW4xEjAQBgNVBAcTCVpoZW5nWmhvdTEZ\nMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxzY2Eub3JnMS5l\neGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABOSlikRSI3U4PT5t\nDWdiCr1UKqGBzlmsW8oVd1MRkp1GjwyX897iw2oNXiticKal5sVxPdRD645GzuNK\njZ5+3XajbTBrMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYI\nKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgVIfqiPN7rD0cv7vP\npH+hzY6N20nnlLtD4KiSz3FlC3AwCgYIKoZIzj0EAwIDRwAwRAIgOA7gzAI6xdO9\nBBK/CKJQNjBMA6/5F+JNKgj4YlcAb6YCIBP39FwkwwA854CmUvBSMPyiCfdGrcgs\nbNup+aXeu9xQ\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "peer1.org1.example.com",
"hostnameOverride": "peer1.org1.example.com"
}
},
"peer0.org2.example.com": {
"url": "grpcs://peer0.org2.example.com:7051",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICRjCCAeygAwIBAgIRAPtcvNUd8zEYhXPwZ8J+4FgwCgYIKoZIzj0EAwIwbTEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRsc2NhLm9yZzIu\nZXhhbXBsZS5jb20wHhcNMjAwOTE4MDIzMDAwWhcNMzAwOTE2MDIzMDAwWjBtMQsw\nCQYDVQQGEwJDTjEOMAwGA1UECBMFSGVOYW4xEjAQBgNVBAcTCVpoZW5nWmhvdTEZ\nMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxzY2Eub3JnMi5l\neGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLZBfLx/IL6eLEB2\nzKkuSbj9PWUlbqLNQvH6W6FC4hsjFEuVDtfaXuQnFc0CsiTgj2ruGAX/oNPFG6aL\n2Arstg+jbTBrMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYI\nKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQg5lgMgWlMMeZqoQt8\naq3xz9d1/V1BtEg2CJodigCOeb8wCgYIKoZIzj0EAwIDSAAwRQIhAL4zz1kzCpO0\nsl1HBxhgrsAhWY8qbyFwkTQbWRbyncy8AiAxBoQJE0YEdDrwbscuH987cUgylRWz\nyNPu+lM0nwTI3w==\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "peer0.org2.example.com",
"hostnameOverride": "peer0.org2.example.com"
}
},
"peer1.org2.example.com": {
"url": "grpcs://peer1.org2.example.com:7051",
"tlsCACerts": {
"pem": "-----BEGIN CERTIFICATE-----\nMIICRjCCAeygAwIBAgIRAPtcvNUd8zEYhXPwZ8J+4FgwCgYIKoZIzj0EAwIwbTEL\nMAkGA1UEBhMCQ04xDjAMBgNVBAgTBUhlTmFuMRIwEAYDVQQHEwlaaGVuZ1pob3Ux\nGTAXBgNVBAoTEG9yZzIuZXhhbXBsZS5jb20xHzAdBgNVBAMTFnRsc2NhLm9yZzIu\nZXhhbXBsZS5jb20wHhcNMjAwOTE4MDIzMDAwWhcNMzAwOTE2MDIzMDAwWjBtMQsw\nCQYDVQQGEwJDTjEOMAwGA1UECBMFSGVOYW4xEjAQBgNVBAcTCVpoZW5nWmhvdTEZ\nMBcGA1UEChMQb3JnMi5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxzY2Eub3JnMi5l\neGFtcGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABLZBfLx/IL6eLEB2\nzKkuSbj9PWUlbqLNQvH6W6FC4hsjFEuVDtfaXuQnFc0CsiTgj2ruGAX/oNPFG6aL\n2Arstg+jbTBrMA4GA1UdDwEB/wQEAwIBpjAdBgNVHSUEFjAUBggrBgEFBQcDAgYI\nKwYBBQUHAwEwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQg5lgMgWlMMeZqoQt8\naq3xz9d1/V1BtEg2CJodigCOeb8wCgYIKoZIzj0EAwIDSAAwRQIhAL4zz1kzCpO0\nsl1HBxhgrsAhWY8qbyFwkTQbWRbyncy8AiAxBoQJE0YEdDrwbscuH987cUgylRWz\nyNPu+lM0nwTI3w==\n-----END CERTIFICATE-----\n"
},
"grpcOptions": {
"ssl-target-name-override": "peer1.org2.example.com",
"hostnameOverride": "peer1.org2.example.com"
}
}
}
}

wallet*

目录结构:

admin是客户端从ca处申请的证书。

User1@org1.example.com是cryptogen生成证书。

.
├── admin
│ ├── 6efed24e38799a56532210c985bf9ac7f0367bdf2c094529408ba06bb8ad3824-priv //私钥
│ ├── 6efed24e38799a56532210c985bf9ac7f0367bdf2c094529408ba06bb8ad3824-pub //公钥
│ └── admin // 用户相关信息
└── User1@org1.example.com
├── msp
│ ├── admincerts // 组织管理员证书
│ │ └── User1@org1.example.com-cert.pem
│ ├── cacerts // 组织根证书
│ │ └── ca.org1.example.com-cert.pem
│ ├── keystore // 私钥
│ │ └── 7365294a1793d88ab82ce6d825bb5fd8e828deed402e25e37f0e2999e9d68278_sk
│ ├── signcerts // 证书
│ │ └── User1@org1.example.com-cert.pem
│ └── tlscacerts // TLS通信证书(客户端使用)
│ └── tlsca.org1.example.com-cert.pem
└── tls
├── ca.crt // 组织根证书
├── client.crt // 证书
└── client.key // 私钥

admin

1
{"name":"admin","mspid":"OwnMSP","roles":null,"affiliation":"","enrollmentSecret":"","enrollment":{"signingIdentity":"6efed24e38799a56532210c985bf9ac7f0367bdf2c094529408ba06bb8ad3824","identity":{"certificate":"-----BEGIN CERTIFICATE-----\nMIICADCCAaagAwIBAgIUcG/93ySJHKn3WeNLxAPZ5R6RZo0wCgYIKoZIzj0EAwIw\ncTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh\nbiBGcmFuY2lzY28xGDAWBgNVBAoTD293bi5leGFtcGxlLmNvbTEbMBkGA1UEAxMS\nY2Eub3duLmV4YW1wbGUuY29tMB4XDTIwMDExMjA3MDkwMFoXDTIxMDExMTA3MTQw\nMFowITEPMA0GA1UECxMGY2xpZW50MQ4wDAYDVQQDEwVhZG1pbjBZMBMGByqGSM49\nAgEGCCqGSM49AwEHA0IABAJsM0RFBuRTCE63qdnIqAxloJJCzpGtYWX8OZQ/e0RL\nPcwVYm3g5EaPfO6ibVyO02wqTSMkWhZmEq5YsirV8PSjbDBqMA4GA1UdDwEB/wQE\nAwIHgDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTK3G8uVQqxI5E+65v3RnXkWKBN\nxTArBgNVHSMEJDAigCChgxTzM7uvN9514LK8agb2vMflWvGE9QuHT72cM6bUdDAK\nBggqhkjOPQQDAgNIADBFAiEAi1o7+Zfj0jYl/OcI+Js9fjfcxlDDiQDrLYWvYgWQ\ntagCIBv1goo/6IYHhN4xL+Tn66qbwqaDljAkCrhm5WNdihhT\n-----END CERTIFICATE-----\n"}}}

lable:admin。

MSPID:OwnMSP。

roles:角色。

affiliation:从属关系,指组织中的部门。

enrollmentSecret:登记密码。

enrollment:登记信息。

signingIdentity:签名身份 。

certificate:证书。

相关实验

cryptogen模块改造为fabric-network可以使用的格式

cryptogen模块生成的证书无法被fabric-network直接使用。下面是处理代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 导入相关类
const {FileSystemWallet, X509WalletMixin} = require('fabric-network');
const fs = require('fs');
const path = require('path');

async function main() {
try {
// 路径
const walletPath = path.join(process.cwd(), 'wallet');
// 文件系统类
const wallet = new FileSystemWallet(walletPath);
// 读取证书
var certificate = fs.readFileSync('./wallet/User1@org1.example.com/User1@org1.example.com-cert.pem', 'utf-8');
console.log("certificate: " + certificate.toString());
// 读取私钥
var key = fs.readFileSync('./wallet/User1@org1.example.com/7365294a1793d88ab82ce6d825bb5fd8e828deed402e25e37f0e2999e9d68278_sk', 'utf-8');
console.log("key: " + key.toString());
// 生成X509证书
const identity = X509WalletMixin.createIdentity('OwnMSP', certificate, key);
// 导入文件系统,标识为User1
await wallet.import('User1', identity);
} catch (error) {
console.error("error.....");
process.exit(1);
}
}
main();

查块命令

fabric-network中给的示例代码并没有给查块的示例代码。

1
2
3
4
5
6
7
8
9
10
// 创建网关对象
const gateway = new Gateway();
// 连接到连接文件所说明的Fabric网络
await gateway.connect(ccpPath, { wallet, identity: 'admin', discovery: { enabled: true, asLocalhost: false } });
// 连接到通道名称为channelss的网络,返回网络对象network
const network = await gateway.getNetwork('channelss');
const channel = network.getChannel()
const data = channel.queryBlock(1)
// const data = channel.queryBlockByHash("hash")
// const data = channel.queryBlockByTxID("txid")

代码和示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
const { FileSystemWallet, Gateway } = require('fabric-network');
require('fabric-client');
const path = require('path');
var querystring= require('querystring');
const ccpPath = path.resolve(__dirname, 'connection', 'connection.json');

async function main() {
try {
// wallet
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath);
console.log("walletPath:", walletPath);
const userExists = await wallet.exists('Admin@org2.example.com');

if (!userExists) {
console.log('user not exists!');
return;
}

// connect
const gateway = new Gateway();
await gateway.connect(ccpPath, { wallet, identity: 'Admin@org2.example.com', discovery: { enabled: true, asLocalhost: false } });

const network = await gateway.getNetwork('mychannel');
var contract = network.getContract('mycc');
var channel = await network.getChannel();
var fabric_client = await gateway.getClient();

// getChannelPeers()
console.log("############","getChannelPeers()","############");
console.log("\nchannel.toString():\n", channel.toString());
console.log("\nchannel.getChannelPeers():\n", channel.getChannelPeers());

// queryBlock()
console.log("############","queryBlock()","############");
var block = await channel.queryBlock(1);
console.log("\nchannel.queryBlock():\n",block);

// queryBlockByHash()
// console.log("############","queryBlockByHash()","############");
// string to bytes possible have err.
// var hash = stringToByte("0b347322a14981acc89203807f250728faa6b6e897f6c78d01f2f6ab5a116682");
// block = await channel.queryBlockByHash(hash);
// console.log("\nchannel.queryBlockByHash():\n",block);

// queryBlockByTxID()
console.log("############","queryBlockByTxID()","############");
block = await channel.queryBlockByTxID("14b06d93331007710210009747f3dd1c339535db97b7642e4871397da8da6090");
console.log("\nchannel.queryBlockByTxID():\n",block);

// evaluateTransaction()
console.log("############","evaluateTransaction()","############");

var result = await contract.evaluateTransaction('query', 'a');
var jsonstr =JSON.parse(byteToString(result));
console.log("\ncontract.evaluateTransaction('query', 'a'):\n", jsonstr);

// submitTransaction
var result_ = await contract.submitTransaction('invoke', 'b', 'a', '1');
console.log("\ncontract.submitTransaction('invoke', 'b', 'a', '1')\n",byteToString(result_));

// evaluateTransaction()
var result__ = await contract.evaluateTransaction('query', 'a');
var jsonstr__ = JSON.parse(byteToString(result__));
console.log("\ncontract.evaluateTransaction('query', 'a'):\n", jsonstr__);
await gateway.disconnect();
} catch (error) {
console.error(`Error: ${error}`);
process.exit(1);
}
}

function byteToString(arr)
{
if(typeof arr === 'string') {
return arr;
}
var str = '',
_arr = arr;
for(var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if(v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for(var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}

function stringToByte(str) {
var bytes = new Array();
var len, c;
len = str.length;
for(var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if(c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if(c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}

main();

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
############ getChannelPeers() ############

channel.toString():
{"name":"mychannel","orderers":["Orderer:{url:grpcs://orderer0.example.com:7050}","Orderer:{url:grpcs://orderer1.example.com:7050}","Orderer:{url:grpcs://orderer2.example.com:7050}"],"peers":["Peer:{url:grpcs://peer0.org2.example.com:7051}"]}

channel.getChannelPeers():
[ ChannelPeer {
_mspid: 'Org2MSP',
_channel:
Channel {
_name: 'mychannel',
_channel_peers: [Map],
_anchor_peers: [],
_orderers: [Map],
_kafka_brokers: [],
_clientContext: [Client],
_msp_manager: [MSPManager],
_discovery_interests: [Map],
_discovery_results: [Object],
_last_discover_timestamp: 1601358389373,
_use_discovery: true,
_as_localhost: false,
_endorsement_handler: [DiscoveryEndorsementHandler],
_commit_handler: [BasicCommitHandler],
_last_refresh_request: [Object] },
_name: 'peer0.org2.example.com:7051',
_peer:
Peer {
_options: [Object],
useWaitForReady: false,
clientCert: undefined,
_url: 'grpcs://peer0.org2.example.com:7051',
_endpoint: [Endpoint],
_name: 'peer0.org2.example.com:7051',
_request_timeout: 45000,
_grpc_wait_for_ready_timeout: 3000,
_endorserClient: [ServiceClient],
_discoveryClient: [ServiceClient] },
_roles: {} } ]
############ queryBlock() ############

channel.queryBlock():
{ header:
{ number: '1',
previous_hash:
'd3f3900e3d0152a2ac076305768c37e82f447cddebacd1edbf1a5110393e5f19',
data_hash:
'12a2fccea15484d28ab733328f574dee681d1eb350d3a3c5e9d552239f845442' },
data: { data: [ [Object] ] },
metadata: { metadata: [ [Object], [Object], [Array] ] } }
############ queryBlockByTxID() ############

channel.queryBlockByTxID():
{ header:
{ number: '5',
previous_hash:
'78a945eee955bdc53311d7dc89f04b8f63b80b2c2603f1ea4c48579c4488416c',
data_hash:
'88094543403f16a488657711c47086939164c154c90b97f46a82bad0dbb550ac' },
data: { data: [ [Object] ] },
metadata: { metadata: [ [Object], [Object], [Array] ] } }
############ evaluateTransaction() ############

contract.evaluateTransaction('query', 'a'):
314

contract.submitTransaction('invoke', 'b', 'a', '1')


contract.evaluateTransaction('query', 'a'):
315
[root@localhost web-9-23]#

queryBlock(blockNumber, target, useAdmin, skipDecode)

Queries the ledger on the target peer for Block by block number.

Parameters:
Name Type Description
blockNumber number The number of the Block in question.
target Peer Optional. The peer to send this query to. If no target is passed, the query is sent to the first peer that was added to the channel object.
useAdmin boolean Optional. Indicates that the admin credentials should be used in making this call to the peer.
skipDecode boolean Optional. If true, this function returns an encoded block.
Returns:

A Promise for a Block at the blockNumber slot in the ledger, fully decoded into an object.

  • Type

    Promise

queryBlockByHash(blockHash, target, useAdmin, skipDecode)

Queries the ledger on the target peer for a Block by block hash.

Parameters:
Name Type Description
blockHash Array. of the Block in question.
target Peer Optional. The peer to send the query to. If no target is passed, the query is sent to the first peer that was added to the channel object.
useAdmin boolean Optional. Indicates that the admin credentials should be used in making this call to the peer.
skipDecode boolean Optional. If true, this function returns an encoded block.
Returns:

A Promise for a Block matching the hash, fully decoded into an object.

  • Type

    Promise


queryBlockByTxID(tx_id, target, useAdmin, skipDecode)

Queries the ledger on the target peer for a Block TransactionID.

Parameters:
Name Type Description
tx_id string The TransactionID of the Block in question.
target Peer Optional. The peer to send the query to. If no target is passed, the query is sent to the first peer that was added to the channel object.
useAdmin boolean Optional. Indicates that the admin credentials should be used in making this call to the peer.
skipDecode boolean Optional. If true, this function returns an encoded block.
Returns:

A Promise for a Block matching the tx_id, fully decoded into an object.

  • Type

    Promise

官网

https://hyperledger.github.io/fabric-sdk-node/release-1.4/Channel.html