mokka
Version:
Mokka Consensus Algorithm implementation in Javascript
116 lines • 5.64 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.NodeApi = void 0;
const EventTypes_1 = __importDefault(require("../constants/EventTypes"));
const EventTypes_2 = __importDefault(require("../constants/EventTypes"));
const MessageTypes_1 = __importDefault(require("../constants/MessageTypes"));
const NodeStates_1 = __importDefault(require("../constants/NodeStates"));
const NodeModel_1 = require("../models/NodeModel");
const VoteModel_1 = require("../models/VoteModel");
const utils = __importStar(require("../utils/cryptoUtils"));
const utils_1 = require("../utils/utils");
const MessageApi_1 = require("./MessageApi");
class NodeApi {
constructor(mokka) {
this.mokka = mokka;
this.messageApi = new MessageApi_1.MessageApi(mokka);
}
join(multiaddr) {
const publicKey = multiaddr.match(/\w+$/).toString();
if (this.mokka.publicKey === publicKey)
return;
const node = new NodeModel_1.NodeModel(null, multiaddr, 'CFT', NodeStates_1.default.STOPPED);
node.write = this.mokka.write.bind(this.mokka);
node.once('end', () => this.leave(node.publicKey));
this.mokka.nodes.set(publicKey, node);
this.buildPublicKeysRootAndCombinations();
this.mokka.emit(EventTypes_1.default.NODE_JOIN, node);
return node;
}
buildPublicKeysRootAndCombinations() {
const sortedPublicKeys = [...this.mokka.nodes.keys(), this.mokka.publicKey].sort();
this.mokka.publicKeysRoot = utils.buildPublicKeysRoot(sortedPublicKeys);
this.mokka.publicKeysCombinationsInQuorum = (0, utils_1.getCombinations)(sortedPublicKeys, this.mokka.majority());
}
leave(publicKey) {
const node = this.mokka.nodes.get(publicKey);
this.mokka.nodes.delete(publicKey);
this.buildPublicKeysRootAndCombinations();
this.mokka.emit(EventTypes_1.default.NODE_LEAVE, node);
}
async promote() {
if (this.mokka.state === NodeStates_1.default.CANDIDATE) {
return;
}
const nonce = Date.now();
this.mokka.setState(NodeStates_1.default.CANDIDATE, this.mokka.term + 1, '');
const publicKeysRootForTerm = utils.buildPublicKeysRootForTerm(this.mokka.publicKeysRoot, this.mokka.term, nonce, this.mokka.publicKey);
const vote = new VoteModel_1.VoteModel(nonce, this.mokka.term, publicKeysRootForTerm);
for (const combination of this.mokka.publicKeysCombinationsInQuorum) {
if (!combination.includes(this.mokka.publicKey)) {
continue;
}
const sharedPublicKeyPartial = utils.buildSharedPublicKeyX(combination, this.mokka.term, nonce, publicKeysRootForTerm);
vote.publicKeyToCombinationMap.set(sharedPublicKeyPartial, combination);
}
this.mokka.setVote(vote);
const votePayload = {
nonce,
publicKey: this.mokka.publicKey,
term: this.mokka.term
};
const selfVoteSignature = utils.buildPartialSignature(this.mokka.privateKey, votePayload.term, votePayload.nonce, publicKeysRootForTerm);
vote.repliesPublicKeyToSignatureMap.set(this.mokka.publicKey, selfVoteSignature);
const packet = this.messageApi.packet(MessageTypes_1.default.VOTE, {
nonce
});
await Promise.all([...this.mokka.nodes.values()].map((node) => this.messageApi.message(packet, node.publicKey)));
await new Promise((res) => {
const timeoutHandler = () => {
this.mokka.removeListener(EventTypes_2.default.STATE, emitHandler);
res();
};
const timeoutId = setTimeout(timeoutHandler, this.mokka.electionTimeout);
const emitHandler = () => {
clearTimeout(timeoutId);
res();
};
this.mokka.once(EventTypes_2.default.STATE, emitHandler);
});
if (this.mokka.state === NodeStates_1.default.CANDIDATE) {
this.mokka.setState(NodeStates_1.default.FOLLOWER, this.mokka.term, null);
}
}
async pingFromLeader(packet) {
if (packet && packet.state === NodeStates_1.default.LEADER) {
this.mokka.logger.trace(`accepted ack`);
this.mokka.heartbeatCtrl.setNextBeat(this.mokka.heartbeatCtrl.timeout());
this.mokka.emit(EventTypes_2.default.ACK);
}
return null;
}
}
exports.NodeApi = NodeApi;
//# sourceMappingURL=NodeApi.js.map