forked from byteball/ocore
-
Notifications
You must be signed in to change notification settings - Fork 0
/
paid_witnessing.js
236 lines (215 loc) · 9.27 KB
/
paid_witnessing.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
/*jslint node: true */
"use strict";
var _ = require('lodash');
var async = require('async');
var storage = require('./storage.js');
var graph = require('./graph.js');
var db = require('./db.js');
var constants = require("./constants.js");
var conf = require("./conf.js");
var mc_outputs = require("./mc_outputs.js");
var profiler = require("./profiler.js");
function calcWitnessEarnings(conn, type, from_main_chain_index, to_main_chain_index, address, callbacks){
conn.query(
"SELECT COUNT(*) AS count FROM units WHERE is_on_main_chain=1 AND is_stable=1 AND main_chain_index>=? AND main_chain_index<=?",
[to_main_chain_index, to_main_chain_index+constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+1],
function(count_rows){
if (count_rows[0].count !== constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+2)
return callbacks.ifError("not enough stable MC units after to_main_chain_index");
mc_outputs.calcEarnings(conn, type, from_main_chain_index, to_main_chain_index, address, callbacks);
}
);
}
/*
function readMaxWitnessSpendableMcIndex(conn, handleMaxSpendableMcIndex){
conn.query("SELECT MAX(main_chain_index) AS max_mc_index FROM units WHERE is_on_main_chain=1 AND is_stable=1", function(rows){
var max_mc_index = rows[0].max_mc_index;
var max_spendable_mc_index = max_mc_index - constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING - 1;
if (max_spendable_mc_index <= 0)
return handleMaxSpendableMcIndex(max_spendable_mc_index);
/ *
function checkIfMajorityWitnessedByParentsAndAdjust(){
readUnitOnMcIndex(conn, max_spendable_mc_index, function(unit){
determineIfMajorityWitnessedByDescendants(conn, unit, arrParents, function(bWitnessed){
if (!bWitnessed){
max_spendable_mc_index--;
checkIfMajorityWitnessedByParentsAndAdjust();
}
else
handleMaxSpendableMcIndex(max_spendable_mc_index);
});
});
}
* /
//arrParents ? checkIfMajorityWitnessedByParentsAndAdjust() :
handleMaxSpendableMcIndex(max_spendable_mc_index);
});
}
*/
function readUnitOnMcIndex(conn, main_chain_index, handleUnit){
conn.query("SELECT unit FROM units WHERE is_on_main_chain=1 AND main_chain_index=?", [main_chain_index], function(rows){
if (rows.length !== 1)
throw Error("no units or more than one unit on MC index "+main_chain_index);
handleUnit(rows[0].unit);
});
}
function updatePaidWitnesses(conn, cb){
console.log("updating paid witnesses");
profiler.start();
storage.readLastStableMcIndex(conn, function(last_stable_mci){
profiler.stop('mc-wc-readLastStableMCI');
var max_spendable_mc_index = getMaxSpendableMciForLastBallMci(last_stable_mci);
(max_spendable_mc_index > 0) ? buildPaidWitnessesTillMainChainIndex(conn, max_spendable_mc_index, cb) : cb();
});
}
function buildPaidWitnessesTillMainChainIndex(conn, to_main_chain_index, cb){
profiler.start();
var cross = (conf.storage === 'sqlite') ? 'CROSS' : ''; // correct the query planner
conn.query(
"SELECT MIN(main_chain_index) AS min_main_chain_index FROM balls "+cross+" JOIN units USING(unit) WHERE count_paid_witnesses IS NULL",
function(rows){
profiler.stop('mc-wc-minMCI');
var main_chain_index = rows[0].min_main_chain_index;
if (main_chain_index > to_main_chain_index)
return cb();
function onIndexDone(err){
if (err) // impossible
throw Error(err);
else{
main_chain_index++;
if (main_chain_index > to_main_chain_index)
cb();
else
buildPaidWitnessesForMainChainIndex(conn, main_chain_index, onIndexDone);
}
}
buildPaidWitnessesForMainChainIndex(conn, main_chain_index, onIndexDone);
}
);
}
function buildPaidWitnessesForMainChainIndex(conn, main_chain_index, cb){
console.log("updating paid witnesses mci "+main_chain_index);
profiler.start();
conn.query(
"SELECT COUNT(*) AS count, SUM(CASE WHEN is_stable=1 THEN 1 ELSE 0 END) AS count_on_stable_mc \n\
FROM units WHERE is_on_main_chain=1 AND main_chain_index>=? AND main_chain_index<=?",
[main_chain_index, main_chain_index+constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+1],
function(rows){
profiler.stop('mc-wc-select-count');
var count = rows[0].count;
var count_on_stable_mc = rows[0].count_on_stable_mc;
if (count !== constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING+2)
throw Error("main chain is not long enough yet for MC index "+main_chain_index);
if (count_on_stable_mc !== count)
throw Error("not enough stable MC units yet after MC index "+main_chain_index+": count_on_stable_mc="+count_on_stable_mc+", count="+count);
profiler.start();
// we read witnesses from MC unit (users can cheat with side-chains to flip the witness list and pay commissions to their own witnesses)
readMcUnitWitnesses(conn, main_chain_index, function(arrWitnesses){
conn.query("CREATE TEMPORARY TABLE paid_witness_events_tmp ( \n\
unit CHAR(44) NOT NULL, \n\
address CHAR(32) NOT NULL, \n\
delay TINYINT NULL)", function(){
conn.query("SELECT * FROM units WHERE main_chain_index=?", [main_chain_index], function(rows){
profiler.stop('mc-wc-select-units');
et=0; rt=0;
async.eachSeries(
rows,
function(row, cb2){
// the unit itself might be never majority witnessed by unit-designated witnesses (which might be far off),
// but its payload commission still belongs to and is spendable by the MC-unit-designated witnesses.
//if (row.is_stable !== 1)
// throw "unit "+row.unit+" is not on stable MC yet";
buildPaidWitnesses(conn, row, arrWitnesses, cb2);
},
function(err){
console.log(rt, et);
if (err) // impossible
throw Error(err);
//var t=Date.now();
profiler.start();
conn.query(
"INSERT INTO witnessing_outputs (main_chain_index, address, amount) \n\
SELECT main_chain_index, address, \n\
SUM(CASE WHEN sequence='good' THEN ROUND(1.0*payload_commission/count_paid_witnesses) ELSE 0 END) \n\
FROM balls \n\
JOIN units USING(unit) \n\
JOIN paid_witness_events_tmp USING(unit) \n\
WHERE main_chain_index=? \n\
GROUP BY address",
[main_chain_index],
function(){
//console.log(Date.now()-t);
conn.query(conn.dropTemporaryTable("paid_witness_events_tmp"), function(){
profiler.stop('mc-wc-aggregate-events');
cb();
});
}
);
}
);
});
}
);
});
}
);
}
function readMcUnitWitnesses(conn, main_chain_index, handleWitnesses){
conn.query("SELECT witness_list_unit, unit FROM units WHERE main_chain_index=? AND is_on_main_chain=1", [main_chain_index], function(rows){
if (rows.length !== 1)
throw Error("not 1 row on MC "+main_chain_index);
var witness_list_unit = rows[0].witness_list_unit ? rows[0].witness_list_unit : rows[0].unit;
storage.readWitnessList(conn, witness_list_unit, handleWitnesses);
});
}
var et, rt;
function buildPaidWitnesses(conn, objUnitProps, arrWitnesses, onDone){
function updateCountPaidWitnesses(count_paid_witnesses){
conn.query("UPDATE balls SET count_paid_witnesses=? WHERE unit=?", [count_paid_witnesses, objUnitProps.unit], function(){
profiler.stop('mc-wc-insert-events');
onDone();
});
}
var unit = objUnitProps.unit;
var to_main_chain_index = objUnitProps.main_chain_index + constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING;
var t=Date.now();
graph.readDescendantUnitsByAuthorsBeforeMcIndex(conn, objUnitProps, arrWitnesses, to_main_chain_index, function(arrUnits){
rt+=Date.now()-t;
t=Date.now();
var strUnitsList = (arrUnits.length === 0) ? 'NULL' : arrUnits.map(function(unit){ return conn.escape(unit); }).join(', ');
//throw "no witnesses before mc "+to_main_chain_index+" for unit "+objUnitProps.unit;
profiler.start();
conn.query( // we don't care if the unit is majority witnessed by the unit-designated witnesses
// _left_ join forces use of indexes in units
// can't get rid of filtering by address because units can be co-authored by witness with somebody else
"SELECT address, MIN(main_chain_index-?) AS delay \n\
FROM units \n\
LEFT JOIN unit_authors USING(unit) \n\
WHERE unit IN("+strUnitsList+") AND address IN(?) AND +sequence='good' \n\
GROUP BY address",
[objUnitProps.main_chain_index, arrWitnesses],
function(rows){
et += Date.now()-t;
var count_paid_witnesses = rows.length;
var arrValues;
if (count_paid_witnesses === 0){ // nobody witnessed, pay equally to all
count_paid_witnesses = arrWitnesses.length;
arrValues = arrWitnesses.map(function(address){ return "("+conn.escape(unit)+", "+conn.escape(address)+", NULL)"; });
}
else
arrValues = rows.map(function(row){ return "("+conn.escape(unit)+", "+conn.escape(row.address)+", "+row.delay+")"; });
profiler.stop('mc-wc-select-events');
profiler.start();
conn.query("INSERT INTO paid_witness_events_tmp (unit, address, delay) VALUES "+arrValues.join(", "), function(){
updateCountPaidWitnesses(count_paid_witnesses);
});
}
);
});
}
function getMaxSpendableMciForLastBallMci(last_ball_mci){
return last_ball_mci - 1 - constants.COUNT_MC_BALLS_FOR_PAID_WITNESSING;
}
exports.updatePaidWitnesses = updatePaidWitnesses;
exports.calcWitnessEarnings = calcWitnessEarnings;
exports.getMaxSpendableMciForLastBallMci = getMaxSpendableMciForLastBallMci;