-
Notifications
You must be signed in to change notification settings - Fork 251
/
Copy pathvalidators.ts
92 lines (86 loc) · 4.2 KB
/
validators.ts
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
import { CurrentEpochValidatorInfo, NextEpochValidatorInfo } from '@near-js/types';
import depd from 'depd';
import { sortBigIntAsc } from './utils';
/** Finds seat price given validators stakes and number of seats.
* Calculation follow the spec: https://nomicon.io/Economics/README.html#validator-selection
* @param validators: current or next epoch validators.
* @param maxNumberOfSeats: maximum number of seats in the network.
* @param minimumStakeRatio: minimum stake ratio
* @param protocolVersion: version of the protocol from genesis config
*/
export function findSeatPrice(validators: (CurrentEpochValidatorInfo | NextEpochValidatorInfo)[], maxNumberOfSeats: number, minimumStakeRatio: number[], protocolVersion?: number): bigint {
if (protocolVersion && protocolVersion < 49) {
return findSeatPriceForProtocolBefore49(validators, maxNumberOfSeats);
}
if (!minimumStakeRatio) {
const deprecate = depd('findSeatPrice(validators, maxNumberOfSeats)');
deprecate('`use `findSeatPrice(validators, maxNumberOfSeats, minimumStakeRatio)` instead');
minimumStakeRatio = [1, 6250]; // hardcoded minimumStakeRation from 12/7/21
}
return findSeatPriceForProtocolAfter49(validators, maxNumberOfSeats, minimumStakeRatio);
}
function findSeatPriceForProtocolBefore49(validators: (CurrentEpochValidatorInfo | NextEpochValidatorInfo)[], numSeats: number): bigint {
const stakes = validators.map(v => BigInt(v.stake)).sort(sortBigIntAsc);
const num = BigInt(numSeats);
const stakesSum = stakes.reduce((a, b) => a + b);
if (stakesSum < num) {
throw new Error('Stakes are below seats');
}
// assert stakesSum >= numSeats
let left = 1n, right = stakesSum + 1n;
while (left !== right - 1n) {
const mid = (left + right) / 2n;
let found = false;
let currentSum = 0n;
for (let i = 0; i < stakes.length; ++i) {
currentSum = currentSum + (stakes[i] / mid);
if (currentSum >= num) {
left = mid;
found = true;
break;
}
}
if (!found) {
right = mid;
}
}
return left;
}
// nearcore reference: https://github.com/near/nearcore/blob/5a8ae263ec07930cd34d0dcf5bcee250c67c02aa/chain/epoch_manager/src/validator_selection.rs#L308;L315
function findSeatPriceForProtocolAfter49(validators: (CurrentEpochValidatorInfo | NextEpochValidatorInfo)[], maxNumberOfSeats: number, minimumStakeRatio: number[]): bigint {
if (minimumStakeRatio.length != 2) {
throw Error('minimumStakeRatio should have 2 elements');
}
const stakes = validators.map(v => BigInt(v.stake)).sort(sortBigIntAsc);
const stakesSum = stakes.reduce((a, b) => a + b);
if (validators.length < maxNumberOfSeats) {
return stakesSum * BigInt(minimumStakeRatio[0]) / BigInt(minimumStakeRatio[1]);
} else {
return stakes[0] + 1n;
}
}
export interface ChangedValidatorInfo {
current: CurrentEpochValidatorInfo;
next: NextEpochValidatorInfo;
}
export interface EpochValidatorsDiff {
newValidators: NextEpochValidatorInfo[];
removedValidators: CurrentEpochValidatorInfo[];
changedValidators: ChangedValidatorInfo[];
}
/** Diff validators between current and next epoch.
* Returns additions, subtractions and changes to validator set.
* @param currentValidators: list of current validators.
* @param nextValidators: list of next validators.
*/
export function diffEpochValidators(currentValidators: CurrentEpochValidatorInfo[], nextValidators: NextEpochValidatorInfo[]): EpochValidatorsDiff {
const validatorsMap = new Map<string, CurrentEpochValidatorInfo>();
currentValidators.forEach(v => validatorsMap.set(v.account_id, v));
const nextValidatorsSet = new Set(nextValidators.map(v => v.account_id));
return {
newValidators: nextValidators.filter(v => !validatorsMap.has(v.account_id)),
removedValidators: currentValidators.filter(v => !nextValidatorsSet.has(v.account_id)),
changedValidators: nextValidators.filter(v => (validatorsMap.has(v.account_id) && validatorsMap.get(v.account_id).stake != v.stake))
.map(v => ({ current: validatorsMap.get(v.account_id), next: v }))
};
}