"use strict";
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PublisherKeyExchange = void 0;
const utils_1 = require("@streamr/utils");
const trackerless_network_1 = require("@streamr/trackerless-network");
const without_1 = __importDefault(require("lodash/without"));
const tsyringe_1 = require("tsyringe");
const Identity_1 = require("../identity/Identity");
const NetworkNodeFacade_1 = require("../NetworkNodeFacade");
const StreamRegistry_1 = require("../contracts/StreamRegistry");
const events_1 = require("../events");
const MessageID_1 = require("../protocol/MessageID");
const StreamMessage_1 = require("../protocol/StreamMessage");
const messageChain_1 = require("../publish/messageChain");
const MessageSigner_1 = require("../signature/MessageSigner");
const SignatureValidator_1 = require("../signature/SignatureValidator");
const LoggerFactory_1 = require("../utils/LoggerFactory");
const validateStreamMessage_1 = require("../utils/validateStreamMessage");
const EncryptionUtil_1 = require("./EncryptionUtil");
const LocalGroupKeyStore_1 = require("./LocalGroupKeyStore");
const Config_1 = require("../Config");
const encryptionCompliance_1 = require("../utils/encryptionCompliance");
const StreamrClientError_1 = require("../StreamrClientError");
/*
 * Sends group key responses
 */
var ResponseType;
(function (ResponseType) {
    ResponseType[ResponseType["NONE"] = 0] = "NONE";
    ResponseType[ResponseType["NORMAL"] = 1] = "NORMAL";
    ResponseType[ResponseType["ERC_1271"] = 2] = "ERC_1271";
})(ResponseType || (ResponseType = {}));
const logger = new utils_1.Logger(module);
let PublisherKeyExchange = class PublisherKeyExchange {
    networkNodeFacade;
    streamRegistry;
    signatureValidator;
    messageSigner;
    store;
    identity;
    logger;
    erc1271Publishers = new Set();
    config;
    constructor(networkNodeFacade, streamRegistry, signatureValidator, messageSigner, store, identity, config, eventEmitter, loggerFactory) {
        this.networkNodeFacade = networkNodeFacade;
        this.streamRegistry = streamRegistry;
        this.signatureValidator = signatureValidator;
        this.messageSigner = messageSigner;
        this.store = store;
        this.identity = identity;
        this.logger = loggerFactory.createLogger(module);
        this.config = config;
        networkNodeFacade.once('start', async () => {
            networkNodeFacade.addMessageListener((msg) => this.onMessage(msg));
            this.logger.debug('Started');
        });
        eventEmitter.on('messagePublished', (msg) => {
            if (msg.signatureType === trackerless_network_1.SignatureType.ERC_1271) {
                const publisherId = msg.getPublisherId();
                if (!this.erc1271Publishers.has(publisherId)) {
                    logger.debug('Add ERC-1271 publisher', { publisherId });
                    this.erc1271Publishers.add(publisherId);
                }
            }
        });
    }
    async onMessage(request) {
        if (request.messageType === StreamMessage_1.StreamMessageType.GROUP_KEY_REQUEST) {
            try {
                const { recipientId, requestId, publicKey, groupKeyIds, encryptionType: keyEncryptionType } = trackerless_network_1.GroupKeyRequest.fromBinary(request.content);
                const recipientUserId = (0, utils_1.toUserId)(recipientId);
                if (!(0, encryptionCompliance_1.isCompliantAsymmetricEncryptionType)(keyEncryptionType, this.config)) {
                    throw new StreamrClientError_1.StreamrClientError(`EncryptionType in key request (${keyEncryptionType}) is not compliant with encryption settings!`, 'ENCRYPTION_POLICY_VIOLATION', request);
                }
                const responseType = await this.getResponseType(recipientUserId);
                if (responseType !== ResponseType.NONE) {
                    this.logger.debug('Handling group key request', { requestId, responseType, keyEncryptionType: trackerless_network_1.AsymmetricEncryptionType[keyEncryptionType] });
                    await (0, validateStreamMessage_1.validateStreamMessage)(request, this.streamRegistry, this.signatureValidator);
                    const authenticatedUser = await this.identity.getUserId();
                    const keys = (0, without_1.default)(await Promise.all(groupKeyIds.map((id) => this.store.get(id, authenticatedUser))), undefined);
                    if (keys.length > 0) {
                        const response = await this.createResponse(keys, responseType, recipientUserId, request.getStreamPartID(), publicKey, request.getPublisherId(), requestId, keyEncryptionType);
                        await this.networkNodeFacade.broadcast(response);
                        this.logger.debug('Handled group key request (found keys)', {
                            groupKeyIds: keys.map((k) => k.id).join(),
                            recipient: request.getPublisherId()
                        });
                    }
                    else {
                        this.logger.debug('Handled group key request (no keys found)', {
                            requestId,
                            recipient: request.getPublisherId()
                        });
                    }
                }
            }
            catch (err) {
                this.logger.debug('Failed to handle group key request', err);
            }
        }
    }
    async getResponseType(publisherId) {
        const myId = await this.identity.getUserId();
        if (publisherId === myId) {
            return ResponseType.NORMAL;
        }
        else if (this.erc1271Publishers.has(publisherId)) {
            return ResponseType.ERC_1271;
        }
        else {
            return ResponseType.NONE;
        }
    }
    async createResponse(keys, responseType, publisherId, streamPartId, publicKey, recipientId, requestId, keyEncryptionType) {
        const encryptedGroupKeys = await Promise.all(keys.map(async (key) => ({
            id: key.id,
            data: await EncryptionUtil_1.EncryptionUtil.encryptForPublicKey(key.data, publicKey, keyEncryptionType)
        })));
        const responseContent = {
            recipientId: (0, utils_1.toUserIdRaw)(recipientId),
            requestId,
            groupKeys: encryptedGroupKeys,
            encryptionType: keyEncryptionType,
        };
        const response = this.messageSigner.createSignedMessage({
            messageId: new MessageID_1.MessageID(utils_1.StreamPartIDUtils.getStreamID(streamPartId), utils_1.StreamPartIDUtils.getStreamPartition(streamPartId), Date.now(), 0, publisherId, (0, messageChain_1.createRandomMsgChainId)()),
            content: trackerless_network_1.GroupKeyResponse.toBinary(responseContent),
            contentType: trackerless_network_1.ContentType.BINARY,
            messageType: StreamMessage_1.StreamMessageType.GROUP_KEY_RESPONSE,
            encryptionType: trackerless_network_1.EncryptionType.NONE,
        }, responseType === ResponseType.NORMAL ? this.identity.getSignatureType() : trackerless_network_1.SignatureType.ERC_1271);
        return response;
    }
};
exports.PublisherKeyExchange = PublisherKeyExchange;
exports.PublisherKeyExchange = PublisherKeyExchange = __decorate([
    (0, tsyringe_1.scoped)(tsyringe_1.Lifecycle.ContainerScoped),
    __param(5, (0, tsyringe_1.inject)(Identity_1.IdentityInjectionToken)),
    __param(6, (0, tsyringe_1.inject)(Config_1.ConfigInjectionToken)),
    __metadata("design:paramtypes", [NetworkNodeFacade_1.NetworkNodeFacade,
        StreamRegistry_1.StreamRegistry,
        SignatureValidator_1.SignatureValidator,
        MessageSigner_1.MessageSigner,
        LocalGroupKeyStore_1.LocalGroupKeyStore,
        Identity_1.Identity, Object, events_1.StreamrClientEventEmitter,
        LoggerFactory_1.LoggerFactory])
], PublisherKeyExchange);
//# sourceMappingURL=PublisherKeyExchange.js.map