This repository has been archived by the owner on Mar 20, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 24
/
Copy pathblockchain.js
287 lines (265 loc) · 10 KB
/
blockchain.js
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/**
* Copyright 2017 HUAWEI. All Rights Reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* @file, definition of the BlockChain class, which is used to interact with backend's blockchain system
*/
'use strict'
var path = require('path');
var Blockchain = class {
constructor(configPath) {
var config = require(configPath);
if(config.hasOwnProperty('fabric')) {
var fabric = require('../fabric/fabric.js');
this.bcType = 'fabric';
this.bcObj = new fabric(configPath);
}
else if(config.hasOwnProperty('sawtooth')) {
var sawtooth = require('../sawtooth/sawtooth.js')
this.bcType = 'sawtooth';
this.bcObj = new sawtooth(configPath);
}
else if(config.hasOwnProperty('iroha')) {
var iroha = require('../iroha/iroha.js');
this.bcType = 'iroha';
this.bcObj = new iroha(configPath);
}
else {
this.bcType = 'unknown';
throw new Error('Unknown blockchain config file ' + configPath);
}
}
/**
* return the blockchain type
* @return {string}
*/
gettype() {
return this.bcType;
}
/**
* prepare the underlying blockchain environment, e.g. join channel for fabric's peers
* the function should be called only once for the same backend's blockchain system
* even if multiple Blockchain objects are instantiated
* @return {Promise}
*/
init() {
return this.bcObj.init();
}
/**
* create needed materials for multiple clients, e.g create account for each client and return the key pairs
* @number, number of clients
* @return {Promise}, array of generated JSON object for each client. The array length should be equal to the input number
* Each object should be passed to corresponding client and be used as a argument of getContext
*/
prepareClients (number) {
return this.bcObj.prepareClients(number);
}
/**
* install smart contract on peers
* the detailed smart contract's information should be defined in the configuration file
* @return {Promise}
*/
installSmartContract() {
return this.bcObj.installSmartContract();
}
/**
* get a system context that will be used to interact with backend's blockchain system
* @name {string}, name of the context
* @args {object}, a JSON object that contains required materials for the client to interact with SUT, e.g key pairs for the client
* the actual format of the object is specified by each blochchain interface implementation
* @return {Promise.resolve(context)}
*/
getContext(name, args) {
return this.bcObj.getContext(name, args);
}
/**
* release the system context
* @return {Promise}
*/
releaseContext(context) {
return this.bcObj.releaseContext(context);
}
/**
* perform an 'invoke' transaction
* @context {Object}, context returned by getContext
* @contractID {string}, smart contract's id
* @contractVer {string}, smart contract's version
* @args {object}, invoking arguments
* @timeout {Number}, return directly after that time in seconds has elapsed
* @return {Promise.resolve(Object)}, return the key informations of the transaction, the format is
* {
* 'id': transaction's id
* 'status': status of the transaction, should be:
* - 'created': successfully created, but not validated or committed yet
* - 'success': successfully validated and committed in the ledger
* 'time_create': time(ms) that the transaction was created
* 'time_final': time(ms) that the transaction was known to be final and committed in ledger
* 'result': response payloads of the transaction request
* ...... : blockchain platform specific values
* }
*/
invokeSmartContract(context, contractID, contractVer, args, timeout) {
if(typeof timeout !== 'number' || timeout < 0) {
return this.bcObj.invokeSmartContract(context, contractID, contractVer, args, 120);
}
else {
return this.bcObj.invokeSmartContract(context, contractID, contractVer, args, timeout);
}
}
/**
* * perform a 'query' transaction to get state from the ledger
* @return {Promsie}, same format as invokeSmartContract's returning
*/
queryState(context, contractID, contractVer, key) {
return this.bcObj.queryState(context, contractID, contractVer, key);
}
/**
* txStatistics = {
* succ : , // number of succeeded txs
* fail : , // number of failed txs
* create : {min: , max: }, // min/max time of tx created, in second
* final : {min: , max: }, // min/max time of tx becoming final, in second
* delay : {min: , max: , sum: , detail:[...]}, // min/max/sum time of txs' processing delay, in second
* // obsoleted throughput : {time: ,...}, // tps of each time slot
* out : [] // user defined output data
* // obsoleted others: {object} // blockchain platform specific values
* }
*/
/**
* generate and return the default statistics of transactions
* @ results {Array}, results of 'invoke'/'query' transactions
* @ detail {Boolean}, whether to keep detailed delay history
* @ return {Promise.resolve(txStatistics)}
*/
// TODO: should be moved to a dependent 'analyser' module in which to do all result analysing work
getDefaultTxStats(results, detail) {
var succ = 0, fail = 0, delay = 0;
var minFinal, maxFinal, minCreate, maxCreate;
var minDelay = 100000, maxDelay = 0;
var delays = [];
for(let i = 0 ; i < results.length ; i++) {
let stat = results[i];
let create = stat['time_create'];
if(typeof minCreate === 'undefined') {
minCreate = create;
maxCreate = create;
}
else {
if(create < minCreate) {
minCreate = create;
}
if(create > maxCreate) {
maxCreate = create;
}
}
if(stat.status === 'success') {
succ++;
let final = stat['time_final'];
let d = (final - create) / 1000;
if(typeof minFinal === 'undefined') {
minFinal = final;
maxFinal = final;
}
else {
if(final < minFinal) {
minFinal = final;
}
if(final > maxFinal) {
maxFinal = final;
}
}
delay += d;
if(d < minDelay) {
minDelay = d;
}
if(d > maxDelay) {
maxDelay = d;
}
if(detail) {
delays.push(d);
}
}
else {
fail++;
}
}
var stats = {
'succ' : succ,
'fail' : fail,
'create' : {'min' : minCreate/1000, 'max' : maxCreate/1000}, // convert to second
'final' : {'min' : minFinal/1000, 'max' : maxFinal/1000 },
'delay' : {'min' : minDelay, 'max' : maxDelay, 'sum' : delay, 'detail': (detail?delays:[]) },
'out' : []
};
return stats;
}
/**
* merge an array of default 'txStatistics', the merged result is in the first object
* @ results {Array}, txStatistics array
* @ return {Number}, 0 if failed; otherwise 1
*/
static mergeDefaultTxStats(results) {
try{
// skip invalid result
var skip = 0;
for(let i = 0 ; i < results.length ; i++) {
let result = results[i];
if(!result.hasOwnProperty('succ') || !result.hasOwnProperty('fail') || (result.succ + result.fail) === 0) {
skip++;
}
else {
break;
}
}
if(skip > 0) {
results.splice(0, skip);
}
if(results.length === 0) return 0;
var r = results[0];
for(let i = 1 ; i < results.length ; i++) {
let v = results[i];
if(!v.hasOwnProperty('succ') || !v.hasOwnProperty('fail') || (v.succ + v.fail) === 0) {
continue;
}
r.succ += v.succ;
r.fail += v.fail;
r.out.push.apply(r.out, v.out);
if(v.create.min < r.create.min) {
r.create.min = v.create.min;
}
if(v.create.max > r.create.max) {
r.create.max = v.create.max;
}
if(v.final.min < r.final.min) {
r.final.min = v.final.min;
}
if(v.final.max > r.final.max) {
r.final.max = v.final.max;
}
if(v.delay.min < r.delay.min) {
r.delay.min = v.delay.min;
}
if(v.delay.max > r.delay.max) {
r.delay.max = v.delay.max;
}
r.delay.sum += v.delay.sum;
for(let j = 0 ; j < v.delay.detail.length ; j++) {
r.delay.detail.push(v.delay.detail[j]);
}
}
return 1;
}
catch(err) {
return 0;
}
}
/**
* create a 'null txStatistics'
* @ results {Object}, txStatistics array
*/
static createNullDefaultTxStats() {
return {succ: 0, fail: 0};
}
}
module.exports = Blockchain;