-
Notifications
You must be signed in to change notification settings - Fork 8
/
amendments.ts
160 lines (146 loc) · 4.45 KB
/
amendments.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
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
import axios from 'axios'
import createHash from 'create-hash'
import { AmendmentInfo } from '../types'
import logger from '../utils/logger'
import { query } from './utils'
const log = logger({ name: 'amendments' })
const amendmentIDs = new Map<string, { name: string; deprecated: boolean }>()
const rippledVersions = new Map<string, string>()
const ACTIVE_AMENDMENT_REGEX =
/^\s*REGISTER_F[A-Z]+\s*\((?<amendmentName>\S+),\s*.*$/u
const RETIRED_AMENDMENT_REGEX =
/^ .*retireFeature\("(?<amendmentName>\S+)"\)[,;].*$/u
const AMENDMENT_VERSION_REGEX =
/\| \[(?<amendmentName>[a-zA-Z0-9_]+)\][^\n]+\| (?<version>v[0-9]*\.[0-9]*\.[0-9]*|TBD) *\|/u
// TODO: Clean this up when this PR is merged:
// https://github.com/XRPLF/rippled/pull/4781
/**
* Fetch a list of amendments names from rippled file.
*
* @returns The list of amendment names.
*/
async function fetchAmendmentNames(): Promise<Map<string, boolean> | null> {
try {
const response = await axios.get(
'https://raw.githubusercontent.com/ripple/rippled/develop/src/ripple/protocol/impl/Feature.cpp',
)
const text = response.data
const amendmentNames: Map<string, boolean> = new Map()
text.split('\n').forEach((line: string) => {
const name = ACTIVE_AMENDMENT_REGEX.exec(line)
if (name) {
amendmentNames.set(name[1], name[0].includes('VoteBehavior::Obsolete'))
} else {
const name2 = RETIRED_AMENDMENT_REGEX.exec(line)
if (name2) {
amendmentNames.set(name2[1], true)
}
}
})
return amendmentNames
} catch (err) {
log.error('Error getting amendment names', err)
return null
}
}
/**
* Extracts Amendment ID from Amendment name inside a buffer.
*
* @param buffer -- The buffer containing the amendment name.
*
* @returns The amendment ID string.
*/
function sha512Half(buffer: Buffer): string {
return createHash('sha512')
.update(buffer)
.digest('hex')
.toUpperCase()
.slice(0, 64)
}
/**
* Maps the id of Amendments to its corresponding names.
*
* @returns Void.
*/
async function nameOfAmendmentID(): Promise<void> {
// The Amendment ID is the hash of the Amendment name
const amendmentNames = await fetchAmendmentNames()
if (amendmentNames !== null) {
amendmentNames.forEach((deprecated, name) => {
amendmentIDs.set(sha512Half(Buffer.from(name, 'ascii')), {
name,
deprecated,
})
})
}
}
/**
* Fetches the versions when amendments are first enabled.
*
* @returns Void.
*/
async function fetchMinRippledVersions(): Promise<void> {
try {
const response = await axios.get(
'https://raw.githubusercontent.com/XRPLF/xrpl-dev-portal/master/content/resources/known-amendments.md',
)
const text = response.data
text.split('\n').forEach((line: string) => {
const found = AMENDMENT_VERSION_REGEX.exec(line)
if (found) {
rippledVersions.set(
found[1],
found[2].startsWith('v') ? found[2].slice(1) : found[2],
)
}
})
} catch (err) {
log.error('Error getting amendment rippled versions', err)
}
}
/**
* Saves a validator to the database.
*
* @param amendment - The amendment to be saved.
* @returns Void.
*/
export async function saveAmendmentInfo(
amendment: AmendmentInfo,
): Promise<void> {
await query('amendments_info')
.insert(amendment)
.onConflict('id')
.merge()
.catch((err) => log.error('Error Saving AmendmentInfo', err))
}
/**
* Delete an amendment incoming when majority is lost or when the amendment is enabled.
*
* @param amendment_id -- The id of the amendment incoming to delete.
* @param networks -- The networks of the amendment being voted.
*/
export async function deleteAmendmentStatus(
amendment_id: string,
networks: string,
): Promise<void> {
await query('amendments_status')
.del()
.where('amendment_id', '=', amendment_id)
.andWhere('networks', '=', networks)
.catch((err) => log.error('Error Saving Amendment Status', err))
}
export async function fetchAmendmentInfo(): Promise<void> {
log.info('Fetch amendments info from data sources...')
await nameOfAmendmentID()
await fetchMinRippledVersions()
amendmentIDs.forEach(async (value, id) => {
const amendment: AmendmentInfo = {
id,
name: value.name,
rippled_version: rippledVersions.get(value.name),
deprecated: value.deprecated,
}
await saveAmendmentInfo(amendment)
})
log.info('Finish fetching amendments info from data sources...')
}