Implement consensus mechanism using mokka (#412)

* Add Consensus class with placeholders

* Implement Consensus constructor

* Move PubsubType to peer package

* Implement remaining methods in Consensus class

* Use existing consensus stream if it exists

* Setup send and receive pipes on consensus streams

* Refactor P2P and consensus setup in server command

* Add Nitro node initialization in server command

* Return objects from server initializations

* Use dynamic imports for ES modules in util package

* Fix util deps

* Change target to es6 to allow creating a Mokka instance

* Set moduleResolution to node16 in util for dynamic imports

* Upgrade @cerc-io/nitro-node package

* Implement retries while sending consensus messages

* Use bunyan for consensus logs and subscribe to state changes

* Use debug for logging state change events

* Handle empty watcher party file path

* Return object from initP2P

* Upgrade @cerc-io/nitro-node package

* Update package versions
This commit is contained in:
prathamesh0 2023-09-14 13:47:31 +05:30 committed by GitHub
parent c80e4d0ab7
commit bd73dae1b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 513 additions and 218 deletions

View File

@ -2,7 +2,7 @@
"packages": [
"packages/*"
],
"version": "0.2.56",
"version": "0.2.57",
"npmClient": "yarn",
"useWorkspaces": true,
"command": {

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/cache",
"version": "0.2.56",
"version": "0.2.57",
"description": "Generic object cache",
"main": "dist/index.js",
"scripts": {

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/cli",
"version": "0.2.56",
"version": "0.2.57",
"main": "dist/index.js",
"license": "AGPL-3.0",
"scripts": {
@ -12,11 +12,13 @@
},
"dependencies": {
"@apollo/client": "^3.7.1",
"@cerc-io/cache": "^0.2.56",
"@cerc-io/ipld-eth-client": "^0.2.56",
"@cerc-io/peer": "^0.2.56",
"@cerc-io/rpc-eth-client": "^0.2.56",
"@cerc-io/util": "^0.2.56",
"@cerc-io/cache": "^0.2.57",
"@cerc-io/ipld-eth-client": "^0.2.57",
"@cerc-io/libp2p": "^0.42.2-laconic-0.1.4",
"@cerc-io/nitro-node": "^0.1.10",
"@cerc-io/peer": "^0.2.57",
"@cerc-io/rpc-eth-client": "^0.2.57",
"@cerc-io/util": "^0.2.57",
"@ethersproject/providers": "^5.4.4",
"@graphql-tools/utils": "^9.1.1",
"@ipld/dag-cbor": "^8.0.0",

View File

@ -11,10 +11,10 @@ import path from 'path';
import {
PeerInitConfig,
PeerIdObj
PeerIdObj,
PubsubType
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
} from '@cerc-io/peer';
import { PubsubType } from '@cerc-io/util';
import { readPeerId } from './utils';

View File

@ -3,6 +3,7 @@
//
import debug from 'debug';
import path from 'path';
import yargs from 'yargs';
import { hideBin } from 'yargs/helpers';
import 'reflect-metadata';
@ -25,17 +26,21 @@ import {
EventWatcher,
GraphWatcherInterface,
Config,
P2PConfig,
PaymentsManager
PaymentsManager,
Consensus,
readParty
} from '@cerc-io/util';
import { TypeSource } from '@graphql-tools/utils';
import {
import type {
RelayNodeInitConfig,
PeerInitConfig,
PeerIdObj,
Peer
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
} from '@cerc-io/peer';
import { Node as NitroNode, utils } from '@cerc-io/nitro-node';
// @ts-expect-error TODO: Resolve (Not able to find the type declarations)
import type { Libp2p } from '@cerc-io/libp2p';
import { BaseCmd } from './base';
import { readPeerId } from './utils/index';
@ -50,6 +55,8 @@ export class ServerCmd {
_argv?: Arguments;
_baseCmd: BaseCmd;
_peer?: Peer;
_nitro?: NitroNode;
_consensus?: Consensus;
constructor () {
this._baseCmd = new BaseCmd();
@ -75,6 +82,14 @@ export class ServerCmd {
return this._peer;
}
get nitro (): NitroNode | undefined {
return this._nitro;
}
get consensus (): Consensus | undefined {
return this._consensus;
}
async initConfig<ConfigType> (): Promise<ConfigType> {
this._argv = this._getArgv();
assert(this._argv);
@ -109,53 +124,15 @@ export class ServerCmd {
await this._baseCmd.initEventWatcher();
}
async exec (
createResolvers: (indexer: IndexerInterface, eventWatcher: EventWatcher) => Promise<any>,
typeDefs: TypeSource,
parseLibp2pMessage?: (peerId: string, data: any) => void,
paymentsManager?: PaymentsManager
): Promise<{
app: Application,
server: ApolloServer
}> {
const config = this._baseCmd.config;
const jobQueue = this._baseCmd.jobQueue;
const indexer = this._baseCmd.indexer;
const eventWatcher = this._baseCmd.eventWatcher;
assert(config);
assert(jobQueue);
assert(indexer);
assert(eventWatcher);
if (config.server.kind === KIND_ACTIVE) {
// Delete jobs to prevent creating jobs after completion of processing previous block.
await jobQueue.deleteAllJobs();
await eventWatcher.start();
}
const resolvers = await createResolvers(indexer, eventWatcher);
// Create an Express app
const app: Application = express();
const server = await createAndStartServer(app, typeDefs, resolvers, config.server, paymentsManager);
await startGQLMetricsServer(config);
const p2pConfig = config.server.p2p;
async initP2P (): Promise<{ relayNode?: Libp2p, peer?: Peer }> {
let relayNode: Libp2p | undefined;
// Start P2P nodes if config provided
if (p2pConfig) {
await this._startP2PNodes(p2pConfig, parseLibp2pMessage);
const p2pConfig = this._baseCmd.config.server.p2p;
if (!p2pConfig) {
return {};
}
return { app, server };
}
async _startP2PNodes (
p2pConfig: P2PConfig,
parseLibp2pMessage?: (peerId: string, data: any) => void
): Promise<void> {
const { createRelayNode, Peer } = await import('@cerc-io/peer');
const {
RELAY_DEFAULT_HOST,
@ -190,7 +167,8 @@ export class ServerCmd {
pubsub: relayConfig.pubsub,
enableDebugInfo: relayConfig.enableDebugInfo
};
await createRelayNode(relayNodeInit);
relayNode = await createRelayNode(relayNodeInit);
}
// Run a peer node if enabled
@ -218,14 +196,110 @@ export class ServerCmd {
};
await this._peer.init(peerNodeInit, peerIdObj);
this._peer.subscribeTopic(peerConfig.pubSubTopic, (peerId, data) => {
if (parseLibp2pMessage) {
parseLibp2pMessage(peerId.toString(), data);
}
});
log(`Peer ID: ${this._peer.peerId?.toString()}`);
}
return { relayNode, peer: this._peer };
}
async initConsensus (): Promise<Consensus | undefined> {
const p2pConfig = this._baseCmd.config.server.p2p;
const { consensus: consensusConfig } = p2pConfig;
// Setup consensus engine if enabled
// Consensus requires p2p peer to be enabled
if (!p2pConfig.enablePeer || !consensusConfig.enabled) {
return;
}
assert(this.peer);
const watcherPartyPeers = readParty(consensusConfig.watcherPartyFile);
// Create and initialize the consensus engine
this._consensus = new Consensus({
peer: this.peer,
publicKey: consensusConfig.publicKey,
privateKey: consensusConfig.privateKey,
party: watcherPartyPeers
});
// Connect registers the required p2p protocol handlers and starts the engine
this._consensus.connect();
log('Consensus engine started');
return this._consensus;
}
async initNitro (nitroContractAddresses: { [key: string]: string }): Promise<NitroNode | undefined> {
// Start a Nitro node
const {
server: {
p2p: {
enablePeer,
nitro: nitroConfig
}
},
upstream: {
ethServer: {
rpcProviderEndpoint
}
}
} = this._baseCmd.config;
// Nitro requires p2p peer to be enabled
if (!enablePeer) {
return;
}
assert(this.peer);
const nitro = await utils.Nitro.setupNode(
nitroConfig.privateKey,
rpcProviderEndpoint,
nitroConfig.chainPrivateKey,
nitroContractAddresses,
this.peer,
path.resolve(nitroConfig.store)
);
this._nitro = nitro.node;
log(`Nitro node started with address: ${this._nitro.address}`);
return this._nitro;
}
async exec (
createResolvers: (indexer: IndexerInterface, eventWatcher: EventWatcher) => Promise<any>,
typeDefs: TypeSource,
paymentsManager?: PaymentsManager
): Promise<{
app: Application,
server: ApolloServer
}> {
const config = this._baseCmd.config;
const jobQueue = this._baseCmd.jobQueue;
const indexer = this._baseCmd.indexer;
const eventWatcher = this._baseCmd.eventWatcher;
assert(config);
assert(jobQueue);
assert(indexer);
assert(eventWatcher);
if (config.server.kind === KIND_ACTIVE) {
// Delete jobs to prevent creating jobs after completion of processing previous block.
await jobQueue.deleteAllJobs();
await eventWatcher.start();
}
const resolvers = await createResolvers(indexer, eventWatcher);
// Create an Express app
const app: Application = express();
const server = await createAndStartServer(app, typeDefs, resolvers, config.server, paymentsManager);
await startGQLMetricsServer(config);
return { app, server };
}
_getArgv (): any {

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/codegen",
"version": "0.2.56",
"version": "0.2.57",
"description": "Code generator",
"private": true,
"main": "index.js",
@ -20,7 +20,7 @@
},
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@cerc-io/util": "^0.2.56",
"@cerc-io/util": "^0.2.57",
"@graphql-tools/load-files": "^6.5.2",
"@poanet/solidity-flattener": "https://github.com/vulcanize/solidity-flattener.git",
"@solidity-parser/parser": "^0.13.2",

View File

@ -41,12 +41,12 @@
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@apollo/client": "^3.3.19",
"@cerc-io/cli": "^0.2.56",
"@cerc-io/ipld-eth-client": "^0.2.56",
"@cerc-io/solidity-mapper": "^0.2.56",
"@cerc-io/util": "^0.2.56",
"@cerc-io/cli": "^0.2.57",
"@cerc-io/ipld-eth-client": "^0.2.57",
"@cerc-io/solidity-mapper": "^0.2.57",
"@cerc-io/util": "^0.2.57",
{{#if (subgraphPath)}}
"@cerc-io/graph-node": "^0.2.56",
"@cerc-io/graph-node": "^0.2.57",
{{/if}}
"@ethersproject/providers": "^5.4.4",
"apollo-type-bigint": "^0.1.3",

View File

@ -1,10 +1,10 @@
{
"name": "@cerc-io/graph-node",
"version": "0.2.56",
"version": "0.2.57",
"main": "dist/index.js",
"license": "AGPL-3.0",
"devDependencies": {
"@cerc-io/solidity-mapper": "^0.2.56",
"@cerc-io/solidity-mapper": "^0.2.57",
"@ethersproject/providers": "^5.4.4",
"@graphprotocol/graph-ts": "^0.22.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
@ -51,9 +51,9 @@
"dependencies": {
"@apollo/client": "^3.3.19",
"@cerc-io/assemblyscript": "0.19.10-watcher-ts-0.1.2",
"@cerc-io/cache": "^0.2.56",
"@cerc-io/ipld-eth-client": "^0.2.56",
"@cerc-io/util": "^0.2.56",
"@cerc-io/cache": "^0.2.57",
"@cerc-io/ipld-eth-client": "^0.2.57",
"@cerc-io/util": "^0.2.57",
"@types/json-diff": "^0.5.2",
"@types/yargs": "^17.0.0",
"bn.js": "^4.11.9",

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/ipld-eth-client",
"version": "0.2.56",
"version": "0.2.57",
"description": "IPLD ETH Client",
"main": "dist/index.js",
"scripts": {
@ -20,7 +20,7 @@
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@apollo/client": "^3.7.1",
"@cerc-io/cache": "^0.2.56",
"@cerc-io/cache": "^0.2.57",
"cross-fetch": "^3.1.4",
"debug": "^4.3.1",
"ethers": "^5.4.4",

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/peer",
"version": "0.2.56",
"version": "0.2.57",
"description": "libp2p module",
"main": "dist/index.js",
"exports": "./dist/index.js",
@ -28,9 +28,8 @@
"test": "mocha dist/peer.test.js --bail"
},
"dependencies": {
"@cerc-io/libp2p": "0.42.2-laconic-0.1.4",
"@cerc-io/libp2p": "^0.42.2-laconic-0.1.4",
"@cerc-io/prometheus-metrics": "1.1.4",
"@cerc-io/util": "^0.2.56",
"@chainsafe/libp2p-gossipsub": "^6.0.0",
"@chainsafe/libp2p-noise": "^11.0.0",
"@libp2p/floodsub": "^6.0.0",

View File

@ -3,8 +3,6 @@ import { hideBin } from 'yargs/helpers';
import fs from 'fs';
import path from 'path';
import { PubsubType } from '@cerc-io/util';
import { RelayNodeInitConfig, createRelayNode } from '../relay.js';
import { PeerIdObj } from '../peer.js';
import {
@ -15,6 +13,7 @@ import {
DEFAULT_PING_INTERVAL,
DIAL_TIMEOUT
} from '../constants.js';
import { PubsubType } from '../utils/index.js';
interface Arguments {
host: string;

View File

@ -4,7 +4,7 @@
export { Peer, PeerIdObj, PeerInitConfig, createPeerId } from './peer.js';
export { RelayNodeInitConfig, createRelayNode } from './relay.js';
export { getPseudonymForPeerId } from './utils/index.js';
export { getPseudonymForPeerId, PubsubType } from './utils/index.js';
export {
RELAY_DEFAULT_HOST,
RELAY_DEFAULT_PORT,

View File

@ -12,7 +12,6 @@ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
import debug from 'debug';
import { PubsubType } from '@cerc-io/util';
import { createLibp2p, Libp2p, Libp2pInit } from '@cerc-io/libp2p';
import { webSockets } from '@libp2p/websockets';
import { noise } from '@chainsafe/libp2p-noise';
@ -42,7 +41,7 @@ import {
P2P_WEBRTC_STAR_ID
} from './constants.js';
import { PeerHearbeatChecker } from './peer-heartbeat-checker.js';
import { debugInfoRequestHandler, dialWithRetry, getConnectionsInfo, getPseudonymForPeerId, getSelfInfo, initPubsub, isMultiaddrBlacklisted, wsPeerFilter } from './utils/index.js';
import { debugInfoRequestHandler, dialWithRetry, getConnectionsInfo, getPseudonymForPeerId, getSelfInfo, initPubsub, isMultiaddrBlacklisted, PubsubType, wsPeerFilter } from './utils/index.js';
import { ConnectionType, DebugPeerInfo, DebugRequest, PeerConnectionInfo, PeerSelfInfo } from './types/debug-info.js';
const log = debug('laconic:peer');

View File

@ -6,7 +6,6 @@ import debug from 'debug';
import assert from 'assert';
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
import { PubsubType } from '@cerc-io/util';
import { Libp2p, createLibp2p } from '@cerc-io/libp2p';
import { noise } from '@chainsafe/libp2p-noise';
import { mplex } from '@libp2p/mplex';
@ -27,7 +26,7 @@ import {
DEBUG_INFO_TOPIC
} from './constants.js';
import { PeerHearbeatChecker } from './peer-heartbeat-checker.js';
import { debugInfoRequestHandler, dialWithRetry, getConnectionsInfo, getPseudonymForPeerId, getSelfInfo, initPubsub, isMultiaddrBlacklisted } from './utils/index.js';
import { PubsubType, debugInfoRequestHandler, dialWithRetry, getConnectionsInfo, getPseudonymForPeerId, getSelfInfo, initPubsub, isMultiaddrBlacklisted } from './utils/index.js';
import { PeerIdObj } from './peer.js';
import { SelfInfo, ConnectionInfo } from './types/debug-info.js';

View File

@ -18,7 +18,6 @@ import { gossipsub } from '@chainsafe/libp2p-gossipsub';
import { ConnectionInfo, ConnectionType, DebugMsg, DebugPeerInfo, DebugResponse, SelfInfo } from '../types/debug-info.js';
import { DEBUG_INFO_TOPIC, DEFAULT_PUBSUB_TYPE, P2P_WEBRTC_STAR_ID, PUBSUB_SIGNATURE_POLICY } from '../constants.js';
import { PeerHearbeatChecker } from '../peer-heartbeat-checker.js';
import { PubsubType } from '@cerc-io/util';
const log = debug('laconic:utils');
@ -28,6 +27,8 @@ export const CODE_CIRCUIT = 290;
const ERR_INVALID_PUBSUB_TYPE = 'Invalid pubsub type';
export type PubsubType = 'floodsub' | 'gossipsub';
interface DialWithRetryOptions {
redialInterval: number
maxRetry: number

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/rpc-eth-client",
"version": "0.2.56",
"version": "0.2.57",
"description": "RPC ETH Client",
"main": "dist/index.js",
"scripts": {
@ -19,9 +19,9 @@
},
"homepage": "https://github.com/cerc-io/watcher-ts#readme",
"dependencies": {
"@cerc-io/cache": "^0.2.56",
"@cerc-io/ipld-eth-client": "^0.2.56",
"@cerc-io/util": "^0.2.56",
"@cerc-io/cache": "^0.2.57",
"@cerc-io/ipld-eth-client": "^0.2.57",
"@cerc-io/util": "^0.2.57",
"chai": "^4.3.4",
"ethers": "^5.4.4",
"left-pad": "^1.3.0",

View File

@ -1,10 +1,10 @@
{
"name": "@cerc-io/solidity-mapper",
"version": "0.2.56",
"version": "0.2.57",
"main": "dist/index.js",
"license": "AGPL-3.0",
"devDependencies": {
"@cerc-io/ipld-eth-client": "^0.2.56",
"@cerc-io/ipld-eth-client": "^0.2.57",
"@ethersproject/abi": "^5.3.0",
"@nomiclabs/hardhat-ethers": "^2.0.2",
"@nomiclabs/hardhat-waffle": "^2.0.1",

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/test",
"version": "0.2.56",
"version": "0.2.57",
"main": "dist/index.js",
"license": "AGPL-3.0",
"private": true,

View File

@ -1,6 +1,6 @@
{
"name": "@cerc-io/tracing-client",
"version": "0.2.56",
"version": "0.2.57",
"description": "ETH VM tracing client",
"main": "dist/index.js",
"scripts": {

View File

@ -1,20 +1,25 @@
{
"name": "@cerc-io/util",
"version": "0.2.56",
"version": "0.2.57",
"main": "dist/index.js",
"license": "AGPL-3.0",
"dependencies": {
"@apollo/utils.keyvaluecache": "^1.0.1",
"@cerc-io/nitro-node": "^0.1.8",
"@cerc-io/solidity-mapper": "^0.2.56",
"@cerc-io/nitro-node": "^0.1.10",
"@cerc-io/peer": "^0.2.57",
"@cerc-io/solidity-mapper": "^0.2.57",
"@cerc-io/ts-channel": "1.0.3-ts-nitro-0.1.1",
"@ethersproject/providers": "^5.4.4",
"@graphql-tools/schema": "^9.0.10",
"@graphql-tools/utils": "^9.1.1",
"@ipld/dag-cbor": "^6.0.12",
"@libp2p/interface-connection": "^3.0.2",
"@libp2p/interface-peer-id": "^2.0.0",
"@libp2p/peer-id": "^2.0.0",
"apollo-server-core": "^3.11.1",
"apollo-server-express": "^3.11.1",
"apollo-server-plugin-response-cache": "^3.8.1",
"bunyan": "^1.8.15",
"debug": "^4.3.1",
"decimal.js": "^10.3.1",
"ethers": "^5.4.4",
@ -25,10 +30,14 @@
"graphql-subscriptions": "^2.0.0",
"graphql-ws": "^5.11.2",
"ipfs-http-client": "^56.0.3",
"it-length-prefixed": "^8.0.4",
"it-pipe": "^2.0.5",
"it-pushable": "^3.1.2",
"js-yaml": "^4.1.0",
"json-bigint": "^1.0.0",
"lodash": "^4.17.21",
"lru-cache": "^10.0.0",
"mokka": "^1.4.2",
"multiformats": "^9.4.8",
"pg": "^8.5.1",
"pg-boss": "^6.1.0",
@ -40,8 +49,9 @@
"yargs": "^17.0.1"
},
"devDependencies": {
"@cerc-io/cache": "^0.2.56",
"@cerc-io/cache": "^0.2.57",
"@nomiclabs/hardhat-waffle": "^2.0.1",
"@types/bunyan": "^1.8.8",
"@types/express": "^4.17.14",
"@types/fs-extra": "^9.0.11",
"@types/js-yaml": "^4.0.4",

View File

@ -9,11 +9,11 @@ import debug from 'debug';
import { ConnectionOptions } from 'typeorm';
import { Config as CacheConfig } from '@cerc-io/cache';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { PubsubType } from '@cerc-io/peer';
const log = debug('vulcanize:config');
export type PubsubType = 'floodsub' | 'gossipsub';
export interface JobQueueConfig {
dbConnectionString: string;
maxCompletionLagInSecs: number;
@ -162,6 +162,14 @@ export interface NitroConfig {
payments: PaymentsConfig;
}
// Consensus config
export interface ConsensusConfig {
enabled: boolean;
publicKey: string;
privateKey: string;
watcherPartyFile: string;
}
// P2P config
export interface P2PConfig {
// Enable relay node
@ -172,7 +180,9 @@ export interface P2PConfig {
enablePeer: boolean;
peer: PeerConfig;
nitro: NitroConfig
nitro: NitroConfig;
consensus: ConsensusConfig;
}
export interface ServerConfig {

View File

@ -0,0 +1,238 @@
//
// Copyright 2023 Vulcanize, Inc.
//
import fs from 'fs';
import path from 'path';
import assert from 'assert';
import debug from 'debug';
import * as bunyan from 'bunyan';
import { Mokka } from 'mokka';
import * as MokkaStates from 'mokka/dist/consensus/constants/NodeStates';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { Pushable } from 'it-pushable';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { Peer } from '@cerc-io/peer';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { Stream as P2PStream } from '@libp2p/interface-connection';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { PeerId } from '@libp2p/interface-peer-id';
const LOG_NAMESPACE = 'laconic:consensus';
const CONSENSUS_LOG_LEVEL = 'debug';
const log = debug(LOG_NAMESPACE);
const CONSENSUS_PROTOCOL = '/consensus/1.0.0';
const NUM_WRITE_ATTEMPTS = 20;
const RETRY_SLEEP_DURATION = 15 * 1000; // 15 seconds
const DEFAULT_HEARTBEAT = 300;
const DEFAULT_ELECTION_TIMEOUT = 1000;
const DEFAULT_PROOF_EXPIRATION = 20000;
const DEFAULT_CRASH_MODEL = 'BFT';
const consensusStates: Record<number, string> = {
[MokkaStates.default.STOPPED]: 'STOPPED',
[MokkaStates.default.LEADER]: 'LEADER',
[MokkaStates.default.CANDIDATE]: 'CANDIDATE',
[MokkaStates.default.FOLLOWER]: 'FOLLOWER'
};
export interface PartyPeer {
peerId: string;
publicKey: string;
}
export interface ConsensusOptions {
peer: Peer;
publicKey: string;
privateKey: string;
party: PartyPeer[];
// For Mokka options (ISettingsInterface)
heartbeat?: number;
electionTimeout?: number;
proofExpiration?: number;
crashModel?: 'CFT' | 'BFT';
}
export class Consensus extends Mokka {
peer: Peer;
party: PartyPeer[];
private messageStreamMap: Map<string, Pushable<any>> = new Map();
constructor (options: ConsensusOptions) {
const heartbeat = options.heartbeat ?? DEFAULT_HEARTBEAT;
const electionTimeout = options.electionTimeout ?? DEFAULT_ELECTION_TIMEOUT;
const proofExpiration = options.proofExpiration ?? DEFAULT_PROOF_EXPIRATION;
const crashModel = options.crashModel ?? DEFAULT_CRASH_MODEL;
// address format: 'libp2p_peerid/node_publickey'
// See:
// https://github.com/ega-forever/mokka#new-mokka-options
// https://github.com/ega-forever/mokka/blob/master/src/consensus/models/NodeModel.ts#L46
const peerId = options.peer.peerId;
const address = `${peerId?.toString()}/${options.publicKey}`;
const logger = bunyan.createLogger({ name: LOG_NAMESPACE, level: CONSENSUS_LOG_LEVEL });
super({
address,
privateKey: options.privateKey,
heartbeat,
electionTimeout,
proofExpiration,
crashModel,
// TODO: Improve logging
logger
});
// Subscribe to state changes
this.on('state', () => {
log(`State changed to ${this.state} (${consensusStates[this.state]}) with term ${this.term}`);
});
this.peer = options.peer;
this.party = options.party;
// Add peer nodes in the party
// TODO: Skip initialization if party not provided?
for (const partyPeer of options.party) {
const address = `${partyPeer.peerId}/${partyPeer.publicKey}`;
this.nodeApi.join(address);
}
}
isLeader (): boolean {
return this.state === MokkaStates.default.LEADER;
}
initialize (): void {
assert(this.peer.node);
this.peer.node.handle(CONSENSUS_PROTOCOL, async ({ stream, connection }) => {
// Setup send and receive pipes
this.handleStream(connection.remotePeer, stream);
});
}
connect (): void {
this.initialize();
// TODO: Handle errors when peers don't join
super.connect();
}
async write (address: string, packet: Buffer): Promise<void> {
assert(this.peer.node);
// TODO: Use a different strategy for retries?
for (let i = 0; i < NUM_WRITE_ATTEMPTS; i += 1) {
try {
let messageStream = this.messageStreamMap.get(address);
if (!messageStream) {
const { peerIdFromString } = await import('@libp2p/peer-id');
const peerId = peerIdFromString(address);
// Dial to the peer over consensus protocol
const p2pStream = await this.peer.node.dialProtocol(peerId, CONSENSUS_PROTOCOL);
// Setup send and receive pipes
messageStream = await this.handleStream(peerId, p2pStream);
}
messageStream.push(packet);
return;
} catch (err) {
log(`Attempt ${i} - Could not open consensus stream to ${address}: ${err}`);
if (i === NUM_WRITE_ATTEMPTS) {
log('Write attempts exhausted');
}
// Retry after a set interval
await new Promise((resolve) => { setTimeout(resolve, RETRY_SLEEP_DURATION); });
}
}
}
async disconnect (): Promise<void> {
assert(this.peer.node);
await super.disconnect();
const { peerIdFromString } = await import('@libp2p/peer-id');
// Close all consensus protocol streams
for (const partyPeer of this.party) {
for (const conn of this.peer.node.getConnections(peerIdFromString(partyPeer.peerId))) {
conn.streams.forEach(stream => {
if (stream.stat.protocol === CONSENSUS_PROTOCOL) {
stream.close();
}
});
}
}
}
private async handleStream (peerId: PeerId, stream: P2PStream): Promise<Pushable<any>> {
const { pushable } = await import('it-pushable');
const { pipe } = await import('it-pipe');
const lp = await import('it-length-prefixed');
const messageStream = pushable({});
try {
// Send message to stream
pipe(
// Read from messageStream (the source)
messageStream,
// Encode with length prefix (so receiving side knows how much data is coming)
lp.encode(),
// Write to the stream (the sink)
stream.sink
);
} catch (err) {
// TODO: Implement retries / handle breakage
log(`Could not send consensus message to ${peerId.toString()}: ${err}`);
}
try {
// Handle message from stream
pipe(
// Read from the stream (the source)
stream.source,
// Decode length-prefixed data
lp.decode(),
// Sink function
async (source) => {
// For each chunk of data
for await (const msg of source) {
await this.emitPacket(Buffer.from(msg.subarray()));
}
}
);
} catch (err) {
log(`Error on consensus message from ${peerId.toString()}: ${err}`);
}
this.messageStreamMap.set(peerId.toString(), messageStream);
return messageStream;
}
}
export const readParty = (filePath: string): PartyPeer[] => {
if (!filePath || filePath === '') {
log('Party peers file path not provided');
return [];
}
const partyFilePath = path.resolve(filePath);
log(`Reading party peers from file ${partyFilePath}`);
const partyJson = fs.readFileSync(partyFilePath, 'utf-8');
return JSON.parse(partyJson);
};

View File

@ -318,7 +318,7 @@ export class Database {
}
async getEntities<Entity> (queryRunner: QueryRunner, entity: new () => Entity, findConditions?: FindManyOptions<Entity>): Promise<Entity[]> {
const repo = queryRunner.manager.getRepository(entity);
const repo = queryRunner.manager.getRepository<Entity>(entity);
const entities = await repo.find(findConditions);
return entities;

View File

@ -222,7 +222,7 @@ export class GraphDatabase {
selections: ReadonlyArray<SelectionNode> = []
): Promise<Entity | undefined> {
let { hash: blockHash, number: blockNumber } = block;
const repo = queryRunner.manager.getRepository(entityType);
const repo = queryRunner.manager.getRepository<Entity>(entityType);
const whereOptions: any = { id };
if (blockNumber) {
@ -419,7 +419,7 @@ export class GraphDatabase {
where: Where = {},
queryOptions: QueryOptions = {}
): Promise<Entity[]> {
const repo = queryRunner.manager.getRepository(entityType);
const repo = queryRunner.manager.getRepository<Entity>(entityType);
const { tableName } = repo.metadata;
let subQuery = repo.createQueryBuilder('subTable')
@ -483,7 +483,7 @@ export class GraphDatabase {
where: Where = {},
queryOptions: QueryOptions = {}
): Promise<Entity[]> {
const repo = queryRunner.manager.getRepository(entityType);
const repo = queryRunner.manager.getRepository<Entity>(entityType);
let subQuery = repo.createQueryBuilder('subTable')
.distinctOn(['subTable.id'])
@ -546,7 +546,7 @@ export class GraphDatabase {
block: BlockHeight,
where: Where = {}
): Promise<Entity[]> {
const repo = queryRunner.manager.getRepository(entityType);
const repo = queryRunner.manager.getRepository<Entity>(entityType);
const { tableName } = repo.metadata;
let selectQueryBuilder = repo.createQueryBuilder(tableName)
@ -582,7 +582,7 @@ export class GraphDatabase {
where: Where = {},
queryOptions: QueryOptions = {}
): Promise<Entity[]> {
const repo = queryRunner.manager.getRepository(entityType);
const repo = queryRunner.manager.getRepository<Entity>(entityType);
const { tableName } = repo.metadata;
let selectQueryBuilder = repo.createQueryBuilder(tableName)
@ -623,7 +623,7 @@ export class GraphDatabase {
return entities as Entity[];
}
async getEntitiesLatest<Entity> (
async getEntitiesLatest<Entity extends ObjectLiteral> (
queryRunner: QueryRunner,
entityType: new () => Entity,
latestEntity: new () => any,
@ -631,8 +631,8 @@ export class GraphDatabase {
queryOptions: QueryOptions = {},
selections: ReadonlyArray<SelectionNode> = []
): Promise<Entity[]> {
const entityRepo = queryRunner.manager.getRepository(entityType);
const latestEntityRepo = queryRunner.manager.getRepository(latestEntity);
const entityRepo = queryRunner.manager.getRepository<Entity>(entityType);
const latestEntityRepo = queryRunner.manager.getRepository<any>(latestEntity);
const latestEntityFields = latestEntityRepo.metadata.columns.map(column => column.propertyName);
const selectionNotInLatestEntity = selections.filter(selection => selection.kind === 'Field' && selection.name.value !== '__typename')
@ -685,8 +685,8 @@ export class GraphDatabase {
where: Where = {},
queryOptions: QueryOptions = {}
): Promise<Entity[]> {
const entityRepo = queryRunner.manager.getRepository(entityType);
const latestEntityRepo = queryRunner.manager.getRepository(latestEntity);
const entityRepo = queryRunner.manager.getRepository<Entity>(entityType);
const latestEntityRepo = queryRunner.manager.getRepository<any>(latestEntity);
let subQuery = entityRepo.createQueryBuilder('subTable')
.where('latest.id = subTable.id')

View File

@ -844,7 +844,7 @@ export const afterEntityInsertOrUpdate = async<Entity> (
}
// Get latest entity's fields to be updated
const latestEntityRepo = event.manager.getRepository(entityTarget);
const latestEntityRepo = event.manager.getRepository<any>(entityTarget);
const fieldsToUpdate = latestEntityRepo.metadata.columns.map(column => column.databaseName).filter(val => val !== 'id');
// Create a latest entity instance and upsert in the db

View File

@ -25,3 +25,4 @@ export * from './graph/state-utils';
export * from './graph/types';
export * from './payments';
export * from './eth';
export * from './consensus';

View File

@ -9,6 +9,7 @@ import JSONbig from 'json-bigint';
import { ethers } from 'ethers';
import _ from 'lodash';
// @ts-expect-error TODO: Resolve (Not able to find the type declarations)
import * as codec from '@ipld/dag-cbor';
import { GetStorageAt, getStorageValue, StorageLayout } from '@cerc-io/solidity-mapper';

View File

@ -2,6 +2,7 @@
// Copyright 2021 Vulcanize, Inc.
//
// @ts-expect-error TODO: Resolve (Not able to find the type declarations)
import { create, IPFSHTTPClient } from 'ipfs-http-client';
export class IPFSClient {

View File

@ -1,3 +1,7 @@
//
// Copyright 2023 Vulcanize, Inc.
//
import debug from 'debug';
import { LRUCache } from 'lru-cache';
import { FieldNode } from 'graphql';

View File

@ -1,8 +1,9 @@
import _ from 'lodash';
import debug from 'debug';
import { sha256 } from 'multiformats/hashes/sha2';
import { CID } from 'multiformats/cid';
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { CID as CIDType } from 'multiformats/cid';
// @ts-expect-error TODO: Resolve (Not able to find the type declarations)
import * as codec from '@ipld/dag-cbor';
import { BlockProgressInterface, GraphDatabaseInterface, StateInterface, StateKind } from './types';
@ -116,7 +117,7 @@ export const getResultState = (state: StateInterface): ResultState => {
export const createOrUpdateStateData = async (
data: StateData,
meta?: StateDataMeta
): Promise<{ cid: CID, data: StateData, bytes: codec.ByteView<StateData> }> => {
): Promise<{ cid: CIDType, data: StateData, bytes: codec.ByteView<StateData> }> => {
if (meta) {
data.meta = meta;
}
@ -124,9 +125,13 @@ export const createOrUpdateStateData = async (
// Encoding the data using dag-cbor codec.
const bytes = codec.encode(data);
const { sha256 } = await import('multiformats/hashes/sha2');
// Calculating sha256 (multi)hash of the encoded data.
const hash = await sha256.digest(bytes);
const { CID } = await import('multiformats/cid');
// Calculating the CID: v1, code: dag-cbor, hash.
const cid = CID.create(1, codec.code, hash);

View File

@ -4,7 +4,7 @@
/* Basic Options */
// "incremental": true, /* Enable incremental compilation */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"target": "es6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
"lib": [ "ES5", "ES6", "ES2020" ], /* Specify library files to be included in the compilation. */
// "allowJs": true, /* Allow javascript files to be compiled. */
@ -44,7 +44,7 @@
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
/* Module Resolution Options */
"moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
"moduleResolution": "Node16", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */

148
yarn.lock
View File

@ -278,79 +278,7 @@
binaryen "101.0.0-nightly.20210723"
long "^4.0.0"
"@cerc-io/libp2p@0.42.2-laconic-0.1.3":
version "0.42.2-laconic-0.1.3"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Flibp2p/-/0.42.2-laconic-0.1.3/libp2p-0.42.2-laconic-0.1.3.tgz#482c88e135141788fdd201fc27fb4f1bdc954f53"
integrity sha512-D8nBtFVB2xUN/QRCSHQVSoJBAk2nH4caqROpWUipRxiDgeoBVpTqYEM9fBzOln6/Yl3BtBxR/fgyaZPTSJQdrg==
dependencies:
"@achingbrain/nat-port-mapper" "^1.0.3"
"@libp2p/crypto" "^1.0.4"
"@libp2p/interface-address-manager" "^2.0.0"
"@libp2p/interface-connection" "^3.0.2"
"@libp2p/interface-connection-encrypter" "^3.0.1"
"@libp2p/interface-connection-manager" "^1.1.1"
"@libp2p/interface-content-routing" "^2.0.0"
"@libp2p/interface-dht" "^2.0.0"
"@libp2p/interface-keychain" "^2.0.4"
"@libp2p/interface-libp2p" "^1.0.0"
"@libp2p/interface-metrics" "^4.0.0"
"@libp2p/interface-peer-discovery" "^1.0.1"
"@libp2p/interface-peer-id" "^2.0.0"
"@libp2p/interface-peer-info" "^1.0.3"
"@libp2p/interface-peer-routing" "^1.0.1"
"@libp2p/interface-peer-store" "^1.2.2"
"@libp2p/interface-pubsub" "^3.0.0"
"@libp2p/interface-registrar" "^2.0.3"
"@libp2p/interface-stream-muxer" "^3.0.0"
"@libp2p/interface-transport" "^2.1.0"
"@libp2p/interfaces" "^3.0.3"
"@libp2p/keychain" "^1.0.0"
"@libp2p/logger" "^2.0.1"
"@libp2p/multistream-select" "^3.0.0"
"@libp2p/peer-collections" "^3.0.0"
"@libp2p/peer-id" "^2.0.0"
"@libp2p/peer-id-factory" "^2.0.0"
"@libp2p/peer-record" "^5.0.0"
"@libp2p/peer-store" "^6.0.0"
"@libp2p/tracked-map" "^3.0.0"
"@libp2p/utils" "^3.0.2"
"@libp2p/webrtc-peer" "^2.0.2"
"@multiformats/mafmt" "^11.0.2"
"@multiformats/multiaddr" "^11.0.0"
abortable-iterator "^4.0.2"
any-signal "^3.0.0"
datastore-core "^8.0.1"
err-code "^3.0.1"
interface-datastore "^7.0.0"
it-all "^2.0.0"
it-drain "^2.0.0"
it-filter "^2.0.0"
it-first "^2.0.0"
it-handshake "^4.1.2"
it-length-prefixed "^8.0.2"
it-map "^2.0.0"
it-merge "^2.0.0"
it-pair "^2.0.2"
it-pipe "^2.0.3"
it-pushable "^3.1.2"
it-sort "^2.0.1"
it-stream-types "^1.0.4"
merge-options "^3.0.4"
multiformats "^11.0.0"
p-fifo "^1.0.0"
p-settle "^5.0.0"
private-ip "^3.0.0"
protons-runtime "^4.0.1"
rate-limiter-flexible "^2.3.11"
retimer "^3.0.0"
set-delayed-interval "^1.0.0"
timeout-abort-controller "^3.0.0"
uint8arraylist "^2.3.2"
uint8arrays "^4.0.2"
wherearewe "^2.0.0"
xsalsa20 "^1.1.0"
"@cerc-io/libp2p@0.42.2-laconic-0.1.4":
"@cerc-io/libp2p@0.42.2-laconic-0.1.4", "@cerc-io/libp2p@^0.42.2-laconic-0.1.4":
version "0.42.2-laconic-0.1.4"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Flibp2p/-/0.42.2-laconic-0.1.4/libp2p-0.42.2-laconic-0.1.4.tgz#ac9347e70d6d3cee040ad02074cae3070b91a8fb"
integrity sha512-gTC62YvkK3P7cWlaH8gQ6lDbqusNiaYI1q7y/+vQ/1s35uStwRn7fvXHC0aY9s36L4S3p1S0sxDzGXM8rtg6+w==
@ -422,37 +350,48 @@
wherearewe "^2.0.0"
xsalsa20 "^1.1.0"
"@cerc-io/nitro-node@^0.1.8":
version "0.1.8"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-node/-/0.1.8/nitro-node-0.1.8.tgz#d2e90f4464645f3dab1ded8aa14e912c3f31ddcf"
integrity sha512-NAZ0miZkA+fBjlUg+7bjj8CWT4y0U7fgmkRFQ0JjyuP5o+bXvac+FtsH5ICeazGmG7Q5BQTQ6bUMiDfz2IuaDw==
"@cerc-io/nitro-node@^0.1.10":
version "0.1.10"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-node/-/0.1.10/nitro-node-0.1.10.tgz#6b37ca555e6ff4c111d68bf1cccc97c12c1905c8"
integrity sha512-kiP4ik5eCw/jM/+3ZOlXi7POMcabgJxup0AoOw2Gqz35lMfGL6QTzdAYU1lgDfq2zDOzKx0G3hcEnfIQ6lRMtA==
dependencies:
"@cerc-io/libp2p" "0.42.2-laconic-0.1.3"
"@cerc-io/nitro-util" "^0.1.8"
"@cerc-io/peer" "^0.2.52"
"@cerc-io/libp2p" "0.42.2-laconic-0.1.4"
"@cerc-io/nitro-protocol" "^2.0.0-alpha.4-ts-port-0.1.2"
"@cerc-io/nitro-util" "^0.1.10"
"@cerc-io/peer" "^0.2.56"
"@cerc-io/ts-channel" "1.0.3-ts-nitro-0.1.1"
"@jpwilliams/waitgroup" "^2.1.0"
"@libp2p/crypto" "^1.0.4"
"@libp2p/tcp" "^6.0.0"
"@multiformats/multiaddr" "^11.1.4"
"@statechannels/exit-format" "^0.2.0"
"@statechannels/nitro-protocol" "^2.0.0-alpha.4"
assert "^2.0.0"
async-mutex "^0.4.0"
debug "^4.3.4"
ethers "^5.7.2"
heap "^0.2.7"
it-pipe "^2.0.5"
level "^8.0.0"
lodash "^4.17.21"
promjs "^0.4.2"
uint8arrays "^4.0.3"
"@cerc-io/nitro-util@^0.1.8":
version "0.1.8"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-util/-/0.1.8/nitro-util-0.1.8.tgz#bdbfd9568a5878aeb1480c3145262ef7beab390c"
integrity sha512-v3wW0rHLkpigp4PiQDixxokTeegx9sPfBzEfZJoEl+/90N8mTweXVNgQFy5IlwbhPBfKUZ8L1kWICMiPjrnI2Q==
"@cerc-io/nitro-protocol@^2.0.0-alpha.4-ts-port-0.1.2":
version "2.0.0-alpha.4-ts-port-0.1.2"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-protocol/-/2.0.0-alpha.4-ts-port-0.1.2/nitro-protocol-2.0.0-alpha.4-ts-port-0.1.2.tgz#6d2f893f5aa08dd5550447f04967b908f3f6d469"
integrity sha512-Cyx2+S/6BlAzvl+LZxwLjK2Y0H01f/kvTYUktdsGHx1eTWXTzS6FQ0nTVwJkKEcO8V/Y50+dc2PwvFXvk8iG9w==
dependencies:
"@openzeppelin/contracts" "^4.7.3"
"@statechannels/exit-format" "^0.2.0"
"@typechain/ethers-v5" "^9.0.0"
"@cerc-io/nitro-util@^0.1.10":
version "0.1.10"
resolved "https://git.vdb.to/api/packages/cerc-io/npm/%40cerc-io%2Fnitro-util/-/0.1.10/nitro-util-0.1.10.tgz#a185fe98ec12c9b7c67b7e882045bf358b803fc2"
integrity sha512-X7jRB8fp6EuUSb0HxReeHxYGWgUa7YgoDwpyHkTbWezlpkj8DmMs5hu/94b+oHmKZtxWxOEnhGfCPIc3soPhsg==
dependencies:
"@cerc-io/nitro-protocol" "^2.0.0-alpha.4-ts-port-0.1.2"
"@cerc-io/ts-channel" "1.0.3-ts-nitro-0.1.1"
"@statechannels/nitro-protocol" "^2.0.0-alpha.4"
assert "^2.0.0"
debug "^4.3.4"
ethers "^5.7.2"
@ -3807,15 +3746,6 @@
ethers "^5.1.4"
lodash "^4.17.21"
"@statechannels/nitro-protocol@^2.0.0-alpha.4":
version "2.0.0-alpha.4"
resolved "https://registry.yarnpkg.com/@statechannels/nitro-protocol/-/nitro-protocol-2.0.0-alpha.4.tgz#ff252e5cd7e73740ad25a35b2af7953b499db63d"
integrity sha512-Nmiyu0h7VjzoYPmzUOVWb7KzzdRRTjnF1YYVqsiHiCEVkjZVBtSK/J8RDL4Ozzn2MwyOIiA90Wlrl4KB+tB43g==
dependencies:
"@openzeppelin/contracts" "^4.7.3"
"@statechannels/exit-format" "^0.2.0"
"@typechain/ethers-v5" "^9.0.0"
"@szmarczak/http-timer@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421"
@ -3902,6 +3832,13 @@
"@types/connect" "*"
"@types/node" "*"
"@types/bunyan@^1.8.8":
version "1.8.8"
resolved "https://registry.yarnpkg.com/@types/bunyan/-/bunyan-1.8.8.tgz#8d6d33f090f37c07e2a80af30ae728450a101008"
integrity sha512-Cblq+Yydg3u+sGiz2mjHjC5MPmdjY+No4qvHrF+BUhblsmSfMvsHLbOG62tPbonsqBj6sbWv1LHcsoe5Jw+/Ow==
dependencies:
"@types/node" "*"
"@types/chai-spies@^1.0.3":
version "1.0.3"
resolved "https://registry.npmjs.org/@types/chai-spies/-/chai-spies-1.0.3.tgz"
@ -4970,6 +4907,13 @@ async-limiter@~1.0.0:
resolved "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async-mutex@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/async-mutex/-/async-mutex-0.4.0.tgz#ae8048cd4d04ace94347507504b3cf15e631c25f"
integrity sha512-eJFZ1YhRR8UN8eBLoNzcDPcy/jqjsg6I1AP+KvWQX80BqOSW1oJPJXDylPUEeMr2ZQvHgnQ//Lp6f3RQ1zI7HA==
dependencies:
tslib "^2.4.0"
async-retry@^1.2.1:
version "1.3.3"
resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.3.3.tgz#0e7f36c04d8478e7a58bdbed80cedf977785f280"
@ -5954,9 +5898,9 @@ builtins@^1.0.3:
resolved "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz"
integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og=
bunyan@^1.8.12:
bunyan@^1.8.12, bunyan@^1.8.15:
version "1.8.15"
resolved "https://registry.npmjs.org/bunyan/-/bunyan-1.8.15.tgz"
resolved "https://registry.yarnpkg.com/bunyan/-/bunyan-1.8.15.tgz#8ce34ca908a17d0776576ca1b2f6cbd916e93b46"
integrity sha512-0tECWShh6wUysgucJcBAoYegf3JJoZWibxdqhTm7OHPeT42qdjkZ29QCMcKwbgU1kiH+auSIasNRXMLWXafXig==
optionalDependencies:
dtrace-provider "~0.8"
@ -9569,7 +9513,7 @@ heap@0.2.6:
resolved "https://registry.npmjs.org/heap/-/heap-0.2.6.tgz"
integrity sha1-CH4fELBGky/IWU3Z5tN4r8nR5aw=
"heap@>= 0.2.0":
"heap@>= 0.2.0", heap@^0.2.7:
version "0.2.7"
resolved "https://registry.yarnpkg.com/heap/-/heap-0.2.7.tgz#1e6adf711d3f27ce35a81fe3b7bd576c2260a8fc"
integrity sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==
@ -12145,6 +12089,14 @@ module-error@^1.0.1, module-error@^1.0.2:
resolved "https://registry.yarnpkg.com/module-error/-/module-error-1.0.2.tgz#8d1a48897ca883f47a45816d4fb3e3c6ba404d86"
integrity sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==
mokka@^1.4.2:
version "1.4.2"
resolved "https://registry.yarnpkg.com/mokka/-/mokka-1.4.2.tgz#b0d7117e216672ff2deda6ef2212bcfa0edb8114"
integrity sha512-Vgki/Fr9fqUMZzChGC1yH64AQg+qkMdZZe3xKrTwYBVGEuq8j2GdnDg8voYXwWl+MLfNzWBnNiejq2xZ8PmojA==
dependencies:
bn.js "^5.2.0"
elliptic "^6.5.4"
moment@^2.19.3:
version "2.29.1"
resolved "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz"