"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const kyber_1 = require("@dedis/kyber");
const light_1 = require("protobufjs/light");
const byzcoin_1 = require("../byzcoin");
const network_1 = require("../network");
const network_2 = require("../network");
const protobuf_1 = require("../protobuf");
const calypso_instance_1 = require("./calypso-instance");
/**
 * OnChainSecretRPC is used to contact the OnChainSecret service of the cothority.
 * With it you can set up a new long-term onchain-secret, give it a policy to accept
 * new requests, and ask for re-encryption requests.
 */
class OnChainSecretRPC {
    constructor(bc, roster) {
        this.bc = bc;
        this.socket = new network_2.RosterWSConnection(bc.getConfig().roster, OnChainSecretRPC.serviceID);
        if (roster) {
            this.list = roster.list;
        }
        else {
            this.list = this.bc.getConfig().roster.list;
        }
    }
    // CreateLTS creates a random LTSID that can be used to reference the LTS group
    // created. It first sends a transaction to ByzCoin to spawn a LTS instance,
    // then it asks the Calypso cothority to start the DKG.
    createLTS(r, darcID, signers) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const buf = Buffer.from(LtsInstanceInfo.encode(new LtsInstanceInfo({ roster: r })).finish());
            const ctx = byzcoin_1.ClientTransaction.make(this.bc.getProtocolVersion(), byzcoin_1.Instruction.createSpawn(darcID, calypso_instance_1.OnChainSecretInstance.contractID, [
                new byzcoin_1.Argument({ name: "lts_instance_info", value: buf }),
            ]));
            yield ctx.updateCountersAndSign(this.bc, [signers]);
            yield this.bc.sendTransactionAndWait(ctx);
            // Ask for the full proof which is easier to verify.
            const p = yield this.bc.getProof(ctx.instructions[0].deriveId(), 1);
            return new network_1.WebSocketConnection(r.list[0].getWebSocketAddress(), OnChainSecretRPC.serviceID)
                .send(new CreateLTS({ proof: p }), CreateLTSReply);
        });
    }
    // authorize adds a ByzCoinID to the list of authorized IDs for each
    // server in the roster. The authorize endpoint refuses requests
    // that do not come from localhost for security reasons.
    //
    // It should be called by the administrator at the beginning, before any other
    // API calls are made. A ByzCoinID that is not authorized will not be allowed to
    // call the other APIs.
    authorize(who, bcid) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const sock = new network_1.WebSocketConnection(who.getWebSocketAddress(), OnChainSecretRPC.serviceID);
            return sock.send(new Authorize({ byzcoinid: bcid }), AuthorizeReply);
        });
    }
    /**
     * authorizeRoster is a convenience method that authorizes all nodes in the bc-roster
     * to create new LTS. For this to work, the nodes must have been started with
     * COTHORITY_ALLOW_INSECURE_ADMIN=true
     *
     * @param roster if given, this roster is used instead of the bc-roster
     */
    authorizeRoster(roster) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!roster) {
                roster = this.bc.getConfig().roster;
            }
            for (const node of roster.list) {
                yield this.authorize(node, this.bc.genesisID);
            }
        });
    }
    // reencryptKey takes as input Read- and Write- Proofs. It verifies that
    // the read/write requests match and then re-encrypts the secret
    // given the public key information of the reader.
    reencryptKey(write, read) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const sock = new network_1.WebSocketConnection(this.list[0].getWebSocketAddress(), OnChainSecretRPC.serviceID);
            return sock.send(new DecryptKey({ read, write }), DecryptKeyReply);
        });
    }
}
exports.OnChainSecretRPC = OnChainSecretRPC;
OnChainSecretRPC.serviceID = "Calypso";
/**
 * LongTermSecret extends the OnChainSecretRPC and also holds the id and the X.
 */
class LongTermSecret extends OnChainSecretRPC {
    constructor(bc, id, X, roster) {
        super(bc, roster);
        this.id = id;
        this.X = X;
    }
    /**
     * fromService returns a new LongTermSecret given a ByzCoinRPC and an LTSID
     * @param bc pointing to the ByzCoin holding the LTS definition
     * @param ltsid which LTSID to look up
     * @param roster can be set if known, else the byzcoin-instance will be looked up.
     */
    static fromService(bc, ltsid, roster) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!roster) {
                const instBS = yield bc.instanceObservable(ltsid);
                roster = LtsInstanceInfo.decode(instBS.getValue().value).roster;
            }
            const conn = new network_2.RosterWSConnection(roster, OnChainSecretRPC.serviceID);
            const reply = yield conn.send(new GetLTSReply({ ltsid }), CreateLTSReply);
            return new LongTermSecret(bc, ltsid, reply.X, roster);
        });
    }
    /**
     * spawn creates a new longtermsecret by spawning a longTermSecret instance, and then performing
     * a DKG using the full roster of bc.
     *
     * @param bc a valid ByzCoin instance
     * @param darcID id of a darc allowed to spawn longTermSecret
     * @param signers needed to authenticate longTermSecret spawns
     * @param roster if given, the roster for the DKG, if null, the full roster of bc will be used
     */
    static spawn(bc, darcID, signers, roster) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            if (!roster) {
                roster = bc.getConfig().roster;
            }
            const ocs = new OnChainSecretRPC(bc);
            try {
                yield ocs.authorizeRoster(roster);
            }
            catch (e) {
                if (e.toString().indexOf("already authorised") < 0) {
                    throw new Error(e);
                }
            }
            const lr = yield ocs.createLTS(roster, darcID, signers);
            return new LongTermSecret(bc, lr.instanceid, lr.X, roster);
        });
    }
}
exports.LongTermSecret = LongTermSecret;
/**
 * Authorize is used to add the given ByzCoinID into the list of authorized IDs.
 */
class Authorize extends light_1.Message {
    constructor(props) {
        super(props);
    }
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("Authorize", Authorize);
    }
}
exports.Authorize = Authorize;
/**
 * AuthorizeReply is returned upon successful authorisation.
 */
class AuthorizeReply extends light_1.Message {
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("AuthorizeReply", AuthorizeReply);
    }
}
exports.AuthorizeReply = AuthorizeReply;
//
/**
 * CreateLTS is used to start a DKG and store the private keys in each node.
 * Prior to using this request, the Calypso roster must be recorded on the
 * ByzCoin blockchain in the instance specified by InstanceID.
 */
class CreateLTS extends light_1.Message {
    constructor(props) {
        super(props);
    }
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("CreateLTS", CreateLTS);
    }
}
exports.CreateLTS = CreateLTS;
/**
 * CreateLTSReply is returned upon successfully setting up the distributed
 * key.
 */
class CreateLTSReply extends light_1.Message {
    get X() {
        return kyber_1.PointFactory.fromProto(this.x);
    }
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("CreateLTSReply", CreateLTSReply);
    }
}
exports.CreateLTSReply = CreateLTSReply;
/**
 * DecryptKey is sent by a reader after he successfully stored a 'Read' request
 * in byzcoin Client.
 */
class DecryptKey extends light_1.Message {
    constructor(props) {
        super(props);
    }
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("DecryptKey", DecryptKey);
    }
}
exports.DecryptKey = DecryptKey;
/**
 * DecryptKeyReply is returned if the service verified successfully that the
 * decryption request is valid.
 */
class DecryptKeyReply extends light_1.Message {
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("DecryptKeyReply", DecryptKeyReply);
    }
    decrypt(priv) {
        return tslib_1.__awaiter(this, void 0, void 0, function* () {
            const X = kyber_1.PointFactory.fromProto(this.x);
            const C = kyber_1.PointFactory.fromProto(this.c);
            /* tslint:disable-next-line: variable-name */
            const XhatEnc = kyber_1.PointFactory.fromProto(this.xhatenc);
            return calypso_instance_1.DecodeKey(X, C, XhatEnc, priv);
        });
    }
}
exports.DecryptKeyReply = DecryptKeyReply;
/**
 * LtsInstanceInfo is the information stored in an LTS instance.
 */
class LtsInstanceInfo extends light_1.Message {
    constructor(props) {
        super(props);
    }
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("LtsInstanceInfo", LtsInstanceInfo);
    }
}
exports.LtsInstanceInfo = LtsInstanceInfo;
/**
 * GetLTSReply will return a stored LTS entry.
 */
class GetLTSReply extends light_1.Message {
    /**
     * @see README#Message classes
     */
    static register() {
        protobuf_1.registerMessage("GetLTSReply", GetLTSReply);
    }
}
exports.GetLTSReply = GetLTSReply;
GetLTSReply.register();
Authorize.register();
AuthorizeReply.register();
CreateLTS.register();
CreateLTSReply.register();
DecryptKey.register();
DecryptKeyReply.register();
LtsInstanceInfo.register();
