From bd73dae1b146e891ceaefa5172a61b188f7fecc5 Mon Sep 17 00:00:00 2001 From: prathamesh0 <42446521+prathamesh0@users.noreply.github.com> Date: Thu, 14 Sep 2023 13:47:31 +0530 Subject: [PATCH] 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 --- lerna.json | 2 +- packages/cache/package.json | 2 +- packages/cli/package.json | 14 +- packages/cli/src/peer.ts | 4 +- packages/cli/src/server.ts | 180 +++++++++---- packages/codegen/package.json | 4 +- .../src/templates/package-template.handlebars | 10 +- packages/graph-node/package.json | 10 +- packages/ipld-eth-client/package.json | 4 +- packages/peer/package.json | 5 +- packages/peer/src/cli/relay.ts | 3 +- packages/peer/src/index.ts | 2 +- packages/peer/src/peer.ts | 3 +- packages/peer/src/relay.ts | 3 +- packages/peer/src/utils/index.ts | 3 +- packages/rpc-eth-client/package.json | 8 +- packages/solidity-mapper/package.json | 4 +- packages/test/package.json | 2 +- packages/tracing-client/package.json | 2 +- packages/util/package.json | 18 +- packages/util/src/config.ts | 16 +- packages/util/src/consensus.ts | 238 ++++++++++++++++++ packages/util/src/database.ts | 2 +- packages/util/src/graph/database.ts | 20 +- packages/util/src/graph/utils.ts | 2 +- packages/util/src/index.ts | 1 + packages/util/src/indexer.ts | 1 + packages/util/src/ipfs.ts | 1 + packages/util/src/payments.ts | 4 + packages/util/src/state-helper.ts | 11 +- packages/util/tsconfig.json | 4 +- yarn.lock | 148 ++++------- 32 files changed, 513 insertions(+), 218 deletions(-) create mode 100644 packages/util/src/consensus.ts diff --git a/lerna.json b/lerna.json index f18c3f93..705b78bf 100644 --- a/lerna.json +++ b/lerna.json @@ -2,7 +2,7 @@ "packages": [ "packages/*" ], - "version": "0.2.56", + "version": "0.2.57", "npmClient": "yarn", "useWorkspaces": true, "command": { diff --git a/packages/cache/package.json b/packages/cache/package.json index 2df92025..66d1e2a2 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -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": { diff --git a/packages/cli/package.json b/packages/cli/package.json index c986d60b..1202acac 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -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", diff --git a/packages/cli/src/peer.ts b/packages/cli/src/peer.ts index 8ed1a6e7..124a7201 100644 --- a/packages/cli/src/peer.ts +++ b/packages/cli/src/peer.ts @@ -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'; diff --git a/packages/cli/src/server.ts b/packages/cli/src/server.ts index af0b179d..7c1a9d0e 100644 --- a/packages/cli/src/server.ts +++ b/packages/cli/src/server.ts @@ -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 (): Promise { 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, - 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 { 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 { + 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 { + // 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, + 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 { diff --git a/packages/codegen/package.json b/packages/codegen/package.json index cd8fda04..aca73c55 100644 --- a/packages/codegen/package.json +++ b/packages/codegen/package.json @@ -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", diff --git a/packages/codegen/src/templates/package-template.handlebars b/packages/codegen/src/templates/package-template.handlebars index 90dd9d0b..cc178aa5 100644 --- a/packages/codegen/src/templates/package-template.handlebars +++ b/packages/codegen/src/templates/package-template.handlebars @@ -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", diff --git a/packages/graph-node/package.json b/packages/graph-node/package.json index 5aa8ea02..abb6e885 100644 --- a/packages/graph-node/package.json +++ b/packages/graph-node/package.json @@ -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", diff --git a/packages/ipld-eth-client/package.json b/packages/ipld-eth-client/package.json index ebe2e155..9197fb62 100644 --- a/packages/ipld-eth-client/package.json +++ b/packages/ipld-eth-client/package.json @@ -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", diff --git a/packages/peer/package.json b/packages/peer/package.json index 346f8566..6e935517 100644 --- a/packages/peer/package.json +++ b/packages/peer/package.json @@ -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", diff --git a/packages/peer/src/cli/relay.ts b/packages/peer/src/cli/relay.ts index 3e970476..c83e8737 100644 --- a/packages/peer/src/cli/relay.ts +++ b/packages/peer/src/cli/relay.ts @@ -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; diff --git a/packages/peer/src/index.ts b/packages/peer/src/index.ts index 2082e9e2..88264ac8 100644 --- a/packages/peer/src/index.ts +++ b/packages/peer/src/index.ts @@ -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, diff --git a/packages/peer/src/peer.ts b/packages/peer/src/peer.ts index 0f3b6198..7a413106 100644 --- a/packages/peer/src/peer.ts +++ b/packages/peer/src/peer.ts @@ -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'); diff --git a/packages/peer/src/relay.ts b/packages/peer/src/relay.ts index b708023a..e76580a7 100644 --- a/packages/peer/src/relay.ts +++ b/packages/peer/src/relay.ts @@ -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'; diff --git a/packages/peer/src/utils/index.ts b/packages/peer/src/utils/index.ts index f1c3ffbd..3eef0def 100644 --- a/packages/peer/src/utils/index.ts +++ b/packages/peer/src/utils/index.ts @@ -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 diff --git a/packages/rpc-eth-client/package.json b/packages/rpc-eth-client/package.json index 9655ad5b..1e21a4aa 100644 --- a/packages/rpc-eth-client/package.json +++ b/packages/rpc-eth-client/package.json @@ -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", diff --git a/packages/solidity-mapper/package.json b/packages/solidity-mapper/package.json index 2a9b75f7..6df6f095 100644 --- a/packages/solidity-mapper/package.json +++ b/packages/solidity-mapper/package.json @@ -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", diff --git a/packages/test/package.json b/packages/test/package.json index 73d0d664..ee2983eb 100644 --- a/packages/test/package.json +++ b/packages/test/package.json @@ -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, diff --git a/packages/tracing-client/package.json b/packages/tracing-client/package.json index 34b7765f..587f6e09 100644 --- a/packages/tracing-client/package.json +++ b/packages/tracing-client/package.json @@ -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": { diff --git a/packages/util/package.json b/packages/util/package.json index 6750d5e1..da6d7105 100644 --- a/packages/util/package.json +++ b/packages/util/package.json @@ -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", diff --git a/packages/util/src/config.ts b/packages/util/src/config.ts index 6782dd07..e73c9d1b 100644 --- a/packages/util/src/config.ts +++ b/packages/util/src/config.ts @@ -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 { diff --git a/packages/util/src/consensus.ts b/packages/util/src/consensus.ts new file mode 100644 index 00000000..0331c93f --- /dev/null +++ b/packages/util/src/consensus.ts @@ -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 = { + [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> = 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 { + 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 { + 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> { + 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); +}; diff --git a/packages/util/src/database.ts b/packages/util/src/database.ts index 135882d1..3f85d04f 100644 --- a/packages/util/src/database.ts +++ b/packages/util/src/database.ts @@ -318,7 +318,7 @@ export class Database { } async getEntities (queryRunner: QueryRunner, entity: new () => Entity, findConditions?: FindManyOptions): Promise { - const repo = queryRunner.manager.getRepository(entity); + const repo = queryRunner.manager.getRepository(entity); const entities = await repo.find(findConditions); return entities; diff --git a/packages/util/src/graph/database.ts b/packages/util/src/graph/database.ts index ce9b86d4..23dbc097 100644 --- a/packages/util/src/graph/database.ts +++ b/packages/util/src/graph/database.ts @@ -222,7 +222,7 @@ export class GraphDatabase { selections: ReadonlyArray = [] ): Promise { let { hash: blockHash, number: blockNumber } = block; - const repo = queryRunner.manager.getRepository(entityType); + const repo = queryRunner.manager.getRepository(entityType); const whereOptions: any = { id }; if (blockNumber) { @@ -419,7 +419,7 @@ export class GraphDatabase { where: Where = {}, queryOptions: QueryOptions = {} ): Promise { - const repo = queryRunner.manager.getRepository(entityType); + const repo = queryRunner.manager.getRepository(entityType); const { tableName } = repo.metadata; let subQuery = repo.createQueryBuilder('subTable') @@ -483,7 +483,7 @@ export class GraphDatabase { where: Where = {}, queryOptions: QueryOptions = {} ): Promise { - const repo = queryRunner.manager.getRepository(entityType); + const repo = queryRunner.manager.getRepository(entityType); let subQuery = repo.createQueryBuilder('subTable') .distinctOn(['subTable.id']) @@ -546,7 +546,7 @@ export class GraphDatabase { block: BlockHeight, where: Where = {} ): Promise { - const repo = queryRunner.manager.getRepository(entityType); + const repo = queryRunner.manager.getRepository(entityType); const { tableName } = repo.metadata; let selectQueryBuilder = repo.createQueryBuilder(tableName) @@ -582,7 +582,7 @@ export class GraphDatabase { where: Where = {}, queryOptions: QueryOptions = {} ): Promise { - const repo = queryRunner.manager.getRepository(entityType); + const repo = queryRunner.manager.getRepository(entityType); const { tableName } = repo.metadata; let selectQueryBuilder = repo.createQueryBuilder(tableName) @@ -623,7 +623,7 @@ export class GraphDatabase { return entities as Entity[]; } - async getEntitiesLatest ( + async getEntitiesLatest ( queryRunner: QueryRunner, entityType: new () => Entity, latestEntity: new () => any, @@ -631,8 +631,8 @@ export class GraphDatabase { queryOptions: QueryOptions = {}, selections: ReadonlyArray = [] ): Promise { - const entityRepo = queryRunner.manager.getRepository(entityType); - const latestEntityRepo = queryRunner.manager.getRepository(latestEntity); + const entityRepo = queryRunner.manager.getRepository(entityType); + const latestEntityRepo = queryRunner.manager.getRepository(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 { - const entityRepo = queryRunner.manager.getRepository(entityType); - const latestEntityRepo = queryRunner.manager.getRepository(latestEntity); + const entityRepo = queryRunner.manager.getRepository(entityType); + const latestEntityRepo = queryRunner.manager.getRepository(latestEntity); let subQuery = entityRepo.createQueryBuilder('subTable') .where('latest.id = subTable.id') diff --git a/packages/util/src/graph/utils.ts b/packages/util/src/graph/utils.ts index 1035834c..d1c3d005 100644 --- a/packages/util/src/graph/utils.ts +++ b/packages/util/src/graph/utils.ts @@ -844,7 +844,7 @@ export const afterEntityInsertOrUpdate = async ( } // Get latest entity's fields to be updated - const latestEntityRepo = event.manager.getRepository(entityTarget); + const latestEntityRepo = event.manager.getRepository(entityTarget); const fieldsToUpdate = latestEntityRepo.metadata.columns.map(column => column.databaseName).filter(val => val !== 'id'); // Create a latest entity instance and upsert in the db diff --git a/packages/util/src/index.ts b/packages/util/src/index.ts index a416ab63..5a4399c5 100644 --- a/packages/util/src/index.ts +++ b/packages/util/src/index.ts @@ -25,3 +25,4 @@ export * from './graph/state-utils'; export * from './graph/types'; export * from './payments'; export * from './eth'; +export * from './consensus'; diff --git a/packages/util/src/indexer.ts b/packages/util/src/indexer.ts index 2dce5648..c9ad2875 100644 --- a/packages/util/src/indexer.ts +++ b/packages/util/src/indexer.ts @@ -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'; diff --git a/packages/util/src/ipfs.ts b/packages/util/src/ipfs.ts index 03284eb6..0666e45f 100644 --- a/packages/util/src/ipfs.ts +++ b/packages/util/src/ipfs.ts @@ -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 { diff --git a/packages/util/src/payments.ts b/packages/util/src/payments.ts index 0c40a96b..dfbea280 100644 --- a/packages/util/src/payments.ts +++ b/packages/util/src/payments.ts @@ -1,3 +1,7 @@ +// +// Copyright 2023 Vulcanize, Inc. +// + import debug from 'debug'; import { LRUCache } from 'lru-cache'; import { FieldNode } from 'graphql'; diff --git a/packages/util/src/state-helper.ts b/packages/util/src/state-helper.ts index 6d98ebd2..cfaf9877 100644 --- a/packages/util/src/state-helper.ts +++ b/packages/util/src/state-helper.ts @@ -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 }> => { +): Promise<{ cid: CIDType, data: StateData, bytes: codec.ByteView }> => { 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); diff --git a/packages/util/tsconfig.json b/packages/util/tsconfig.json index 0e983efb..6ecebdf5 100644 --- a/packages/util/tsconfig.json +++ b/packages/util/tsconfig.json @@ -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. */ diff --git a/yarn.lock b/yarn.lock index 9f8cc290..0f323866 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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"