-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconsensus.js
234 lines (209 loc) · 9.14 KB
/
consensus.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
import {tmURL, nodeURL, addDetails, makeTableElement2, addDetails2, timeSince, renderHeader} from './common.js';
await renderHeader();
async function buildValidatorAddressArray(validators, tmValMap) {
function bytesToHex(bytes) {
return Array.from(bytes).map(byte => byte.toString(16).padStart(2, '0')).join('');
}
const result = [];
for (let i = 0; i < validators.length; i++) {
let val = validators[i];
const v = {};
const consPubKey = atob(val?.consensus_pubkey?.key);
let bytes = new Uint8Array(consPubKey.length);
for (let j = 0; j < consPubKey.length; j++) {
bytes[j] = consPubKey.charCodeAt(j);
}
// const bytesHash = await window.crypto.subtle.digest('SHA-256', bytes);
// const addr = bytesHash.slice(0,20);
// const addrArray = new Uint8Array(addr);
// const addrHex = bytesToHex(addrArray).toUpperCase();
v.pub_key = val.consensus_pubkey.key;
v.operator_address = val.operator_address;
v.moniker = val.description.moniker;
v.consenus_addr_hex = tmValMap[v.pub_key];
// v.consensus_addr_bech32 = encode("zetavalcons", convertbits(addrArray, 8, 5, true), encodings.BECH32);
result.push(v);
}
return result;
}
async function consensusState() {
console.log("hello");
const div = document.getElementById('consensus-state');
const p1 = await fetch(`${tmURL}/consensus_state`, {method: 'GET'});
if (!p1.ok) {
console.log("consensus_state fetch error");
return;
}
const data = await p1.json();
console.log("consensus-state", data);
const proposerIndex = data?.result?.round_state?.proposer?.index;
// const p2 = await fetch(`${tmURL}/dump_consensus_state`, {method: 'GET'});
// if (!p2.ok) {
// console.log("dump_consensus_state fetch error");
// return;
// }
// const data2 = await p2.json();
// console.log("dump_state", data2);
const p3 = await fetch(`${nodeURL}/cosmos/staking/v1beta1/validators`, {method: 'GET'});
if (!p3.ok) {
console.log("staking validators fetch error");
return;
}
const data3 = await p3.json();
console.log(data3);
let resource = `${tmURL}/validators?per_page=200`;
let p4 = await fetch(resource, {method: 'GET',});
let data4 = await p4.json();
const tmVals = data4?.result?.validators;
const tmValMap = {};
for (let i = 0; i < tmVals.length; i++) {
tmValMap[tmVals[i].pub_key.value] = tmVals[i].address;
}
console.log(tmValMap);
console.log("tmVals", tmVals);
let totalVP = 0;
for (let i = 0; i < tmVals.length; i++) {
totalVP += parseInt(tmVals[i].voting_power);
}
console.log("total voting power", totalVP);
console.log("data3.validators", data3?.validators);
const vals = await buildValidatorAddressArray(data3?.validators, tmValMap);
console.log(vals);
const sortedVals = [];
const sortedPowers = [];
for (let i = 0; i < tmVals.length; i++) {
console.log("tmVals[i].address", tmVals[i].address);
sortedPowers.push(tmVals[i].voting_power/totalVP*100);
for (let j = 0; j <vals.length; j++) {
const val = vals[j];
// console.log("val.consenus_addr_hex", val.consenus_addr_hex);
if (val.consenus_addr_hex === tmVals[i].address && val.moniker) {
sortedVals.push(val.moniker);
break;
}
}
}
console.log("sortedVals", sortedVals);
const roundState = data?.result?.round_state;
div.appendChild(addDetails(`${roundState["height/round/step"]}`, JSON.stringify(roundState, null, 2)));
const regex = /{(.*):(.*)}/;
for (let i = 0; i < roundState?.height_vote_set.length; i++) {
const voteSet = roundState?.height_vote_set[i];
// console.log(voteSet);
const pv = voteSet?.prevotes;
const pc = voteSet?.precommits;
const round = voteSet?.round;
console.log("round", round);
// console.log("prevotes", pv);
// console.log("precommits", pc);
const prevotesBitArray = voteSet?.prevotes_bit_array;
// div.appendChild(addDetails(`prevotes_bit_array`, prevotesBitArray));
const prevotes = prevotesBitArray.match(regex)[2];
const [prevoteMonikers, pvVP] = bitsToMonikers(prevotes, tmVals, data3?.validators);
const apv = annotate_votes(pv, sortedVals, sortedPowers);
const tapv = tally_votes(apv);
// console.log("apv", apv);
const apc = annotate_votes(pc, sortedVals, sortedPowers);
// console.log(tally_votes(apc));
const tapc = tally_votes(apc);
const precommitBitArray = voteSet?.precommits_bit_array;
const precommits = precommitBitArray.match(regex)[2];
const [precommitMonikers, pcVP] = bitsToMonikers(precommits, tmVals, data3?.validators);
div.appendChild(addDetails2(`round ${voteSet.round} prevotes\t ${JSON.stringify(tapv)}`, makeTableElement2(apv, ["moniker", "type", "block", "power", "timestamp"])));
div.appendChild(addDetails2(`round ${voteSet.round} precommits\t ${JSON.stringify(tapc)}`, makeTableElement2(apc, ["moniker", "type", "block", "power", "timestamp"])));
}
}
// annotate the votes with monikers; votes are arrays of things like this
// "Vote{0:330F77EB744F 2172953/00/SIGNED_MSG_TYPE_PREVOTE(Prevote) 000000000000 F6C79E13E2C0 @ 2023-10-24T15:52:57.104726171Z}",
// "Vote{1:76DB9660002D 2172953/00/SIGNED_MSG_TYPE_PREVOTE(Prevote) B24FF10CC94D 3A86182164C8 @ 2023-10-24T15:52:57.565323198Z}",
// "Vote{2:AF31318E7245 2172953/00/SIGNED_MSG_TYPE_PREVOTE(Prevote) 000000000000 60C2E9D485EE @ 2023-10-24T15:52:57.154497833Z}",
// "Vote{3:40971601D676 2172953/00/SIGNED_MSG_TYPE_PREVOTE(Prevote) B24FF10CC94D 36D8B3DB1593 @ 2023-10-24T15:52:57.230847226Z}",
// "nil-Vote",
// -------------
// 000000000000 hash is the real nil vote in consensus; B24FF10CC94D is a vote on that block; nil-Vote is missing vote
function annotate_votes(votes, monikers, powers) {
let results = [];
for (let i = 0; i < votes.length; i++) {
let v = {};
const vote = votes[i];
// console.log("moniker", monikers[i]);
// test for nil vote
v.moniker = monikers[i];
// truncate moniker to at most 25 characters
console.log("moniker", v.moniker);
if (v.moniker && v.moniker.length > 25) {
v.moniker = v.moniker.slice(0, 25) + "...";
}
v.power = powers[i];
if (vote.includes("nil-Vote")) {
// console.log("nil vote");
v.type = "missed vote";
results.push(v);
// results.push(`missed vote ${monikers[i]}`);
}
// extract timestamp
const regex2 = /@ (.*?)Z/;
const m2 = vote.match(regex2);
if (m2) {
// parse this string "2023-10-24T15:52:57.230847226Z" into local time zone
const isoString = m2[1];
const truncatedString = isoString.slice(0, 24) + "Z";
v.timestamp = timeSince(new Date(truncatedString));
}
// extract the 000000000000 hash
const regex = /Vote{.*:.*\/.*\/.*\(.*\) (.*?) .* @/;
const m = vote.match(regex);
if (m) {
const hash = m[1];
// console.log(hash);
if (hash === "000000000000") {
v.type = "nil vote";
// results.push(`nil vote: ${monikers[i]}`);
} else {
v.type = "voted";
v.block = hash;
// results.push(`voted: ${monikers[i]}`);
}
results.push(v);
}
}
return results;
}
// post process the above results;
// tally the power of missed votes, nil votes, and voted
function tally_votes(results) {
let missed = 0;
let nil = 0;
let voted = 0;
for (let i = 0; i < results.length; i++) {
const v = results[i];
if (v.type === "missed vote") {
missed += v.power;
} else if (v.type === "nil vote") {
nil += v.power;
} else if (v.type === "voted") {
voted += v.power;
}
}
return {missed: missed, voted: voted, nil: nil};
}
// annotate_votes(["Vote{0:330F77EB744F 2172953/00/SIGNED_MSG_TYPE_PREVOTE(Prevote) 000000000000 F6C79E13E2C0 @ 2023-10-24T15:52:57.104726171Z}","Vote{1:76DB9660002D 2172953/00/SIGNED_MSG_TYPE_PREVOTE(Prevote) B24FF10CC94D 3A86182164C8 @ 2023-10-24T15:52:57.565323198Z}", "nil-Vote"]);
await consensusState();
// input: xxx_xx_; returns monikers for the _ bits
function bitsToMonikers(bits, tmVals, validators) {
const monikers = [];
let VP = 0;
for (let i = 0; i < bits.length; i++) {
if (bits[i] === '_') {
const pubkey = tmVals[i].pub_key.value;
for (let j = 0; j < validators.length; j++) {
if (validators[j].consensus_pubkey.key === pubkey) {
monikers.push(validators[j].description.moniker);
break;
}
}
VP += parseInt(tmVals[i].voting_power);
}
}
return [monikers, VP];
}