-
Notifications
You must be signed in to change notification settings - Fork 98
/
balances.js
202 lines (195 loc) · 8.68 KB
/
balances.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
/*jslint node: true */
"use strict";
var _ = require('lodash');
var constants = require('./constants.js');
var db = require('./db');
function readBalance(walletOrAddress, handleBalance){
var start_time = Date.now();
var walletIsAddress = typeof walletOrAddress === 'string' && walletOrAddress.length === 32; // ValidationUtils.isValidAddress
var join_my_addresses = walletIsAddress ? "" : "JOIN my_addresses USING(address)";
var where_condition = walletIsAddress ? "address=?" : "wallet=?";
var assocBalances = {base: {stable: 0, pending: 0}};
assocBalances[constants.BLACKBYTES_ASSET] = {is_private: 1, stable: 0, pending: 0};
db.query(
"SELECT asset, is_stable, SUM(amount) AS balance \n\
FROM outputs "+join_my_addresses+" CROSS JOIN units USING(unit) \n\
WHERE is_spent=0 AND "+where_condition+" AND sequence='good' \n\
GROUP BY asset, is_stable",
[walletOrAddress],
function(rows){
for (var i=0; i<rows.length; i++){
var row = rows[i];
var asset = row.asset || "base";
if (!assocBalances[asset])
assocBalances[asset] = {stable: 0, pending: 0};
assocBalances[asset][row.is_stable ? 'stable' : 'pending'] = row.balance;
}
var my_addresses_join = walletIsAddress ? "" : "my_addresses CROSS JOIN";
var using = walletIsAddress ? "" : "USING(address)";
db.query(
"SELECT SUM(total) AS total FROM ( \n\
SELECT SUM(amount) AS total FROM "+my_addresses_join+" witnessing_outputs "+using+" WHERE is_spent=0 AND "+where_condition+" \n\
UNION ALL \n\
SELECT SUM(amount) AS total FROM "+my_addresses_join+" headers_commission_outputs "+using+" WHERE is_spent=0 AND "+where_condition+" ) AS t",
[walletOrAddress,walletOrAddress],
function(rows) {
if(rows.length){
assocBalances["base"]["stable"] += rows[0].total;
}
if (assocBalances[constants.BLACKBYTES_ASSET].stable === 0 && assocBalances[constants.BLACKBYTES_ASSET].pending === 0)
delete assocBalances[constants.BLACKBYTES_ASSET];
for (var asset in assocBalances)
assocBalances[asset].total = assocBalances[asset].stable + assocBalances[asset].pending;
// add 0-balance assets
db.query(
"SELECT DISTINCT asset FROM outputs " + join_my_addresses + " WHERE " + where_condition,
[walletOrAddress],
function (rows) {
var assets = rows.map(function (row) { return row.asset; }).filter(function (asset) { return (asset && asset !== constants.BLACKBYTES_ASSET) });
if (assets.length === 0)
return handleBalance(assocBalances);
for (var i = 0; i < assets.length; i++) {
var asset = assets[i];
if (!assocBalances[asset])
assocBalances[asset] = { stable: 0, pending: 0, total: 0 };
}
db.query("SELECT unit FROM assets WHERE unit IN(" + assets.map(db.escape).join(', ') + ") AND is_private=1", function (asset_rows) {
for (var i = 0; i < asset_rows.length; i++)
assocBalances[asset_rows[i].unit].is_private = 1;
console.log('reading balances of ' + walletOrAddress + ' took ' + (Date.now() - start_time) + 'ms')
handleBalance(assocBalances);
});
}
);
}
);
}
);
}
function readOutputsBalance(wallet, handleBalance){
var walletIsAddress = typeof wallet === 'string' && wallet.length === 32; // ValidationUtils.isValidAddress
var join_my_addresses = walletIsAddress ? "" : "JOIN my_addresses USING(address)";
var where_condition = walletIsAddress ? "address=?" : "wallet=?";
var assocBalances = {base: {stable: 0, pending: 0}};
db.query(
"SELECT asset, is_stable, SUM(amount) AS balance \n\
FROM outputs "+join_my_addresses+" CROSS JOIN units USING(unit) \n\
WHERE is_spent=0 AND "+where_condition+" AND sequence='good' \n\
GROUP BY asset, is_stable",
[wallet],
function(rows){
for (var i=0; i<rows.length; i++){
var row = rows[i];
var asset = row.asset || "base";
if (!assocBalances[asset])
assocBalances[asset] = {stable: 0, pending: 0};
assocBalances[asset][row.is_stable ? 'stable' : 'pending'] = row.balance;
}
for (var asset in assocBalances)
assocBalances[asset].total = assocBalances[asset].stable + assocBalances[asset].pending;
handleBalance(assocBalances);
}
);
}
function readSharedAddressesOnWallet(wallet, handleSharedAddresses){
db.query("SELECT DISTINCT shared_address_signing_paths.shared_address FROM my_addresses \n\
JOIN shared_address_signing_paths USING(address) \n\
LEFT JOIN prosaic_contracts ON prosaic_contracts.shared_address = shared_address_signing_paths.shared_address \n\
WHERE wallet=? AND prosaic_contracts.hash IS NULL", [wallet], function(rows){
var arrSharedAddresses = rows.map(function(row){ return row.shared_address; });
if (arrSharedAddresses.length === 0)
return handleSharedAddresses([]);
readSharedAddressesDependingOnAddresses(arrSharedAddresses, function(arrNewSharedAddresses){
handleSharedAddresses(arrSharedAddresses.concat(arrNewSharedAddresses));
});
});
}
function readSharedAddressesDependingOnAddresses(arrMemberAddresses, handleSharedAddresses){
var strAddressList = arrMemberAddresses.map(db.escape).join(', ');
db.query("SELECT DISTINCT shared_address FROM shared_address_signing_paths WHERE address IN("+strAddressList+")", function(rows){
var arrSharedAddresses = rows.map(function(row){ return row.shared_address; });
if (arrSharedAddresses.length === 0)
return handleSharedAddresses([]);
var arrNewMemberAddresses = _.difference(arrSharedAddresses, arrMemberAddresses);
if (arrNewMemberAddresses.length === 0)
return handleSharedAddresses([]);
readSharedAddressesDependingOnAddresses(arrNewMemberAddresses, function(arrNewSharedAddresses){
handleSharedAddresses(arrNewMemberAddresses.concat(arrNewSharedAddresses));
});
});
}
function readSharedBalance(wallet, handleBalance){
var assocBalances = {};
readSharedAddressesOnWallet(wallet, function(arrSharedAddresses){
if (arrSharedAddresses.length === 0)
return handleBalance(assocBalances);
var strAddressList = arrSharedAddresses.map(db.escape).join(', ');
db.query(
"SELECT asset, address, is_stable, SUM(amount) AS balance \n\
FROM outputs CROSS JOIN units USING(unit) \n\
WHERE is_spent=0 AND sequence='good' AND address IN("+strAddressList+") \n\
GROUP BY asset, address, is_stable \n\
UNION ALL \n\
SELECT NULL AS asset, address, 1 AS is_stable, SUM(amount) AS balance FROM witnessing_outputs \n\
WHERE is_spent=0 AND address IN("+strAddressList+") GROUP BY address \n\
UNION ALL \n\
SELECT NULL AS asset, address, 1 AS is_stable, SUM(amount) AS balance FROM headers_commission_outputs \n\
WHERE is_spent=0 AND address IN("+strAddressList+") GROUP BY address",
function(rows){
for (var i=0; i<rows.length; i++){
var row = rows[i];
var asset = row.asset || "base";
if (!assocBalances[asset])
assocBalances[asset] = {};
if (!assocBalances[asset][row.address])
assocBalances[asset][row.address] = {stable: 0, pending: 0};
assocBalances[asset][row.address][row.is_stable ? 'stable' : 'pending'] += row.balance;
}
for (var asset in assocBalances)
for (var address in assocBalances[asset])
assocBalances[asset][address].total = assocBalances[asset][address].stable + assocBalances[asset][address].pending;
handleBalance(assocBalances);
}
);
});
}
function readAllUnspentOutputs(exclude_from_circulation, handleSupply) {
if (!exclude_from_circulation)
exclude_from_circulation = [];
var supply = {
addresses: 0,
txouts: 0,
total_amount: 0,
circulating_txouts: 0,
circulating_amount: 0,
headers_commission_amount: 0,
payload_commission_amount: 0,
};
db.query('SELECT address, COUNT(*) AS count, SUM(amount) AS amount FROM outputs WHERE is_spent=0 AND asset IS null GROUP BY address;', function(rows) {
if (rows.length) {
supply.addresses += rows.length;
rows.forEach(function(row) {
supply.txouts += row.count;
supply.total_amount += row.amount;
if (!exclude_from_circulation.includes(row.address)) {
supply.circulating_txouts += row.count;
supply.circulating_amount += row.amount;
}
});
}
db.query('SELECT "headers_commission_amount" AS amount_name, SUM(amount) AS amount FROM headers_commission_outputs WHERE is_spent=0 UNION SELECT "payload_commission_amount" AS amount_name, SUM(amount) AS amount FROM witnessing_outputs WHERE is_spent=0;', function(rows) {
if (rows.length) {
rows.forEach(function(row) {
supply.total_amount += row.amount;
supply.circulating_amount += row.amount;
supply[row.amount_name] += row.amount;
});
}
handleSupply(supply);
});
});
}
exports.readBalance = readBalance;
exports.readOutputsBalance = readOutputsBalance;
exports.readSharedBalance = readSharedBalance;
exports.readAllUnspentOutputs = readAllUnspentOutputs;