import { TopicValidatorResult } from "@libp2p/gossipsub";
import { AttestationError, GossipAction, GossipActionError } from "../../chain/errors/index.js";
import { prettyPrintPeerIdStr } from "../util.js";
/**
 * Similar to getGossipValidatorFn but return a function to accept a batch of beacon_attestation messages
 * with the same attestation data
 */
export function getGossipValidatorBatchFn(gossipHandlers, modules) {
    const { logger, metrics } = modules;
    return async function gossipValidatorBatchFn(messageInfos) {
        // all messageInfos have same topic type
        const type = messageInfos[0].topic.type;
        try {
            const results = await gossipHandlers[type](messageInfos.map((messageInfo) => ({
                gossipData: {
                    serializedData: messageInfo.msg.data,
                    msgSlot: messageInfo.msgSlot,
                    indexed: messageInfo.indexed,
                },
                topic: messageInfo.topic,
                peerIdStr: messageInfo.propagationSource,
                seenTimestampSec: messageInfo.seenTimestampSec,
            })));
            return results.map((e, i) => {
                if (e == null) {
                    return TopicValidatorResult.Accept;
                }
                const { clientAgent, clientVersion, propagationSource } = messageInfos[i];
                if (!(e instanceof AttestationError)) {
                    logger.debug(`Gossip batch validation ${type} threw a non-AttestationError`, { peerId: prettyPrintPeerIdStr(propagationSource), clientAgent, clientVersion }, e);
                    metrics?.networkProcessor.gossipValidationIgnore.inc({ topic: type });
                    return TopicValidatorResult.Ignore;
                }
                switch (e.action) {
                    case GossipAction.IGNORE:
                        metrics?.networkProcessor.gossipValidationIgnore.inc({ topic: type });
                        // only beacon_attestation topic is validated in batch
                        metrics?.networkProcessor.gossipAttestationIgnoreByReason.inc({ reason: e.type.code });
                        return TopicValidatorResult.Ignore;
                    case GossipAction.REJECT:
                        metrics?.networkProcessor.gossipValidationReject.inc({ topic: type });
                        // only beacon_attestation topic is validated in batch
                        metrics?.networkProcessor.gossipAttestationRejectByReason.inc({ reason: e.type.code });
                        logger.debug(`Gossip validation ${type} rejected`, { peerId: prettyPrintPeerIdStr(propagationSource), clientAgent, clientVersion }, e);
                        return TopicValidatorResult.Reject;
                }
            });
        }
        catch (e) {
            // Don't expect error here
            logger.debug(`Gossip batch validation ${type} threw an error`, {}, e);
            const results = [];
            for (let i = 0; i < messageInfos.length; i++) {
                results.push(TopicValidatorResult.Ignore);
            }
            return results;
        }
    };
}
/**
 * Returns a GossipSub validator function from a GossipHandlerFn. GossipHandlerFn may throw GossipActionError if one
 * or more validation conditions from the consensus-specs#p2p-interface are not satisfied.
 *
 * This function receives a string topic and a binary message `InMessage` and deserializes both using caches.
 * - The topic string should be known in advance and pre-computed
 * - The message.data should already by uncompressed when computing its msgID
 *
 * All logging and metrics associated with gossip object validation should happen in this function. We want to know
 * - In debug logs what objects are we processing, the result and some succint metadata
 * - In metrics what's the throughput and ratio of accept/ignore/reject per type
 *
 * @see getGossipHandlers for reasoning on why GossipHandlerFn are used for gossip validation.
 */
export function getGossipValidatorFn(gossipHandlers, modules) {
    const { logger, metrics } = modules;
    return async function gossipValidatorFn({ topic, msg, propagationSource, clientAgent, clientVersion, seenTimestampSec, msgSlot, }) {
        const type = topic.type;
        try {
            await gossipHandlers[type]({
                gossipData: { serializedData: msg.data, msgSlot },
                topic,
                peerIdStr: propagationSource,
                seenTimestampSec,
            });
            metrics?.networkProcessor.gossipValidationAccept.inc({ topic: type });
            return TopicValidatorResult.Accept;
        }
        catch (e) {
            if (!(e instanceof GossipActionError)) {
                // not deserve to log error here, it looks too dangerous to users
                logger.debug(`Gossip validation ${type} threw a non-GossipActionError`, { peerId: prettyPrintPeerIdStr(propagationSource), clientAgent, clientVersion }, e);
                return TopicValidatorResult.Ignore;
            }
            // Metrics on specific error reason
            // Note: LodestarError.code are bounded pre-declared error messages, not from arbitrary error.message
            metrics?.networkProcessor.gossipValidationError.inc({
                topic: type,
                error: e.type.code,
            });
            switch (e.action) {
                case GossipAction.IGNORE:
                    metrics?.networkProcessor.gossipValidationIgnore.inc({ topic: type });
                    return TopicValidatorResult.Ignore;
                case GossipAction.REJECT:
                    metrics?.networkProcessor.gossipValidationReject.inc({ topic: type });
                    logger.debug(`Gossip validation ${type} rejected`, { peerId: prettyPrintPeerIdStr(propagationSource), clientAgent, clientVersion }, e);
                    return TopicValidatorResult.Reject;
            }
        }
    };
}
//# sourceMappingURL=gossipValidatorFn.js.map