Home Reference Source Test

src/main/generic/network/address/SeedList.js

class SeedList {
    /**
     * @param {string} url
     * @param {PublicKey} [publicKey]
     * @returns {Promise.<SeedList>}
     */
    static async retrieve(url, publicKey) {
        return SeedList.parse(await HttpRequest.get(url, SeedList.REQUEST_TIMEOUT, SeedList.MAX_SIZE), publicKey);
    }

    /**
     * @param {string} listStr
     * @param {PublicKey} [publicKey]
     * @returns {SeedList}
     * @private
     */
    static parse(listStr, publicKey) {
        const seeds = [];

        // Filter empty and comment lines.
        const lines = listStr
            .split(/\r\n|\n|\r/)
            .filter(line => line.length > 0 && !line.startsWith('#'));

        // Read seed addresses. Ignore the last line here.
        for (let i = 0; i < lines.length - 1; i++) {
            const line = lines[i];
            try {
                seeds.push(WsBasePeerAddress.fromSeedString(line.trim()));
            } catch (e) {
                Log.w(SeedList, `Ignoring malformed seed address ${line}`);
            }
        }

        // Try to parse the last line as a signature.
        const lastLine = lines[lines.length - 1];
        let signature = null;
        try {
            signature = new Signature(BufferUtils.fromHex(lastLine.trim()));
        } catch (e) {
            // ignore
        }

        // If the last line doesn't parse as a signature, try to parse it as a PeerAddress.
        if (!signature) {
            try {
                seeds.push(WsBasePeerAddress.fromSeedString(lastLine.trim()));
            } catch (e) {
                Log.w(SeedList, `Ignoring malformed signature/seed address ${lastLine}`);
            }
        }

        // If we don't have a public key, skip the signature check.
        if (!publicKey) {
            return new SeedList(seeds, null, signature);
        }

        // We have a public key, but no signature. Fail.
        if (!signature) {
            throw new Error('Missing signature');
        }

        // Verify signature.
        const data = SeedList._serializeSeeds(seeds);
        if (!signature.verify(publicKey, data)) {
            throw new Error('Invalid signature');
        }

        // Signature ok.
        return new SeedList(seeds, publicKey, signature);
    }

    /**
     * @param {Array.<PeerAddress>} seeds
     * @returns {Uint8Array}
     */
    static _serializeSeeds(seeds) {
        return BufferUtils.fromAscii(seeds.map(seed => seed.toSeedString()).join('\n'));
    }

    /**
     * @param {Array.<PeerAddress>} seeds
     * @param {PublicKey} [publicKey]
     * @param {Signature} [signature]
     */
    constructor(seeds, publicKey, signature) {
        this._seeds = seeds;
        this._publicKey = publicKey;
        this._signature = signature;
    }

    /**
     * @returns {Uint8Array}
     */
    serializeContent() {
        return SeedList._serializeSeeds(this._seeds);
    }

    /**
     * @type {Array.<PeerAddress>}
     */
    get seeds() {
        return this._seeds;
    }

    /**
     * @type {PublicKey}
     */
    get publicKey() {
        return this._publicKey;
    }

    /**
     * @type {Signature}
     */
    get signature() {
        return this._signature;
    }
}
SeedList.MAX_SIZE = 1024 * 128; // 128 kb
SeedList.REQUEST_TIMEOUT = 8000; // 8 seconds
Class.register(SeedList);

class SeedListUrl {
    /**
     * @param {string} url
     * @param {string} [publicKeyHex]
     */
    constructor(url, publicKeyHex) {
        this._url = url;
        this._publicKey = publicKeyHex ? new PublicKey(BufferUtils.fromHex(publicKeyHex)) : null;
    }

    /**
     * @type {string}
     */
    get url() {
        return this._url;
    }

    /**
     * @returns {PublicKey}
     */
    get publicKey() {
        return this._publicKey;
    }
}
Class.register(SeedListUrl);