Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/shared/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,15 @@ interface UNL {
blob: string
}

interface UNLV2 {
public_key: string
manifest: string
blobs_v2: Array<{
signature: string
blob: string
}>
}

interface UNLValidator {
// this public key is the validator's master key unlike the validation_public_key in ValidationRaw
validation_public_key: string
Expand All @@ -179,6 +188,7 @@ interface UNLValidator {
interface UNLBlob {
sequence: number
expiration: number
effective?: number
validators: UNLValidator[]
}

Expand Down Expand Up @@ -224,6 +234,7 @@ export {
Manifest,
StreamManifest,
UNL,
UNLV2,
UNLBlob,
UNLValidator,
DatabaseManifest,
Expand Down
57 changes: 52 additions & 5 deletions src/shared/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import axios, { AxiosRequestConfig } from 'axios'
import { unixTimeToRippleTime } from 'xrpl'
import { normalizeManifest } from 'xrpl-validator-domains'

import { getNetworks, query } from '../database'
import { UNL, UNLBlob, UNLValidator } from '../types'
import { UNL, UNLBlob, UNLV2, UNLValidator } from '../types'

import logger from './logger'

Expand All @@ -18,16 +19,62 @@ const HTTPS_PORT = 51234
export async function fetchValidatorList(url: string): Promise<UNLBlob> {
try {
const resp = await axios.get(`http://${url}`)
const unl: UNL = resp.data
const buf = Buffer.from(unl.blob, 'base64')
const blobParsed: UNLBlob = JSON.parse(buf.toString('ascii'))
return blobParsed
const unl: UNL | UNLV2 = resp.data
if ('blob' in unl) {
return parseBlob(unl.blob)
}
return getActiveBlobV2(unl)
} catch (err: unknown) {
log.error(`Error fetching validator List for ${url}`, err)
return Promise.reject()
}
}

/**
* Retrieves the most recent active UNLBlob from a UNLv2 object.
*
* An active UNLBlob for v2 is one whose effective time is the latest among those
* where the current time falls within the effective and expiration range.
*
* @param unl - The input UNLv2 object.
* @returns The active UNLBlob parsed from UNLV2 format.
* @throws Error if no active blob found.
*/
export function getActiveBlobV2(unl: UNLV2): UNLBlob {
let activeBlob
for (const blobItem of unl.blobs_v2) {
const currentBlob: UNLBlob = parseBlob(blobItem.blob)
const now = unixTimeToRippleTime(Date.now())
if (
currentBlob.effective !== undefined &&
currentBlob.effective <= now &&
currentBlob.expiration >= now &&
(!activeBlob ||
(activeBlob.effective && currentBlob.effective > activeBlob.effective))
) {
activeBlob = currentBlob
}
}
if (!activeBlob) {
throw new Error(
`Unable to find an active blob for unl at ${unl.public_key}`,
)
}
return activeBlob
}

/**
* Parse a UNLBlob.
*
* @param blob - The UNLBlob to parse.
* @returns The parsed UNLBlob.
*/
export function parseBlob(blob: string): UNLBlob {
const buf = Buffer.from(blob, 'base64')
const blobParsed: UNLBlob = JSON.parse(buf.toString('ascii'))
return blobParsed
}

/**
* Get the network entry node's url for a validator.
*
Expand Down
Loading