Completed
Pull Request — master (#99)
by
unknown
03:07
created

lib/btccom.convert.js   C

Complexity

Total Complexity 56
Complexity/F 2.33

Size

Lines of Code 407
Function Count 24

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 2 Features 2
Metric Value
cc 0
wmc 56
c 6
b 2
f 2
nc 128
mnd 3
bc 45
fnc 24
dl 0
loc 407
rs 6
bpm 1.875
cpm 2.3333
noi 9

21 Functions

Rating   Name   Duplication   Size   Complexity  
B btccom.convert.js ➔ getBase58AddressHash160 0 19 6
A btccom.convert.js ➔ getScriptAsm 0 9 2
A BtccomConverter.getUrlForAllblocks 0 3 1
A btccom.convert.js ➔ getAddressScriptInfo 0 8 2
D btccom.convert.js ➔ convertBtccomTxToBlocktrail 0 97 10
A btccom.convert.js ➔ utcTimestampToISODateStr 0 4 1
A BtccomConverter.getUrlForAddress 0 3 1
A BtccomConverter.getUrlForAddressTransactions 0 3 1
A BtccomConverter.getUrlForAddressUnspent 0 3 1
A btccom.convert.js ➔ BtccomConverter 0 17 1
A BtccomConverter.getUrlForBlock 0 3 1
A btccom.convert.js ➔ getType 0 8 2
A BtccomConverter.convertBlockTx 0 15 1
B BtccomConverter.convertBlock 0 29 4
A BtccomConverter.convertTx 0 5 1
A BtccomConverter.getUrlForTransaction 0 3 1
A BtccomConverter.getUrlForBlockTransaction 0 3 1
B btccom.convert.js ➔ convertBtccomOutputScriptType 0 16 7
B BtccomConverter.convertAddress 0 26 1
A BtccomConverter.convertAddressTx 0 75 1
B BtccomConverter.convertAddressUnspentOutputs 0 30 1

How to fix   Complexity   

Complexity

Complex classes like lib/btccom.convert.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
var RestClient = require('./rest_client');
2
var Wallet = require('./wallet');
3
var bitcoinJS = require('bitcoinjs-lib');
4
5
var BtccomConverter = function(network, useNewCashAddr) {
6
    this.network = network;
7
    this.useNewCashAddr = useNewCashAddr;
8
9
    this.specialRawTxClientOnly = new RestClient({
10
        host: "btc.com",
11
        https: true,
12
        port: 443,
13
        endpoint: "/"
14
    });
15
    this.btccomApiClient = new RestClient({
16
        host: "chain.api.btc.com",
17
        https: true,
18
        port: 443,
19
        endpoint: "/v3"
20
    });
21
};
22
23
function getAddressScriptInfo(address, network, useNewCashAddr) {
24
    try {
25
        var addressScriptInfo = bitcoinJS.address.toOutputScript(address, network, useNewCashAddr);
26
    } catch (e) {
27
        addressScriptInfo = null;
28
    }
29
    return addressScriptInfo;
30
}
31
32
function getScriptAsm(chunks) {
33
34
    try {
35
        var scriptAsm = bitcoinJS.script.toASM(chunks);
36
    } catch (e) {
37
        scriptAsm = null;
38
    }
39
    return scriptAsm;
40
}
41
42
function getType(script) {
43
    try {
44
        var type = bitcoinJS.script.classifyOutput(script);
45
    } catch (e) {
46
        type = null;
47
    }
48
    return type;
49
}
50
51
function getBase58AddressHash160(address, network, useNewCashAddr) {
52
    try {
53
        var addressInfo = Wallet.getAddressAndType(address, network, useNewCashAddr);
54
    } catch (e) {
55
        console.log(e);
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
56
        return null;
57
    }
58
59
    if (addressInfo.type == "base58") {
60
        return addressInfo.decoded.hash;
61
    } else if (addressInfo.type == "bech32") {
62
        if (addressInfo.data.length == 20) {
63
            return addressInfo.decoded.hash;
64
        }
65
        return null;
66
    } else if (addressInfo.type == "") {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if addressInfo.type == "" is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
67
        return addressInfo.decoded.hash;
68
    }
69
}
70
71
var utcTimestampToISODateStr = function(time) {
72
    new Date
0 ignored issues
show
Unused Code Best Practice introduced by
The object created with new Date() is not used but discarded. Consider invoking another function instead of a constructor if you are doing this purely for side effects.
Loading history...
73
    return (new Date(time)).toISOString();
74
};
75
76
BtccomConverter.prototype.getUrlForBlock = function(blockHash) {
77
    return "/block/" + blockHash;
78
};
79
80
BtccomConverter.prototype.getUrlForTransaction = function(txId) {
81
    return "/tx/" + txId + "?verbose=3"
82
};
83
84
BtccomConverter.prototype.getUrlForBlockTransaction = function(blockHash) {
85
    return "/block/" + blockHash + "/tx"
86
};
87
88
BtccomConverter.prototype.getUrlForAddress = function(address) {
89
    return "/address/" + address;
90
};
91
92
BtccomConverter.prototype.getUrlForAddressTransactions = function(address) {
93
    return "/address/" + address + "/tx?verbose=3";
94
};
95
96
BtccomConverter.prototype.getUrlForAddressUnspent = function(address) {
97
    return "/address/" + address + "/unspent";
98
};
99
100
BtccomConverter.prototype.getUrlForAllblocks = function() {
101
    return "/all-blocks";
102
};
103
104
105
BtccomConverter.prototype.convertBlock = function(oldData) {
106
    var data = {};
107
    data.hash = oldData.data.hash;
108
    data.version = oldData.data.version;
109
    data.height = oldData.data.height;
110
    data.block_time = utcTimestampToISODateStr(oldData.data.timestamp);
111
    data.arrival_time = utcTimestampToISODateStr(oldData.data.timestamp);
112
    data.bits = oldData.data.bits;
113
    data.nonce = oldData.data.nonce;
114
    data.merkleroot = oldData.data.mrkl_root;
115
    data.prev_block = oldData.data.prev_block_hash;
116
    data.next_block = oldData.data.next_block_hash ;
117
    data.byte_size = oldData.data.size;
118
    data.difficulty = oldData.data.pool_difficulty; //todo is there a replacement? kill this off for now
119
    data.transactions = oldData.data.tx_count;
120
    data.reward_block = oldData.data.reward_block;
121
    data.reward_fees = oldData.data.reward_fees;
122
    data.created_at = oldData.data.created_at;
123
    data.confirmations = oldData.data.confirmations;
124
    data.is_orphan = oldData.data.is_orphan ;
125
    data.is_sw_block = oldData.data.is_sw_block;
126
    data.byte_size = oldData.data.stripped_size;
127
    data.weight = oldData.data.weight;
128
    data.miningpool_name = oldData.data.miningpool_name ? oldData.data.miningpool_name : null;
129
    data.miningpool_url  = oldData.data.miningpool_url ?  oldData.data.miningpool_url : null;
130
    data.miningpool_slug = oldData.data.miningpool_slug ? oldData.data.miningpool_slug : null;
131
132
    return data;
133
};
134
135
136
BtccomConverter.prototype.convertBlockTx = function(oldData) {
137
138
    var list = [];
139
    oldData.data.list.forEach(function(j1) {
140
        var j = convertBtccomTxToBlocktrail(j1);
141
142
        list.push(j);
143
    });
144
    return {
145
        data: list,
146
        current_page: oldData.data.page,
147
        per_page: oldData.data.pagesize,
148
        total: oldData.data.total_count
149
    };
150
};
151
152
function convertBtccomTxToBlocktrail(tx) {
153
    var data = {};
154
155
    data.size = tx.vsize;
156
    data.hash = tx.hash;
157
    data.block_height = tx.block_height ;
158
    data.block_time = utcTimestampToISODateStr(tx.block_time)
159
    data.block_hash = 123//todo;
160
    data.confirmations = tx.confirmations;
161
    data.is_coinbase = tx.is_coinbase;
162
163
    var totalInputValue;
164
    if (data.is_coinbase) {
165
        totalInputValue = tx.outputs[0].value - tx.fee
166
    } else {
167
        totalInputValue = tx.inputs_value;
168
    }
169
170
    data.total_input_value = totalInputValue;
171
    data.total_output_value = tx.outputs[0].value;
172
    data.total_fee = tx.fee;
173
    data.contains_dust = null; //todo is there a replacement? kill this off for now
174
    data.inputs = [];
175
    data.outputs = [];
176
    for (var inputIdx in tx.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
177
        var i = tx.inputs[inputIdx];
178
        var scriptType;
179
        var inputValue;
180
        var inputTxid;
181
        var outpointIdx;
182
183
        if (data.is_coinbase && i.prev_position == -1 && i.prev_tx_hash == "0000000000000000000000000000000000000000000000000000000000000000") {
184
            scriptType = "coinbase";
185
            inputTxid = null; // debatable.
186
            inputValue = totalInputValue;
187
            outpointIdx = 0xffffffff;
0 ignored issues
show
Unused Code introduced by
The variable outpointIdx seems to be never used. Consider removing it.
Loading history...
188
        } else {
189
            scriptType = i.prev_type;
190
            inputValue = i.prev_value;
191
            inputTxid = i.prev_tx_hash;
192
            outpointIdx = i.prev_position
193
        }
194
195
        var prevAddress = null;
196
        if (typeof i.prev_addresses === "Array") {
197
            prevAddress = i.prev_addresses;
198
        }
199
200
        data.inputs.push({
201
            output_hash: inputTxid,
202
            output_index: inputIdx,
203
            output_confirmed: false, //todo ask ruben
204
            value: inputValue,
205
            sequence: i.sequence,
206
            address: prevAddress,
207
            type: scriptType,
208
            prev_value: 0,
209
            multisig: i.multisig == undefined ? null : i.multisig,
210
            multisig_addresses: i.multisig_addresses == undefined ? null : i.multisig_addresses,
211
            script_signature: i.script_hex
212
        });
213
    }
214
215
    for (var outIdx in tx.outputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
216
        var i = tx.outputs[outIdx];
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable i already seems to be declared on line 177. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
217
        data.outputs.push({
218
            index: outIdx,
219
            value: i.value,
220
            address: i.addresses,
221
            type: convertBtccomOutputScriptType(i.type),
222
            script: i.script_asm,
223
            script_hex: i.script_hex,
224
            spent_hash: i.spent_by_tx,
225
            spent_index: i.spent_by_tx_position
226
        });
227
    }
228
229
    data.size = tx.size;
230
    data.is_double_spend = tx.is_double_spend;
231
    data.opt_in_rbf = false; //todo ask ruben
232
    data.unconfirmed_inputs = true; //todo ask ruben
233
    data.lock_time_timestamp = null;
234
    data.lock_time_block_height = null;
235
236
    /*
237
    Extra fields from Btc.com
238
    */
239
240
    data.is_sw_tx = tx.is_sw_tx;
241
    data.weight = tx.weight;
242
    data.witness_hash = tx.witness_hash;
243
    data.lock_time  = tx.lock_time;
244
    data.sigops = tx.sigops;
245
    data.version = tx.version;
246
247
    return data;
248
}
249
250
BtccomConverter.prototype.convertTx = function(oldData, rawTx) {
251
    var data = convertBtccomTxToBlocktrail(oldData.data);
252
    data.raw = rawTx;
253
    return data;
254
}
255
256
function convertBtccomOutputScriptType(scriptType) {
257
    switch (scriptType) {
258
        case "P2PKH_PUBKEY":
259
            return "pubkey";
260
        case "P2PKH":
261
            return "pubkeyhash";
262
        case "P2SH":
263
            return "scripthash";
264
        case "P2WSH_V0":
265
            return "witnessscripthash";
266
        case "P2WPKH":
267
            return "witnesspubkeyhash";
268
        default:
269
            throw new Error("Not implemented yet, do this later")
270
    }
271
}
272
/// self.converter.convertAddressTx(self.converter.handleResponse(data))
273
BtccomConverter.prototype.convertAddressTx = function(oldData) {
274
    var data = [];
275
276
277
    oldData.data.list.forEach(function(i) {
278
        var e  = {};
279
        var inputs = [];
280
        var outputs = [];
281
282
        e.hash = i.hash;
283
        e.time = i.block_time;
284
        e.confirmations = i.confirmations;
285
        e.block_height = i.block_height;
286
        e.block_hash = i.hash;
287
        e.is_coinbase = i.is_coinbase;
288
        e.estimated_value = i.estimated_value; //todo ask ruben
289
        e.total_input_value = i.inputs_value;
290
        e.total_output_value = i.outputs_value;
291
        e.total_fee = i.fee;
292
        e.estimated_change = i.estimated_change;
293
        e.estimated_change_address = i.estimated_change_address;
294
295
        for (var j in i.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
296
            var inIdx = i.inputs[j];
297
            inputs.push({
298
                index: j,
299
                output_hash: inIdx.prev_tx_hash,
300
                output_index: inIdx.sequence,
301
                value: inIdx.prev_value,
302
                address: inIdx.prev_addresses,
303
                type:  e.is_coinbase ? e.is_coinbase : convertBtccomOutputScriptType(inIdx.prev_type),
304
                multisig: inIdx.multisig == undefined ? null : inIdx.multisig,
305
                script_signature: inIdx.script_hex,
306
                script: inIdx.script_asm
307
            });
308
        }
309
310
        for (var j in i.outputs) {
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable j already seems to be declared on line 295. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
311
            var outIdx = i.outputs[j];
312
            outputs.push({
313
                index: j,
314
                value: outIdx.value,
315
                address: outIdx.addresses,
316
                type: outIdx.type,
317
                multisig: outIdx.multisig == undefined ? null : outIdx.multisig,
318
                script: outIdx.script_asm,
319
                spent_hash: outIdx.spent_by_tx == undefined ? null : outIdx.spent_by_tx,
320
                script_hex: outIdx.script_hex,
321
                spent_index: outIdx.spent_by_tx_position
322
323
            });
324
        }
325
326
        e.inputs = inputs;
327
        e.outputs  = outputs;
328
329
        /*
330
        Extra fields from Btc.com
331
         */
332
333
        e.is_double_spend = i.is_double_spend;
334
        e.is_sw_tx = i.is_sw_tx;
335
        e.weight = i.weight;
336
        e.witness_hash = i.witness_hash;
337
        e.version = i.version;
338
        data.push(e);
339
    });
340
341
    return {
342
        data: data,
343
        current_page: oldData.data.page,
344
        per_page: oldData.data.pagesize,
345
        total: oldData.data.total_count
346
    };
347
};
348
349
BtccomConverter.prototype.convertAddress = function(oldData, firstSeen) {
350
351
    var data = {};
352
    data.address = oldData.data.address;
353
    data.hash160 = getBase58AddressHash160(oldData.data.address, this.network, this.useNewCashAddr).toString("hex");
354
    data.balance = oldData.data.balance;
355
    data.received = oldData.data.received;
356
    data.sent = oldData.data.sent;
357
    data.transactions = oldData.data.tx_count;
358
    data.utxos = oldData.data.unspent_tx_count - 1;
359
    data.unconfirmed_received = oldData.data.unconfirmed_received;
360
    data.unconfirmed_sent = oldData.data.unconfirmed_sent;
361
    data.unconfirmed_transactions = oldData.data.unconfirmed_tx_count;
362
    data.unconfirmed_utxos = oldData.data.unspent_tx_count;
363
    data.first_tx = oldData.data.first_tx;
364
    data.last_tx = oldData.data.last_tx;
365
    data.category = null; //todo is there a replacement? kill this off for now
366
    data.tag = null; //todo is there a replacement? kill this off for now
367
    data.first_seen = firstSeen;
368
    data.last_seen = firstSeen;
369
370
    return {
371
        data: data
372
    };
373
374
}
375
376
BtccomConverter.prototype.convertAddressUnspentOutputs = function(oldData, address) {
377
378
    var data = [];
379
    var script = getAddressScriptInfo(address, this.network, this.useNewCashAddr);
380
    var script_hex = script.toString("hex");
381
    var script_asm = getScriptAsm(script);
382
    var type = getType(script);
383
    oldData.data.list.forEach(function(i) {
384
        data.push({
385
            hash: i.tx_hash,
386
            time: undefined, //todo
387
            confirmations: i.confirmations,
388
            is_coinbase: true, //todo ask Ruben
389
            value: i.value,
390
            index: i.tx_output_n,
391
            address: address,
392
            type: type,
393
            multisig: null,
394
            script: script_asm,
395
            script_hex: script_hex
396
        })
397
398
    });
399
400
    return {
401
        data: data,
402
        current_page: oldData.data.page,
403
        total: oldData.data.total_count
404
    }
405
}
406
407
exports = module.exports = BtccomConverter;
408