Skip to content

Commit 66c023f

Browse files
authored
feat: Use feature for amendment name (#291)
## High Level Overview of Change <!-- Please include a summary/list of the changes. If too broad, please consider splitting into multiple PRs. If a relevant Asana task, please link it here. --> Resolve: #278 ### Context of Change <!-- Please include the context of a change. If a bug fix, when was the bug introduced? What was the behavior? If a new feature, why was this architecture chosen? What were the alternatives? If a refactor, how is this better than the previous implementation? If there is a design document for this feature, please link it here. --> Missing new amendments due to the change in rippled code structure to parse name: ![Screenshot 2024-12-19 at 11 32 43 AM](https://github.com/user-attachments/assets/0da97dff-9d6d-4840-b8cf-6b44ba628053) ### Type of Change <!-- Please check relevant options, delete irrelevant ones. --> - [x] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Refactor (non-breaking change that only restructures code) - [ ] Tests (You added tests for code that already exists, or your new feature included in this PR) - [ ] Documentation Updates - [ ] Release ## Before / After <!-- If just refactoring / back-end changes, this can be just an in-English description of the change at a technical level. If a UI change, screenshots should be included. --> Data should show up: ![Screenshot 2024-12-19 at 11 58 52 AM](https://github.com/user-attachments/assets/5198638f-4c69-4158-9a31-fa004aab78c4) <!-- ## Future Tasks For future tasks related to PR. --> VHS is lacking tests at the moment. There will be a separate ticket to add comprehensive tests throughout the code
1 parent 7c0180e commit 66c023f

File tree

4 files changed

+118
-80
lines changed

4 files changed

+118
-80
lines changed

package-lock.json

-20
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

-2
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
"homepage": "https://github.com/ripple/validator-history-service#readme",
3232
"devDependencies": {
3333
"@types/axios": "^0.14.0",
34-
"@types/create-hash": "^1.2.2",
3534
"@types/express": "4.17.21",
3635
"@types/jest": "^26.0.19",
3736
"@types/nconf": "^0.10.0",
@@ -62,7 +61,6 @@
6261
"@types/bunyan": "^1.8.7",
6362
"axios": "^0.21.1",
6463
"bunyan": "^1.8.15",
65-
"create-hash": "^1.2.0",
6664
"dotenv": "^16.3.1",
6765
"express": "4.21.2",
6866
"knex": "2.5.1",

src/connection-manager/wsHandling.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,10 @@ import {
1414
saveAmendmentStatus,
1515
saveAmendmentsStatus,
1616
} from '../shared/database'
17-
import { deleteAmendmentStatus } from '../shared/database/amendments'
17+
import {
18+
NETWORKS_HOSTS,
19+
deleteAmendmentStatus,
20+
} from '../shared/database/amendments'
1821
import {
1922
AmendmentStatus,
2023
DatabaseValidator,
@@ -32,11 +35,6 @@ const LEDGER_HASHES_SIZE = 10
3235
const GOT_MAJORITY_FLAG = 65536
3336
const LOST_MAJORITY_FLAG = 131072
3437
const FOURTEEN_DAYS_IN_MILLISECONDS = 14 * 24 * 60 * 60 * 1000
35-
const NETWORKS_HOSTS = new Map([
36-
['main', 'ws://s2.ripple.com:51233'],
37-
['test', 'wss://s.altnet.rippletest.net:51233'],
38-
['dev', 'wss://s.devnet.rippletest.net:51233'],
39-
])
4038

4139
const log = logger({ name: 'connections' })
4240

src/shared/database/amendments.ts

+114-52
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import axios from 'axios'
2-
import createHash from 'create-hash'
2+
import { Client, ErrorResponse } from 'xrpl'
3+
import {
4+
FeatureAllResponse,
5+
FeatureOneResponse,
6+
} from 'xrpl/dist/npm/models/methods/feature'
37

48
import { AmendmentInfo } from '../types'
59
import logger from '../utils/logger'
@@ -9,78 +13,135 @@ import { query } from './utils'
913
const log = logger({ name: 'amendments' })
1014

1115
const amendmentIDs = new Map<string, { name: string; deprecated: boolean }>()
16+
const votingAmendmentsToTrack = new Set<string>()
1217
const rippledVersions = new Map<string, string>()
13-
14-
const ACTIVE_AMENDMENT_REGEX =
15-
/^\s*REGISTER_F[A-Z]+\s*\((?<amendmentName>\S+),\s*.*$/u
16-
const RETIRED_AMENDMENT_REGEX =
17-
/^ .*retireFeature\("(?<amendmentName>\S+)"\)[,;].*$/u
18+
// TODO: Use feature RPC instead when this issue is fixed and released:
19+
// https://github.com/XRPLF/rippled/issues/4730
20+
const RETIRED_AMENDMENTS = [
21+
'MultiSign',
22+
'TrustSetAuth',
23+
'FeeEscalation',
24+
'PayChan',
25+
'CryptoConditions',
26+
'TickSize',
27+
'fix1368',
28+
'Escrow',
29+
'fix1373',
30+
'EnforceInvariants',
31+
'SortedDirectories',
32+
'fix1201',
33+
'fix1512',
34+
'fix1523',
35+
'fix1528',
36+
]
1837

1938
const AMENDMENT_VERSION_REGEX =
2039
/\| \[(?<amendmentName>[a-zA-Z0-9_]+)\][^\n]+\| (?<version>v[0-9]*\.[0-9]*\.[0-9]*|TBD) *\|/u
2140

22-
// TODO: Clean this up when this PR is merged:
23-
// https://github.com/XRPLF/rippled/pull/4781
41+
export const NETWORKS_HOSTS = new Map([
42+
['main', 'ws://s2.ripple.com:51233'],
43+
['test', 'wss://s.altnet.rippletest.net:51233'],
44+
['dev', 'wss://s.devnet.rippletest.net:51233'],
45+
])
46+
2447
/**
25-
* Fetch a list of amendments names from rippled file.
48+
* Fetch amendments information including id, name, and deprecated status.
2649
*
27-
* @returns The list of amendment names.
50+
* @returns Void.
2851
*/
29-
async function fetchAmendmentNames(): Promise<Map<string, boolean> | null> {
52+
async function fetchAmendmentsList(): Promise<void> {
53+
for (const [network, url] of NETWORKS_HOSTS) {
54+
await fetchNetworkAmendments(network, url)
55+
}
56+
}
57+
58+
/**
59+
* Fetch amendments information including id, name, and deprecated status of a network.
60+
*
61+
* @param network - The network being retrieved.
62+
* @param url - The Faucet URL of the network.
63+
*
64+
* @returns Void.
65+
*/
66+
async function fetchNetworkAmendments(
67+
network: string,
68+
url: string,
69+
): Promise<void> {
3070
try {
31-
const response = await axios.get(
32-
'https://raw.githubusercontent.com/XRPLF/rippled/develop/src/libxrpl/protocol/Feature.cpp',
33-
)
34-
const text = response.data
35-
const amendmentNames: Map<string, boolean> = new Map()
36-
text.split('\n').forEach((line: string) => {
37-
const name = ACTIVE_AMENDMENT_REGEX.exec(line)
38-
if (name) {
39-
amendmentNames.set(name[1], name[0].includes('VoteBehavior::Obsolete'))
40-
} else {
41-
const name2 = RETIRED_AMENDMENT_REGEX.exec(line)
42-
if (name2) {
43-
amendmentNames.set(name2[1], true)
44-
}
45-
}
71+
log.info(`Updating amendment info for ${network}...`)
72+
const client = new Client(url)
73+
await client.connect()
74+
const featureAllResponse: FeatureAllResponse = await client.request({
75+
command: 'feature',
4676
})
47-
return amendmentNames
48-
} catch (err) {
49-
log.error('Error getting amendment names', err)
50-
return null
77+
78+
const featuresAll = featureAllResponse.result.features
79+
80+
for (const id of Object.keys(featuresAll)) {
81+
addAmendmentToCache(id, featuresAll[id].name)
82+
}
83+
84+
// Some amendments in voting are not available in feature all request.
85+
// This loop tries to fetch them in feature one.
86+
for (const amendment_id of votingAmendmentsToTrack) {
87+
const featureOneResponse: FeatureOneResponse | ErrorResponse =
88+
await client.request({
89+
command: 'feature',
90+
feature: amendment_id,
91+
})
92+
93+
// eslint-disable-next-line max-depth -- The depth is only 2, try catch should not count.
94+
if ('result' in featureOneResponse) {
95+
const feature = featureOneResponse.result[amendment_id]
96+
addAmendmentToCache(amendment_id, feature.name)
97+
}
98+
}
99+
100+
await client.disconnect()
101+
102+
log.info(`Finished updating amendment info for ${network}...`)
103+
} catch (error) {
104+
log.error(
105+
`Failed to update amendment info for ${network} due to error: ${String(
106+
error,
107+
)}`,
108+
)
51109
}
52110
}
53111

54112
/**
55-
* Extracts Amendment ID from Amendment name inside a buffer.
113+
* Add an amendment to amendmentIds cache and remove it from the votingAmendmentToTrack cache.
56114
*
57-
* @param buffer -- The buffer containing the amendment name.
58-
*
59-
* @returns The amendment ID string.
115+
* @param id - The id of the amendment to add.
116+
* @param name - The name of the amendment to add.
60117
*/
61-
function sha512Half(buffer: Buffer): string {
62-
return createHash('sha512')
63-
.update(buffer)
64-
.digest('hex')
65-
.toUpperCase()
66-
.slice(0, 64)
118+
function addAmendmentToCache(id: string, name: string): void {
119+
amendmentIDs.set(id, {
120+
name,
121+
deprecated: RETIRED_AMENDMENTS.includes(name),
122+
})
123+
votingAmendmentsToTrack.delete(id)
67124
}
68125

69126
/**
70-
* Maps the id of Amendments to its corresponding names.
127+
* Fetch amendments in voting.
71128
*
72129
* @returns Void.
73130
*/
74-
async function nameOfAmendmentID(): Promise<void> {
75-
// The Amendment ID is the hash of the Amendment name
76-
const amendmentNames = await fetchAmendmentNames()
77-
if (amendmentNames !== null) {
78-
amendmentNames.forEach((deprecated, name) => {
79-
amendmentIDs.set(sha512Half(Buffer.from(name, 'ascii')), {
80-
name,
81-
deprecated,
82-
})
83-
})
131+
async function fetchVotingAmendments(): Promise<void> {
132+
const votingDb = await query('ballot')
133+
.select('amendments')
134+
.then(async (res) =>
135+
res.map((vote: { amendments: string | null }) => vote.amendments),
136+
)
137+
for (const amendmentsDb of votingDb) {
138+
if (!amendmentsDb) {
139+
continue
140+
}
141+
const amendments = amendmentsDb.split(',')
142+
for (const amendment of amendments) {
143+
votingAmendmentsToTrack.add(amendment)
144+
}
84145
}
85146
}
86147

@@ -145,7 +206,8 @@ export async function deleteAmendmentStatus(
145206

146207
export async function fetchAmendmentInfo(): Promise<void> {
147208
log.info('Fetch amendments info from data sources...')
148-
await nameOfAmendmentID()
209+
await fetchVotingAmendments()
210+
await fetchAmendmentsList()
149211
await fetchMinRippledVersions()
150212
amendmentIDs.forEach(async (value, id) => {
151213
const amendment: AmendmentInfo = {

0 commit comments

Comments
 (0)