Skip to content
only-required-headers-synced-when-idle.js 3.45 KiB
Newer Older
const utils = require("./utils");

async function run(nodeName, networkInfo, args) {
    const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName];
    const api = await zombie.connect(wsUri, userDefinedTypes);

    // parse arguments
    const exitAfterSeconds = Number(args[0]);
    const bridgedChain = require("./chains/" + args[1]);

    // start listening to new blocks
    let atLeastOneMessageReceived = false;
    let atLeastOneMessageDelivered = false;
    const unsubscribe = await api.rpc.chain.subscribeNewHeads(async function (header) {
        const apiAtParent = await api.at(header.parentHash);
        const apiAtCurrent = await api.at(header.hash);
        const currentEvents = await apiAtCurrent.query.system.events();

        const messagesReceived = currentEvents.find((record) => {
            return record.event.section == bridgedChain.messagesPalletName
                && record.event.method == "MessagesReceived";
        }) != undefined;
        const messagesDelivered = currentEvents.find((record) => {
            return record.event.section == bridgedChain.messagesPalletName &&
                record.event.method == "MessagesDelivered";
        }) != undefined;
        const hasMessageUpdates = messagesReceived || messagesDelivered;
        atLeastOneMessageReceived = atLeastOneMessageReceived || messagesReceived;
        atLeastOneMessageDelivered = atLeastOneMessageDelivered || messagesDelivered;

        if (!hasMessageUpdates) {
            // if there are no any message update transactions, we only expect mandatory GRANDPA
            // headers and initial parachain headers
            await utils.ensureOnlyMandatoryGrandpaHeadersImported(
                bridgedChain,
                apiAtParent,
                apiAtCurrent,
                currentEvents,
            );
            await utils.ensureOnlyInitialParachainHeaderImported(
                bridgedChain,
                apiAtParent,
                apiAtCurrent,
                currentEvents,
            );
        } else {
            const messageTransactions = (messagesReceived ? 1 : 0) + (messagesDelivered ? 1 : 0);

            // otherwise we only accept at most one GRANDPA header
            const newGrandpaHeaders = utils.countGrandpaHeaderImports(bridgedChain, currentEvents);
            if (newGrandpaHeaders > 1) {
                utils.logEvents(currentEvents);
                throw new Error("Unexpected relay chain header import: " + newGrandpaHeaders + " / " + messageTransactions);
            }

            // ...and at most one parachain header
            const newParachainHeaders = utils.countParachainHeaderImports(bridgedChain, currentEvents);
            if (newParachainHeaders > 1) {
                utils.logEvents(currentEvents);
                throw new Error("Unexpected parachain header import: " + newParachainHeaders + " / " + messageTransactions);
            }
        }
    });

    // wait until we have received + delivered messages OR until timeout
    await utils.pollUntil(
        exitAfterSeconds,
        () => { return atLeastOneMessageReceived && atLeastOneMessageDelivered; },
        () => { unsubscribe(); },
        () => {
            if (!atLeastOneMessageReceived) {
                throw new Error("No messages received from bridged chain");
            }
            if (!atLeastOneMessageDelivered) {
                throw new Error("No messages delivered to bridged chain");
            }
        },
    );
}

module.exports = { run }