diff --git a/.circleci/config.yml b/.circleci/config.yml index 587978f2a4..9d3cb169a3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -47,16 +47,14 @@ jobs: - ./packages/core-error-tracker-sentry/node_modules - ./packages/core-event-emitter/node_modules - ./packages/core-forger/node_modules - - ./packages/core-graphql/node_modules - ./packages/core-http-utils/node_modules - ./packages/core-interfaces/node_modules - ./packages/core-jest-matchers/node_modules - ./packages/core-json-rpc/node_modules - ./packages/core-logger/node_modules - - ./packages/core-logger-winston/node_modules + - ./packages/core-logger-pino/node_modules - ./packages/core-p2p/node_modules - ./packages/core-snapshots/node_modules - - ./packages/core-snapshots-cli/node_modules - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules @@ -155,16 +153,14 @@ jobs: - ./packages/core-error-tracker-sentry/node_modules - ./packages/core-event-emitter/node_modules - ./packages/core-forger/node_modules - - ./packages/core-graphql/node_modules - ./packages/core-http-utils/node_modules - ./packages/core-interfaces/node_modules - ./packages/core-jest-matchers/node_modules - ./packages/core-json-rpc/node_modules - ./packages/core-logger/node_modules - - ./packages/core-logger-winston/node_modules + - ./packages/core-logger-pino/node_modules - ./packages/core-p2p/node_modules - ./packages/core-snapshots/node_modules - - ./packages/core-snapshots-cli/node_modules - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules @@ -263,16 +259,14 @@ jobs: - ./packages/core-error-tracker-sentry/node_modules - ./packages/core-event-emitter/node_modules - ./packages/core-forger/node_modules - - ./packages/core-graphql/node_modules - ./packages/core-http-utils/node_modules - ./packages/core-interfaces/node_modules - ./packages/core-jest-matchers/node_modules - ./packages/core-json-rpc/node_modules - ./packages/core-logger/node_modules - - ./packages/core-logger-winston/node_modules + - ./packages/core-logger-pino/node_modules - ./packages/core-p2p/node_modules - ./packages/core-snapshots/node_modules - - ./packages/core-snapshots-cli/node_modules - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules @@ -294,11 +288,6 @@ jobs: command: >- cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core-forger && yarn test:coverage - - run: - name: core-graphql - command: >- - cd ~/core/.circleci && ./rebuild-db.sh && cd - ~/core/packages/core-graphql && yarn test:coverage - run: name: core-http-utils command: >- @@ -320,15 +309,20 @@ jobs: cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core-logger && yarn test:coverage - run: - name: core-logger-winston + name: core-logger-pino command: >- cd ~/core/.circleci && ./rebuild-db.sh && cd - ~/core/packages/core-logger-winston && yarn test:coverage + ~/core/packages/core-logger-pino && yarn test:coverage - run: name: core-p2p command: >- cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core-p2p && yarn test:coverage + - run: + name: core-snapshots + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-snapshots && yarn test:coverage - run: name: Last 1000 lines of test output when: on_fail @@ -386,16 +380,14 @@ jobs: - ./packages/core-error-tracker-sentry/node_modules - ./packages/core-event-emitter/node_modules - ./packages/core-forger/node_modules - - ./packages/core-graphql/node_modules - ./packages/core-http-utils/node_modules - ./packages/core-interfaces/node_modules - ./packages/core-jest-matchers/node_modules - ./packages/core-json-rpc/node_modules - ./packages/core-logger/node_modules - - ./packages/core-logger-winston/node_modules + - ./packages/core-logger-pino/node_modules - ./packages/core-p2p/node_modules - ./packages/core-snapshots/node_modules - - ./packages/core-snapshots-cli/node_modules - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules @@ -407,11 +399,6 @@ jobs: - run: name: Create .core/database directory command: mkdir -p $HOME/.core/database - - run: - name: core-snapshots - command: >- - cd ~/core/.circleci && ./rebuild-db.sh && cd - ~/core/packages/core-snapshots && yarn test:coverage - run: name: core-test-utils command: >- @@ -504,16 +491,14 @@ jobs: - ./packages/core-error-tracker-sentry/node_modules - ./packages/core-event-emitter/node_modules - ./packages/core-forger/node_modules - - ./packages/core-graphql/node_modules - ./packages/core-http-utils/node_modules - ./packages/core-interfaces/node_modules - ./packages/core-jest-matchers/node_modules - ./packages/core-json-rpc/node_modules - ./packages/core-logger/node_modules - - ./packages/core-logger-winston/node_modules + - ./packages/core-logger-pino/node_modules - ./packages/core-p2p/node_modules - ./packages/core-snapshots/node_modules - - ./packages/core-snapshots-cli/node_modules - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules @@ -535,11 +520,6 @@ jobs: command: >- cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core-forger && yarn test:coverage - - run: - name: core-graphql - command: >- - cd ~/core/.circleci && ./rebuild-db.sh && cd - ~/core/packages/core-graphql && yarn test:coverage - run: name: core-http-utils command: >- @@ -561,15 +541,20 @@ jobs: cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core-logger && yarn test:coverage - run: - name: core-logger-winston + name: core-logger-pino command: >- cd ~/core/.circleci && ./rebuild-db.sh && cd - ~/core/packages/core-logger-winston && yarn test:coverage + ~/core/packages/core-logger-pino && yarn test:coverage - run: name: core-p2p command: >- cd ~/core/.circleci && ./rebuild-db.sh && cd ~/core/packages/core-p2p && yarn test:coverage + - run: + name: core-snapshots + command: >- + cd ~/core/.circleci && ./rebuild-db.sh && cd + ~/core/packages/core-snapshots && yarn test:coverage - run: name: Last 1000 lines of test output when: on_fail @@ -627,16 +612,14 @@ jobs: - ./packages/core-error-tracker-sentry/node_modules - ./packages/core-event-emitter/node_modules - ./packages/core-forger/node_modules - - ./packages/core-graphql/node_modules - ./packages/core-http-utils/node_modules - ./packages/core-interfaces/node_modules - ./packages/core-jest-matchers/node_modules - ./packages/core-json-rpc/node_modules - ./packages/core-logger/node_modules - - ./packages/core-logger-winston/node_modules + - ./packages/core-logger-pino/node_modules - ./packages/core-p2p/node_modules - ./packages/core-snapshots/node_modules - - ./packages/core-snapshots-cli/node_modules - ./packages/core-test-utils/node_modules - ./packages/core-tester-cli/node_modules - ./packages/core-transaction-pool/node_modules @@ -648,11 +631,6 @@ jobs: - run: name: Create .core/database directory command: mkdir -p $HOME/.core/database - - run: - name: core-snapshots - command: >- - cd ~/core/.circleci && ./rebuild-db.sh && cd - ~/core/packages/core-snapshots && yarn test:coverage - run: name: core-test-utils command: >- diff --git a/benchmark/block/create.js b/benchmark/block/create.js new file mode 100644 index 0000000000..43714399ea --- /dev/null +++ b/benchmark/block/create.js @@ -0,0 +1,25 @@ +const { + models +} = require('@arkecosystem/crypto') + +const dataEmpty = require('../helpers').getJSONFixture('block/deserialized/no-transactions'); +const dataFull = require('../helpers').getJSONFixture('block/deserialized/transactions'); +const serializedEmpty = require('../helpers').getFixture('block/serialized/no-transactions.txt'); +const serializedFull = require('../helpers').getFixture('block/serialized/transactions.txt'); + +exports['fromData (0)'] = () => { + return new models.Block(dataEmpty); +}; + +exports['fromData (150)'] = () => { + return new models.Block(dataFull); +}; + +exports['fromHex (0)'] = () => { + return new models.Block(serializedEmpty); +}; + +exports['fromHex (150)'] = () => { + return new models.Block(serializedFull); +}; + diff --git a/benchmark/crypto/hash-algorithms.js b/benchmark/crypto/hash-algorithms.js new file mode 100644 index 0000000000..a137369d85 --- /dev/null +++ b/benchmark/crypto/hash-algorithms.js @@ -0,0 +1,49 @@ +const { + HashAlgorithms, + Transaction +} = require('@arkecosystem/crypto') +const createHash = require("create-hash"); +const nodeSha256 = (bytes) => createHash("sha256").update(bytes).digest() + +const data = require('../helpers').getJSONFixture('transaction/deserialized/0'); +const transactionBytes = Transaction.toBytes(data); + +exports['bcrypto.sha256'] = () => { + HashAlgorithms.sha256(transactionBytes); +}; + +exports['node.sha256'] = () => { + nodeSha256(transactionBytes); +}; + +exports['bcrypto.sha1'] = () => { + HashAlgorithms.sha1(transactionBytes); +}; + +exports['node.sha1'] = () => { + createHash("sha1").update(transactionBytes).digest(); +}; + +exports['bcrypto.ripemd160'] = () => { + HashAlgorithms.ripemd160(transactionBytes); +}; + +exports['node.ripemd160'] = () => { + createHash("ripemd160").update(transactionBytes).digest(); +}; + +exports['bcrypto.hash160'] = () => { + HashAlgorithms.hash160(transactionBytes); +}; + +exports['node.hash160'] = () => { + createHash("ripemd160").update(nodeSha256(transactionBytes)).digest(); +}; + +exports['bcrypto.hash256'] = () => { + HashAlgorithms.hash256(transactionBytes); +}; + +exports['node.hash256'] = () => { + nodeSha256(nodeSha256(transactionBytes)); +}; \ No newline at end of file diff --git a/benchmark/index.js b/benchmark/index.js index 9032af69ab..ee8d959502 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -4,12 +4,16 @@ const { configManager } = require("@arkecosystem/crypto"); configManager.setFromPreset("mainnet"); benchmarker('core', [ + { name: 'new Block()', scenarios: require('./block/create') }, { name: 'Block.serialize (0 transactions)', scenarios: require('./block/serialize') }, { name: 'Block.serialize (150 transactions)', scenarios: require('./block/serializeFull') }, - { name: 'Block.deserialize (0 transactions)', scenarios: require('./block/deserialize/0') }, { name: 'Block.deserialize (150 transactions)', scenarios: require('./block/deserialize/150') }, + { name: 'new Transaction (Type 0)', scenarios: require('./transaction/create/0') }, { name: 'Transaction.serialize (Type 0)', scenarios: require('./transaction/serialize/0') }, { name: 'Transaction.deserialize (Type 0)', scenarios: require('./transaction/deserialize/0') }, + + { name: 'HashAlgorithms', scenarios: require('./crypto/hash-algorithms') }, + ], { hideSummary: true }); diff --git a/benchmark/transaction/create/0.js b/benchmark/transaction/create/0.js new file mode 100644 index 0000000000..3d179daf8c --- /dev/null +++ b/benchmark/transaction/create/0.js @@ -0,0 +1,19 @@ +const { + Transaction +} = require('@arkecosystem/crypto') + +const data = require('../../helpers').getJSONFixture('transaction/deserialized/0'); +const serializedHex = require('../../helpers').getFixture('transaction/serialized/0.txt'); +const serializedBytes = Buffer.from(serializedHex, "hex"); + +exports['fromData'] = () => { + return Transaction.fromData(data); +}; + +exports['fromHex'] = () => { + return Transaction.fromHex(serializedHex); +}; + +exports['fromBytes'] = () => { + return Transaction.fromBytes(serializedBytes); +}; diff --git a/benchmark/transaction/deserialize/methods.js b/benchmark/transaction/deserialize/methods.js index 8730ccfd88..19f0920c22 100644 --- a/benchmark/transaction/deserialize/methods.js +++ b/benchmark/transaction/deserialize/methods.js @@ -1,7 +1,7 @@ const { - Transaction + TransactionDeserializer } = require('@arkecosystem/crypto') exports.deserialize = data => { - return Transaction.fromHex(data) + return TransactionDeserializer.deserialize(data) } diff --git a/benchmark/transaction/serialize/0.js b/benchmark/transaction/serialize/0.js index 4a3455549e..25784a41e7 100644 --- a/benchmark/transaction/serialize/0.js +++ b/benchmark/transaction/serialize/0.js @@ -1,9 +1,9 @@ const { - Transaction + TransactionSerializer } = require('@arkecosystem/crypto') const data = require('../../helpers').getJSONFixture('transaction/deserialized/0'); exports['core'] = () => { - return Transaction.fromData(data); + return TransactionSerializer.getBytes(data); }; diff --git a/packages/core-graphql/.gitattributes b/deprecated/core-graphql/.gitattributes similarity index 100% rename from packages/core-graphql/.gitattributes rename to deprecated/core-graphql/.gitattributes diff --git a/packages/core-graphql/README.md b/deprecated/core-graphql/README.md similarity index 100% rename from packages/core-graphql/README.md rename to deprecated/core-graphql/README.md diff --git a/packages/core-graphql/__tests__/__support__/setup.ts b/deprecated/core-graphql/__tests__/__support__/setup.ts similarity index 94% rename from packages/core-graphql/__tests__/__support__/setup.ts rename to deprecated/core-graphql/__tests__/__support__/setup.ts index 326ac4d09f..e2e1c5033a 100644 --- a/packages/core-graphql/__tests__/__support__/setup.ts +++ b/deprecated/core-graphql/__tests__/__support__/setup.ts @@ -13,7 +13,7 @@ export const setUp = async () => { process.env.CORE_GRAPHQL_ENABLED = "true"; await setUpContainer({ - exclude: ["@arkecosystem/core-api", "@arkecosystem/core-forger", "@arkecosystem/core-graphql"], + exclude: ["@arkecosystem/core-api", "@arkecosystem/core-forger"], }); const { plugin } = require("../../src"); diff --git a/packages/core-graphql/__tests__/__support__/utils.ts b/deprecated/core-graphql/__tests__/__support__/utils.ts similarity index 100% rename from packages/core-graphql/__tests__/__support__/utils.ts rename to deprecated/core-graphql/__tests__/__support__/utils.ts diff --git a/packages/core-graphql/__tests__/api/address.test.ts b/deprecated/core-graphql/__tests__/api/address.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/address.test.ts rename to deprecated/core-graphql/__tests__/api/address.test.ts diff --git a/packages/core-graphql/__tests__/api/block.test.ts b/deprecated/core-graphql/__tests__/api/block.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/block.test.ts rename to deprecated/core-graphql/__tests__/api/block.test.ts diff --git a/packages/core-graphql/__tests__/api/blocks.test.ts b/deprecated/core-graphql/__tests__/api/blocks.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/blocks.test.ts rename to deprecated/core-graphql/__tests__/api/blocks.test.ts diff --git a/packages/core-graphql/__tests__/api/transaction.test.ts b/deprecated/core-graphql/__tests__/api/transaction.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/transaction.test.ts rename to deprecated/core-graphql/__tests__/api/transaction.test.ts diff --git a/packages/core-graphql/__tests__/api/transactions.test.ts b/deprecated/core-graphql/__tests__/api/transactions.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/transactions.test.ts rename to deprecated/core-graphql/__tests__/api/transactions.test.ts diff --git a/packages/core-graphql/__tests__/api/wallet.test.ts b/deprecated/core-graphql/__tests__/api/wallet.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/wallet.test.ts rename to deprecated/core-graphql/__tests__/api/wallet.test.ts diff --git a/packages/core-graphql/__tests__/api/wallets.test.ts b/deprecated/core-graphql/__tests__/api/wallets.test.ts similarity index 100% rename from packages/core-graphql/__tests__/api/wallets.test.ts rename to deprecated/core-graphql/__tests__/api/wallets.test.ts diff --git a/packages/core-graphql/package.json b/deprecated/core-graphql/package.json similarity index 88% rename from packages/core-graphql/package.json rename to deprecated/core-graphql/package.json index 59fba1d0cf..6c704d42ae 100644 --- a/packages/core-graphql/package.json +++ b/deprecated/core-graphql/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-graphql", "description": "GraphQL Integration for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Lúcio Rubens " ], @@ -31,10 +31,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "apollo-server-hapi": "^2.4.0", "dayjs-ext": "^2.2.0", "graphql-tools-types": "^1.2.1" diff --git a/packages/core-graphql/src/apollo-server.ts b/deprecated/core-graphql/src/apollo-server.ts similarity index 100% rename from packages/core-graphql/src/apollo-server.ts rename to deprecated/core-graphql/src/apollo-server.ts diff --git a/packages/core-graphql/src/defaults.ts b/deprecated/core-graphql/src/defaults.ts similarity index 100% rename from packages/core-graphql/src/defaults.ts rename to deprecated/core-graphql/src/defaults.ts diff --git a/packages/core-graphql/src/defs/index.ts b/deprecated/core-graphql/src/defs/index.ts similarity index 100% rename from packages/core-graphql/src/defs/index.ts rename to deprecated/core-graphql/src/defs/index.ts diff --git a/packages/core-graphql/src/defs/inputs.ts b/deprecated/core-graphql/src/defs/inputs.ts similarity index 100% rename from packages/core-graphql/src/defs/inputs.ts rename to deprecated/core-graphql/src/defs/inputs.ts diff --git a/packages/core-graphql/src/defs/root.ts b/deprecated/core-graphql/src/defs/root.ts similarity index 100% rename from packages/core-graphql/src/defs/root.ts rename to deprecated/core-graphql/src/defs/root.ts diff --git a/packages/core-graphql/src/defs/types.ts b/deprecated/core-graphql/src/defs/types.ts similarity index 100% rename from packages/core-graphql/src/defs/types.ts rename to deprecated/core-graphql/src/defs/types.ts diff --git a/packages/core-graphql/src/helpers/format-orderBy.ts b/deprecated/core-graphql/src/helpers/format-orderBy.ts similarity index 100% rename from packages/core-graphql/src/helpers/format-orderBy.ts rename to deprecated/core-graphql/src/helpers/format-orderBy.ts diff --git a/packages/core-graphql/src/helpers/index.ts b/deprecated/core-graphql/src/helpers/index.ts similarity index 100% rename from packages/core-graphql/src/helpers/index.ts rename to deprecated/core-graphql/src/helpers/index.ts diff --git a/packages/core-graphql/src/helpers/unserialize-transactions.ts b/deprecated/core-graphql/src/helpers/unserialize-transactions.ts similarity index 100% rename from packages/core-graphql/src/helpers/unserialize-transactions.ts rename to deprecated/core-graphql/src/helpers/unserialize-transactions.ts diff --git a/packages/core-graphql/src/index.ts b/deprecated/core-graphql/src/index.ts similarity index 94% rename from packages/core-graphql/src/index.ts rename to deprecated/core-graphql/src/index.ts index 867988e21d..8989a6cc6d 100644 --- a/packages/core-graphql/src/index.ts +++ b/deprecated/core-graphql/src/index.ts @@ -12,7 +12,7 @@ export const plugin: Container.PluginDescriptor = { alias: "graphql", async register(container: Container.IContainer, options) { if (!options.enabled) { - container.resolvePlugin("logger").info("GraphQL API is disabled :grey_exclamation:"); + container.resolvePlugin("logger").info("GraphQL API is disabled"); return; } diff --git a/packages/core-graphql/src/repositories/blocks.ts b/deprecated/core-graphql/src/repositories/blocks.ts similarity index 100% rename from packages/core-graphql/src/repositories/blocks.ts rename to deprecated/core-graphql/src/repositories/blocks.ts diff --git a/packages/core-graphql/src/repositories/index.ts b/deprecated/core-graphql/src/repositories/index.ts similarity index 100% rename from packages/core-graphql/src/repositories/index.ts rename to deprecated/core-graphql/src/repositories/index.ts diff --git a/packages/core-graphql/src/repositories/repository.ts b/deprecated/core-graphql/src/repositories/repository.ts similarity index 100% rename from packages/core-graphql/src/repositories/repository.ts rename to deprecated/core-graphql/src/repositories/repository.ts diff --git a/packages/core-graphql/src/repositories/transactions.ts b/deprecated/core-graphql/src/repositories/transactions.ts similarity index 100% rename from packages/core-graphql/src/repositories/transactions.ts rename to deprecated/core-graphql/src/repositories/transactions.ts diff --git a/packages/core-graphql/src/repositories/utils/filter-query.ts b/deprecated/core-graphql/src/repositories/utils/filter-query.ts similarity index 100% rename from packages/core-graphql/src/repositories/utils/filter-query.ts rename to deprecated/core-graphql/src/repositories/utils/filter-query.ts diff --git a/packages/core-graphql/src/resolvers/index.ts b/deprecated/core-graphql/src/resolvers/index.ts similarity index 100% rename from packages/core-graphql/src/resolvers/index.ts rename to deprecated/core-graphql/src/resolvers/index.ts diff --git a/packages/core-graphql/src/resolvers/queries/block/block.ts b/deprecated/core-graphql/src/resolvers/queries/block/block.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/block/block.ts rename to deprecated/core-graphql/src/resolvers/queries/block/block.ts diff --git a/packages/core-graphql/src/resolvers/queries/block/blocks.ts b/deprecated/core-graphql/src/resolvers/queries/block/blocks.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/block/blocks.ts rename to deprecated/core-graphql/src/resolvers/queries/block/blocks.ts diff --git a/packages/core-graphql/src/resolvers/queries/index.ts b/deprecated/core-graphql/src/resolvers/queries/index.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/index.ts rename to deprecated/core-graphql/src/resolvers/queries/index.ts diff --git a/packages/core-graphql/src/resolvers/queries/transaction/transaction.ts b/deprecated/core-graphql/src/resolvers/queries/transaction/transaction.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/transaction/transaction.ts rename to deprecated/core-graphql/src/resolvers/queries/transaction/transaction.ts diff --git a/packages/core-graphql/src/resolvers/queries/transaction/transactions.ts b/deprecated/core-graphql/src/resolvers/queries/transaction/transactions.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/transaction/transactions.ts rename to deprecated/core-graphql/src/resolvers/queries/transaction/transactions.ts diff --git a/packages/core-graphql/src/resolvers/queries/wallet/wallet.ts b/deprecated/core-graphql/src/resolvers/queries/wallet/wallet.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/wallet/wallet.ts rename to deprecated/core-graphql/src/resolvers/queries/wallet/wallet.ts diff --git a/packages/core-graphql/src/resolvers/queries/wallet/wallets.ts b/deprecated/core-graphql/src/resolvers/queries/wallet/wallets.ts similarity index 100% rename from packages/core-graphql/src/resolvers/queries/wallet/wallets.ts rename to deprecated/core-graphql/src/resolvers/queries/wallet/wallets.ts diff --git a/packages/core-graphql/src/resolvers/relationship/block.ts b/deprecated/core-graphql/src/resolvers/relationship/block.ts similarity index 100% rename from packages/core-graphql/src/resolvers/relationship/block.ts rename to deprecated/core-graphql/src/resolvers/relationship/block.ts diff --git a/packages/core-graphql/src/resolvers/relationship/transaction.ts b/deprecated/core-graphql/src/resolvers/relationship/transaction.ts similarity index 100% rename from packages/core-graphql/src/resolvers/relationship/transaction.ts rename to deprecated/core-graphql/src/resolvers/relationship/transaction.ts diff --git a/packages/core-graphql/src/resolvers/relationship/wallet.ts b/deprecated/core-graphql/src/resolvers/relationship/wallet.ts similarity index 100% rename from packages/core-graphql/src/resolvers/relationship/wallet.ts rename to deprecated/core-graphql/src/resolvers/relationship/wallet.ts diff --git a/packages/core-graphql/src/server.ts b/deprecated/core-graphql/src/server.ts similarity index 100% rename from packages/core-graphql/src/server.ts rename to deprecated/core-graphql/src/server.ts diff --git a/packages/core-graphql/tsconfig.json b/deprecated/core-graphql/tsconfig.json similarity index 100% rename from packages/core-graphql/tsconfig.json rename to deprecated/core-graphql/tsconfig.json diff --git a/packages/core-logger-winston/.gitattributes b/deprecated/core-logger-winston/.gitattributes similarity index 100% rename from packages/core-logger-winston/.gitattributes rename to deprecated/core-logger-winston/.gitattributes diff --git a/packages/core-logger-winston/README.md b/deprecated/core-logger-winston/README.md similarity index 100% rename from packages/core-logger-winston/README.md rename to deprecated/core-logger-winston/README.md diff --git a/packages/core-logger-winston/__tests__/logger.test.ts b/deprecated/core-logger-winston/__tests__/logger.test.ts similarity index 100% rename from packages/core-logger-winston/__tests__/logger.test.ts rename to deprecated/core-logger-winston/__tests__/logger.test.ts diff --git a/packages/core-logger-winston/package.json b/deprecated/core-logger-winston/package.json similarity index 93% rename from packages/core-logger-winston/package.json rename to deprecated/core-logger-winston/package.json index ad0805e767..7389fc5508 100644 --- a/packages/core-logger-winston/package.json +++ b/deprecated/core-logger-winston/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-logger-winston", "description": "Winston Logger for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Brian Faust " @@ -33,8 +33,8 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-logger": "^2.2.0-beta.4", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-logger": "^2.2.0-beta.7", "chalk": "^2.4.2", "colors": "^1.3.3", "dayjs-ext": "^2.2.0", diff --git a/packages/core-logger-winston/src/defaults.ts b/deprecated/core-logger-winston/src/defaults.ts similarity index 100% rename from packages/core-logger-winston/src/defaults.ts rename to deprecated/core-logger-winston/src/defaults.ts diff --git a/packages/core-logger-winston/src/driver.ts b/deprecated/core-logger-winston/src/driver.ts similarity index 100% rename from packages/core-logger-winston/src/driver.ts rename to deprecated/core-logger-winston/src/driver.ts diff --git a/packages/core-logger-winston/src/formatter.ts b/deprecated/core-logger-winston/src/formatter.ts similarity index 100% rename from packages/core-logger-winston/src/formatter.ts rename to deprecated/core-logger-winston/src/formatter.ts diff --git a/packages/core-logger-winston/src/index.ts b/deprecated/core-logger-winston/src/index.ts similarity index 100% rename from packages/core-logger-winston/src/index.ts rename to deprecated/core-logger-winston/src/index.ts diff --git a/packages/core-logger-winston/src/plugin.ts b/deprecated/core-logger-winston/src/plugin.ts similarity index 100% rename from packages/core-logger-winston/src/plugin.ts rename to deprecated/core-logger-winston/src/plugin.ts diff --git a/packages/core-logger-winston/tsconfig.json b/deprecated/core-logger-winston/tsconfig.json similarity index 100% rename from packages/core-logger-winston/tsconfig.json rename to deprecated/core-logger-winston/tsconfig.json diff --git a/packages/core-snapshots-cli/README.md b/deprecated/core-snapshots-cli/README.md similarity index 100% rename from packages/core-snapshots-cli/README.md rename to deprecated/core-snapshots-cli/README.md diff --git a/packages/core-snapshots-cli/__tests__/.gitkeep b/deprecated/core-snapshots-cli/__tests__/.gitkeep similarity index 100% rename from packages/core-snapshots-cli/__tests__/.gitkeep rename to deprecated/core-snapshots-cli/__tests__/.gitkeep diff --git a/packages/core-snapshots-cli/bin/run b/deprecated/core-snapshots-cli/bin/run similarity index 100% rename from packages/core-snapshots-cli/bin/run rename to deprecated/core-snapshots-cli/bin/run diff --git a/packages/core-snapshots-cli/bin/run.cmd b/deprecated/core-snapshots-cli/bin/run.cmd similarity index 100% rename from packages/core-snapshots-cli/bin/run.cmd rename to deprecated/core-snapshots-cli/bin/run.cmd diff --git a/packages/core-snapshots-cli/package.json b/deprecated/core-snapshots-cli/package.json similarity index 94% rename from packages/core-snapshots-cli/package.json rename to deprecated/core-snapshots-cli/package.json index 500f006fe7..6341f23611 100644 --- a/packages/core-snapshots-cli/package.json +++ b/deprecated/core-snapshots-cli/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-snapshots-cli", "description": "Provides live cli interface to the core-snapshots module for ARK Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Kristjan Košič " ], @@ -55,9 +55,9 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-snapshots": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-snapshots": "^2.2.0-beta.7", "@oclif/command": "^1.5.10", "@oclif/config": "^1.12.6", "@oclif/plugin-help": "^2.1.6", diff --git a/packages/core-snapshots-cli/src/commands/command.ts b/deprecated/core-snapshots-cli/src/commands/command.ts similarity index 100% rename from packages/core-snapshots-cli/src/commands/command.ts rename to deprecated/core-snapshots-cli/src/commands/command.ts diff --git a/packages/core-snapshots-cli/src/commands/dump.ts b/deprecated/core-snapshots-cli/src/commands/dump.ts similarity index 100% rename from packages/core-snapshots-cli/src/commands/dump.ts rename to deprecated/core-snapshots-cli/src/commands/dump.ts diff --git a/packages/core-snapshots-cli/src/commands/restore.ts b/deprecated/core-snapshots-cli/src/commands/restore.ts similarity index 100% rename from packages/core-snapshots-cli/src/commands/restore.ts rename to deprecated/core-snapshots-cli/src/commands/restore.ts diff --git a/packages/core-snapshots-cli/src/commands/rollback.ts b/deprecated/core-snapshots-cli/src/commands/rollback.ts similarity index 100% rename from packages/core-snapshots-cli/src/commands/rollback.ts rename to deprecated/core-snapshots-cli/src/commands/rollback.ts diff --git a/packages/core-snapshots-cli/src/commands/truncate.ts b/deprecated/core-snapshots-cli/src/commands/truncate.ts similarity index 100% rename from packages/core-snapshots-cli/src/commands/truncate.ts rename to deprecated/core-snapshots-cli/src/commands/truncate.ts diff --git a/packages/core-snapshots-cli/src/commands/verify.ts b/deprecated/core-snapshots-cli/src/commands/verify.ts similarity index 100% rename from packages/core-snapshots-cli/src/commands/verify.ts rename to deprecated/core-snapshots-cli/src/commands/verify.ts diff --git a/packages/core-snapshots-cli/src/index.ts b/deprecated/core-snapshots-cli/src/index.ts similarity index 100% rename from packages/core-snapshots-cli/src/index.ts rename to deprecated/core-snapshots-cli/src/index.ts diff --git a/packages/core-snapshots-cli/src/utils.ts b/deprecated/core-snapshots-cli/src/utils.ts similarity index 89% rename from packages/core-snapshots-cli/src/utils.ts rename to deprecated/core-snapshots-cli/src/utils.ts index 779bcdfc6f..1ef2a1b995 100644 --- a/packages/core-snapshots-cli/src/utils.ts +++ b/deprecated/core-snapshots-cli/src/utils.ts @@ -6,7 +6,7 @@ export const setUpLite = async options => { await app.setUp("2.0.0", options, { include: [ "@arkecosystem/core-logger", - "@arkecosystem/core-logger-winston", + "@arkecosystem/core-logger-pino", "@arkecosystem/core-event-emitter", "@arkecosystem/core-snapshots", ], diff --git a/packages/core-snapshots-cli/tsconfig.json b/deprecated/core-snapshots-cli/tsconfig.json similarity index 100% rename from packages/core-snapshots-cli/tsconfig.json rename to deprecated/core-snapshots-cli/tsconfig.json diff --git a/lerna.json b/lerna.json index 4ac36c9a22..e918c80142 100644 --- a/lerna.json +++ b/lerna.json @@ -3,5 +3,5 @@ "npmClient": "yarn", "packages": ["packages/*", "plugins/*"], "useWorkspaces": true, - "version": "2.2.0-beta.4" + "version": "2.2.0-beta.7" } diff --git a/packages/core-api/__tests__/__support__/setup.ts b/packages/core-api/__tests__/__support__/setup.ts index d990a0a17e..def706b934 100644 --- a/packages/core-api/__tests__/__support__/setup.ts +++ b/packages/core-api/__tests__/__support__/setup.ts @@ -24,7 +24,6 @@ async function setUp() { await setUpContainer({ exclude: [ "@arkecosystem/core-webhooks", - "@arkecosystem/core-graphql", "@arkecosystem/core-forger", "@arkecosystem/core-json-rpc", "@arkecosystem/core-api", diff --git a/packages/core-api/__tests__/v2/handlers/delegates.test.ts b/packages/core-api/__tests__/v2/handlers/delegates.test.ts index e80ab0ff6c..1b65d387c1 100644 --- a/packages/core-api/__tests__/v2/handlers/delegates.test.ts +++ b/packages/core-api/__tests__/v2/handlers/delegates.test.ts @@ -4,7 +4,7 @@ import { utils } from "../utils"; import { blocks2to100 } from "../../../../core-test-utils/src/fixtures/testnet/blocks2to100"; -import { models } from "@arkecosystem/crypto"; +import { Bignum, models } from "@arkecosystem/crypto"; const { Block } = models; import { app } from "@arkecosystem/core-container"; @@ -16,6 +16,12 @@ const delegate = { publicKey: "0377f81a18d25d77b100cb17e829a72259f08334d064f6c887298917a04df8f647", }; +const delegate2 = { + username: "genesis_10", + address: "AFyf2qVpX2JbpKcy29XbusedCpFDeYFX8Q", + publicKey: "02f7acb179ddfddb2e220aa600921574646ac59fd3f1ae6255ada40b9a7fab75fd", +}; + beforeAll(async () => { await setUp(); await calculateRanks(); @@ -38,6 +44,34 @@ describe("API 2.0 - Delegates", () => { response.data.data.forEach(utils.expectDelegate); expect(response.data.data.sort((a, b) => a.rank < b.rank)).toEqual(response.data.data); }); + + it("should GET all the delegates sorted by votes,asc", async () => { + const wm = app.resolvePlugin("database").walletManager; + const wallet = wm.findByUsername("genesis_51"); + wallet.voteBalance = new Bignum(1); + wm.reindex(wallet); + + const response = await utils[request]("GET", "delegates", { orderBy: "votes:asc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data[0].username).toBe(wallet.username); + expect(response.data.data[0].votes).toBe(+wallet.voteBalance.toFixed()); + }); + + it("should GET all the delegates sorted by votes,desc", async () => { + const wm = app.resolvePlugin("database").walletManager; + const wallet = wm.findByUsername("genesis_1"); + wallet.voteBalance = new Bignum(12500000000000000); + wm.reindex(wallet); + + const response = await utils[request]("GET", "delegates", { orderBy: "votes:desc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data[0].username).toBe(wallet.username); + expect(response.data.data[0].votes).toBe(+wallet.voteBalance.toFixed()); + }); }, ); @@ -144,6 +178,20 @@ describe("API 2.0 - Delegates", () => { utils.expectDelegate(response.data.data[0], delegate); }); + + it("should POST a search for delegates with any of the specified usernames", async () => { + const response = await utils[request]("POST", "delegates/search", { + usernames: [delegate.username, delegate2.username], + }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data).toHaveLength(2); + + for (const delegate of response.data.data) { + utils.expectDelegate(delegate); + } + }); }, ); }); diff --git a/packages/core-api/__tests__/v2/handlers/wallets.test.ts b/packages/core-api/__tests__/v2/handlers/wallets.test.ts index 0a20e72838..6177592c85 100644 --- a/packages/core-api/__tests__/v2/handlers/wallets.test.ts +++ b/packages/core-api/__tests__/v2/handlers/wallets.test.ts @@ -1,4 +1,5 @@ import "@arkecosystem/core-test-utils"; +import { Bignum } from "@arkecosystem/crypto"; import { setUp, tearDown } from "../../__support__/setup"; import { utils } from "../utils"; @@ -28,6 +29,24 @@ describe("API 2.0 - Wallets", () => { utils.expectWallet(response.data.data[0]); }); + + it("should GET all the wallets sorted by balance,asc", async () => { + const response = await utils[request]("GET", "wallets", { orderBy: "balance:asc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data[0].address).toBe("APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn"); + expect(response.data.data[0].balance).toBe(-12500000000000000); + }); + + it("should GET all the wallets sorted by balance,desc", async () => { + const response = await utils[request]("GET", "wallets", { orderBy: "balance:desc" }); + expect(response).toBeSuccessfulResponse(); + expect(response.data.data).toBeArray(); + + expect(response.data.data[0].address).toBe("ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo"); + expect(response.data.data[0].balance).toBe(245100000000000); + }); }, ); }); diff --git a/packages/core-api/__tests__/v2/utils.ts b/packages/core-api/__tests__/v2/utils.ts index 0e9f9367a2..f7e6f446aa 100644 --- a/packages/core-api/__tests__/v2/utils.ts +++ b/packages/core-api/__tests__/v2/utils.ts @@ -12,21 +12,17 @@ class Helpers { "Content-Type": "application/json", }; - const server = app.resolvePlugin("api"); - - return ApiHelpers.request(server.http, method, url, headers, params); + return ApiHelpers.request(app.resolvePlugin("api").http, method, url, headers, params); } public async requestWithAcceptHeader(method, path, params = {}) { const url = `http://localhost:4003/api/${path}`; const headers = { - Accept: "application/vnd.core-api.v2+json", + "API-Version": 2, "Content-Type": "application/json", }; - const server = app.resolvePlugin("api"); - - return ApiHelpers.request(server.http, method, url, headers, params); + return ApiHelpers.request(app.resolvePlugin("api").http, method, url, headers, params); } public expectJson(response) { diff --git a/packages/core-api/package.json b/packages/core-api/package.json index a92ab2a661..3613500511 100644 --- a/packages/core-api/package.json +++ b/packages/core-api/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-api", "description": "Public API for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Kristjan Košič ", "Brian Faust " @@ -33,13 +33,13 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-transaction-pool": "^2.2.0-beta.4", - "@arkecosystem/core-utils": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", - "@arkecosystem/utils": "^0.2.3", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-transaction-pool": "^2.2.0-beta.7", + "@arkecosystem/core-utils": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", + "@arkecosystem/utils": "^0.2.4", "@faustbrian/hapi-version": "^0.2.11", "@types/lodash.orderby": "^4.6.4", "@types/lodash.partition": "^4.6.4", @@ -58,7 +58,7 @@ "lodash.snakecase": "^4.1.1" }, "devDependencies": { - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "@types/boom": "^7.2.1", "@types/ip": "^1.1.0", "@types/joi": "^14.3.1", diff --git a/packages/core-api/src/formats.ts b/packages/core-api/src/formats.ts index e9874eaaae..03da6890e4 100644 --- a/packages/core-api/src/formats.ts +++ b/packages/core-api/src/formats.ts @@ -74,15 +74,4 @@ export const registerFormats = (ajv: Ajv) => { } }, }); - - ajv.addFormat("vendorField", { - type: "string", - validate: value => { - try { - return Buffer.from(value).length < 65; - } catch (e) { - return false; - } - }, - }); }; diff --git a/packages/core-api/src/plugin.ts b/packages/core-api/src/plugin.ts index be469c15b2..52aaa651e3 100644 --- a/packages/core-api/src/plugin.ts +++ b/packages/core-api/src/plugin.ts @@ -8,7 +8,7 @@ export const plugin: Container.PluginDescriptor = { alias: "api", async register(container: Container.IContainer, options) { if (!options.enabled) { - container.resolvePlugin("logger").info("Public API is disabled :grey_exclamation:"); + container.resolvePlugin("logger").info("Public API is disabled"); return false; } diff --git a/packages/core-api/src/repositories/repository.ts b/packages/core-api/src/repositories/repository.ts index 28f30d118d..0c2723869e 100644 --- a/packages/core-api/src/repositories/repository.ts +++ b/packages/core-api/src/repositories/repository.ts @@ -3,7 +3,6 @@ import { Database, TransactionPool } from "@arkecosystem/core-interfaces"; import snakeCase from "lodash/snakeCase"; import { IRepository } from "../interfaces"; - // TODO: Deprecate this with v1 export abstract class Repository implements IRepository { public databaseService = app.resolvePlugin("database"); diff --git a/packages/core-api/src/repositories/transactions.ts b/packages/core-api/src/repositories/transactions.ts index fb685c4ec5..8f3d117dfe 100644 --- a/packages/core-api/src/repositories/transactions.ts +++ b/packages/core-api/src/repositories/transactions.ts @@ -6,7 +6,6 @@ import { IRepository } from "../interfaces"; import { Repository } from "./repository"; import { buildFilterQuery } from "./utils/build-filter-query"; - // TODO: Deprecate this with v1 export class TransactionsRepository extends Repository implements IRepository { constructor() { diff --git a/packages/core-api/src/services/cache.ts b/packages/core-api/src/services/cache.ts index f0f354bee2..a9d60fe067 100644 --- a/packages/core-api/src/services/cache.ts +++ b/packages/core-api/src/services/cache.ts @@ -1,5 +1,5 @@ import { app } from "@arkecosystem/core-container"; -import { createHash } from "crypto"; +import { HashAlgorithms } from "@arkecosystem/crypto"; import Hapi from "hapi"; export class ServerCache { @@ -30,9 +30,7 @@ export class ServerCache { } private generateCacheKey(value: object): string { - return createHash("sha256") - .update(JSON.stringify(value)) - .digest("hex"); + return HashAlgorithms.sha256(JSON.stringify(value)).toString("hex"); } private getCacheTimeout(): number | boolean { diff --git a/packages/core-api/src/versions/1/shared/transformers/ports.ts b/packages/core-api/src/versions/1/shared/transformers/ports.ts index 579a94b156..51adcfbdae 100644 --- a/packages/core-api/src/versions/1/shared/transformers/ports.ts +++ b/packages/core-api/src/versions/1/shared/transformers/ports.ts @@ -3,7 +3,6 @@ export function transformPortsLegacy(config: any) { const keys = [ "@arkecosystem/core-p2p", "@arkecosystem/core-api", - "@arkecosystem/core-graphql", "@arkecosystem/core-json-rpc", "@arkecosystem/core-webhooks", ]; diff --git a/packages/core-api/src/versions/2/delegates/methods.ts b/packages/core-api/src/versions/2/delegates/methods.ts index 80d80778bc..696e1bfb7f 100644 --- a/packages/core-api/src/versions/2/delegates/methods.ts +++ b/packages/core-api/src/versions/2/delegates/methods.ts @@ -56,7 +56,10 @@ const voters = async request => { return Boom.notFound("Delegate not found"); } - const wallets = await databaseService.wallets.findAllByVote(delegate.publicKey, paginate(request)); + const wallets = await databaseService.wallets.findAllByVote(delegate.publicKey, { + ...request.query, + ...paginate(request), + }); return toPagination(request, wallets, "wallet"); }; diff --git a/packages/core-api/src/versions/2/delegates/schema.ts b/packages/core-api/src/versions/2/delegates/schema.ts index 8ebb84cf26..68d43b6c70 100644 --- a/packages/core-api/src/versions/2/delegates/schema.ts +++ b/packages/core-api/src/versions/2/delegates/schema.ts @@ -1,6 +1,9 @@ +import { app } from "@arkecosystem/core-container"; import * as Joi from "joi"; import { pagination } from "../shared/schemas/pagination"; +const config = app.getConfig(); + const schemaIdentifier = Joi.string() .regex(/^[a-zA-Z0-9!@$&_.]+$/) .min(1) @@ -52,9 +55,19 @@ export const show: object = { }; export const search: object = { - query: pagination, + query: { + ...pagination, + ...{ + orderBy: Joi.string(), + }, + }, payload: { username: schemaUsername, + usernames: Joi.array() + .unique() + .min(1) + .max(config.getMilestone().activeDelegates) + .items(schemaUsername), }, }; diff --git a/packages/core-api/src/versions/2/node/controller.ts b/packages/core-api/src/versions/2/node/controller.ts index e43092307e..1ea37903bc 100644 --- a/packages/core-api/src/versions/2/node/controller.ts +++ b/packages/core-api/src/versions/2/node/controller.ts @@ -42,7 +42,8 @@ export class NodeController extends Controller { public async configuration(request: Hapi.Request, h: Hapi.ResponseToolkit) { try { - const transactionsBusinessRepository = app.resolvePlugin("database").transactionsBusinessRepository; + const transactionsBusinessRepository = app.resolvePlugin("database") + .transactionsBusinessRepository; const feeStatisticsData = await transactionsBusinessRepository.getFeeStatistics(); const network = this.config.get("network"); diff --git a/packages/core-api/src/versions/2/shared/transformers/ports.ts b/packages/core-api/src/versions/2/shared/transformers/ports.ts index 3193f00719..0c1b99b198 100644 --- a/packages/core-api/src/versions/2/shared/transformers/ports.ts +++ b/packages/core-api/src/versions/2/shared/transformers/ports.ts @@ -3,7 +3,6 @@ export function transformPorts(config: any) { const keys = [ "@arkecosystem/core-p2p", "@arkecosystem/core-api", - "@arkecosystem/core-graphql", "@arkecosystem/core-json-rpc", "@arkecosystem/core-webhooks", ]; diff --git a/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts b/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts index e42d267b7a..643850b062 100644 --- a/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts +++ b/packages/core-blockchain/__tests__/blockchain-networkStart.test.ts @@ -28,9 +28,9 @@ describe("constructor - networkStart", () => { await __start(true); expect(loggerWarn).toHaveBeenCalledWith( - "Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong. :warning:", + "Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong.", ); - expect(loggerInfo).toHaveBeenCalledWith("Starting Ark Core for a new world, welcome aboard :rocket:"); + expect(loggerInfo).toHaveBeenCalledWith("Starting Ark Core for a new world, welcome aboard"); }); describe("dispatch", () => { @@ -50,7 +50,7 @@ describe("constructor - networkStart", () => { }); blockchain.dispatch("STOP"); - expect(loggerError).toHaveBeenCalledWith("No action 'yooo' found :interrobang:"); + expect(loggerError).toHaveBeenCalledWith("No action 'yooo' found"); }); }); }); diff --git a/packages/core-blockchain/__tests__/blockchain.test.ts b/packages/core-blockchain/__tests__/blockchain.test.ts index cb1fe5f033..d2e3a4c879 100644 --- a/packages/core-blockchain/__tests__/blockchain.test.ts +++ b/packages/core-blockchain/__tests__/blockchain.test.ts @@ -217,7 +217,7 @@ describe("Blockchain", () => { expect(mockCallback.mock.calls.length).toBe(1); expect(loggerInfo).toHaveBeenCalledWith( - `Block ${lastBlockCopy.data.height.toLocaleString()} disregarded because on a fork :knife_fork_plate:`, + `Block ${lastBlockCopy.data.height.toLocaleString()} disregarded because on a fork`, ); expect(blockchain.getLastBlock().data.id).toBe(lastBlock.data.id); }); @@ -249,7 +249,7 @@ describe("Blockchain", () => { expect(mockCallback.mock.calls.length).toBe(1); expect(loggerWarn).toHaveBeenCalledWith( - `Block ${lastBlockCopy.data.height.toLocaleString()} disregarded because verification failed :scroll:`, + `Block ${lastBlockCopy.data.height.toLocaleString()} disregarded because verification failed`, ); expect(blockchain.getLastBlock().data.id).toBe(lastBlock.data.id); }); @@ -355,7 +355,7 @@ describe("Blockchain", () => { const debugMessage = `Blockchain not ready to accept new block at height ${lastBlock.data.height.toLocaleString()}. Last block: ${( lastBlock.data.height - 2 - ).toLocaleString()} :warning:`; + ).toLocaleString()}`; expect(mockLoggerDebug).toHaveBeenCalledWith(debugMessage); expect(blockchain.getLastBlock().data.height).toBe(lastBlock.data.height - 2); @@ -447,7 +447,7 @@ describe("Blockchain", () => { await blockchain.handleIncomingBlock(blocks101to155[54]); - expect(loggerInfo).toHaveBeenCalledWith("Block disregarded because blockchain is not ready :exclamation:"); + expect(loggerInfo).toHaveBeenCalledWith("Block disregarded because blockchain is not ready"); blockchain.state.started = true; mockGetSlotNumber.mockRestore(); diff --git a/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts b/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts index f6652a9136..c508942e27 100644 --- a/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts +++ b/packages/core-blockchain/__tests__/processor/handlers/accept-handler.test.ts @@ -37,7 +37,7 @@ describe("Accept handler", () => { blockchain.state.forkedBlock = new Block(blocks2to100[0]); expect(await handler.execute()).toBe(BlockProcessorResult.Accepted); - expect(loggerInfo).toHaveBeenCalledWith("Successfully recovered from fork :star2:"); + expect(loggerInfo).toHaveBeenCalledWith("Successfully recovered from fork"); expect(blockchain.state.forkedBlock).toBe(null); }); diff --git a/packages/core-blockchain/__tests__/state-machine.test.ts b/packages/core-blockchain/__tests__/state-machine.test.ts index bcd5347c2d..89e8151cd6 100644 --- a/packages/core-blockchain/__tests__/state-machine.test.ts +++ b/packages/core-blockchain/__tests__/state-machine.test.ts @@ -126,13 +126,13 @@ describe("State Machine", () => { it(`should dispatch the event "FORK" if - stateStorage.noBlockCounter > 5 and process queue is empty - stateStorage.p2pUpdateCounter + 1 > 3 (network keeps missing blocks) - - blockchain.p2p.updatePeersOnMissingBlocks() returns "rollback"`, async () => { + - blockchain.p2p.checkNetworkHealth() returns a forked network status`, async () => { blockchain.isSynced = jest.fn(() => false); blockchain.processQueue.length = jest.fn(() => 0); stateStorage.noBlockCounter = 6; stateStorage.p2pUpdateCounter = 3; // @ts-ignore - jest.spyOn(blockchain.p2p, "updatePeersOnMissingBlocks").mockImplementation(() => "rollback"); + jest.spyOn(blockchain.p2p, "checkNetworkHealth").mockImplementation(() => ({ forked: true })); await expect(actionMap.checkLastDownloadedBlockSynced).toDispatch(blockchain, "FORK"); }); @@ -201,7 +201,7 @@ describe("State Machine", () => { const logger = container.resolvePlugin("logger"); const loggerInfo = jest.spyOn(logger, "info"); actionMap.downloadPaused(); - expect(loggerInfo).lastCalledWith("Blockchain download paused :clock1030:"); + expect(loggerInfo).lastCalledWith("Blockchain download paused"); }); }); @@ -222,7 +222,7 @@ describe("State Machine", () => { const logger = container.resolvePlugin("logger"); const loggerInfo = jest.spyOn(logger, "info"); actionMap.stopped(); - expect(loggerInfo).lastCalledWith("The blockchain has been stopped :guitar:"); + expect(loggerInfo).lastCalledWith("The blockchain has been stopped"); }); }); @@ -230,7 +230,7 @@ describe("State Machine", () => { it("should call container forceExit with error message", () => { const forceExit = jest.spyOn(container, "forceExit").mockImplementationOnce(() => null); actionMap.exitApp(); - expect(forceExit).lastCalledWith("Failed to startup blockchain. Exiting Ark Core! :rotating_light:"); + expect(forceExit).lastCalledWith("Failed to startup blockchain. Exiting Ark Core!"); }); }); @@ -304,8 +304,8 @@ describe("State Machine", () => { it("should verify database integrity if database recovery was not successful (!restoredDatabaseIntegrity)", async () => { await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); - expect(loggerInfo).nthCalledWith(1, "Verifying database integrity :hourglass_flowing_sand:"); - expect(loggerInfo).nthCalledWith(2, "Verified database integrity :smile_cat:"); + expect(loggerInfo).nthCalledWith(1, "Verifying database integrity"); + expect(loggerInfo).nthCalledWith(2, "Verified database integrity"); }); it("should dispatch ROLLBACK if database recovery was not successful and verifyBlockchain failed", async () => { @@ -315,7 +315,7 @@ describe("State Machine", () => { }); await expect(() => actionMap.init()).toDispatch(blockchain, "ROLLBACK"); - expect(loggerError).nthCalledWith(1, "FATAL: The database is corrupted :fire:"); + expect(loggerError).nthCalledWith(1, "FATAL: The database is corrupted"); }); it("should skip database integrity check if database recovery was successful (restoredDatabaseIntegrity)", async () => { @@ -324,7 +324,7 @@ describe("State Machine", () => { await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); expect(loggerInfo).nthCalledWith( 1, - "Skipping database integrity check after successful database recovery :smile_cat:", + "Skipping database integrity check after successful database recovery", ); }); @@ -347,7 +347,7 @@ describe("State Machine", () => { await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); expect(databaseMocks.buildWallets).toHaveBeenCalledWith(1); expect(loggerVerbose).toHaveBeenCalledWith( - "TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY. :bangbang:", + "TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY.", ); }); @@ -394,7 +394,7 @@ describe("State Machine", () => { await expect(() => actionMap.init()).toDispatch(blockchain, "STARTED"); expect(loggerWarn).toHaveBeenCalledWith( - "Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown. :hammer:", + "Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown.", ); expect(databaseMocks.saveWallets).toHaveBeenCalledWith(true); }); @@ -553,7 +553,7 @@ describe("State Machine", () => { actionMap.analyseFork(); - expect(loggerInfo).toHaveBeenCalledWith("Analysing fork :mag:"); + expect(loggerInfo).toHaveBeenCalledWith("Analysing fork"); }); }); @@ -575,7 +575,7 @@ describe("State Machine", () => { ]; await expect(() => actionMap.startForkRecovery()).toDispatch(blockchain, "SUCCESS"); - expect(loggerInfo).toHaveBeenCalledWith("Starting fork recovery :fork_and_knife:"); + expect(loggerInfo).toHaveBeenCalledWith("Starting fork recovery"); methodsCalled.forEach(method => { expect(method).toHaveBeenCalled(); }); @@ -609,9 +609,7 @@ describe("State Machine", () => { await expect(() => actionMap.rollbackDatabase()).toDispatch(blockchain, "SUCCESS"); - expect(loggerInfo).toHaveBeenCalledWith( - "Database integrity verified again after rollback to height 1 :green_heart:", - ); + expect(loggerInfo).toHaveBeenCalledWith("Database integrity verified again after rollback to height 1"); expect(removeTopBlocks).toHaveBeenCalledTimes(3); // because the 3rd time verifyBlockchain returned true }); @@ -637,9 +635,7 @@ describe("State Machine", () => { await expect(() => actionMap.rollbackDatabase()).toDispatch(blockchain, "FAILURE"); - expect(loggerError).toHaveBeenCalledWith( - "FATAL: Failed to restore database integrity :skull: :skull: :skull:", - ); + expect(loggerError).toHaveBeenCalledWith("FATAL: Failed to restore database integrity"); expect(removeTopBlocks).toHaveBeenCalledTimes(5); // because after 5 times we get past maxBlockRewind }); }); diff --git a/packages/core-blockchain/package.json b/packages/core-blockchain/package.json index bce5d70059..f5a074edea 100644 --- a/packages/core-blockchain/package.json +++ b/packages/core-blockchain/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-blockchain", "description": "Blockchain Manager for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Kristjan Košič ", @@ -34,10 +34,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-utils": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-utils": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/lodash.get": "^4.4.4", "async": "^2.6.2", "awilix": "^4.2.0", @@ -49,8 +49,8 @@ "xstate": "^4.3.1" }, "devDependencies": { - "@arkecosystem/core-p2p": "^2.2.0-beta.4", - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-p2p": "^2.2.0-beta.7", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "@types/async": "^2.4.0", "@types/pluralize": "^0.0.29", "@types/pretty-ms": "^4.0.0", diff --git a/packages/core-blockchain/src/blockchain.ts b/packages/core-blockchain/src/blockchain.ts index 808cdd05eb..7a1765ee68 100644 --- a/packages/core-blockchain/src/blockchain.ts +++ b/packages/core-blockchain/src/blockchain.ts @@ -75,9 +75,9 @@ export class Blockchain implements blockchain.IBlockchain { if (this.state.networkStart) { logger.warn( - "Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong. :warning:", + "Ark Core is launched in Genesis Start mode. This is usually for starting the first node on the blockchain. Unless you know what you are doing, this is likely wrong.", ); - logger.info("Starting Ark Core for a new world, welcome aboard :rocket:"); + logger.info("Starting Ark Core for a new world, welcome aboard"); } this.actions = stateMachine.actionMap(this); @@ -110,7 +110,7 @@ export class Blockchain implements blockchain.IBlockchain { if (action) { setTimeout(() => action.call(this, event), 0); } else { - logger.error(`No action '${actionKey}' found :interrobang:`); + logger.error(`No action '${actionKey}' found`); } }); @@ -216,7 +216,7 @@ export class Blockchain implements blockchain.IBlockchain { * Hand the given transactions to the transaction handler. */ public async postTransactions(transactions: Transaction[]) { - logger.info(`Received ${transactions.length} new ${pluralize("transaction", transactions.length)} :moneybag:`); + logger.info(`Received ${transactions.length} new ${pluralize("transaction", transactions.length)}`); await this.transactionPool.addTransactions(transactions); } @@ -246,7 +246,7 @@ export class Blockchain implements blockchain.IBlockchain { this.dispatch("NEWBLOCK"); this.enqueueBlocks([block]); } else { - logger.info(`Block disregarded because blockchain is not ready :exclamation:`); + logger.info(`Block disregarded because blockchain is not ready`); } } @@ -289,7 +289,7 @@ export class Blockchain implements blockchain.IBlockchain { this.state.lastDownloadedBlock = newLastBlock; }; - logger.info(`Removing ${pluralize("block", height - newHeight, true)} to reset current round :warning:`); + logger.info(`Removing ${pluralize("block", height - newHeight, true)} to reset current round`); let count = 0; const max = this.state.getLastBlock().data.height - newHeight; @@ -435,10 +435,10 @@ export class Blockchain implements blockchain.IBlockchain { return callback(); } this.state.lastDownloadedBlock = lastBlock; - logger.info(`Block ${block.data.height.toLocaleString()} disregarded because on a fork :knife_fork_plate:`); + logger.info(`Block ${block.data.height.toLocaleString()} disregarded because on a fork`); return callback(); } - logger.warn(`Block ${block.data.height.toLocaleString()} disregarded because verification failed :scroll:`); + logger.warn(`Block ${block.data.height.toLocaleString()} disregarded because verification failed`); logger.warn(JSON.stringify(block.verification, null, 4)); return callback(); } @@ -479,9 +479,13 @@ export class Blockchain implements blockchain.IBlockchain { /** * Fork the chain at the given block. */ - public forkBlock(block: models.Block): void { + public forkBlock(block: models.Block, numberOfBlockToRollback?: number): void { this.state.forkedBlock = block; + if (numberOfBlockToRollback) { + this.state.numberOfBlocksToRollback = numberOfBlockToRollback; + } + this.dispatch("FORK"); } @@ -525,7 +529,7 @@ export class Blockchain implements blockchain.IBlockchain { block = block || this.getLastBlock(); const remaining = slots.getTime() - block.data.timestamp; - logger.info(`Remaining block timestamp ${remaining} :hourglass:`); + logger.info(`Remaining block timestamp ${remaining}`); // stop fast rebuild 7 days before the last network block return slots.getTime() - block.data.timestamp < 3600 * 24 * 7; diff --git a/packages/core-blockchain/src/processor/block-processor.ts b/packages/core-blockchain/src/processor/block-processor.ts index 29d51782db..3009be231d 100644 --- a/packages/core-blockchain/src/processor/block-processor.ts +++ b/packages/core-blockchain/src/processor/block-processor.ts @@ -71,7 +71,7 @@ export class BlockProcessor { this.logger.warn( `Block ${block.data.height.toLocaleString()} (${ block.data.id - }) disregarded because verification failed :scroll:`, + }) disregarded because verification failed`, ); this.logger.warn(JSON.stringify(block.verification, null, 4)); return false; @@ -90,7 +90,7 @@ export class BlockProcessor { ); if (forgedIds.length > 0) { this.logger.warn( - `Block ${block.data.height.toLocaleString()} disregarded, because it contains already forged transactions :scroll:`, + `Block ${block.data.height.toLocaleString()} disregarded, because it contains already forged transactions`, ); this.logger.debug(`${JSON.stringify(forgedIds, null, 4)}`); return true; diff --git a/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts b/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts index 91f9472a82..89f23694da 100644 --- a/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts +++ b/packages/core-blockchain/src/processor/handlers/accept-block-handler.ts @@ -11,7 +11,7 @@ export class AcceptBlockHandler extends BlockHandler { // Check if we recovered from a fork if (state.forkedBlock && state.forkedBlock.data.height === this.block.data.height) { - this.logger.info("Successfully recovered from fork :star2:"); + this.logger.info("Successfully recovered from fork"); state.forkedBlock = null; } diff --git a/packages/core-blockchain/src/processor/handlers/exception-handler.ts b/packages/core-blockchain/src/processor/handlers/exception-handler.ts index 52f05e8bec..50328146db 100644 --- a/packages/core-blockchain/src/processor/handlers/exception-handler.ts +++ b/packages/core-blockchain/src/processor/handlers/exception-handler.ts @@ -11,9 +11,7 @@ export class ExceptionHandler extends BlockHandler { return super.execute(); } - this.logger.warn( - `Block ${this.block.data.height.toLocaleString()} (${this.block.data.id}) forcibly accepted. :exclamation:`, - ); + this.logger.warn(`Block ${this.block.data.height.toLocaleString()} (${this.block.data.id}) forcibly accepted.`); return new AcceptBlockHandler(this.blockchain, this.block).execute(); } diff --git a/packages/core-blockchain/src/processor/handlers/unchained-handler.ts b/packages/core-blockchain/src/processor/handlers/unchained-handler.ts index f706288a43..8bfe994f7e 100644 --- a/packages/core-blockchain/src/processor/handlers/unchained-handler.ts +++ b/packages/core-blockchain/src/processor/handlers/unchained-handler.ts @@ -1,3 +1,5 @@ +// tslint:disable:max-classes-per-file + import { app } from "@arkecosystem/core-container"; import { models } from "@arkecosystem/crypto"; import { Blockchain } from "../../blockchain"; @@ -6,6 +8,7 @@ import { BlockHandler } from "./block-handler"; enum UnchainedBlockStatus { NotReadyToAcceptNewHeight, + ExceededNotReadyToAcceptNewHeightMaxAttempts, AlreadyInBlockchain, EqualToLastBlock, GeneratorMismatch, @@ -13,7 +16,40 @@ enum UnchainedBlockStatus { InvalidTimestamp, } +class BlockNotReadyCounter { + public static maxAttempts = 5; + + private id = ""; + private attempts = 0; + + public increment(block: models.Block): boolean { + const { id } = block.data; + let attemptsLeft = false; + + if (this.id !== id) { + this.reset(); + this.id = id; + } + + this.attempts += 1; + + attemptsLeft = this.attempts <= BlockNotReadyCounter.maxAttempts; + if (!attemptsLeft) { + this.reset(); + } + + return attemptsLeft; + } + + public reset() { + this.attempts = 0; + this.id = ""; + } +} + export class UnchainedHandler extends BlockHandler { + public static notReadyCounter = new BlockNotReadyCounter(); + public constructor( protected blockchain: Blockchain, protected block: models.Block, @@ -39,6 +75,11 @@ export class UnchainedHandler extends BlockHandler { return BlockProcessorResult.Rejected; } + case UnchainedBlockStatus.ExceededNotReadyToAcceptNewHeightMaxAttempts: { + this.blockchain.forkBlock(this.block, 5000); // TODO: find a better heuristic based on peer information + return BlockProcessorResult.DiscardedButCanBeBroadcasted; + } + case UnchainedBlockStatus.GeneratorMismatch: case UnchainedBlockStatus.InvalidTimestamp: { return BlockProcessorResult.Rejected; @@ -54,7 +95,7 @@ export class UnchainedHandler extends BlockHandler { const lastBlock = this.blockchain.getLastBlock(); if (this.block.data.height > lastBlock.data.height + 1) { this.logger.debug( - `Blockchain not ready to accept new block at height ${this.block.data.height.toLocaleString()}. Last block: ${lastBlock.data.height.toLocaleString()} :warning:`, + `Blockchain not ready to accept new block at height ${this.block.data.height.toLocaleString()}. Last block: ${lastBlock.data.height.toLocaleString()}`, ); // Also remove all remaining queued blocks. Since blocks are downloaded in batches, @@ -65,15 +106,27 @@ export class UnchainedHandler extends BlockHandler { this.logger.debug(`Discarded ${this.blockchain.processQueue.length()} downloaded blocks.`); } - return UnchainedBlockStatus.NotReadyToAcceptNewHeight; + // If we consecutively fail to accept the same block, our chain is likely forked. In this + // case `increment` returns false. + if (UnchainedHandler.notReadyCounter.increment(this.block)) { + return UnchainedBlockStatus.NotReadyToAcceptNewHeight; + } + + this.logger.debug( + `Blockchain is still not ready to accept block at height ${this.block.data.height.toLocaleString()} after ${ + BlockNotReadyCounter.maxAttempts + } tries. Going to rollback. :warning:`, + ); + + return UnchainedBlockStatus.ExceededNotReadyToAcceptNewHeightMaxAttempts; } else if (this.block.data.height < lastBlock.data.height) { this.logger.debug( - `Block ${this.block.data.height.toLocaleString()} disregarded because already in blockchain :warning:`, + `Block ${this.block.data.height.toLocaleString()} disregarded because already in blockchain`, ); return UnchainedBlockStatus.AlreadyInBlockchain; } else if (this.block.data.height === lastBlock.data.height && this.block.data.id === lastBlock.data.id) { - this.logger.debug(`Block ${this.block.data.height.toLocaleString()} just received :chains:`); + this.logger.debug(`Block ${this.block.data.height.toLocaleString()} just received`); return UnchainedBlockStatus.EqualToLastBlock; } else if (this.block.data.timestamp < lastBlock.data.timestamp) { this.logger.debug( @@ -82,14 +135,14 @@ export class UnchainedHandler extends BlockHandler { return UnchainedBlockStatus.InvalidTimestamp; } else { if (this.isValidGenerator) { - this.logger.warn(`Detect double forging by ${this.block.data.generatorPublicKey} :chains:`); + this.logger.warn(`Detect double forging by ${this.block.data.generatorPublicKey}`); return UnchainedBlockStatus.DoubleForging; } this.logger.info( `Forked block disregarded because it is not allowed to be forged. Caused by delegate: ${ this.block.data.generatorPublicKey - } :bangbang:`, + }`, ); return UnchainedBlockStatus.GeneratorMismatch; diff --git a/packages/core-blockchain/src/state-machine.ts b/packages/core-blockchain/src/state-machine.ts index 1ef7c2d599..77f68953d6 100644 --- a/packages/core-blockchain/src/state-machine.ts +++ b/packages/core-blockchain/src/state-machine.ts @@ -63,19 +63,17 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ // tried to download but no luck after 5 tries (looks like network missing blocks) if (stateStorage.noBlockCounter > 5 && blockchain.processQueue.length() === 0) { - // TODO: make this dynamic in 2.1 - logger.info( - "Tried to sync 5 times to different nodes, looks like the network is missing blocks :umbrella:", - ); + logger.info("Tried to sync 5 times to different nodes, looks like the network is missing blocks"); stateStorage.noBlockCounter = 0; event = "NETWORKHALTED"; if (stateStorage.p2pUpdateCounter + 1 > 3) { - logger.info("Network keeps missing blocks. :umbrella:"); + logger.info("Network keeps missing blocks."); - const result = await blockchain.p2p.updatePeersOnMissingBlocks(); - if (result === "rollback") { + const networkStatus = await blockchain.p2p.checkNetworkHealth(); + if (networkStatus.forked) { + stateStorage.numberOfBlocksToRollback = networkStatus.blocksToRollback; event = "FORK"; } @@ -104,7 +102,7 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ }, downloadFinished() { - logger.info("Block download finished :rocket:"); + logger.info("Block download finished"); if (stateStorage.networkStart) { // next time we will use normal behaviour @@ -118,7 +116,7 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ async rebuildFinished() { try { - logger.info("Blockchain rebuild finished :chains:"); + logger.info("Blockchain rebuild finished"); stateStorage.rebuild = false; @@ -135,24 +133,24 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ } }, - downloadPaused: () => logger.info("Blockchain download paused :clock1030:"), + downloadPaused: () => logger.info("Blockchain download paused"), syncingComplete() { - logger.info("Blockchain 100% in sync :100:"); + logger.info("Blockchain 100% in sync"); blockchain.dispatch("SYNCFINISHED"); }, rebuildingComplete() { - logger.info("Blockchain rebuild complete :unicorn_face:"); + logger.info("Blockchain rebuild complete"); blockchain.dispatch("REBUILDCOMPLETE"); }, stopped() { - logger.info("The blockchain has been stopped :guitar:"); + logger.info("The blockchain has been stopped"); }, exitApp() { - app.forceExit("Failed to startup blockchain. Exiting Ark Core! :rotating_light:"); + app.forceExit("Failed to startup blockchain. Exiting Ark Core!"); }, async init() { @@ -160,14 +158,12 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ let block = await blockchain.database.getLastBlock(); if (!block) { - logger.warn("No block found in database :hushed:"); + logger.warn("No block found in database"); block = new Block(config.get("genesisBlock")); if (block.data.payloadHash !== config.get("network.nethash")) { - logger.error( - "FATAL: The genesis block payload hash is different from configured the nethash :rotating_light:", - ); + logger.error("FATAL: The genesis block payload hash is different from configured the nethash"); return blockchain.dispatch("FAILURE"); } @@ -176,19 +172,19 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ } if (!blockchain.database.restoredDatabaseIntegrity) { - logger.info("Verifying database integrity :hourglass_flowing_sand:"); + logger.info("Verifying database integrity"); const blockchainAudit = await blockchain.database.verifyBlockchain(); if (!blockchainAudit.valid) { - logger.error("FATAL: The database is corrupted :fire:"); + logger.error("FATAL: The database is corrupted"); logger.error(JSON.stringify(blockchainAudit.errors, null, 4)); return blockchain.dispatch("ROLLBACK"); } - logger.info("Verified database integrity :smile_cat:"); + logger.info("Verified database integrity"); } else { - logger.info("Skipping database integrity check after successful database recovery :smile_cat:"); + logger.info("Skipping database integrity check after successful database recovery"); } // only genesis block? special case of first round needs to be dealt with @@ -219,7 +215,7 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ slots.getTime() - block.data.timestamp > 3600 * 24 * 7 && !!localConfig.get("fastRebuild"); if (process.env.NODE_ENV === "test") { - logger.verbose("TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY. :bangbang:"); + logger.verbose("TEST SUITE DETECTED! SYNCING WALLETS AND STARTING IMMEDIATELY."); stateStorage.setLastBlock(new Block(config.get("genesisBlock"))); await blockchain.database.buildWallets(block.data.height); @@ -248,7 +244,7 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ const verifiedWalletsIntegrity = await blockchain.database.buildWallets(block.data.height); if (!verifiedWalletsIntegrity && block.data.height > 1) { logger.warn( - "Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown. :hammer:", + "Rebuilding wallets table because of some inconsistencies. Most likely due to an unfortunate shutdown.", ); await blockchain.database.saveWallets(true); } @@ -362,20 +358,21 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ }, async analyseFork() { - logger.info("Analysing fork :mag:"); + logger.info("Analysing fork"); }, async startForkRecovery() { - logger.info("Starting fork recovery :fork_and_knife:"); + logger.info("Starting fork recovery"); blockchain.clearAndStopQueue(); await blockchain.database.commitQueuedQueries(); const random = 4 + Math.floor(Math.random() * 99); // random int inside [4, 102] range - await blockchain.removeBlocks(random); + await blockchain.removeBlocks(stateStorage.numberOfBlocksToRollback || random); + stateStorage.numberOfBlocksToRollback = null; - logger.info(`Removed ${pluralize("block", random, true)} :wastebasket:`); + logger.info(`Removed ${pluralize("block", random, true)}`); await blockchain.transactionPool.buildWallets(); await blockchain.p2p.refreshPeersAfterFork(); @@ -384,7 +381,7 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ }, async rollbackDatabase() { - logger.info("Trying to restore database integrity :fire_engine:"); + logger.info("Trying to restore database integrity"); const { maxBlockRewind, steps } = localConfig.get("databaseRollback"); let blockchainAudit; @@ -400,7 +397,7 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ if (!blockchainAudit.valid) { // TODO: multiple attempts? rewind further? restore snapshot? - logger.error("FATAL: Failed to restore database integrity :skull: :skull: :skull:"); + logger.error("FATAL: Failed to restore database integrity"); logger.error(JSON.stringify(blockchainAudit.errors, null, 4)); blockchain.dispatch("FAILURE"); return; @@ -410,12 +407,11 @@ blockchainMachine.actionMap = (blockchain: Blockchain) => ({ const lastBlock = await blockchain.database.getLastBlock(); logger.info( - `Database integrity verified again after rollback to height ${lastBlock.data.height.toLocaleString()} :green_heart:`, + `Database integrity verified again after rollback to height ${lastBlock.data.height.toLocaleString()}`, ); blockchain.dispatch("SUCCESS"); }, }); -const stateMachine = blockchainMachine; -export { stateMachine }; +export const stateMachine = blockchainMachine; diff --git a/packages/core-blockchain/src/state-storage.ts b/packages/core-blockchain/src/state-storage.ts index 867ac7bdfe..d218c511f4 100644 --- a/packages/core-blockchain/src/state-storage.ts +++ b/packages/core-blockchain/src/state-storage.ts @@ -36,6 +36,7 @@ export class StateStorage implements Blockchain.IStateStorage { public wakeUpTimeout: any; public noBlockCounter: number; public p2pUpdateCounter: number; + public numberOfBlocksToRollback: number | null; public networkStart: boolean; constructor() { @@ -57,6 +58,7 @@ export class StateStorage implements Blockchain.IStateStorage { this.noBlockCounter = 0; this.p2pUpdateCounter = 0; this.networkStart = false; + this.numberOfBlocksToRollback = null; this.clear(); } diff --git a/packages/core-blockchain/src/utils/validate-generator.ts b/packages/core-blockchain/src/utils/validate-generator.ts index f517b58948..34f3c5f799 100644 --- a/packages/core-blockchain/src/utils/validate-generator.ts +++ b/packages/core-blockchain/src/utils/validate-generator.ts @@ -16,7 +16,7 @@ export const validateGenerator = async (block: models.Block): Promise = logger.debug( `Could not decide if delegate ${generatorUsername} (${ block.data.generatorPublicKey - }) is allowed to forge block ${block.data.height.toLocaleString()} :grey_question:`, + }) is allowed to forge block ${block.data.height.toLocaleString()}`, ); } else if (forgingDelegate.publicKey !== block.data.generatorPublicKey) { const forgingUsername = database.walletManager.findByPublicKey(forgingDelegate.publicKey).username; @@ -24,7 +24,7 @@ export const validateGenerator = async (block: models.Block): Promise = logger.warn( `Delegate ${generatorUsername} (${ block.data.generatorPublicKey - }) not allowed to forge, should be ${forgingUsername} (${forgingDelegate.publicKey}) :-1:`, + }) not allowed to forge, should be ${forgingUsername} (${forgingDelegate.publicKey})`, ); return false; @@ -33,7 +33,7 @@ export const validateGenerator = async (block: models.Block): Promise = logger.debug( `Delegate ${generatorUsername} (${ block.data.generatorPublicKey - }) allowed to forge block ${block.data.height.toLocaleString()} :+1:`, + }) allowed to forge block ${block.data.height.toLocaleString()}`, ); return true; diff --git a/packages/core-container/__tests__/__stubs__/config/milestones.json b/packages/core-container/__tests__/__stubs__/config/milestones.json index 51c32313cf..740754fc8d 100644 --- a/packages/core-container/__tests__/__stubs__/config/milestones.json +++ b/packages/core-container/__tests__/__stubs__/config/milestones.json @@ -22,10 +22,15 @@ "multiPayment": 0, "delegateResignation": 0 } - } + }, + "vendorFieldLength": 64 }, { "height": 75600, "reward": 200000000 + }, + { + "height": 100000, + "vendorFieldLength": 255 } ] diff --git a/packages/core-container/package.json b/packages/core-container/package.json index ac80f12ef1..efe5f69dae 100644 --- a/packages/core-container/package.json +++ b/packages/core-container/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-container", "description": "Container for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -32,8 +32,8 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/fs-extra": "^5.0.5", "@types/hoek": "^4.1.3", "@types/joi": "^14.3.1", diff --git a/packages/core-container/src/config/loaders/file-loader.ts b/packages/core-container/src/config/loaders/file-loader.ts index 5e52776e5b..53bb2794d6 100644 --- a/packages/core-container/src/config/loaders/file-loader.ts +++ b/packages/core-container/src/config/loaders/file-loader.ts @@ -53,6 +53,14 @@ class FileLoader { throw new Error("An invalid configuration was provided or is inaccessible due to it's security settings."); } + for (const file of ["peers.json", "plugins.js"]) { + const fullPath = `${basePath}/${file}`; + + if (!existsSync(fullPath)) { + throw new Error(`The ${fullPath} file could not be found.`); + } + } + const configTree = {}; for (const file of readdirSync(basePath)) { if ([".js", ".json"].includes(extname(file))) { diff --git a/packages/core-container/src/container.ts b/packages/core-container/src/container.ts index 058ec4bbe0..608ec339d4 100644 --- a/packages/core-container/src/container.ts +++ b/packages/core-container/src/container.ts @@ -247,7 +247,7 @@ export class Container implements container.IContainer { const logger = this.resolvePlugin("logger"); if (logger) { logger.suppressConsoleOutput(this.silentShutdown); - logger.info("Core is trying to gracefully shut down to avoid data corruption :pizza:"); + logger.info("Core is trying to gracefully shut down to avoid data corruption"); } try { diff --git a/packages/core-database-postgres/package.json b/packages/core-database-postgres/package.json index b7b7e8b840..f51294475e 100644 --- a/packages/core-database-postgres/package.json +++ b/packages/core-database-postgres/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-database-postgres", "description": "PostgreSQL integration for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -33,11 +33,11 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-database": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-utils": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-database": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-utils": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/bluebird": "^3.5.25", "@types/lodash.chunk": "^4.2.4", "@types/pluralize": "^0.0.29", diff --git a/packages/core-database-postgres/src/models/block.ts b/packages/core-database-postgres/src/models/block.ts index 9e821aa0b6..866de36f18 100644 --- a/packages/core-database-postgres/src/models/block.ts +++ b/packages/core-database-postgres/src/models/block.ts @@ -3,76 +3,74 @@ import { bignumify } from "@arkecosystem/core-utils"; import { Model } from "./model"; export class Block extends Model { - constructor(pgp) { - super(pgp); this.columnsDescriptor = [ { name: "id", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "version", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "timestamp", - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "previous_block", prop: "previousBlock", def: null, - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "height", - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "number_of_transactions", prop: "numberOfTransactions", - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "total_amount", prop: "totalAmount", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "total_fee", prop: "totalFee", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "reward", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "payload_length", prop: "payloadLength", - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE], }, { name: "payload_hash", prop: "payloadHash", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "generator_public_key", prop: "generatorPublicKey", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "block_signature", prop: "blockSignature", - supportedOperators: [ Database.SearchOperator.OP_EQ ] - } + supportedOperators: [Database.SearchOperator.OP_EQ], + }, ]; } @@ -83,5 +81,4 @@ export class Block extends Model { public getTable() { return "blocks"; } - } diff --git a/packages/core-database-postgres/src/models/migration.ts b/packages/core-database-postgres/src/models/migration.ts index b5092c05b9..56b56c717b 100644 --- a/packages/core-database-postgres/src/models/migration.ts +++ b/packages/core-database-postgres/src/models/migration.ts @@ -1,14 +1,13 @@ import { Model } from "./model"; export class Migration extends Model { - constructor(pgp) { super(pgp); this.columnsDescriptor = [ { - name: "name" - } + name: "name", + }, ]; } diff --git a/packages/core-database-postgres/src/models/model.ts b/packages/core-database-postgres/src/models/model.ts index f3b732ef0f..f74f862902 100644 --- a/packages/core-database-postgres/src/models/model.ts +++ b/packages/core-database-postgres/src/models/model.ts @@ -2,15 +2,14 @@ import { Database } from "@arkecosystem/core-interfaces"; import sql from "sql"; interface ColumnDescriptor { - name: string, - supportedOperators?: Database.SearchOperator[], - prop?: string, - init?: any, - def?: any + name: string; + supportedOperators?: Database.SearchOperator[]; + prop?: string; + init?: any; + def?: any; } export abstract class Model implements Database.IDatabaseModel { - protected columnsDescriptor: ColumnDescriptor[]; protected columnSet: any; @@ -18,8 +17,7 @@ export abstract class Model implements Database.IDatabaseModel { * Create a new model instance. * @param {Object} pgp */ - protected constructor(public pgp) { - } + protected constructor(public pgp) {} /** * Get table name for model. @@ -33,17 +31,19 @@ export abstract class Model implements Database.IDatabaseModel { */ public getColumnSet() { if (!this.columnSet) { - this.columnSet = this.createColumnSet(this.columnsDescriptor.map(col => { - const colDef: any = { - name: col.name - }; - ["prop", "init", "def"].forEach(prop => { - if (col.hasOwnProperty(prop)) { - colDef[prop] = col[prop]; - } - }); - return colDef; - })); + this.columnSet = this.createColumnSet( + this.columnsDescriptor.map(col => { + const colDef: any = { + name: col.name, + }; + ["prop", "init", "def"].forEach(prop => { + if (col.hasOwnProperty(prop)) { + colDef[prop] = col[prop]; + } + }); + return colDef; + }), + ); } return this.columnSet; } @@ -52,8 +52,8 @@ export abstract class Model implements Database.IDatabaseModel { return this.columnsDescriptor.map(col => { return { fieldName: col.prop || col.name, - supportedOperators: col.supportedOperators - } + supportedOperators: col.supportedOperators, + }; }); } @@ -77,7 +77,6 @@ export abstract class Model implements Database.IDatabaseModel { }); } - /** * Convert the "camelCase" keys to "snake_case". * @return {ColumnSet} diff --git a/packages/core-database-postgres/src/models/round.ts b/packages/core-database-postgres/src/models/round.ts index 30b64eb3ac..20cf894493 100644 --- a/packages/core-database-postgres/src/models/round.ts +++ b/packages/core-database-postgres/src/models/round.ts @@ -9,19 +9,27 @@ export class Round extends Model { { name: "public_key", prop: "publicKey", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "balance", prop: "voteBalance", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_LTE, + Database.SearchOperator.OP_GTE, + ], }, { name: "round", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_LTE, + Database.SearchOperator.OP_GTE, + ], }, - ] + ]; } /** diff --git a/packages/core-database-postgres/src/models/transaction.ts b/packages/core-database-postgres/src/models/transaction.ts index 95ff716577..0224e84d14 100644 --- a/packages/core-database-postgres/src/models/transaction.ts +++ b/packages/core-database-postgres/src/models/transaction.ts @@ -3,65 +3,76 @@ import { bignumify } from "@arkecosystem/core-utils"; import { Model } from "./model"; export class Transaction extends Model { - constructor(pgp) { super(pgp); this.columnsDescriptor = [ { name: "id", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "version", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "block_id", prop: "blockId", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "sequence", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "timestamp", - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_EQ ] + supportedOperators: [ + Database.SearchOperator.OP_LTE, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_EQ, + ], }, { name: "sender_public_key", prop: "senderPublicKey", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "recipient_id", prop: "recipientId", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "type", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "vendor_field_hex", prop: "vendorFieldHex", - supportedOperators: [ Database.SearchOperator.OP_LIKE ] + supportedOperators: [Database.SearchOperator.OP_LIKE], }, { name: "amount", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_EQ ] + supportedOperators: [ + Database.SearchOperator.OP_LTE, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_EQ, + ], }, { name: "fee", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_LTE, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_EQ ] + supportedOperators: [ + Database.SearchOperator.OP_LTE, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_EQ, + ], }, { name: "serialized", init: col => col.value, - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, ]; } diff --git a/packages/core-database-postgres/src/models/wallet.ts b/packages/core-database-postgres/src/models/wallet.ts index 26075036e9..18e3c0b33d 100644 --- a/packages/core-database-postgres/src/models/wallet.ts +++ b/packages/core-database-postgres/src/models/wallet.ts @@ -3,55 +3,70 @@ import { bignumify } from "@arkecosystem/core-utils"; import { Model } from "./model"; export class Wallet extends Model { - constructor(pgp) { super(pgp); this.columnsDescriptor = [ { name: "address", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "public_key", prop: "publicKey", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "second_public_key", prop: "secondPublicKey", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN ] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_IN], }, { name: "vote", - supportedOperators: [ Database.SearchOperator.OP_EQ ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { name: "username", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_LIKE] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_LIKE], }, { name: "balance", init: col => bignumify(col.value).toFixed(), - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_LTE, + ], }, { name: "vote_balance", prop: "voteBalance", init: col => (col.value ? bignumify(col.value).toFixed() : null), - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_LTE, + ], }, { name: "produced_blocks", prop: "producedBlocks", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_LTE, + ], }, { name: "missed_blocks", prop: "missedBlocks", - supportedOperators: [ Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE ] - } - ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_LTE, + ], + }, + ]; } /** diff --git a/packages/core-database-postgres/src/repositories/blocks.ts b/packages/core-database-postgres/src/repositories/blocks.ts index 8ec5f2fc80..8e1111a393 100644 --- a/packages/core-database-postgres/src/repositories/blocks.ts +++ b/packages/core-database-postgres/src/repositories/blocks.ts @@ -16,7 +16,11 @@ export class BlocksRepository extends Repository implements Database.IBlocksRepo } public async findByIds(ids: string[]) { - const query = this.query.select().from(this.query).where(this.query.id.in(ids)).group(this.query.id); + const query = this.query + .select() + .from(this.query) + .where(this.query.id.in(ids)) + .group(this.query.id); return await this.findMany(query); } @@ -135,7 +139,6 @@ export class BlocksRepository extends Repository implements Database.IBlocksRepo return await this.findManyWithCount(selectQuery, params.paginate, params.orderBy); } - public async search(params: Database.SearchParameters) { // TODO: we're selecting all the columns right now. Add support for choosing specific columns, when it proves useful. const selectQuery = this.query.select().from(this.query); diff --git a/packages/core-database-postgres/src/repositories/repository.ts b/packages/core-database-postgres/src/repositories/repository.ts index 447ede1761..9580d55afc 100644 --- a/packages/core-database-postgres/src/repositories/repository.ts +++ b/packages/core-database-postgres/src/repositories/repository.ts @@ -74,7 +74,7 @@ export abstract class Repository implements Database.IRepository { } protected propToColumnName(prop: string): string { - if(prop) { + if (prop) { const columnSet = this.model.getColumnSet(); const columnDef = columnSet.columns.find(col => col.prop === prop || col.name === prop); return columnDef ? columnDef.name : null; @@ -90,8 +90,11 @@ export abstract class Repository implements Database.IRepository { return this.db.manyOrNone(query.toQuery()); } - protected async findManyWithCount(selectQuery, paginate?: Database.SearchPaginate, orderBy?: Database.SearchOrderBy[]): Promise { - + protected async findManyWithCount( + selectQuery, + paginate?: Database.SearchPaginate, + orderBy?: Database.SearchOrderBy[], + ): Promise { if (!!orderBy) { orderBy.forEach(o => selectQuery.order(this.query[o.field][o.direction])); } diff --git a/packages/core-database-postgres/src/repositories/transactions.ts b/packages/core-database-postgres/src/repositories/transactions.ts index 54d237b2b9..6fdf3ee24b 100644 --- a/packages/core-database-postgres/src/repositories/transactions.ts +++ b/packages/core-database-postgres/src/repositories/transactions.ts @@ -1,6 +1,6 @@ import { Database } from "@arkecosystem/core-interfaces"; import { slots } from "@arkecosystem/crypto"; -import { models } from "@arkecosystem/crypto" +import { models } from "@arkecosystem/crypto"; import dayjs from "dayjs-ext"; import partition from "lodash/partition"; import { Transaction } from "../models"; @@ -92,8 +92,13 @@ export class TransactionsRepository extends Repository implements Database.ITran .from(this.query) // Should make this '30' figure configurable .where( - this.query.timestamp.gte(slots.getTime(dayjs().subtract(30, "day").valueOf()) - ) + this.query.timestamp.gte( + slots.getTime( + dayjs() + .subtract(30, "day") + .valueOf(), + ), + ), ) .and(this.query.fee.gte(minFeeBroadcast)) .group(this.query.type) @@ -103,7 +108,9 @@ export class TransactionsRepository extends Repository implements Database.ITran } public async findAllByWallet(wallet: any, paginate?: Database.SearchPaginate, orderBy?: Database.SearchOrderBy[]) { - const selectQuery = this.query.select().from(this.query) + const selectQuery = this.query + .select() + .from(this.query) .where(this.query.sender_public_key.equals(wallet.publicKey)) .or(this.query.recipient_id.equals(wallet.address)); @@ -111,7 +118,9 @@ export class TransactionsRepository extends Repository implements Database.ITran } public async findWithVendorField() { - const selectQuery = this.query.select().from(this.query) + const selectQuery = this.query + .select() + .from(this.query) .where(this.query.vendor_field_hex.isNotNull()); return this.findMany(selectQuery); @@ -122,13 +131,16 @@ export class TransactionsRepository extends Repository implements Database.ITran if (!parameters.paginate) { parameters.paginate = { limit: 100, - offset: 0 - } + offset: 0, + }; } const selectQuery = this.query.select().from(this.query); const params = parameters.parameters; if (params.length) { - const [customOps, otherOps] = partition(params, param => param.operator === Database.SearchOperator.OP_CUSTOM); + const [customOps, otherOps] = partition( + params, + param => param.operator === Database.SearchOperator.OP_CUSTOM, + ); const hasNonCustomOps = otherOps.length > 0; @@ -144,14 +156,16 @@ export class TransactionsRepository extends Repository implements Database.ITran if (o.field === "ownerWallet") { const wallet = o.value as models.Wallet; if (hasNonCustomOps) { - selectQuery - .and(this.query.sender_public_key.equals(wallet.publicKey).or(this.query.recipient_id.equals(wallet.address))); + selectQuery.and( + this.query.sender_public_key + .equals(wallet.publicKey) + .or(this.query.recipient_id.equals(wallet.address)), + ); } else { selectQuery .where(this.query.sender_public_key.equals(wallet.publicKey)) .or(this.query.recipient_id.equals(wallet.address)); } - } }); } @@ -162,8 +176,8 @@ export class TransactionsRepository extends Repository implements Database.ITran if (!parameters.paginate) { parameters.paginate = { limit: 100, - offset: 0 - } + offset: 0, + }; } const selectQuery = this.query.select().from(this.query); const params = parameters.parameters; @@ -171,14 +185,18 @@ export class TransactionsRepository extends Repository implements Database.ITran // 'search' doesn't support custom-op 'ownerId' like 'findAll' can const ops = params.filter(value => value.operator !== Database.SearchOperator.OP_CUSTOM); - const [participants, rest] = partition(ops, op => ["sender_public_key", "recipient_id"].includes(this.propToColumnName(op.field))); + const [participants, rest] = partition(ops, op => + ["sender_public_key", "recipient_id"].includes(this.propToColumnName(op.field)), + ); if (participants.length > 0) { const [first, last] = participants; selectQuery.where(this.query[this.propToColumnName(first.field)][first.operator](first.value)); if (last) { - const usesInOperator = participants.every(condition => condition.operator === Database.SearchOperator.OP_IN); + const usesInOperator = participants.every( + condition => condition.operator === Database.SearchOperator.OP_IN, + ); if (usesInOperator) { selectQuery.or(this.query[this.propToColumnName(last.field)][last.operator](last.value)); } else { @@ -192,7 +210,9 @@ export class TransactionsRepository extends Repository implements Database.ITran } for (const condition of rest) { - selectQuery.and(this.query[this.propToColumnName(condition.field)][condition.operator](condition.value)); + selectQuery.and( + this.query[this.propToColumnName(condition.field)][condition.operator](condition.value), + ); } } return this.findManyWithCount(selectQuery, parameters.paginate, parameters.orderBy); diff --git a/packages/core-database/__tests__/__fixtures__/mock-database-model.ts b/packages/core-database/__tests__/__fixtures__/mock-database-model.ts index 664e03e850..8560f514d6 100644 --- a/packages/core-database/__tests__/__fixtures__/mock-database-model.ts +++ b/packages/core-database/__tests__/__fixtures__/mock-database-model.ts @@ -9,25 +9,28 @@ export class MockDatabaseModel implements Database.IDatabaseModel { return [ { fieldName: "id", - supportedOperators: [Database.SearchOperator.OP_EQ] + supportedOperators: [Database.SearchOperator.OP_EQ], }, { fieldName: "timestamp", - supportedOperators: [Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE] + supportedOperators: [Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE], }, { fieldName: "sentence", - supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_LIKE] + supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_LIKE], }, { fieldName: "basket", - supportedOperators: [Database.SearchOperator.OP_IN] + supportedOperators: [Database.SearchOperator.OP_IN], }, { fieldName: "range", - supportedOperators: [Database.SearchOperator.OP_EQ, Database.SearchOperator.OP_GTE, Database.SearchOperator.OP_LTE] - } - ] + supportedOperators: [ + Database.SearchOperator.OP_EQ, + Database.SearchOperator.OP_GTE, + Database.SearchOperator.OP_LTE, + ], + }, + ]; } - } diff --git a/packages/core-database/__tests__/repositories/blocks-business-repository.test.ts b/packages/core-database/__tests__/repositories/blocks-business-repository.test.ts index 1ae64ec4c9..0234bf5968 100644 --- a/packages/core-database/__tests__/repositories/blocks-business-repository.test.ts +++ b/packages/core-database/__tests__/repositories/blocks-business-repository.test.ts @@ -9,45 +9,40 @@ describe("Blocks Business Repository", () => { beforeEach(() => { blocksBusinessRepository = new BlocksBusinessRepository(() => databaseService); databaseService = { - connection: new DatabaseConnectionStub() + connection: new DatabaseConnectionStub(), } as Database.IDatabaseService; }); describe("findAll", () => { it("should invoke findAll on db repository", async () => { - databaseService.connection.blocksRepository = { findAll: async params => params, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findAll").mockImplementation(async () => true); - await blocksBusinessRepository.findAll({ limit: 50, - offset: 20 + offset: 20, }); - expect(databaseService.connection.blocksRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ - orderBy: [{ field: "height", direction: "desc" }] - })); + orderBy: [{ field: "height", direction: "desc" }], + }), + ); }); }); describe("findById", () => { it("should invoke findById on db repository", async () => { - databaseService.connection.blocksRepository = { - findById : async id => id + findById: async id => id, } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findById").mockImplementation(async () => true); - await blocksBusinessRepository.findById("some id"); - expect(databaseService.connection.blocksRepository.findById).toHaveBeenCalledWith("some id"); }); }); @@ -56,105 +51,103 @@ describe("Blocks Business Repository", () => { it("should invoke search on db repository", async () => { databaseService.connection.blocksRepository = { search: async params => params, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "search").mockImplementation(async () => true); - await blocksBusinessRepository.search({ limit: 50, offset: 20, - id: 20 + id: 20, }); - expect(databaseService.connection.blocksRepository.search).toHaveBeenCalledWith( expect.objectContaining({ - parameters: [{ - field: "id", - operator: expect.anything(), - value: 20 - }], - orderBy: [{ field: "height", direction: "desc" }] - })); + parameters: [ + { + field: "id", + operator: expect.anything(), + value: 20, + }, + ], + orderBy: [{ field: "height", direction: "desc" }], + }), + ); }); }); describe("findAllByGenerator", () => { it("should search by generatorPublicKey", async () => { - databaseService.connection.blocksRepository = { findAll: async params => params, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findAll").mockImplementation(async () => true); - - await blocksBusinessRepository.findAllByGenerator("pubKey", {limit: 50, offset: 13}); - + await blocksBusinessRepository.findAllByGenerator("pubKey", { limit: 50, offset: 13 }); expect(databaseService.connection.blocksRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ - parameters: [{ - field: "generatorPublicKey", - operator: expect.anything(), - value: "pubKey" - }], + parameters: [ + { + field: "generatorPublicKey", + operator: expect.anything(), + value: "pubKey", + }, + ], paginate: { offset: 13, - limit: 50 - } - }) + limit: 50, + }, + }), ); - }); }); - describe('findLastByPublicKey', () => { - it('should search by publicKey', async () => { - + describe("findLastByPublicKey", () => { + it("should search by publicKey", async () => { databaseService.connection.blocksRepository = { findAll: async params => params, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findAll").mockImplementation(async () => true); - await blocksBusinessRepository.findLastByPublicKey("pubKey"); - expect(databaseService.connection.blocksRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ - parameters: [{ - field: "generatorPublicKey", - operator: expect.anything(), - value: "pubKey" - }] - }) + parameters: [ + { + field: "generatorPublicKey", + operator: expect.anything(), + value: "pubKey", + }, + ], + }), ); }); }); - describe('findByHeight', () => { - it('should search by height', async () => { + describe("findByHeight", () => { + it("should search by height", async () => { databaseService.connection.blocksRepository = { findAll: async params => params, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findAll").mockImplementation(async () => true); - await blocksBusinessRepository.findByHeight(1); - expect(databaseService.connection.blocksRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ - parameters: [{ - field: "height", - operator: expect.anything(), - value: 1 - }] - }) + parameters: [ + { + field: "height", + operator: expect.anything(), + value: 1, + }, + ], + }), ); }); }); diff --git a/packages/core-database/__tests__/repositories/delegates-business-repository.test.ts b/packages/core-database/__tests__/repositories/delegates-business-repository.test.ts index fb4dc410fd..abc331cd2d 100644 --- a/packages/core-database/__tests__/repositories/delegates-business-repository.test.ts +++ b/packages/core-database/__tests__/repositories/delegates-business-repository.test.ts @@ -212,6 +212,114 @@ describe("Delegate Repository", () => { }); }); + describe("by `usernames`", () => { + it("should search by exact match", () => { + const usernames = [ + "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + "username-AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "username-AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + ]; + const { count, rows } = repository.search({ usernames }); + + expect(count).toBe(3); + expect(rows).toHaveLength(3); + + rows.forEach(row => { + expect(usernames.includes(row.username)).toBeTrue(); + }); + }); + + describe('when a username is "undefined"', () => { + it("should return it", () => { + // Index a wallet with username "undefined" + walletManager.allByAddress()[0].username = "undefined"; + + const usernames = ["undefined"]; + const { count, rows } = repository.search({ usernames }); + + expect(count).toBe(1); + expect(rows).toHaveLength(1); + expect(rows[0].username).toEqual(usernames[0]); + }); + }); + + describe("when the username does not exist", () => { + it("should return no results", () => { + const { count, rows } = repository.search({ + usernames: ["unknown-dummy-username"], + }); + + expect(count).toBe(0); + expect(rows).toHaveLength(0); + }); + }); + + it("should be ok with params", () => { + const { count, rows } = repository.search({ + usernames: [ + "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + "username-AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + ], + offset: 1, + limit: 10, + }); + expect(count).toBe(2); + expect(rows).toHaveLength(1); + }); + + it("should be ok with params (no offset)", () => { + const { count, rows } = repository.search({ + usernames: [ + "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + "username-AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + ], + limit: 1, + }); + expect(count).toBe(2); + expect(rows).toHaveLength(1); + }); + + it("should be ok with params (offset = 0)", () => { + const { count, rows } = repository.search({ + usernames: [ + "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + "username-AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + ], + offset: 0, + limit: 2, + }); + expect(count).toBe(2); + expect(rows).toHaveLength(2); + }); + + it("should be ok with params (no limit)", () => { + const { count, rows } = repository.search({ + usernames: [ + "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn", + "username-AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + ], + offset: 1, + }); + expect(count).toBe(2); + expect(rows).toHaveLength(1); + }); + }); + + describe("when searching by `username` and `usernames`", () => { + it("should search delegates only by `username`", () => { + const username = "username-APnhwwyTbMiykJwYbGhYjNgtHiVJDSEhSn"; + const usernames = [ + "username-AG8kwwk4TsYfA2HdwaWBVAJQBj6VhdcpMo", + "username-AHXtmB84sTZ9Zd35h9Y1vfFvPE2Xzqj8ri", + ]; + + const { count, rows } = repository.search({ username, usernames }); + + expect(count).toBe(1); + expect(rows).toHaveLength(1); + }); + }); + describe("when searching without params", () => { it("should return all results", () => { const { count, rows } = repository.search({}); diff --git a/packages/core-database/__tests__/repositories/transactions-business-repository.test.ts b/packages/core-database/__tests__/repositories/transactions-business-repository.test.ts index eeecfff308..53c0a9cb71 100644 --- a/packages/core-database/__tests__/repositories/transactions-business-repository.test.ts +++ b/packages/core-database/__tests__/repositories/transactions-business-repository.test.ts @@ -5,33 +5,35 @@ import { TransactionsBusinessRepository } from "../../src/repositories/transacti import { DatabaseConnectionStub } from "../__fixtures__/database-connection-stub"; import { MockDatabaseModel } from "../__fixtures__/mock-database-model"; -describe('Transactions Business Repository', () => { +describe("Transactions Business Repository", () => { let transactionsBusinessRepository: Database.ITransactionsBusinessRepository; let databaseService: Database.IDatabaseService; beforeEach(() => { transactionsBusinessRepository = new TransactionsBusinessRepository(() => databaseService); databaseService = { - connection: new DatabaseConnectionStub() + connection: new DatabaseConnectionStub(), } as Database.IDatabaseService; }); - describe('findAll', () => { - - it('should invoke findAll on db repository', async () => { + describe("findAll", () => { + it("should invoke findAll on db repository", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - - - await transactionsBusinessRepository.findAll({ - id: "id", - offset: 10, - limit: 1000, - orderBy: "id:asc" - }, "asc"); + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); + await transactionsBusinessRepository.findAll( + { + id: "id", + offset: 10, + limit: 1000, + orderBy: "id:asc", + }, + "asc", + ); expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ @@ -39,566 +41,513 @@ describe('Transactions Business Repository', () => { { field: "id", operator: expect.anything(), - value: "id" - } + value: "id", + }, ], paginate: { offset: 10, - limit: 1000 + limit: 1000, }, orderBy: expect.arrayContaining([ { field: "id", - direction: "asc" + direction: "asc", }, { field: "sequence", - direction: "asc" - } - ]) - }) - ) + direction: "asc", + }, + ]), + }), + ); }); - - }); - describe('allVotesBySender', async () => { - - it('should search by senderPublicKey and type=vote', async () => { - + describe("allVotesBySender", async () => { + it("should search by senderPublicKey and type=vote", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.allVotesBySender("pubKey"); - expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ parameters: expect.arrayContaining([ { field: "senderPublicKey", operator: expect.anything(), - value: "pubKey" + value: "pubKey", }, { field: "type", operator: expect.anything(), - value: constants.TransactionTypes.Vote - } - ]) - }) + value: constants.TransactionTypes.Vote, + }, + ]), + }), ); }); }); - describe('findAllByBlock', () => { - - it('should search by blockId', async () => { - + describe("findAllByBlock", () => { + it("should search by blockId", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.findAllByBlock("blockId"); - expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "blockId", operator: expect.anything(), - value: "blockId" - } + value: "blockId", + }, ], orderBy: [ { field: "sequence", - direction: "asc" - } - ] - }) - ) + direction: "asc", + }, + ], + }), + ); }); }); - describe('findAllByRecipient', () => { - - it('should search by recipientId', async () => { - + describe("findAllByRecipient", () => { + it("should search by recipientId", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.findAllByRecipient("recipientId"); - expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "recipientId", operator: expect.anything(), - value: "recipientId" - } - ] - }) - ) + value: "recipientId", + }, + ], + }), + ); }); }); - describe('findAllBySender', () => { - - it('should search senderPublicKey', async () => { - + describe("findAllBySender", () => { + it("should search senderPublicKey", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.findAllBySender("pubKey"); - expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "senderPublicKey", operator: expect.anything(), - value: "pubKey" - } - ] - }) - ) + value: "pubKey", + }, + ], + }), + ); }); }); - - describe('findAllByType', () => { - - it('should search transaction type', async () => { - + describe("findAllByType", () => { + it("should search transaction type", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.findAllByType(constants.TransactionTypes.Transfer); - expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "type", operator: expect.anything(), - value: constants.TransactionTypes.Transfer - } - ] - }) - ) + value: constants.TransactionTypes.Transfer, + }, + ], + }), + ); }); }); - describe('findAllByWallet', () => { - - it('should search by wallet', async () => { - + describe("findAllByWallet", () => { + it("should search by wallet", async () => { databaseService.connection.transactionsRepository = { findAllByWallet: async wallet => wallet, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAllByWallet").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAllByWallet").mockImplementation( + async () => ({ rows: [] }), + ); await transactionsBusinessRepository.findAllByWallet({}); - expect(databaseService.connection.transactionsRepository.findAllByWallet).toHaveBeenCalled(); }); }); - describe('findById', () => { - - it('should invoke findById on db repository', async () => { - + describe("findById", () => { + it("should invoke findById on db repository", async () => { databaseService.connection.transactionsRepository = { findById: async id => id, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findById").mockImplementation(async () => true); - + jest.spyOn(databaseService.connection.transactionsRepository, "findById").mockImplementation( + async () => true, + ); await transactionsBusinessRepository.findById("id"); - expect(databaseService.connection.transactionsRepository.findById).toHaveBeenCalledWith("id"); }); }); - describe('findByTypeAndId', () => { - - it('should search type & id', async () => { - + describe("findByTypeAndId", () => { + it("should search type & id", async () => { databaseService.connection.transactionsRepository = { findAll: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "findAll").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.findByTypeAndId(constants.TransactionTypes.Transfer, "id"); - expect(databaseService.connection.transactionsRepository.findAll).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "type", operator: expect.anything(), - value: constants.TransactionTypes.Transfer + value: constants.TransactionTypes.Transfer, }, { field: "id", operator: expect.anything(), - value: "id" - } - ] - }) - ) + value: "id", + }, + ], + }), + ); }); }); - describe('findWithVendorField', () => { - - it('should invoke findWithVendorField on db repository', async () => { - + describe("findWithVendorField", () => { + it("should invoke findWithVendorField on db repository", async () => { databaseService.connection.transactionsRepository = { findWithVendorField: async () => true, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "findWithVendorField").mockImplementation(async () => []); - + jest.spyOn(databaseService.connection.transactionsRepository, "findWithVendorField").mockImplementation( + async () => [], + ); await transactionsBusinessRepository.findWithVendorField(); - expect(databaseService.connection.transactionsRepository.findWithVendorField).toHaveBeenCalled(); }); }); - describe('getFeeStatistics', () => { - - it('should invoke getFeeStatistics on db repository', async () => { - + describe("getFeeStatistics", () => { + it("should invoke getFeeStatistics on db repository", async () => { databaseService.connection.transactionsRepository = { getFeeStatistics: async minFeeBroadcast => minFeeBroadcast, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "getFeeStatistics").mockImplementation(async () => true); + jest.spyOn(databaseService.connection.transactionsRepository, "getFeeStatistics").mockImplementation( + async () => true, + ); jest.spyOn(app, "resolveOptions").mockReturnValue({ dynamicFees: { - minFeeBroadcast: 100 - } + minFeeBroadcast: 100, + }, }); - await transactionsBusinessRepository.getFeeStatistics(); - expect(databaseService.connection.transactionsRepository.getFeeStatistics).toHaveBeenCalledWith(100); }); }); - describe('search', () => { - - it('should invoke search on db repository', async () => { - + describe("search", () => { + it("should invoke search on db repository", async () => { databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.search({}); - expect(databaseService.connection.transactionsRepository.search).toHaveBeenCalled(); }); - it('should return no rows if exception thrown', async () => { - - + it("should return no rows if exception thrown", async () => { databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => { throw new Error("bollocks"); }); - const result = await transactionsBusinessRepository.search({}); - expect(databaseService.connection.transactionsRepository.search).toHaveBeenCalled(); expect(result.rows).toHaveLength(0); expect(result.count).toEqual(0); }); - it('should return no rows if senderId is an invalid address', async () => { - + it("should return no rows if senderId is an invalid address", async () => { databaseService.walletManager = { - exists: addressOrPublicKey => false + exists: addressOrPublicKey => false, } as Database.IWalletManager; databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - const result = await transactionsBusinessRepository.search({ - senderId: "some invalid address" + senderId: "some invalid address", }); - expect(result.rows).toHaveLength(0); expect(result.count).toEqual(0); }); - it('should lookup senders address from senderId', async () => { - + it("should lookup senders address from senderId", async () => { databaseService.walletManager = { exists: addressOrPublicKey => true, - findByAddress: address => ({ publicKey: "pubKey" }) + findByAddress: address => ({ publicKey: "pubKey" }), } as Database.IWalletManager; databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ + rows: [], + })); await transactionsBusinessRepository.search({ - senderId: "some invalid address" + senderId: "some invalid address", }); - expect(databaseService.connection.transactionsRepository.search).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "senderPublicKey", operator: expect.anything(), - value: "pubKey" - } - ] - }) - ) + value: "pubKey", + }, + ], + }), + ); }); - it('should lookup ownerIds wallet when searching', async () => { - + it("should lookup ownerIds wallet when searching", async () => { databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [] })); + jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ + rows: [], + })); const expectedWallet = {}; databaseService.walletManager = { - findByAddress: address => expectedWallet + findByAddress: address => expectedWallet, } as Database.IWalletManager; - await transactionsBusinessRepository.search({ - ownerId: "ownerId" + ownerId: "ownerId", }); - expect(databaseService.connection.transactionsRepository.search).toHaveBeenCalledWith( expect.objectContaining({ parameters: [ { field: "ownerWallet", operator: expect.anything(), - value: expectedWallet - } - ] - }) + value: expectedWallet, + }, + ], + }), ); }); - it('should set recipientId=addresses if former not supplied', async () => { - + it("should set recipientId=addresses if former not supplied", async () => { databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [] })); - + jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ + rows: [], + })); - const addressList = [ - "addy1", - "addy2" - ]; + const addressList = ["addy1", "addy2"]; const params = { addresses: addressList, - senderPublicKey: "pubKey" + senderPublicKey: "pubKey", }; await transactionsBusinessRepository.search(params); - expect(databaseService.connection.transactionsRepository.search).toHaveBeenCalledWith( expect.objectContaining({ parameters: expect.arrayContaining([ { field: "recipientId", operator: expect.anything(), - value: addressList + value: addressList, }, { field: "senderPublicKey", operator: expect.anything(), - value: "pubKey" + value: "pubKey", }, - ]) - }) - ) + ]), + }), + ); }); - it('should set senderPublicKey=addresses if former not supplied', async () => { - + it("should set senderPublicKey=addresses if former not supplied", async () => { databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; - jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [] })); + jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ + rows: [], + })); const expectedWallet = {}; databaseService.walletManager = { - exists: addressOrPublicKey => false + exists: addressOrPublicKey => false, } as Database.IWalletManager; jest.spyOn(databaseService.walletManager, "exists").mockReturnValue(false); - await transactionsBusinessRepository.search({ - addresses: [ - "addy1", - "addy2" - ], - recipientId: "someId" + addresses: ["addy1", "addy2"], + recipientId: "someId", }); - expect(databaseService.connection.transactionsRepository.search).toHaveBeenCalledWith( expect.objectContaining({ parameters: expect.arrayContaining([ { field: "recipientId", operator: expect.anything(), - value: "someId" + value: "someId", }, { field: "senderPublicKey", operator: expect.anything(), - value: expect.anything() - } - ]) - }) + value: expect.anything(), + }, + ]), + }), ); expect(databaseService.walletManager.exists).toHaveBeenNthCalledWith(1, "addy1"); expect(databaseService.walletManager.exists).toHaveBeenNthCalledWith(2, "addy2"); }); - it('should cache blocks if cache-miss ', async () => { - + it("should cache blocks if cache-miss ", async () => { const expectedBlockId = 1; const expectedHeight = 100; databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [ { - blockId: expectedBlockId - } - ] + blockId: expectedBlockId, + }, + ], })); databaseService.connection.blocksRepository = { - findByIds: async id => [{}] + findByIds: async id => [{}], } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findByIds").mockImplementation(async () => [ { id: expectedBlockId, - height: expectedHeight - } + height: expectedHeight, + }, ]); databaseService.cache = new Map(); jest.spyOn(databaseService.cache, "set").mockReturnThis(); - await transactionsBusinessRepository.search({}); - expect(databaseService.connection.blocksRepository.findByIds).toHaveBeenCalled(); - expect(databaseService.cache.set).toHaveBeenCalledWith( - `heights:${expectedBlockId}`, - expectedHeight - ); - + expect(databaseService.cache.set).toHaveBeenCalledWith(`heights:${expectedBlockId}`, expectedHeight); }); - it('should not cache blocks if cache-hit', async () => { - + it("should not cache blocks if cache-hit", async () => { const expectedBlockId = 1; const expectedHeight = 100; databaseService.connection.transactionsRepository = { search: async parameters => parameters, - getModel: () => new MockDatabaseModel() + getModel: () => new MockDatabaseModel(), } as Database.ITransactionsRepository; jest.spyOn(databaseService.connection.transactionsRepository, "search").mockImplementation(async () => ({ rows: [ { - blockId: expectedBlockId - } - ] + blockId: expectedBlockId, + }, + ], })); databaseService.connection.blocksRepository = { - findByIds: async id => [{}] + findByIds: async id => [{}], } as Database.IBlocksRepository; jest.spyOn(databaseService.connection.blocksRepository, "findByIds").mockImplementation(async () => [{}]); databaseService.cache = new Map(); jest.spyOn(databaseService.cache, "get").mockReturnValue(expectedHeight); - const result = await transactionsBusinessRepository.search({}); - expect(databaseService.connection.blocksRepository.findByIds).not.toHaveBeenCalled(); expect(databaseService.cache.get).toHaveBeenCalledWith(`heights:${expectedBlockId}`); expect(result.rows).toHaveLength(1); expect(result.rows[0].block).toEqual({ id: expectedBlockId, - height: expectedHeight - }) + height: expectedHeight, + }); }); - }); }); diff --git a/packages/core-database/__tests__/repositories/utils/search-parameter-converter.test.ts b/packages/core-database/__tests__/repositories/utils/search-parameter-converter.test.ts index 73ac93e537..2a7f21fc9c 100644 --- a/packages/core-database/__tests__/repositories/utils/search-parameter-converter.test.ts +++ b/packages/core-database/__tests__/repositories/utils/search-parameter-converter.test.ts @@ -2,29 +2,24 @@ import { Database } from "@arkecosystem/core-interfaces"; import { SearchParameterConverter } from "../../../src/repositories/utils/search-parameter-converter"; import { MockDatabaseModel } from "../../__fixtures__/mock-database-model"; - -describe('SearchParameterConverter', () => { - +describe("SearchParameterConverter", () => { let searchParameterConverter: SearchParameterConverter; beforeEach(() => { searchParameterConverter = new SearchParameterConverter(new MockDatabaseModel()); }); - it('should parse all supported operators', () => { - + it("should parse all supported operators", () => { const params = { id: "343-guilty-spark", timestamp: { from: "100", to: "1000" }, sentence: "a partial", - basket: ["apples", "pears", "bananas"] + basket: ["apples", "pears", "bananas"], }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.orderBy).toHaveLength(0); - expect(searchParameters.paginate).toBeNull() + expect(searchParameters.paginate).toBeNull(); expect(searchParameters.parameters).toHaveLength(5); expect(searchParameters.parameters[0].field).toEqual("id"); expect(searchParameters.parameters[0].value).toEqual("343-guilty-spark"); @@ -49,29 +44,24 @@ describe('SearchParameterConverter', () => { it('should default to "equals" when from,to fields not set', () => { const params = { - range: "10" + range: "10", }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.parameters).toHaveLength(1); expect(searchParameters.parameters[0].field).toEqual("range"); expect(searchParameters.parameters[0].value).toEqual("10"); expect(searchParameters.parameters[0].operator).toEqual(Database.SearchOperator.OP_EQ); }); - it('should parse from,to fields when present', () => { - + it("should parse from,to fields when present", () => { const params = { - range: { from: 10, to: 20 } + range: { from: 10, to: 20 }, }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.parameters).toHaveLength(2); expect(searchParameters.parameters[0].field).toEqual("range"); expect(searchParameters.parameters[0].value).toEqual(10); @@ -82,34 +72,28 @@ describe('SearchParameterConverter', () => { expect(searchParameters.parameters[1].operator).toEqual(Database.SearchOperator.OP_LTE); }); - it('should parse unknown fields as custom', () => { - + it("should parse unknown fields as custom", () => { const params = { - "john": "doe" + john: "doe", }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.parameters).toHaveLength(1); expect(searchParameters.parameters[0].field).toEqual("john"); expect(searchParameters.parameters[0].value).toEqual("doe"); expect(searchParameters.parameters[0].operator).toEqual(Database.SearchOperator.OP_CUSTOM); }); - it('should parse orderBy & paginate from params', () => { - + it("should parse orderBy & paginate from params", () => { const params = { orderBy: "field:asc", offset: 20, - limit: 50 + limit: 50, }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.orderBy).toHaveLength(1); expect(searchParameters.orderBy[0].field).toEqual("field"); expect(searchParameters.orderBy[0].direction).toEqual("asc"); @@ -118,31 +102,25 @@ describe('SearchParameterConverter', () => { expect(searchParameters.paginate.limit).toEqual(50); }); - it('should should apply default paginate values', () => { - + it("should should apply default paginate values", () => { const params = { - offset: 1 + offset: 1, }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.paginate.limit).toEqual(100); expect(searchParameters.paginate.offset).toEqual(1); }); - it('should apply default paginate values if nonsensical data provided', () => { - + it("should apply default paginate values if nonsensical data provided", () => { const params = { offset: NaN, - limit: NaN + limit: NaN, }; - const searchParameters = searchParameterConverter.convert(params); - expect(searchParameters.paginate.offset).toEqual(0); expect(searchParameters.paginate.limit).toEqual(100); }); diff --git a/packages/core-database/__tests__/repositories/wallets-business-repository.test.ts b/packages/core-database/__tests__/repositories/wallets-business-repository.test.ts index d974631d93..20a693c75f 100644 --- a/packages/core-database/__tests__/repositories/wallets-business-repository.test.ts +++ b/packages/core-database/__tests__/repositories/wallets-business-repository.test.ts @@ -141,6 +141,8 @@ describe("Wallet Repository", () => { if (i < 17) { wallet.vote = vote; } + + wallet.balance = new Bignum(0); }); walletManager.index(wallets); }); diff --git a/packages/core-database/package.json b/packages/core-database/package.json index fa56b71231..28a21643f7 100644 --- a/packages/core-database/package.json +++ b/packages/core-database/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-database", "description": "Database Interface for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Kristjan Košič ", @@ -34,11 +34,11 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-utils": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", - "@arkecosystem/utils": "^0.2.3", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-utils": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", + "@arkecosystem/utils": "^0.2.4", "@types/lodash.clonedeep": "^4.5.4", "@types/lodash.compact": "^3.0.4", "@types/lodash.uniq": "^4.5.4", @@ -48,7 +48,7 @@ "pluralize": "^7.0.0" }, "devDependencies": { - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "@types/pluralize": "^0.0.29" }, "publishConfig": { diff --git a/packages/core-database/src/database-service-factory.ts b/packages/core-database/src/database-service-factory.ts index 358e18ce43..b5093c8d0c 100644 --- a/packages/core-database/src/database-service-factory.ts +++ b/packages/core-database/src/database-service-factory.ts @@ -19,7 +19,7 @@ export const databaseServiceFactory = async ( new WalletsBusinessRepository(() => databaseService), new DelegatesBusinessRepository(() => databaseService), new TransactionsBusinessRepository(() => databaseService), - new BlocksBusinessRepository(() => databaseService) + new BlocksBusinessRepository(() => databaseService), ); await databaseService.init(); return databaseService; diff --git a/packages/core-database/src/database-service.ts b/packages/core-database/src/database-service.ts index 1c8135315d..90bd40e179 100644 --- a/packages/core-database/src/database-service.ts +++ b/packages/core-database/src/database-service.ts @@ -1,9 +1,8 @@ import { app } from "@arkecosystem/core-container"; import { Blockchain, Database, EventEmitter, Logger } from "@arkecosystem/core-interfaces"; import { roundCalculator } from "@arkecosystem/core-utils"; -import { Bignum, constants, crypto as arkCrypto, models, Transaction } from "@arkecosystem/crypto"; +import { Bignum, constants, crypto, HashAlgorithms, models, Transaction } from "@arkecosystem/crypto"; import assert from "assert"; -import crypto from "crypto"; import cloneDeep from "lodash/cloneDeep"; import pluralize from "pluralize"; import { WalletManager } from "./wallet-manager"; @@ -78,7 +77,7 @@ export class DatabaseService implements Database.IDatabaseService { this.forgingDelegates.length === 0 || (this.forgingDelegates.length && this.forgingDelegates[0].round !== round) ) { - this.logger.info(`Starting Round ${round.toLocaleString()} :dove_of_peace:`); + this.logger.info(`Starting Round ${round.toLocaleString()}`); try { this.updateDelegateStats(this.forgingDelegates); @@ -97,7 +96,7 @@ export class DatabaseService implements Database.IDatabaseService { } else { this.logger.warn( // tslint:disable-next-line:max-line-length - `Round ${round.toLocaleString()} has already been applied. This should happen only if you are a forger. :warning:`, + `Round ${round.toLocaleString()} has already been applied. This should happen only if you are a forger.`, ); } } @@ -154,10 +153,7 @@ export class DatabaseService implements Database.IDatabaseService { } const seedSource = round.toString(); - let currentSeed = crypto - .createHash("sha256") - .update(seedSource, "utf8") - .digest(); + let currentSeed = HashAlgorithms.sha256(seedSource); for (let i = 0, delCount = delegates.length; i < delCount; i++) { for (let x = 0; x < 4 && i < delCount; i++, x++) { @@ -166,10 +162,7 @@ export class DatabaseService implements Database.IDatabaseService { delegates[newIndex] = delegates[i]; delegates[i] = b; } - currentSeed = crypto - .createHash("sha256") - .update(currentSeed) - .digest(); + currentSeed = HashAlgorithms.sha256(currentSeed); } this.forgingDelegates = delegates.map(delegate => { @@ -392,7 +385,7 @@ export class DatabaseService implements Database.IDatabaseService { const { round, nextRound, maxDelegates } = roundCalculator.calculateRound(height); if (nextRound === round + 1 && height >= maxDelegates) { - this.logger.info(`Back to previous round: ${round.toLocaleString()} :back:`); + this.logger.info(`Back to previous round: ${round.toLocaleString()}`); const delegates = await this.calcPreviousActiveDelegates(round); this.forgingDelegates = await this.getActiveDelegates(height, delegates); @@ -520,7 +513,7 @@ export class DatabaseService implements Database.IDatabaseService { } public async verifyTransaction(transaction: Transaction): Promise { - const senderId = arkCrypto.getAddress(transaction.data.senderPublicKey, this.config.get("network.pubKeyHash")); + const senderId = crypto.getAddress(transaction.data.senderPublicKey, this.config.get("network.pubKeyHash")); const sender = this.walletManager.findByAddress(senderId); // should exist diff --git a/packages/core-database/src/repositories/blocks-business-repository.ts b/packages/core-database/src/repositories/blocks-business-repository.ts index 8b24eeec43..29f20d90c9 100644 --- a/packages/core-database/src/repositories/blocks-business-repository.ts +++ b/packages/core-database/src/repositories/blocks-business-repository.ts @@ -2,9 +2,7 @@ import { Database } from "@arkecosystem/core-interfaces"; import { SearchParameterConverter } from "./utils/search-parameter-converter"; export class BlocksBusinessRepository implements Database.IBlocksBusinessRepository { - - constructor(private databaseServiceProvider: () => Database.IDatabaseService) { - } + constructor(private databaseServiceProvider: () => Database.IDatabaseService) {} /* TODO: Remove with v1 */ public async findAll(params: Database.IParameters) { @@ -39,10 +37,9 @@ export class BlocksBusinessRepository implements Database.IBlocksBusinessReposit // default order-by searchParameters.orderBy.push({ field: "height", - direction: "desc" + direction: "desc", }); } return searchParameters; } - } diff --git a/packages/core-database/src/repositories/delegates-business-repository.ts b/packages/core-database/src/repositories/delegates-business-repository.ts index 576670de31..2f1f353be1 100644 --- a/packages/core-database/src/repositories/delegates-business-repository.ts +++ b/packages/core-database/src/repositories/delegates-business-repository.ts @@ -1,7 +1,9 @@ import { Database } from "@arkecosystem/core-interfaces"; import { delegateCalculator } from "@arkecosystem/core-utils"; import { orderBy } from "@arkecosystem/utils"; +import filterRows from "./utils/filter-rows"; import limitRows from "./utils/limit-rows"; +import { sortEntries } from "./utils/sort-entries"; export class DelegatesBusinessRepository implements Database.IDelegatesBusinessRepository { /** @@ -26,12 +28,12 @@ export class DelegatesBusinessRepository implements Database.IDelegatesBusinessR * @return {Object} */ public findAll(params: Database.IParameters = {}) { - const delegates = this.getLocalDelegates(); + this.applyOrder(params); - const [iteratee, order] = this.orderBy(params); + const delegates = sortEntries(params, this.getLocalDelegates(), ["rate", "asc"]); return { - rows: limitRows(orderBy(delegates, [iteratee], [order as "desc" | "asc"]), params), + rows: limitRows(delegates, params), count: delegates.length, }; } @@ -41,29 +43,26 @@ export class DelegatesBusinessRepository implements Database.IDelegatesBusinessR * TODO Currently it searches by username only * @param {Object} [params] * @param {String} [params.username] - Search by username + * @param {Array} [params.usernames] - Search by usernames */ public search(params: Database.IParameters) { - let delegates = this.getLocalDelegates(); - if (params.hasOwnProperty("username")) { - delegates = delegates.filter(delegate => delegate.username.indexOf(params.username as string) > -1); - } - - if (params.orderBy) { - const orderByField = params.orderBy.split(":")[0]; - const orderByDirection = params.orderBy.split(":")[1] || "desc"; + const query: any = { + like: ["username"], + }; - delegates = delegates.sort((a, b) => { - if (orderByDirection === "desc" && a[orderByField] < b[orderByField]) { - return -1; - } + if (params.usernames) { + if (!params.username) { + params.username = params.usernames; + query.like.shift(); + query.in = ["username"]; + } + delete params.usernames; + } - if (orderByDirection === "asc" && a[orderByField] > b[orderByField]) { - return 1; - } + this.applyOrder(params); - return 0; - }); - } + let delegates = filterRows(this.getLocalDelegates(), params, query); + delegates = sortEntries(params, delegates, ["rate", "asc"]); return { rows: limitRows(delegates, params), @@ -99,25 +98,32 @@ export class DelegatesBusinessRepository implements Database.IDelegatesBusinessR }); } - private orderBy(params): string[] { + private applyOrder(params): string { + const assignOrder = (params, value) => (params.orderBy = value.join(":")); + if (!params.orderBy) { - return ["rate", "asc"]; + return assignOrder(params, ["rate", "asc"]); } const orderByMapped = params.orderBy.split(":").map(p => p.toLowerCase()); + if (orderByMapped.length !== 2 || ["desc", "asc"].includes(orderByMapped[1]) !== true) { - return ["rate", "asc"]; + return assignOrder(params, ["rate", "asc"]); } - return [this.manipulateIteratee(orderByMapped[0]), orderByMapped[1]]; + return assignOrder(params, [this.manipulateIteratee(orderByMapped[0]), orderByMapped[1]]); } private manipulateIteratee(iteratee): any { switch (iteratee) { + case "votes": + return "voteBalance"; case "rank": return "rate"; case "productivity": return delegateCalculator.calculateProductivity; + case "votes": + return "voteBalance"; case "approval": return delegateCalculator.calculateApproval; default: diff --git a/packages/core-database/src/repositories/transactions-business-repository.ts b/packages/core-database/src/repositories/transactions-business-repository.ts index ef3f1f5115..dcb48cb74e 100644 --- a/packages/core-database/src/repositories/transactions-business-repository.ts +++ b/packages/core-database/src/repositories/transactions-business-repository.ts @@ -4,9 +4,7 @@ import { constants } from "@arkecosystem/crypto"; import { SearchParameterConverter } from "./utils/search-parameter-converter"; export class TransactionsBusinessRepository implements Database.ITransactionsBusinessRepository { - - constructor(private databaseServiceProvider: () => Database.IDatabaseService) { - } + constructor(private databaseServiceProvider: () => Database.IDatabaseService) {} public async allVotesBySender(senderPublicKey: any, parameters: any) { return this.findAll({ @@ -18,7 +16,9 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus // TODO: Remove with v1 public async findAll(params: any, sequenceOrder: "asc" | "desc" = "desc") { try { - const result = await this.databaseServiceProvider().connection.transactionsRepository.findAll(this.parseSearchParameters(params, sequenceOrder)); + const result = await this.databaseServiceProvider().connection.transactionsRepository.findAll( + this.parseSearchParameters(params, sequenceOrder), + ); result.rows = await this.mapBlocksToTransactions(result.rows); return result; @@ -32,7 +32,7 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus } public async findAllByRecipient(recipientId: any, parameters: any = {}) { - return this.findAll({ recipientId, ...parameters }) + return this.findAll({ recipientId, ...parameters }); } public async findAllBySender(senderPublicKey: any, parameters: any = {}) { @@ -46,7 +46,11 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus public async findAllByWallet(wallet: any, parameters: any) { const transactionsRepository = this.databaseServiceProvider().connection.transactionsRepository; const searchParameters = new SearchParameterConverter(transactionsRepository.getModel()).convert(parameters); - const result = await transactionsRepository.findAllByWallet(wallet, searchParameters.paginate, searchParameters.orderBy); + const result = await transactionsRepository.findAllByWallet( + wallet, + searchParameters.paginate, + searchParameters.orderBy, + ); return await this.mapBlocksToTransactions(result.rows); } @@ -70,18 +74,21 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus public async getFeeStatistics() { const opts = app.resolveOptions("transactionPool"); - return await this.databaseServiceProvider().connection.transactionsRepository.getFeeStatistics(opts.dynamicFees.minFeeBroadcast) + return await this.databaseServiceProvider().connection.transactionsRepository.getFeeStatistics( + opts.dynamicFees.minFeeBroadcast, + ); } public async search(params: any) { - try { - const result = await this.databaseServiceProvider().connection.transactionsRepository.search(this.parseSearchParameters(params)); + const result = await this.databaseServiceProvider().connection.transactionsRepository.search( + this.parseSearchParameters(params), + ); result.rows = await this.mapBlocksToTransactions(result.rows); return result; } catch (e) { - return { rows: [], count: 0}; + return { rows: [], count: 0 }; } } @@ -91,9 +98,8 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus } private async mapBlocksToTransactions(rows) { - if (!Array.isArray(rows)) { - rows = [rows] + rows = [rows]; } // 1. get heights from cache @@ -114,7 +120,6 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus // 2. get uncached blocks from database if (missingFromCache.length) { - const blocksRepository = this.databaseServiceProvider().connection.blocksRepository; const result = await blocksRepository.findByIds(missingFromCache.map(d => d.blockId)); @@ -180,20 +185,21 @@ export class TransactionsBusinessRepository implements Database.ITransactionsBus delete params.ownerId; } - const searchParameters = new SearchParameterConverter(databaseService.connection.transactionsRepository.getModel()).convert(params); + const searchParameters = new SearchParameterConverter( + databaseService.connection.transactionsRepository.getModel(), + ).convert(params); if (!searchParameters.paginate) { searchParameters.paginate = { offset: 0, - limit: 100 + limit: 100, }; } searchParameters.orderBy.push({ field: "sequence", - direction: sequenceOrder + direction: sequenceOrder, }); return searchParameters; } - } diff --git a/packages/core-database/src/repositories/utils/filter-rows.ts b/packages/core-database/src/repositories/utils/filter-rows.ts index 0cf7962b95..022648a796 100644 --- a/packages/core-database/src/repositories/utils/filter-rows.ts +++ b/packages/core-database/src/repositories/utils/filter-rows.ts @@ -15,6 +15,14 @@ export = (rows: T[], params, filters) => } } + if (filters.hasOwnProperty("like")) { + for (const elem of filters.like) { + if (params[elem] && !item[elem].includes(params[elem])) { + return false; + } + } + } + if (filters.hasOwnProperty("between")) { for (const elem of filters.between) { if (!params[elem]) { diff --git a/packages/core-database/src/repositories/utils/search-parameter-converter.ts b/packages/core-database/src/repositories/utils/search-parameter-converter.ts index 6561dca606..cc5ce8c05e 100644 --- a/packages/core-database/src/repositories/utils/search-parameter-converter.ts +++ b/packages/core-database/src/repositories/utils/search-parameter-converter.ts @@ -2,16 +2,13 @@ import { Database } from "@arkecosystem/core-interfaces"; import snakeCase from "lodash/snakeCase"; export class SearchParameterConverter implements Database.ISearchParameterConverter { - - constructor(private databaseModel: Database.IDatabaseModel) { - } + constructor(private databaseModel: Database.IDatabaseModel) {} public convert(params: Database.IParameters, orderBy?: any, paginate?: any): Database.SearchParameters { - const searchParameters: Database.SearchParameters = { orderBy: [], paginate: null, - parameters: [] + parameters: [], }; if (!params || !Object.keys(params).length) { @@ -39,18 +36,18 @@ export class SearchParameterConverter implements Database.ISearchParameterConver if (paginate) { searchParameters.paginate = { limit: Number.isInteger(paginate.limit) ? paginate.limit : 100, - offset: Number.isInteger(paginate.offset) && +paginate.offset > 0 ? paginate.offset : 0 - } + offset: Number.isInteger(paginate.offset) && +paginate.offset > 0 ? paginate.offset : 0, + }; } } private parseOrderBy(searchParameters: Database.SearchParameters, orderBy?: any) { - if (orderBy && typeof (orderBy) === "string") { + if (orderBy && typeof orderBy === "string") { const fieldDirection = orderBy.split(":").map(o => o.toLowerCase()); if (fieldDirection.length === 2 && (fieldDirection[1] === "asc" || fieldDirection[1] === "desc")) { searchParameters.orderBy.push({ field: snakeCase(fieldDirection[0]), - direction: (fieldDirection[1] as "asc" | "desc") + direction: fieldDirection[1] as "asc" | "desc", }); } } @@ -67,7 +64,8 @@ export class SearchParameterConverter implements Database.ISearchParameterConver orderBy, limit and offset are parsed earlier. page, pagination are added automatically by hapi-pagination */ - Object.keys(params).filter(value => !["orderBy", "limit", "offset", "page", "pagination"].includes(value)) + Object.keys(params) + .filter(value => !["orderBy", "limit", "offset", "page", "pagination"].includes(value)) .forEach(fieldName => { const fieldDescriptor = mapByFieldName[fieldName] as Database.SearchableField; @@ -79,7 +77,7 @@ export class SearchParameterConverter implements Database.ISearchParameterConver searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_CUSTOM, - value: params[fieldName] + value: params[fieldName], }); return; } @@ -88,20 +86,22 @@ export class SearchParameterConverter implements Database.ISearchParameterConver searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_LIKE, - value: `%${params[fieldName]}%` + value: `%${params[fieldName]}%`, }); return; } // 'between' - if (fieldDescriptor.supportedOperators.includes(Database.SearchOperator.OP_GTE) || - fieldDescriptor.supportedOperators.includes(Database.SearchOperator.OP_LTE)) { + if ( + fieldDescriptor.supportedOperators.includes(Database.SearchOperator.OP_GTE) || + fieldDescriptor.supportedOperators.includes(Database.SearchOperator.OP_LTE) + ) { // check if we have 'to' & 'from', if not, default to OP_EQ if (!params[fieldName].hasOwnProperty("from") && !params[fieldName].hasOwnProperty("to")) { searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_EQ, - value: params[fieldName] + value: params[fieldName], }); return; } else { @@ -109,26 +109,29 @@ export class SearchParameterConverter implements Database.ISearchParameterConver searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_GTE, - value: params[fieldName].from - }) + value: params[fieldName].from, + }); } if (params[fieldName].hasOwnProperty("to")) { searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_LTE, - value: params[fieldName].to - }) + value: params[fieldName].to, + }); } return; } } // If we support 'IN', then the value must be an array(of values) - if (fieldDescriptor.supportedOperators.includes(Database.SearchOperator.OP_IN) && Array.isArray(params[fieldName])) { + if ( + fieldDescriptor.supportedOperators.includes(Database.SearchOperator.OP_IN) && + Array.isArray(params[fieldName]) + ) { searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_IN, - value: params[fieldName] + value: params[fieldName], }); return; } @@ -138,7 +141,7 @@ export class SearchParameterConverter implements Database.ISearchParameterConver searchParameters.parameters.push({ field: fieldName, operator: Database.SearchOperator.OP_EQ, - value: params[fieldName] + value: params[fieldName], }); return; } diff --git a/packages/core-database/src/repositories/utils/sort-entries.ts b/packages/core-database/src/repositories/utils/sort-entries.ts new file mode 100644 index 0000000000..1e8a31188d --- /dev/null +++ b/packages/core-database/src/repositories/utils/sort-entries.ts @@ -0,0 +1,14 @@ +import { Database } from "@arkecosystem/core-interfaces"; +import { orderBy } from "@arkecosystem/utils"; + +export function sortEntries(params: Database.IParameters, entries: any[], defaultValue) { + const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : defaultValue; + + if (["balance", "voteBalance"].includes(iteratee)) { + return Object.values(entries).sort((a: any, b: any) => { + return order === "asc" ? a[iteratee].comparedTo(b[iteratee]) : b[iteratee].comparedTo(a[iteratee]); + }); + } + + return orderBy(entries, [iteratee], [order as "desc" | "asc"]); +} diff --git a/packages/core-database/src/repositories/wallets-business-repository.ts b/packages/core-database/src/repositories/wallets-business-repository.ts index 8e8d4ac5c9..34ff82ef27 100644 --- a/packages/core-database/src/repositories/wallets-business-repository.ts +++ b/packages/core-database/src/repositories/wallets-business-repository.ts @@ -2,6 +2,7 @@ import { Database } from "@arkecosystem/core-interfaces"; import { orderBy } from "@arkecosystem/utils"; import filterRows from "./utils/filter-rows"; import limitRows from "./utils/limit-rows"; +import { sortEntries } from "./utils/sort-entries"; export class WalletsBusinessRepository implements Database.IWalletsBusinessRepository { /** @@ -24,12 +25,10 @@ export class WalletsBusinessRepository implements Database.IWalletsBusinessRepos * @return {Object} */ public findAll(params: Database.IParameters = {}) { - const wallets = this.all(); - - const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : ["rate", "asc"]; + const wallets = sortEntries(params, this.all(), ["rate", "asc"]); return { - rows: limitRows(orderBy(wallets, [iteratee], [order as "desc" | "asc"]), params), + rows: limitRows(wallets, params), count: wallets.length, }; } @@ -43,10 +42,8 @@ export class WalletsBusinessRepository implements Database.IWalletsBusinessRepos public findAllByVote(publicKey: string, params: Database.IParameters = {}) { const wallets = this.all().filter(wallet => wallet.vote === publicKey); - const [iteratee, order] = params.orderBy ? params.orderBy.split(":") : ["balance", "desc"]; - return { - rows: limitRows(orderBy(wallets, [iteratee], [order as "desc" | "asc"]), params), + rows: limitRows(sortEntries(params, wallets, ["balance", "desc"]), params), count: wallets.length, }; } @@ -69,7 +66,7 @@ export class WalletsBusinessRepository implements Database.IWalletsBusinessRepos * Find all wallets sorted by balance. */ public top(params: Database.IParameters = {}) { - const wallets = Object.values(this.all()).sort((a: any, b: any) => +b.balance.minus(a.balance).toFixed()); + const wallets = sortEntries(params, this.all(), ["balance", "desc"]); return { rows: limitRows(wallets, params), diff --git a/packages/core-database/src/wallet-manager.ts b/packages/core-database/src/wallet-manager.ts index f2bc3bc937..6b4908c5b1 100644 --- a/packages/core-database/src/wallet-manager.ts +++ b/packages/core-database/src/wallet-manager.ts @@ -302,7 +302,7 @@ export class WalletManager implements Database.IWalletManager { this.logger.debug(`Delegate by address: ${this.byAddress[generator]}`); if (this.byAddress[generator]) { - this.logger.info("This look like a bug, please report :bug:"); + this.logger.info("This look like a bug, please report"); } throw new Error(`Could not find delegate with publicKey ${generatorPublicKey}`); @@ -350,9 +350,7 @@ export class WalletManager implements Database.IWalletManager { const delegate = this.byPublicKey[block.data.generatorPublicKey]; if (!delegate) { - app.forceExit( - `Failed to lookup generator '${block.data.generatorPublicKey}' of block '${block.data.id}'. :skull:`, - ); + app.forceExit(`Failed to lookup generator '${block.data.generatorPublicKey}' of block '${block.data.id}'.`); } const revertedTransactions = []; diff --git a/packages/core-debugger-cli/package.json b/packages/core-debugger-cli/package.json index 940e4e6231..de3b8907f0 100644 --- a/packages/core-debugger-cli/package.json +++ b/packages/core-debugger-cli/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-debugger-cli", "description": "Debugger CLI for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -39,7 +39,7 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@oclif/command": "^1.5.10", "@oclif/config": "^1.12.6", "@oclif/plugin-help": "^2.1.6", diff --git a/packages/core-elasticsearch/package.json b/packages/core-elasticsearch/package.json index d18450e4e9..6cbabcf18e 100644 --- a/packages/core-elasticsearch/package.json +++ b/packages/core-elasticsearch/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-elasticsearch", "description": "A powerful Elasticsearch integration for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -26,10 +26,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/elasticsearch": "^5.0.30", "@types/fs-extra": "^5.0.5", "@types/joi": "^14.3.1", diff --git a/packages/core-error-tracker-bugsnag/package.json b/packages/core-error-tracker-bugsnag/package.json index ac5c0f5841..9515a57f2e 100644 --- a/packages/core-error-tracker-bugsnag/package.json +++ b/packages/core-error-tracker-bugsnag/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-error-tracker-bugsnag", "description": "Bugsnag error tracker integration for Ark Core.", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], diff --git a/packages/core-error-tracker-sentry/package.json b/packages/core-error-tracker-sentry/package.json index 9ce5d2f3ad..c1fe997e65 100644 --- a/packages/core-error-tracker-sentry/package.json +++ b/packages/core-error-tracker-sentry/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-error-tracker-sentry", "description": "Sentry error tracker integration for Ark Core.", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], diff --git a/packages/core-event-emitter/package.json b/packages/core-event-emitter/package.json index ca30fb0662..c4a8ee7d37 100644 --- a/packages/core-event-emitter/package.json +++ b/packages/core-event-emitter/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-event-emitter", "description": "Event Manager for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], diff --git a/packages/core-forger/package.json b/packages/core-forger/package.json index 9b5fed3dfc..95c0ace05b 100644 --- a/packages/core-forger/package.json +++ b/packages/core-forger/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-forger", "description": "Forger for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Kristjan Košič ", @@ -33,10 +33,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-p2p": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-p2p": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/lodash.isempty": "^4.4.4", "@types/lodash.sample": "^4.2.4", "@types/lodash.uniq": "^4.5.4", @@ -49,7 +49,7 @@ "pluralize": "^7.0.0" }, "devDependencies": { - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "axios-mock-adapter": "^1.16.0" }, "publishConfig": { diff --git a/packages/core-forger/src/client.ts b/packages/core-forger/src/client.ts index f19b579f8c..c91b28275a 100644 --- a/packages/core-forger/src/client.ts +++ b/packages/core-forger/src/client.ts @@ -44,7 +44,7 @@ export class Client { this.logger.debug( `Broadcasting forged block id:${block.id} at height:${block.height.toLocaleString()} with ${ block.numberOfTransactions - } transactions to ${this.host} :package:`, + } transactions to ${this.host}`, ); return this.__post(`${this.host}/internal/blocks`, { block }); @@ -164,7 +164,7 @@ export class Client { this.host = host; } catch (error) { - this.logger.debug(`${host} didn't respond to the forger. Trying another host :sparkler:`); + this.logger.debug(`${host} didn't respond to the forger. Trying another host`); if (wait > 0) { await delay(wait); diff --git a/packages/core-forger/src/index.ts b/packages/core-forger/src/index.ts index c01e157169..2d45bfd850 100644 --- a/packages/core-forger/src/index.ts +++ b/packages/core-forger/src/index.ts @@ -13,7 +13,7 @@ export const plugin: Container.PluginDescriptor = { const logger = container.resolvePlugin("logger"); if (!forgers) { - logger.info("Forger is disabled :grey_exclamation:"); + logger.info("Forger is disabled"); return false; } diff --git a/packages/core-forger/src/manager.ts b/packages/core-forger/src/manager.ts index 46b9fd6ac6..ef8970f17c 100644 --- a/packages/core-forger/src/manager.ts +++ b/packages/core-forger/src/manager.ts @@ -147,7 +147,7 @@ export class ForgerManager { } // README: The Blockchain is ready but an action still failed. - this.logger.error(`Forging failed: ${error.message} :bangbang:`); + this.logger.error(`Forging failed: ${error.message}`); if (!isEmpty(round)) { this.logger.info( @@ -188,7 +188,7 @@ export class ForgerManager { const block = await delegate.forge(transactions, blockOptions); const username = this.usernames[delegate.publicKey]; - this.logger.info(`Forged new block ${block.data.id} by delegate ${username} (${delegate.publicKey}) :trident:`); + this.logger.info(`Forged new block ${block.data.id} by delegate ${username} (${delegate.publicKey})`); await this.client.broadcast(block.toJson()); @@ -212,7 +212,7 @@ export class ForgerManager { this.logger.debug( `Received ${pluralize("transaction", transactions.length, true)} from the pool containing ${ response.poolSize - } :money_with_wings:`, + }`, ); } diff --git a/packages/core-http-utils/package.json b/packages/core-http-utils/package.json index e434641f2c..b4cff9b962 100644 --- a/packages/core-http-utils/package.json +++ b/packages/core-http-utils/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-http-utils", "description": "Http Utilities for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -31,10 +31,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/crypto": "^2.2.0-beta.3", - "@arkecosystem/core-container": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/boom": "^7.2.1", - "@types/micromatch": "^3.1.0", + "@types/hapi": "^18.0.0", "boom": "^7.3.0", "expand-home-dir": "^0.0.3", "good": "^8.1.2", @@ -42,10 +42,10 @@ "good-squeeze": "^5.1.0", "hapi": "^18.1.0", "hapi-trailing-slash": "^3.0.1", - "inert": "^5.1.2", - "lout": "^11.1.0", - "micromatch": "^3.1.10", - "vision": "^5.4.4" + "nanomatch": "^1.2.13" + }, + "devDependencies": { + "axios": "^0.18.0" }, "publishConfig": { "access": "public" @@ -55,11 +55,5 @@ }, "jest": { "preset": "../../jest-preset.json" - }, - "devDependencies": { - "@types/hapi": "^18.0.0", - "@types/inert": "^5.1.2", - "@types/vision": "^5.3.5", - "axios": "^0.18.0" } } diff --git a/packages/core-http-utils/src/plugins/whitelist.ts b/packages/core-http-utils/src/plugins/whitelist.ts index 9bed9351aa..372c8f160a 100644 --- a/packages/core-http-utils/src/plugins/whitelist.ts +++ b/packages/core-http-utils/src/plugins/whitelist.ts @@ -1,6 +1,6 @@ import { app } from "@arkecosystem/core-container"; import Boom from "boom"; -import mm from "micromatch"; +import nm from "nanomatch"; export const whitelist = { name: "whitelist", @@ -14,7 +14,7 @@ export const whitelist = { if (Array.isArray(options.whitelist)) { for (const ip of options.whitelist) { try { - if (mm.isMatch(remoteAddress, ip)) { + if (nm.isMatch(remoteAddress, ip)) { return h.continue; } } catch { @@ -24,7 +24,7 @@ export const whitelist = { } app.resolvePlugin("logger").warn( - `${remoteAddress} tried to access the ${options.name} without being whitelisted :warning:`, + `${remoteAddress} tried to access the ${options.name} without being whitelisted`, ); return Boom.forbidden(); diff --git a/packages/core-http-utils/src/server/create-secure.ts b/packages/core-http-utils/src/server/create-secure.ts index 29f47097e8..d391e0b919 100644 --- a/packages/core-http-utils/src/server/create-secure.ts +++ b/packages/core-http-utils/src/server/create-secure.ts @@ -2,7 +2,7 @@ import expandHomeDir from "expand-home-dir"; import { readFileSync } from "fs"; import { createServer } from "./create"; -async function createSecureServer(options, callback, secure) { +export async function createSecureServer(options, callback, secure, plugins?: any[]) { options.host = secure.host; options.port = secure.port; options.tls = { @@ -10,7 +10,5 @@ async function createSecureServer(options, callback, secure) { cert: readFileSync(expandHomeDir(secure.cert)), }; - return createServer(options, callback); + return createServer(options, callback, plugins); } - -export { createSecureServer }; diff --git a/packages/core-http-utils/src/server/create.ts b/packages/core-http-utils/src/server/create.ts index 3025af1071..a37ef3e64c 100644 --- a/packages/core-http-utils/src/server/create.ts +++ b/packages/core-http-utils/src/server/create.ts @@ -1,10 +1,14 @@ import Hapi from "hapi"; import { monitorServer } from "./monitor"; -async function createServer(options, callback: any = null) { +export async function createServer(options, callback: any = null, plugins?: any[]) { const server = new Hapi.Server(options); - await server.register([require("vision"), require("inert"), require("lout")]); + if (Array.isArray(plugins)) { + for (const plugin of plugins) { + await server.register(plugin); + } + } await server.register({ plugin: require("hapi-trailing-slash"), @@ -21,5 +25,3 @@ async function createServer(options, callback: any = null) { return server; } - -export { createServer }; diff --git a/packages/core-http-utils/src/server/monitor.ts b/packages/core-http-utils/src/server/monitor.ts index 08499fce71..c209e5b4bb 100644 --- a/packages/core-http-utils/src/server/monitor.ts +++ b/packages/core-http-utils/src/server/monitor.ts @@ -1,4 +1,4 @@ -async function monitorServer(server) { +export async function monitorServer(server) { return server.register({ plugin: require("good"), options: { @@ -18,5 +18,3 @@ async function monitorServer(server) { }, }); } - -export { monitorServer }; diff --git a/packages/core-http-utils/src/server/mount.ts b/packages/core-http-utils/src/server/mount.ts index a2b08e6ae0..4053902d9b 100644 --- a/packages/core-http-utils/src/server/mount.ts +++ b/packages/core-http-utils/src/server/mount.ts @@ -1,6 +1,6 @@ import { app } from "@arkecosystem/core-container"; -async function mountServer(name, server) { +export async function mountServer(name, server) { try { await server.start(); @@ -11,5 +11,3 @@ async function mountServer(name, server) { app.forceExit(`Could not start ${name} Server!`, error); } } - -export { mountServer }; diff --git a/packages/core-interfaces/package.json b/packages/core-interfaces/package.json index 0b4e52a9b6..1fbdd165fe 100644 --- a/packages/core-interfaces/package.json +++ b/packages/core-interfaces/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-interfaces", "description": "Interface types for essential Ark core modules", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Kristjan Košič ", @@ -30,7 +30,7 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/crypto": "^2.2.0-beta.7", "awilix": "^4.2.0", "eventemitter3": "^3.1.0" }, diff --git a/packages/core-interfaces/src/core-database/business-repository/blocks-business-repository.ts b/packages/core-interfaces/src/core-database/business-repository/blocks-business-repository.ts index da85048162..9fca9787a3 100644 --- a/packages/core-interfaces/src/core-database/business-repository/blocks-business-repository.ts +++ b/packages/core-interfaces/src/core-database/business-repository/blocks-business-repository.ts @@ -2,7 +2,6 @@ import { SearchPaginate } from "../search"; import { IParameters } from "./parameters"; export interface IBlocksBusinessRepository { - search(params: IParameters): Promise; findAll(params: IParameters): Promise; diff --git a/packages/core-interfaces/src/core-database/business-repository/transactions-business-repository.ts b/packages/core-interfaces/src/core-database/business-repository/transactions-business-repository.ts index 952a15f198..c6067f4dde 100644 --- a/packages/core-interfaces/src/core-database/business-repository/transactions-business-repository.ts +++ b/packages/core-interfaces/src/core-database/business-repository/transactions-business-repository.ts @@ -1,7 +1,6 @@ import { IParameters } from "./parameters"; export interface ITransactionsBusinessRepository { - findAll(params: IParameters, sequenceOrder?: "asc" | "desc"): Promise; findAllLegacy(parameters: IParameters): Promise; diff --git a/packages/core-interfaces/src/core-database/database-model/database-model.ts b/packages/core-interfaces/src/core-database/database-model/database-model.ts index 88a7b240db..e264c5a446 100644 --- a/packages/core-interfaces/src/core-database/database-model/database-model.ts +++ b/packages/core-interfaces/src/core-database/database-model/database-model.ts @@ -5,7 +5,6 @@ export interface SearchableField { supportedOperators: SearchOperator[]; } export interface IDatabaseModel { - getName(): string; /* A list of fields on this model that can be queried, and each search-operator they support */ diff --git a/packages/core-interfaces/src/core-database/database-repository/repository.ts b/packages/core-interfaces/src/core-database/database-repository/repository.ts index 163b49b12d..68bbfeb1ed 100644 --- a/packages/core-interfaces/src/core-database/database-repository/repository.ts +++ b/packages/core-interfaces/src/core-database/database-repository/repository.ts @@ -3,7 +3,7 @@ import { IDatabaseModel } from "../database-model"; export interface IRepository { getModel(): IDatabaseModel; - estimate() : Promise; + estimate(): Promise; truncate(): Promise; diff --git a/packages/core-interfaces/src/core-database/search/search-parameter-converter.ts b/packages/core-interfaces/src/core-database/search/search-parameter-converter.ts index 51de9f6b43..954dde093b 100644 --- a/packages/core-interfaces/src/core-database/search/search-parameter-converter.ts +++ b/packages/core-interfaces/src/core-database/search/search-parameter-converter.ts @@ -2,6 +2,5 @@ import { IParameters } from "../business-repository"; import { SearchParameters } from "./search-parameters"; export interface ISearchParameterConverter { - convert(params: IParameters, orderBy?: any, paginate?: any): SearchParameters; } diff --git a/packages/core-interfaces/src/core-database/search/search-parameters.ts b/packages/core-interfaces/src/core-database/search/search-parameters.ts index cd0d0bfc3b..34d6552e2d 100644 --- a/packages/core-interfaces/src/core-database/search/search-parameters.ts +++ b/packages/core-interfaces/src/core-database/search/search-parameters.ts @@ -1,23 +1,22 @@ export enum SearchOperator { - OP_EQ = 'equals', - OP_IN = 'in', - OP_GTE = 'gte', - OP_LTE = 'lte', - OP_LIKE = 'like', + OP_EQ = "equals", + OP_IN = "in", + OP_GTE = "gte", + OP_LTE = "lte", + OP_LIKE = "like", // placeholder. For parameters that require custom(not a 1-to-1 field to column mapping) filtering logic on the data-layer repo - OP_CUSTOM = 'custom_operator' + OP_CUSTOM = "custom_operator", } - export interface SearchParameter { - field : string; + field: string; value: any; operator: SearchOperator; } export interface SearchOrderBy { field: string; - direction: 'asc' | 'desc'; + direction: "asc" | "desc"; } export interface SearchPaginate { diff --git a/packages/core-interfaces/src/core-p2p/monitor.ts b/packages/core-interfaces/src/core-p2p/monitor.ts index 7d3ddd08e8..5e412874eb 100644 --- a/packages/core-interfaces/src/core-p2p/monitor.ts +++ b/packages/core-interfaces/src/core-p2p/monitor.ts @@ -1,3 +1,8 @@ +export interface INetworkStatus { + forked: boolean; + blocksToRollback?: number; +} + export interface IMonitor { peers: { [ip: string]: any }; @@ -59,13 +64,6 @@ export interface IMonitor { peerHasCommonBlocks(peer: any, blockIds: any): Promise; - /** - * Get a random, available peer. - * @param {(Number|undefined)} acceptableDelay - * @return {IPeer} - */ - getRandomPeer(acceptableDelay?: any, downloadSize?: any, failedAttempts?: any): any; - /** * Populate list of available peers from random peers. */ @@ -117,26 +115,11 @@ export interface IMonitor { broadcastTransactions(transactions: any): Promise; /** - * Update all peers based on height and last block id. - * - * Grouping peers by height and then by common id results in one of the following - * scenarios: - * - * 1) Same height, same common id - * 2) Same height, mixed common id - * 3) Mixed height, same common id - * 4) Mixed height, mixed common id - * - * Scenario 1: Do nothing. - * Scenario 2-4: - * - If own height is ahead of majority do nothing for now. - * - Pick most common id from peers with most common height and calculate quota, - * depending on which the node rolls back or waits. - * - * NOTE: Only called when the network is consecutively missing blocks `p2pUpdateCounter` times. - * @return {String} - */ - updatePeersOnMissingBlocks(): Promise; + * Check if too many peers are forked and if rollback is necessary. + * Returns the number of blocks to rollback if any. + * @return {Promise} + */ + checkNetworkHealth(): Promise; /** * Dump the list of active peers. diff --git a/packages/core-jest-matchers/package.json b/packages/core-jest-matchers/package.json index c0bed871ec..c8d0e01a4e 100644 --- a/packages/core-jest-matchers/package.json +++ b/packages/core-jest-matchers/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-jest-matchers", "description": "Jest matchers for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust ", "Erwann Gentric ", @@ -33,9 +33,9 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", - "@arkecosystem/utils": "^0.2.3", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", + "@arkecosystem/utils": "^0.2.4", "@types/bip39": "^2.4.1", "@types/lodash.get": "^4.4.4", "@types/lodash.isequal": "^4.5.3", diff --git a/packages/core-json-rpc/__tests__/__support__/setup.ts b/packages/core-json-rpc/__tests__/__support__/setup.ts index 4290b0cbc8..8d11b40a2f 100644 --- a/packages/core-json-rpc/__tests__/__support__/setup.ts +++ b/packages/core-json-rpc/__tests__/__support__/setup.ts @@ -16,12 +16,7 @@ export async function setUp() { process.env.CORE_JSON_RPC_ENABLED = true; await setUpContainer({ - exclude: [ - "@arkecosystem/core-webhooks", - "@arkecosystem/core-graphql", - "@arkecosystem/core-forger", - "@arkecosystem/core-json-rpc", - ], + exclude: ["@arkecosystem/core-webhooks", "@arkecosystem/core-forger", "@arkecosystem/core-json-rpc"], }); const { plugin } = require("../../src"); diff --git a/packages/core-json-rpc/package.json b/packages/core-json-rpc/package.json index 65a4adfe50..7bd190e83e 100644 --- a/packages/core-json-rpc/package.json +++ b/packages/core-json-rpc/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-json-rpc", "description": "A JSON-RPC 2.0 Specification compliant server to interact with the Ark Blockchain.", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Brian Faust " @@ -32,10 +32,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@keyv/sqlite": "^2.0.0", "@types/bip39": "^2.4.1", "@types/boom": "^7.2.1", @@ -55,8 +55,8 @@ "wif": "^2.0.6" }, "devDependencies": { - "@arkecosystem/core-p2p": "^2.2.0-beta.4", - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-p2p": "^2.2.0-beta.7", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "@types/is-reachable": "^3.0.0", "@types/keyv__sqlite": "^2.0.0", "axios-mock-adapter": "^1.16.0" diff --git a/packages/core-json-rpc/src/index.ts b/packages/core-json-rpc/src/index.ts index 384b06471b..71303fff87 100644 --- a/packages/core-json-rpc/src/index.ts +++ b/packages/core-json-rpc/src/index.ts @@ -12,7 +12,7 @@ export const plugin: Container.PluginDescriptor = { const logger = container.resolvePlugin("logger"); if (!options.enabled) { - logger.info("JSON-RPC Server is disabled :grey_exclamation:"); + logger.info("JSON-RPC Server is disabled"); return; } diff --git a/packages/core-json-rpc/src/server/index.ts b/packages/core-json-rpc/src/server/index.ts index fc1a9b5715..08dbe2aedb 100755 --- a/packages/core-json-rpc/src/server/index.ts +++ b/packages/core-json-rpc/src/server/index.ts @@ -7,7 +7,7 @@ import { Processor } from "./services/processor"; export async function startServer(options) { if (options.allowRemote) { app.resolvePlugin("logger").warn( - "JSON-RPC server allows remote connections, this is a potential security risk :warning:", + "JSON-RPC server allows remote connections, this is a potential security risk", ); } diff --git a/packages/core-logger-pino/.gitattributes b/packages/core-logger-pino/.gitattributes new file mode 100644 index 0000000000..60cc52db63 --- /dev/null +++ b/packages/core-logger-pino/.gitattributes @@ -0,0 +1,11 @@ +# Path-based git attributes +# https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html + +# Ignore all test and documentation with "export-ignore". +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.travis.yml export-ignore +/__tests__ export-ignore +/docs export-ignore +/README.md export-ignore diff --git a/packages/core-logger-pino/README.md b/packages/core-logger-pino/README.md new file mode 100644 index 0000000000..6ba178e95b --- /dev/null +++ b/packages/core-logger-pino/README.md @@ -0,0 +1,22 @@ +# Ark Core - Pino Logger + +

+ +

+ +## Documentation + +You can find installation instructions and detailed instructions on how to use this package at the [dedicated documentation site](https://docs.ark.io/guidebook/core/plugins/core-logger-pino.html). + +## Security + +If you discover a security vulnerability within this package, please send an e-mail to security@ark.io. All security vulnerabilities will be promptly addressed. + +## Credits + +- [Brian Faust](https://github.com/faustbrian) +- [All Contributors](../../../../contributors) + +## License + +[MIT](LICENSE) © [ArkEcosystem](https://ark.io) diff --git a/packages/core-logger-pino/__tests__/logger.test.ts b/packages/core-logger-pino/__tests__/logger.test.ts new file mode 100644 index 0000000000..9298d01951 --- /dev/null +++ b/packages/core-logger-pino/__tests__/logger.test.ts @@ -0,0 +1,92 @@ +import { AbstractLogger } from "@arkecosystem/core-logger"; +import * as capcon from "capture-console"; +import "jest-extended"; +import { tmpdir } from "os"; +import { PinoLogger } from "../src"; + +let logger: AbstractLogger; +let message; + +beforeAll(() => { + process.env.CORE_PATH_LOG = tmpdir(); + + const driver = new PinoLogger({ level: "trace" }); + + logger = driver.make(); + + capcon.startCapture(process.stdout, stdout => { + message += stdout; + }); + + capcon.startCapture(process.stderr, stderr => { + message += stderr; + }); +}); + +describe("Logger", () => { + describe("error", () => { + it("should log a message", () => { + logger.error("error_message"); + + expect(message).toMatch(/error/); + expect(message).toMatch(/error_message/); + message = null; + }); + }); + + describe("warn", () => { + it("should log a message", () => { + logger.warn("warning_message"); + + expect(message).toMatch(/warn/); + expect(message).toMatch(/warning_message/); + message = null; + }); + }); + + describe("info", () => { + it("should log a message", () => { + logger.info("info_message"); + + expect(message).toMatch(/info/); + expect(message).toMatch(/info_message/); + message = null; + }); + }); + + describe("debug", () => { + it("should log a message", () => { + logger.debug("debug_message"); + + expect(message).toMatch(/debug/); + expect(message).toMatch(/debug_message/); + message = null; + }); + }); + + describe("verbose", () => { + it("should log a message", () => { + logger.verbose("verbose_message"); + + expect(message).toMatch(/verbose/); + expect(message).toMatch(/verbose_message/); + message = null; + }); + }); + + describe("suppressConsoleOutput", () => { + it("should suppress console output", () => { + logger.suppressConsoleOutput(true); + + logger.info("silent_message"); + expect(message).toBeNull(); + + logger.suppressConsoleOutput(false); + + logger.info("non_silent_message"); + expect(message).toMatch(/non_silent_message/); + + message = null; + }); + }); +}); diff --git a/packages/core-logger-pino/package.json b/packages/core-logger-pino/package.json new file mode 100644 index 0000000000..a70599abf6 --- /dev/null +++ b/packages/core-logger-pino/package.json @@ -0,0 +1,55 @@ +{ + "name": "@arkecosystem/core-logger-pino", + "description": "Pino integration for Ark Core", + "version": "2.2.0-beta.7", + "contributors": [ + "Brian Faust " + ], + "license": "MIT", + "main": "dist/index", + "types": "dist/index", + "files": [ + "dist" + ], + "scripts": { + "publish:alpha": "npm publish --tag alpha", + "publish:beta": "npm publish --tag beta", + "publish:rc": "npm publish --tag rc", + "publish:stable": "npm publish --tag latest", + "prepublishOnly": "yarn build", + "pretest": "yarn lint && yarn build", + "compile": "../../node_modules/typescript/bin/tsc", + "build": "yarn clean && yarn compile", + "build:watch": "yarn clean && yarn compile -w", + "clean": "del dist", + "docs": "../../node_modules/typedoc/bin/typedoc src --out docs", + "lint": "../../node_modules/tslint/bin/tslint -c ../../tslint.json 'src/**/*.ts' '__tests__/**/*.ts' --fix", + "test": "cross-env CORE_ENV=test jest --runInBand --forceExit", + "test:coverage": "cross-env CORE_ENV=test jest --coverage --coveragePathIgnorePatterns='/(defaults.ts|index.ts)$' --runInBand --forceExit", + "test:debug": "cross-env CORE_ENV=test node --inspect-brk ../../node_modules/.bin/jest --runInBand", + "test:watch": "cross-env CORE_ENV=test jest --runInBand --watch", + "test:watch:all": "cross-env CORE_ENV=test jest --runInBand --watchAll", + "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" + }, + "dependencies": { + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-logger": "^2.2.0-beta.7", + "lodash.isempty": "^4.4.0", + "pino-multi-stream": "^4.0.2", + "rotating-file-stream": "^1.4.0" + }, + "devDependencies": { + "@types/capture-console": "^1.0.0", + "@types/lodash.isempty": "^4.4.4", + "capture-console": "^1.0.1" + }, + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=10.x" + }, + "jest": { + "preset": "../../jest-preset.json" + } +} diff --git a/packages/core-logger-pino/src/defaults.ts b/packages/core-logger-pino/src/defaults.ts new file mode 100644 index 0000000000..501931a77f --- /dev/null +++ b/packages/core-logger-pino/src/defaults.ts @@ -0,0 +1,3 @@ +export const defaults = { + level: process.env.CORE_LOG_LEVEL || "debug", +}; diff --git a/packages/core-logger-pino/src/driver.ts b/packages/core-logger-pino/src/driver.ts new file mode 100644 index 0000000000..5a2654ac0e --- /dev/null +++ b/packages/core-logger-pino/src/driver.ts @@ -0,0 +1,110 @@ +import { AbstractLogger } from "@arkecosystem/core-logger"; +import isEmpty from "lodash/isEmpty"; +import pino from "pino"; +import { multistream } from "pino-multi-stream"; +import PinoPretty from "pino-pretty"; +import { getPrettyStream } from "pino/lib/tools"; +import rfs from "rotating-file-stream"; +import { inspect } from "util"; + +export class PinoLogger extends AbstractLogger { + public logger: pino.Logger; + public silent: boolean = false; + + constructor(readonly options) { + super(options); + } + + public make() { + this.logger = pino( + { + base: null, + safe: true, + level: this.options.level, + }, + multistream([ + { level: this.options.level, stream: this.getConsoleStream() }, + { level: "trace", stream: this.getFileStream() }, + ]), + ); + + return this; + } + + public error(message: any): void { + this.createLog("error", message); + } + + public warn(message: any): void { + this.createLog("warn", message); + } + + public info(message: any): void { + this.createLog("info", message); + } + + public debug(message: any): void { + this.createLog("debug", message); + } + + public verbose(message: any): void { + this.createLog("trace", message); + } + + public suppressConsoleOutput(suppress: boolean): void { + this.silent = suppress; + } + + private createLog(method: string, message: any): void { + if (this.silent) { + return; + } + + if (isEmpty(message)) { + return; + } + + if (typeof message !== "string") { + message = inspect(message, { depth: 1 }); + } + + this.logger[method](message); + } + + private getConsoleStream() { + return getPrettyStream( + { + levelFirst: false, + translateTime: true, + colorize: true, + }, + PinoPretty, + process.stdout, + ); + } + + private getFileStream() { + const withLeadingZero = (num: number) => (num > 9 ? "" : "0") + num; + + const createFileName = (time: Date) => { + if (!time) { + return new Date().toISOString().slice(0, 10) + ".log"; + } + + console.log(time.getMonth()); + + const year = withLeadingZero(time.getFullYear()); + const month = withLeadingZero(time.getMonth()); + const day = withLeadingZero(time.getDate()); + + return `${year}-${month}-${day}.log`; + }; + + return rfs(createFileName, { + path: process.env.CORE_PATH_LOG, + interval: "1d", + maxSize: "100M", + maxFiles: 10, + }); + } +} diff --git a/packages/core-logger-pino/src/index.ts b/packages/core-logger-pino/src/index.ts new file mode 100644 index 0000000000..064795df6c --- /dev/null +++ b/packages/core-logger-pino/src/index.ts @@ -0,0 +1,2 @@ +export * from "./driver"; +export * from "./plugin"; diff --git a/packages/core-logger-pino/src/plugin.ts b/packages/core-logger-pino/src/plugin.ts new file mode 100644 index 0000000000..db57c3a778 --- /dev/null +++ b/packages/core-logger-pino/src/plugin.ts @@ -0,0 +1,33 @@ +import { Container } from "@arkecosystem/core-interfaces"; +import { LogManager } from "@arkecosystem/core-logger"; +import { defaults } from "./defaults"; +import { PinoLogger } from "./driver"; + +export const plugin: Container.PluginDescriptor = { + pkg: require("../package.json"), + defaults, + alias: "logger", + extends: "@arkecosystem/core-logger", + async register(container: Container.IContainer, options) { + const logManager: LogManager = container.resolvePlugin("logManager"); + await logManager.makeDriver(new PinoLogger(options)); + + const driver = logManager.driver(); + driver.debug(`Data Directory => ${process.env.CORE_PATH_DATA}`); + driver.debug(`Config Directory => ${process.env.CORE_PATH_CONFIG}`); + + if (process.env.CORE_PATH_CACHE) { + driver.debug(`Cache Directory => ${process.env.CORE_PATH_CACHE}`); + } + + if (process.env.CORE_PATH_LOG) { + driver.debug(`Log Directory => ${process.env.CORE_PATH_LOG}`); + } + + if (process.env.CORE_PATH_TEMP) { + driver.debug(`Temp Directory => ${process.env.CORE_PATH_TEMP}`); + } + + return driver; + }, +}; diff --git a/packages/core-logger-pino/tsconfig.json b/packages/core-logger-pino/tsconfig.json new file mode 100644 index 0000000000..0b089c5fa8 --- /dev/null +++ b/packages/core-logger-pino/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src/**/**.ts"] +} diff --git a/packages/core-logger/package.json b/packages/core-logger/package.json index e67428dfa8..85d10aac83 100644 --- a/packages/core-logger/package.json +++ b/packages/core-logger/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-logger", "description": "Logger Manager for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], diff --git a/packages/core-p2p/__tests__/monitor.test.ts b/packages/core-p2p/__tests__/monitor.test.ts index 194b945eaa..9b7c9f1484 100644 --- a/packages/core-p2p/__tests__/monitor.test.ts +++ b/packages/core-p2p/__tests__/monitor.test.ts @@ -34,6 +34,7 @@ beforeEach(async () => { const initialPeer = new Peer(ip, 4000); initialPeersMock[ip] = Object.assign(initialPeer, initialPeer.headers, { ban: 0, + verification: { forked: false }, }); }); @@ -86,16 +87,6 @@ describe("Monitor", () => { }); }); - describe("getRandomPeer", () => { - it("should be ok", async () => { - const peer = monitor.getRandomPeer(); - - expect(peer).toBeObject(); - expect(peer).toHaveProperty("ip"); - expect(peer).toHaveProperty("port"); - }); - }); - describe("discoverPeers", () => { it("should be ok", async () => { axiosMock.onGet(/.*\/peer\/status/).reply(() => [ @@ -109,9 +100,14 @@ describe("Monitor", () => { }, peerMock.headers, ]); - axiosMock - .onGet(/.*\/peer\/list/) - .reply(() => [200, { peers: [peerMock.toBroadcastInfo()] }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/list/).reply(() => [ + 200, + { + peers: [peerMock.toBroadcastInfo()], + success: true, + }, + peerMock.headers, + ]); await monitor.discoverPeers(); const peers = monitor.getPeers(); @@ -162,13 +158,29 @@ describe("Monitor", () => { describe("downloadBlocks", () => { it("should be ok", async () => { - axiosMock - .onGet(/.*\/peer\/blocks\/common/) - .reply(() => [200, { success: true, common: true }, peerMock.headers]); - axiosMock.onGet(/.*\/peer\/status/).reply(() => [200, { success: true, height: 2 }, peerMock.headers]); - axiosMock - .onGet(/.*\/peer\/blocks/) - .reply(() => [200, { blocks: [{ id: 1 }, { id: 2 }] }, peerMock.headers]); + axiosMock.onGet(/.*\/peer\/blocks\/common/).reply(() => [ + 200, + { + success: true, + common: true, + }, + peerMock.headers, + ]); + axiosMock.onGet(/.*\/peer\/status/).reply(() => [ + 200, + { + success: true, + height: 2, + }, + peerMock.headers, + ]); + axiosMock.onGet(/.*\/peer\/blocks/).reply(() => [ + 200, + { + blocks: [{ height: 1, id: "1" }, { height: 2, id: "2" }], + }, + peerMock.headers, + ]); const blocks = await monitor.downloadBlocks(1); diff --git a/packages/core-p2p/__tests__/peer-verifier.test.ts b/packages/core-p2p/__tests__/peer-verifier.test.ts index 89088d047d..76c2df720b 100644 --- a/packages/core-p2p/__tests__/peer-verifier.test.ts +++ b/packages/core-p2p/__tests__/peer-verifier.test.ts @@ -36,18 +36,12 @@ beforeEach(() => { describe("Peer Verifier", () => { describe("checkState", () => { - it("invalid state", async () => { - const peerVerifier = new PeerVerifier(peerMock); - const state = {}; - const isLegit = await peerVerifier.checkState({}, new Date().getTime() + 10000); - expect(isLegit).toBe(false); - }); - it("identical chains", async () => { const peerVerifier = new PeerVerifier(peerMock); const state = { header: { height: 1, id: genesisBlock.data.id } }; - const isLegit = await peerVerifier.checkState(state, new Date().getTime() + 10000); - expect(isLegit).toBe(true); + const result = await peerVerifier.checkState(state, new Date().getTime() + 10000); + expect(result).toBeObject(); + expect(result.forked).toBe(false); }); it("different chains, including the genesis block", async () => { @@ -62,8 +56,8 @@ describe("Peer Verifier", () => { const peerVerifier = new PeerVerifier(peerMock); const state = { header: { height: 1, id: "123" } }; - const isLegit = await peerVerifier.checkState(state, new Date().getTime() + 10000); - expect(isLegit).toBe(false); + const result = await peerVerifier.checkState(state, new Date().getTime() + 10000); + expect(result).toBeNull(); }); it("bogus replies for common block", async () => { @@ -87,8 +81,8 @@ describe("Peer Verifier", () => { const peerVerifier = new PeerVerifier(peerMock); const state = { header: { height: 1, id: "123" } }; - const isLegit = await peerVerifier.checkState(state, new Date().getTime() + 10000); - expect(isLegit).toBe(false); + const result = await peerVerifier.checkState(state, new Date().getTime() + 10000); + expect(result).toBeNull(); } }); @@ -125,8 +119,8 @@ describe("Peer Verifier", () => { const peerVerifier = new PeerVerifier(peerMock); const state = { header: { height: 2, id: block2.id } }; - const isLegit = await peerVerifier.checkState(state, new Date().getTime() + 10000); - expect(isLegit).toBe(false); + const result = await peerVerifier.checkState(state, new Date().getTime() + 10000); + expect(result).toBeNull(); } }); @@ -151,8 +145,9 @@ describe("Peer Verifier", () => { const peerVerifier = new PeerVerifier(peerMock); const state = { header: { height: 2, id: blocks2to100Json[0].id } }; - const isLegit = await peerVerifier.checkState(state, new Date().getTime() + 10000); - expect(isLegit).toBe(true); + const result = await peerVerifier.checkState(state, new Date().getTime() + 10000); + expect(result).toBeObject(); + expect(result.forked).toBe(false); }); }); }); diff --git a/packages/core-p2p/__tests__/peer.test.ts b/packages/core-p2p/__tests__/peer.test.ts index 30ef9ccc46..efa34ebba8 100644 --- a/packages/core-p2p/__tests__/peer.test.ts +++ b/packages/core-p2p/__tests__/peer.test.ts @@ -82,7 +82,7 @@ describe("Peer", () => { }); it("should return the blocks with status 200", async () => { - const blocks = [{}]; + const blocks = []; axiosMock.onGet(`${peerMock.url}/peer/blocks`).reply(200, { blocks }, peerMock.headers); const result = await peerMock.downloadBlocks(1); @@ -119,7 +119,7 @@ describe("Peer", () => { it("should not be ok", async () => { axiosMock.onGet(`${peerMock.url}/peer/status`).reply(() => [500, {}, peerMock.headers]); - return expect(peerMock.ping(1)).rejects.toThrowError("is unresponsive"); + return expect(peerMock.ping(1)).rejects.toThrowError("could not get status response"); }); it.each([200, 500, 503])("should update peer status from http response %i", async status => { @@ -173,7 +173,14 @@ describe("Peer", () => { }, peerMock.headers, ]); - axiosMock.onGet(`${peerMock.url}/peer/list`).reply(() => [200, { peers: peersMock }, peerMock.headers]); + axiosMock.onGet(`${peerMock.url}/peer/list`).reply(() => [ + 200, + { + peers: peersMock, + success: true, + }, + peerMock.headers, + ]); const peers = await peerMock.getPeers(); @@ -183,7 +190,7 @@ describe("Peer", () => { describe("height", () => { it("should update the height after download", async () => { - const blocks = [{}]; + const blocks = []; const headers = Object.assign({}, peerMock.headers, { height: 1 }); axiosMock.onGet(`${peerMock.url}/peer/blocks`).reply(200, { blocks }, headers); diff --git a/packages/core-p2p/package.json b/packages/core-p2p/package.json index 74128f9a6e..4e15ab21fc 100644 --- a/packages/core-p2p/package.json +++ b/packages/core-p2p/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-p2p", "description": "P2P API for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Kristjan Košič ", @@ -35,11 +35,11 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-transaction-pool": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-transaction-pool": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/joi": "^14.3.1", "@types/lodash.chunk": "^4.2.4", "@types/lodash.flatten": "^4.4.4", @@ -51,7 +51,6 @@ "@types/lodash.shuffle": "^4.2.4", "@types/lodash.sumby": "^4.6.4", "@types/lodash.take": "^4.1.4", - "@types/micromatch": "^3.1.0", "@types/pluralize": "^0.0.29", "@types/semver": "^5.5.0", "ajv": "^6.9.1", @@ -73,14 +72,14 @@ "lodash.shuffle": "^4.2.0", "lodash.sumby": "^4.6.0", "lodash.take": "^4.1.1", - "micromatch": "^3.1.10", + "nanomatch": "^1.2.13", "pluralize": "^7.0.0", "pretty-ms": "^4.0.0", "semver": "^5.6.0", "sntp": "^3.0.2" }, "devDependencies": { - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "@types/boom": "^7.2.1", "@types/ip": "^1.1.0", "@types/pretty-ms": "^4.0.0", diff --git a/packages/core-p2p/src/monitor.ts b/packages/core-p2p/src/monitor.ts index 95f632f878..a69814dddb 100644 --- a/packages/core-p2p/src/monitor.ts +++ b/packages/core-p2p/src/monitor.ts @@ -6,7 +6,6 @@ import { slots } from "@arkecosystem/crypto"; import dayjs from "dayjs-ext"; import delay from "delay"; import fs from "fs"; -import flatten from "lodash/flatten"; import groupBy from "lodash/groupBy"; import sample from "lodash/sample"; import shuffle from "lodash/shuffle"; @@ -25,6 +24,11 @@ let config; let logger: Logger.ILogger; let emitter: EventEmitter.EventEmitter; +interface IAcceptNewPeerOptions { + seed?: boolean; + lessVerbose?: boolean; +} + export class Monitor implements P2P.IMonitor { public peers: { [ip: string]: any }; public server: any; @@ -134,7 +138,7 @@ export class Monitor implements P2P.IMonitor { * @param {Peer} peer * @throws {Error} If invalid peer */ - public async acceptNewPeer(peer, seed?: boolean) { + public async acceptNewPeer(peer, options: IAcceptNewPeerOptions = {}) { if (this.config.disableDiscovery && !this.pendingPeers[peer.ip]) { logger.warn(`Rejected ${peer.ip} because the relay is in non-discovery mode.`); return; @@ -167,7 +171,7 @@ export class Monitor implements P2P.IMonitor { return this.guard.suspend(newPeer); } - if (!this.guard.isValidNetwork(peer) && !seed) { + if (!this.guard.isValidNetwork(peer) && !options.seed) { logger.debug( `Rejected peer ${peer.ip} as it isn't on the same network. Expected: ${config.get( "network.nethash", @@ -188,7 +192,9 @@ export class Monitor implements P2P.IMonitor { this.peers[peer.ip] = newPeer; - logger.debug(`Accepted new peer ${newPeer.ip}:${newPeer.port}`); + if (!options.lessVerbose) { + logger.debug(`Accepted new peer ${newPeer.ip}:${newPeer.port}`); + } emitter.emit("peer.added", newPeer); } catch (error) { @@ -220,7 +226,7 @@ export class Monitor implements P2P.IMonitor { const pingDelay = fast ? 1500 : localConfig.get("globalTimeout"); const max = keys.length; - logger.info(`Checking ${max} peers :telescope:`); + logger.info(`Checking ${max} peers`); const peerErrors = {}; await Promise.all( keys.map(async ip => { @@ -307,66 +313,25 @@ export class Monitor implements P2P.IMonitor { return false; } - /** - * Get a random, available peer. - * @param {(Number|undefined)} acceptableDelay - * @return {Peer} - */ - public getRandomPeer(acceptableDelay?, downloadSize?, failedAttempts?) { - failedAttempts = failedAttempts === undefined ? 0 : failedAttempts; - - const peersAll = this.getPeers(); - - const peersFiltered = peersAll.filter(peer => { - if (peer.ban < new Date().getTime()) { - return true; - } - - if (acceptableDelay && peer.delay < acceptableDelay) { - return true; - } - - if (downloadSize && peer.downloadSize !== downloadSize) { - return true; - } - - return false; - }); - - const randomPeer = sample(peersFiltered); - if (!randomPeer) { - failedAttempts++; - - if (failedAttempts > 10) { - throw new Error( - `Failed to pick a random peer from our list of ${peersAll.length} peers ` + - `(${peersFiltered.length} after filtering)`, - ); - } else if (failedAttempts > 5) { - return this.getRandomPeer(null, downloadSize, failedAttempts); - } - - return this.getRandomPeer(acceptableDelay, downloadSize, failedAttempts); - } - - return randomPeer; - } - /** * Populate list of available peers from random peers. */ public async discoverPeers() { + const queryAtLeastNPeers = 4; + let queriedPeers = 0; + const shuffledPeers = shuffle(this.getPeers()); for (const peer of shuffledPeers) { try { const hisPeers = await peer.getPeers(); - await Promise.all(hisPeers.map(p => this.acceptNewPeer(p))); + queriedPeers++; + await Promise.all(hisPeers.map(p => this.acceptNewPeer(p, { lessVerbose: true }))); } catch (error) { // Just try with the next peer from shuffledPeers. } - if (this.hasMinimumPeers()) { + if (this.hasMinimumPeers() && queriedPeers >= queryAtLeastNPeers) { return; } } @@ -445,10 +410,6 @@ export class Monitor implements P2P.IMonitor { if (forkedBlock) { this.suspendPeer(forkedBlock.ip); } - - const recentBlockIds = await this.__getRecentBlockIds(); - - await Promise.all(this.getPeers().map(peer => this.peerHasCommonBlocks(peer, recentBlockIds))); } /** @@ -460,7 +421,7 @@ export class Monitor implements P2P.IMonitor { let randomPeer; try { - randomPeer = await this.getRandomPeer(); + randomPeer = this.getRandomPeerForDownloadingBlocks(); } catch (error) { logger.error(`Could not download blocks: ${error.message}`); @@ -548,126 +509,51 @@ export class Monitor implements P2P.IMonitor { } /** - * Update all peers based on height and last block id. - * - * Grouping peers by height and then by common id results in one of the following - * scenarios: - * - * 1) Same height, same common id - * 2) Same height, mixed common id - * 3) Mixed height, same common id - * 4) Mixed height, mixed common id - * - * Scenario 1: Do nothing. - * Scenario 2-4: - * - If own height is ahead of majority do nothing for now. - * - Pick most common id from peers with most common height and calculate quota, - * depending on which the node rolls back or waits. - * - * NOTE: Only called when the network is consecutively missing blocks `p2pUpdateCounter` times. - * @return {String} + * Check if too many peers are forked and if rollback is necessary. + * Returns the number of blocks to rollback if any. + * @return {Promise} */ - public async updatePeersOnMissingBlocks() { - // First ping all peers to get updated heights and remove unresponsive ones. + public async checkNetworkHealth(): Promise { if (!this.__isColdStartActive()) { - await this.cleanPeers(true); + await this.cleanPeers(true, true); + await this.guard.resetSuspendedPeers(); } - const peersGroupedByHeight = groupBy(this.getPeers(), "state.height"); - const commonHeightGroups = Object.values(peersGroupedByHeight).sort((a, b) => b.length - a.length); - const peersMostCommonHeight = commonHeightGroups[0]; - const groupedByCommonId = groupBy(peersMostCommonHeight, "state.header.id"); - const commonIdGroupCount = Object.keys(groupedByCommonId).length; - let state = ""; + const lastBlock = app.resolve("state").getLastBlock(); - if (commonHeightGroups.length === 1 && commonIdGroupCount === 1) { - // No need to do anything. - return state; - } + const peers = this.getPeers(); + const suspendedPeers = Object.values(this.getSuspendedPeers()) + .map(suspendedPeer => suspendedPeer.peer) + .filter(peer => peer.verification !== null); - const lastBlock = app.resolve("state").getLastBlock(); + const allPeers = [...peers, ...suspendedPeers]; + const forkedPeers = allPeers.filter(peer => peer.verification.forked); + const majorityOnOurChain = forkedPeers.length / allPeers.length < 0.5; - // Do nothing if majority of peers are lagging behind - if (commonHeightGroups.length > 1) { - if (lastBlock.data.height > peersMostCommonHeight[0].state.height) { - logger.info( - `${pluralize( - "peer", - peersMostCommonHeight.length, - true, - )} are at height ${peersMostCommonHeight[0].state.height.toLocaleString()} and lagging behind last height ${lastBlock.data.height.toLocaleString()}. :zzz:`, - ); - return state; - } + if (majorityOnOurChain) { + logger.info("The majority of peers is not forked. No need to rollback."); + return { forked: false }; } - // Sort common id groups by length DESC - const commonIdGroups = Object.values(groupedByCommonId).sort((a, b) => b.length - a.length); + const groupedByCommonHeight = groupBy(allPeers, "verification.highestCommonHeight"); - // Peers are sitting on the same height, but there might not be enough - // quorum to move on, because of different last blocks. - if (commonIdGroupCount > 1) { - const chosenPeers = commonIdGroups[0]; - const restGroups = commonIdGroups.slice(1); + const groupedByLength = groupBy(Object.values(groupedByCommonHeight), "length"); - if (restGroups.some(group => group.length === chosenPeers.length)) { - logger.warn("Peers are evenly split at same height with different block ids. :zap:"); - } + // Sort by longest + // @ts-ignore + const longest = Object.keys(groupedByLength).sort((a, b) => b - a)[0]; + const longestGroups = groupedByLength[longest]; - logger.info( - `Detected peers at the same height ${peersMostCommonHeight[0].state.height.toLocaleString()} with different block ids: ${JSON.stringify( - Object.keys(groupedByCommonId).map(k => `${k}: ${groupedByCommonId[k].length}`), - null, - 4, - )}`, - ); + // Sort by highest common height DESC + longestGroups.sort((a, b) => b[0].verification.highestCommonHeight - a[0].verification.highestCommonHeight); + const peersMostCommonHeight = longestGroups[0]; - const badLastBlock = - chosenPeers[0].state.height === lastBlock.data.height && - chosenPeers[0].state.header.id !== lastBlock.data.id; - const quota = chosenPeers.length / flatten(commonIdGroups).length; - if (quota < 0.66) { - // or quota too low TODO: find better number - logger.info(`Common id quota '${quota}' is too low. Going to rollback. :repeat:`); - state = "rollback"; - } else if (badLastBlock) { - // Rollback if last block is bad and quota high - logger.info( - `Last block id ${lastBlock.data.id} is bad, ` + - `but got enough common id quota: ${quota}. Going to rollback. :repeat:`, - ); - state = "rollback"; - } - - if (state === "rollback") { - // Ban all rest peers - const peersToBan = flatten(restGroups); - peersToBan.forEach(peer => { - (peer as any).commonId = false; - this.suspendPeer(peer.ip); - }); - - logger.debug( - `Banned ${pluralize( - "peer", - peersToBan.length, - true, - )} at height '${peersMostCommonHeight[0].state.height.toLocaleString()}' which do not have common id '${ - chosenPeers[0].state.header.id - }'.`, - ); - } - } else { - // Under certain circumstances the headers can be missing (i.e. seed peers when starting up) - const commonHeader = peersMostCommonHeight[0].state.header; - logger.info( - `All peers at most common height ${peersMostCommonHeight[0].state.height.toLocaleString()} share the same block id${ - commonHeader ? ` '${commonHeader.id}'` : "" - }. :pray:`, - ); - } + const { highestCommonHeight } = peersMostCommonHeight[0].verification; + logger.info(`Rolling back to most common height ${highestCommonHeight}. Own height: ${lastBlock.data.height}`); - return state; + // Now rollback blocks equal to the distance to the most common height. + const blocksToRollback = lastBlock.data.height - highestCommonHeight; + return { forked: true, blocksToRollback }; } /** @@ -729,14 +615,33 @@ export class Monitor implements P2P.IMonitor { logger.info(`Your NTP connectivity has been verified by ${host}`); - logger.info( - `Local clock is off by ${time.t < 0 ? "-" : ""}${prettyMs(Math.abs(time.t))} from NTP :alarm_clock:`, - ); + logger.info(`Local clock is off by ${time.t < 0 ? "-" : ""}${prettyMs(Math.abs(time.t))} from NTP`); } catch (error) { logger.error(error.message); } } + /** + * Get a random peer for downloading blocks. + * @return {Peer} + * @throws {Error} if a peer could not be selected + */ + private getRandomPeerForDownloadingBlocks() { + const now = new Date().getTime(); + const peersAll = this.getPeers(); + + const peersFiltered = peersAll.filter(peer => peer.ban < now && !peer.verification.forked); + + if (peersFiltered.length === 0) { + throw new Error( + `Failed to pick a random peer from our list of ${peersAll.length} peers: ` + + `all are either banned or on a different chain than us`, + ); + } + + return sample(peersFiltered); + } + /** * Schedule the next update network status. * @param {Number} nextUpdateInSeconds @@ -778,7 +683,7 @@ export class Monitor implements P2P.IMonitor { const peerList = config.get("peers.list"); if (!peerList) { - app.forceExit("No seed peers defined in peers.json :interrobang:"); + app.forceExit("No seed peers defined in peers.json"); } let peers = peerList.map(peer => { @@ -793,7 +698,7 @@ export class Monitor implements P2P.IMonitor { return Promise.all( Object.values(peers).map((peer: any) => { delete this.guard.suspensions[peer.ip]; - return this.acceptNewPeer(peer, true); + return this.acceptNewPeer(peer, { seed: true, lessVerbose: true }); }), ); } diff --git a/packages/core-p2p/src/peer-verifier.ts b/packages/core-p2p/src/peer-verifier.ts index 3d17930251..acc0ce845f 100644 --- a/packages/core-p2p/src/peer-verifier.ts +++ b/packages/core-p2p/src/peer-verifier.ts @@ -1,3 +1,4 @@ +// tslint:disable:max-classes-per-file import { app } from "@arkecosystem/core-container"; import { Database, Logger } from "@arkecosystem/core-interfaces"; import { CappedSet, NSect, roundCalculator } from "@arkecosystem/core-utils"; @@ -7,7 +8,11 @@ import { inspect } from "util"; import { Peer } from "./peer"; enum Severity { - /** Printed at every step of the verification, even if leading to a successful verification. */ + /** + * Printed at every step of the verification, even if leading to a successful verification. + * Multiple such messages are printed even for successfully verified peers. To enable these + * messages define CORE_P2P_PEER_VERIFIER_DEBUG_EXTRA in the environment. + */ DEBUG_EXTRA, /** One such message per successful peer verification is printed. */ @@ -15,9 +20,14 @@ enum Severity { /** Failures to verify peer state, either designating malicious peer or communication issues. */ INFO, +} - /** Discarded. */ - IGNORE, +export class PeerVerificationResult { + public constructor(readonly myHeight: number, readonly hisHeight: number, readonly highestCommonHeight: number) {} + + get forked(): boolean { + return this.highestCommonHeight !== this.myHeight && this.highestCommonHeight !== this.hisHeight; + } } export class PeerVerifier { @@ -72,60 +82,35 @@ export class PeerVerifier { * This means that we have forked and the peer's chain is lower. * We verify: same as 2. * - * @param {Object} claimedState the claimed state of the peer, as returned by `/peer/status` + * @param {Object} claimedState the claimed state of the peer, as returned by `/peer/status`. + * The caller should ensure that it is a valid state: must have .header.height and .header.id + * properties. * @param {Number} deadline operation deadline, in milliseconds since Epoch - * @return {Boolean} true if the peer's blockchain is verified to be legit (albeit it may be - * different than our blockchain) + * @return {PeerVerificationResut|null} PeerVerificationResut object if the peer's blockchain + * is verified to be legit (albeit it may be different than our blockchain), or null if + * the peer's state could not be verified. * @throws {Error} if the state verification could not complete before the deadline */ - public async checkState(claimedState: any, deadline: number): Promise { - if (this.isStateInvalid(claimedState)) { - return false; - } - + public async checkState(claimedState: any, deadline: number): Promise { const ourHeight: number = await this.ourHeight(); - + const claimedHeight = Number(claimedState.header.height); if (await this.weHavePeersHighestBlock(claimedState, ourHeight)) { // Case3 and Case5 - return true; + return new PeerVerificationResult(ourHeight, claimedHeight, claimedHeight); } - const claimedHeight = Number(claimedState.header.height); - const highestCommonBlockHeight = await this.findHighestCommonBlockHeight(claimedHeight, ourHeight, deadline); if (highestCommonBlockHeight === null) { - return false; + return null; } if (!(await this.verifyPeerBlocks(highestCommonBlockHeight + 1, claimedHeight, deadline))) { - return false; + return null; } - this.log(Severity.IGNORE, "success"); + this.log(Severity.DEBUG_EXTRA, "success"); - return true; - } - - /** - * Check if the state claimed by the peer is definitely invalid. - * @param {Object} claimedState peer's claimed state (from `/peer/status`) - * @return {Boolean} true if invalid - */ - private isStateInvalid(claimedState: any): boolean { - if ( - typeof claimedState === "object" && - typeof claimedState.header === "object" && - Number.isInteger(claimedState.header.height) && - claimedState.header.height > 0 && - typeof claimedState.header.id === "string" && - claimedState.header.id.length > 0 - ) { - return false; - } - - this.log(Severity.INFO, `peer's claimed state is invalid: ${this.anyToString(claimedState)}`); - - return true; + return new PeerVerificationResult(ourHeight, claimedHeight, highestCommonBlockHeight); } /** @@ -183,13 +168,13 @@ export class PeerVerifier { if (ourBlockAtHisHeight.id === claimedState.header.id) { if (claimedHeight === ourHeight) { this.log( - Severity.IGNORE, + Severity.DEBUG_EXTRA, `success: peer's latest block is the same as our latest ` + `block (height=${claimedHeight}, id=${claimedState.header.id}). Identical chains.`, ); } else { this.log( - Severity.IGNORE, + Severity.DEBUG_EXTRA, `success: peer's latest block ` + `(height=${claimedHeight}, id=${claimedState.header.id}) is part of our chain. ` + `Peer is ${ourHeight - claimedHeight} block(s) behind us.`, @@ -262,22 +247,9 @@ export class PeerVerifier { return null; } - if ( - typeof highestCommon !== "object" || - typeof highestCommon.id !== "string" || - !Number.isInteger(highestCommon.height) - ) { - this.log( - Severity.INFO, - `failure: erroneous reply from peer for common blocks ` + - `${ourBlocksPrint}: ${this.anyToString(highestCommon)}`, - ); - return null; - } - if (!probesHeightById[highestCommon.id]) { this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: bogus reply from peer for common blocks ${ourBlocksPrint}: ` + `peer replied with block id ${highestCommon.id} which we did not ask for`, ); @@ -286,7 +258,7 @@ export class PeerVerifier { if (probesHeightById[highestCommon.id] !== highestCommon.height) { this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: bogus reply from peer for common blocks ${ourBlocksPrint}: ` + `peer pretends to have block with id ${highestCommon.id} at height ` + `${highestCommon.height}, however a block with the same id is at ` + @@ -406,20 +378,15 @@ export class PeerVerifier { response = await this.peer.getPeerBlocks(height - 1, msRemaining); } catch (err) { this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: could not get blocks starting from height ${height} from peer: exception: ${err.message}`, ); return false; } - if ( - typeof response !== "object" || - typeof response.data !== "object" || - !Array.isArray(response.data.blocks) || - response.data.blocks.length === 0 - ) { + if (response.data.blocks.length === 0) { this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: could not get blocks starting from height ${height} ` + `from peer: unexpected response: ${this.anyToString(response)}`, ); @@ -428,15 +395,6 @@ export class PeerVerifier { for (let i = 0; i < response.data.blocks.length; i++) { blocksByHeight[height + i] = response.data.blocks[i]; - if (typeof blocksByHeight[height + i] !== "object") { - this.log( - Severity.INFO, - `failure: could not get blocks starting from height ${height} ` + - `from peer: the block at height ${height + i} is not an object: ` + - this.anyToString(response), - ); - return false; - } } return true; @@ -468,7 +426,7 @@ export class PeerVerifier { if (!block.verification.verified) { this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: peer's block at height ${expectedHeight} does not pass crypto-validation`, ); return false; @@ -478,7 +436,7 @@ export class PeerVerifier { if (height !== expectedHeight) { this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: asked for block at height ${expectedHeight}, but got a block with height ${height} instead`, ); return false; @@ -496,7 +454,7 @@ export class PeerVerifier { } this.log( - Severity.INFO, + Severity.DEBUG_EXTRA, `failure: block ${this.anyToString(blockData)} is not signed by any of the delegates ` + `for the corresponding round: ` + this.anyToString(Object.values(delegatesByPublicKey)), diff --git a/packages/core-p2p/src/peer.ts b/packages/core-p2p/src/peer.ts index 4593f99e16..e1158aec1d 100644 --- a/packages/core-p2p/src/peer.ts +++ b/packages/core-p2p/src/peer.ts @@ -2,10 +2,12 @@ import { app } from "@arkecosystem/core-container"; import { Logger, P2P } from "@arkecosystem/core-interfaces"; import axios from "axios"; import dayjs from "dayjs-ext"; +import Joi from "joi"; import util from "util"; import { config as localConfig } from "./config"; import { PeerPingTimeoutError, PeerStatusResponseError, PeerVerificationFailedError } from "./errors"; -import { PeerVerifier } from "./peer-verifier"; +import { PeerVerificationResult, PeerVerifier } from "./peer-verifier"; +import { replySchemas } from "./reply-schemas"; export class Peer implements P2P.IPeer { public downloadSize: any; @@ -31,6 +33,7 @@ export class Peer implements P2P.IPeer { public state: any; public url: string; public lastPinged: dayjs.Dayjs | null; + public verification: PeerVerificationResult | null; private config: any; private logger: Logger.ILogger; @@ -49,6 +52,7 @@ export class Peer implements P2P.IPeer { this.state = {}; this.offences = []; this.lastPinged = null; + this.verification = null; this.headers = { version: app.getVersion(), @@ -166,11 +170,7 @@ export class Peer implements P2P.IPeer { return blocks; } catch (error) { - this.logger.debug( - `Cannot download blocks from peer ${this.url} - ${util.inspect(error, { - depth: 1, - })}`, - ); + this.logger.debug(`Cannot download blocks from peer ${this.url} because of "${error.message}"`); this.ban = new Date().getTime() + (Math.floor(Math.random() * 40) + 20) * 60000; @@ -196,7 +196,7 @@ export class Peer implements P2P.IPeer { const body = await this.__get("/peer/status", delay); if (!body) { - throw new Error(`Peer ${this.ip} is unresponsive`); + throw new Error(`Peer ${this.ip}: could not get status response`); } if (!body.success) { @@ -210,7 +210,8 @@ export class Peer implements P2P.IPeer { throw new PeerPingTimeoutError(delay); } - if (!(await peerVerifier.checkState(body, deadline))) { + this.verification = await peerVerifier.checkState(body, deadline); + if (this.verification === null) { throw new PeerVerificationFailedError(); } } @@ -237,6 +238,10 @@ export class Peer implements P2P.IPeer { const body = await this.__get("/peer/list"); + if (!body) { + return []; + } + const blacklisted = {}; localConfig.get("blacklist", []).forEach(ipaddr => (blacklisted[ipaddr] = true)); return body.peers.filter(peer => !blacklisted[peer.ip]); @@ -269,8 +274,6 @@ export class Peer implements P2P.IPeer { } if (!body.common) { - const bodyStr = util.inspect(body, { depth: 2 }); - this.logger.error(`${errorMessage}: falsy "common" property in response: ${bodyStr}`); return false; } @@ -298,10 +301,14 @@ export class Peer implements P2P.IPeer { timeout: timeout || this.config.get("peers.globalTimeout"), }); - this.delay = new Date().getTime() - temp; - this.__parseHeaders(response); + if (!this.validateReply(response.data, endpoint)) { + return; + } + + this.delay = new Date().getTime() - temp; + if (!response.data) { this.logger.debug(`Request to ${this.url}${endpoint} failed: empty response`); return; @@ -369,10 +376,58 @@ export class Peer implements P2P.IPeer { * @return {(Object[]|undefined)} */ public async getPeerBlocks(afterBlockHeight: number): Promise { - return axios.get(`${this.url}/peer/blocks`, { + const endpoint = "/peer/blocks"; + const response = await axios.get(`${this.url}${endpoint}`, { params: { lastBlockHeight: afterBlockHeight }, headers: this.headers, timeout: 10000, }); + + if (!this.validateReply(response.data, endpoint)) { + throw new Error("Invalid reply to request for blocks"); + } + + return response; + } + + /** + * Validate a reply from the peer according to a predefined JSON schema rules. + * @param {Object} reply peer's reply + * @param {String} endpoint the path in the URL for which we got the reply, e.g. /peer/status + * @return {Boolean} true if validated successfully + */ + private validateReply(reply: any, endpoint: string): boolean { + let schema = replySchemas[endpoint]; + if (schema === undefined) { + // See if any of the keys in replySchemas is a prefix of endpoint and pick the longest one. + let len = 0; + const definedEndpoints = Object.keys(replySchemas); + for (const d of definedEndpoints) { + if (endpoint.startsWith(d) && len < d.length) { + schema = replySchemas[d]; + len = d.length; + } + } + + if (schema === undefined) { + this.logger.error( + `Can't validate reply from "${endpoint}": none of the predefined ` + + `schemas matches: ` + + JSON.stringify(definedEndpoints), + ); + return false; + } + } + + const result = Joi.validate(reply, schema, { allowUnknown: true, convert: false }); + + if (result.error) { + this.logger.error( + `Got unexpected reply from ${this.url}${endpoint}: ${JSON.stringify(reply)}: ` + result.error.message, + ); + return false; + } + + return true; } } diff --git a/packages/core-p2p/src/reply-schemas.ts b/packages/core-p2p/src/reply-schemas.ts new file mode 100644 index 0000000000..aa76891353 --- /dev/null +++ b/packages/core-p2p/src/reply-schemas.ts @@ -0,0 +1,81 @@ +import Joi from "joi"; + +export const replySchemas: any = { + "/peer/blocks": Joi.object().keys({ + blocks: Joi.array() + .items( + Joi.object().keys({ + height: Joi.number() + .integer() + .min(1) + .required(), + id: Joi.string() + .max(64) + .hex() + .required(), + }), + ) + .required(), + }), + "/peer/blocks/common?ids=": Joi.object() + .keys({ + common: [ + Joi.object() + .keys({ + height: Joi.number() + .integer() + .min(1) + .required(), + id: Joi.string() + .max(64) + .hex() + .required(), + }) + .required(), + Joi.any().valid(null), + ], + success: Joi.boolean() + .equal(true) + .required(), + }) + .required(), + "/peer/list": Joi.object() + .keys({ + peers: Joi.array() + .items( + Joi.object().keys({ + ip: Joi.string() + .ip({ cidr: "forbidden" }) + .required(), + status: [Joi.string(), Joi.number().integer()], + }), + ) + .required(), + success: Joi.boolean() + .equal(true) + .required(), + }) + .required(), + "/peer/status": Joi.object() + .keys({ + header: Joi.object() + .keys({ + height: Joi.number() + .integer() + .min(1) + .required(), + id: Joi.string() + .max(64) + .hex() + .required(), + }) + .required(), + height: Joi.number() + .integer() + .min(1), + success: Joi.boolean() + .equal(true) + .required(), + }) + .required(), +}; diff --git a/packages/core-p2p/src/server/versions/config/transformers/plugins.ts b/packages/core-p2p/src/server/versions/config/transformers/plugins.ts index b5f7bcc1e8..87bd14e921 100644 --- a/packages/core-p2p/src/server/versions/config/transformers/plugins.ts +++ b/packages/core-p2p/src/server/versions/config/transformers/plugins.ts @@ -4,12 +4,7 @@ * @return {Object} */ export function transformPlugins(config) { - const allowed = [ - "@arkecosystem/core-api", - "@arkecosystem/core-graphql", - "@arkecosystem/core-json-rpc", - "@arkecosystem/core-webhooks", - ]; + const allowed = ["@arkecosystem/core-api", "@arkecosystem/core-json-rpc", "@arkecosystem/core-webhooks"]; const result = {}; diff --git a/packages/core-p2p/src/utils/is-whitelisted.ts b/packages/core-p2p/src/utils/is-whitelisted.ts index 6e40c9e619..d83fe22758 100644 --- a/packages/core-p2p/src/utils/is-whitelisted.ts +++ b/packages/core-p2p/src/utils/is-whitelisted.ts @@ -1,4 +1,4 @@ -import mm from "micromatch"; +import nm from "nanomatch"; /** * Check if the given IP address is whitelisted. @@ -6,7 +6,7 @@ import mm from "micromatch"; export const isWhitelisted = (whitelist: string[], ip: string): boolean => { if (Array.isArray(whitelist)) { for (const item of whitelist) { - if (mm.isMatch(ip, item)) { + if (nm.isMatch(ip, item)) { return true; } } diff --git a/packages/core-snapshots/package.json b/packages/core-snapshots/package.json index 6ebe484ceb..f5857bfbaa 100644 --- a/packages/core-snapshots/package.json +++ b/packages/core-snapshots/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-snapshots", "description": "Provides live local streamed snapshots functionality for ARK Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Kristjan Košič " ], @@ -32,10 +32,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-database-postgres": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-database-postgres": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/bluebird": "^3.5.25", "@types/create-hash": "^1.2.0", "@types/fs-extra": "^5.0.5", diff --git a/packages/core-snapshots/src/manager.ts b/packages/core-snapshots/src/manager.ts index 0e872d2305..0042a9c4af 100644 --- a/packages/core-snapshots/src/manager.ts +++ b/packages/core-snapshots/src/manager.ts @@ -55,7 +55,7 @@ export class SnapshotManager { logger.info( `Import from folder ${ params.meta.folder - } completed. Last block in database: ${lastBlock.height.toLocaleString()} :+1:`, + } completed. Last block in database: ${lastBlock.height.toLocaleString()}`, ); if (!params.skipRestartRound) { const newLastBlock = await this.database.rollbackChain(lastBlock.height); diff --git a/packages/core-snapshots/src/transport/index.ts b/packages/core-snapshots/src/transport/index.ts index 8dca3e4d96..779acaceca 100644 --- a/packages/core-snapshots/src/transport/index.ts +++ b/packages/core-snapshots/src/transport/index.ts @@ -132,7 +132,7 @@ export const verifyTable = async (table, options) => { }); readStream.on("finish", () => { - logger.info(`Snapshot file ${sourceFile} succesfully verified :+1:`); + logger.info(`Snapshot file ${sourceFile} succesfully verified`); }); }; diff --git a/packages/core-snapshots/src/transport/verification.ts b/packages/core-snapshots/src/transport/verification.ts index d56558d57f..6e28194bdc 100644 --- a/packages/core-snapshots/src/transport/verification.ts +++ b/packages/core-snapshots/src/transport/verification.ts @@ -1,7 +1,6 @@ import { app } from "@arkecosystem/core-container"; import { Logger } from "@arkecosystem/core-interfaces"; -import { crypto, models, Transaction } from "@arkecosystem/crypto"; -import createHash from "create-hash"; +import { crypto, HashAlgorithms, models, Transaction } from "@arkecosystem/crypto"; import { camelizeKeys } from "xcase"; const { Block } = models; @@ -49,9 +48,7 @@ export const verifyData = (context, data, prevData, signatureVerification) => { // TODO: manually calculate block ID and compare to existing if (signatureVerification) { const bytes: any = Block.serialize(camelizeKeys(data), false); - const hash = createHash("sha256") - .update(bytes) - .digest(); + const hash = HashAlgorithms.sha256(bytes); const signatureVerify = crypto.verifyHash(hash, data.block_signature, data.generator_public_key); if (!signatureVerify) { diff --git a/packages/core-snapshots/src/utils.ts b/packages/core-snapshots/src/utils.ts index 6b1559e74c..16121a9318 100644 --- a/packages/core-snapshots/src/utils.ts +++ b/packages/core-snapshots/src/utils.ts @@ -33,7 +33,7 @@ export const copySnapshot = (sourceFolder, destFolder, codec) => { fs.ensureFileSync(paths.dest.transactions); if (!fs.existsSync(paths.source.blocks) || !fs.existsSync(paths.source.transactions)) { - app.forceExit(`Unable to copy snapshot from ${sourceFolder} as it doesn't exist :bomb:`); + app.forceExit(`Unable to copy snapshot from ${sourceFolder} as it doesn't exist`); } fs.copyFileSync(paths.source.blocks, paths.dest.blocks); @@ -73,7 +73,7 @@ export const getSnapshotInfo = folder => { export const readMetaJSON = folder => { const metaFileInfo = this.getFilePath("meta.json", folder); if (!fs.existsSync(metaFileInfo)) { - app.forceExit("Meta file meta.json not found. Exiting :bomb:"); + app.forceExit("Meta file meta.json not found. Exiting"); } return fs.readJSONSync(metaFileInfo); diff --git a/packages/core-test-utils/package.json b/packages/core-test-utils/package.json index 4d8dc67fd8..eec7fb6393 100644 --- a/packages/core-test-utils/package.json +++ b/packages/core-test-utils/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-test-utils", "description": "Test Utilities for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust ", "Erwann Gentric ", @@ -34,10 +34,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/core-jest-matchers": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/core-jest-matchers": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/bip39": "^2.4.1", "@types/lodash.get": "^4.4.4", "@types/lodash.isequal": "^4.5.3", diff --git a/packages/core-test-utils/src/config/testnet/plugins.js b/packages/core-test-utils/src/config/testnet/plugins.js index be658bba73..9d8318c623 100644 --- a/packages/core-test-utils/src/config/testnet/plugins.js +++ b/packages/core-test-utils/src/config/testnet/plugins.js @@ -1,19 +1,6 @@ module.exports = { "@arkecosystem/core-event-emitter": {}, - "@arkecosystem/core-logger-winston": { - transports: { - console: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - dailyRotate: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - }, - }, + "@arkecosystem/core-logger-pino": {}, "@arkecosystem/core-database-postgres": { connection: { host: process.env.CORE_DB_HOST || "localhost", @@ -59,11 +46,6 @@ module.exports = { whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], }, }, - "@arkecosystem/core-graphql": { - enabled: process.env.CORE_GRAPHQL_ENABLED, - host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", - port: process.env.CORE_GRAPHQL_PORT || 4005, - }, "@arkecosystem/core-forger": { hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4000}`], }, diff --git a/packages/core-test-utils/src/config/unitnet/plugins.js b/packages/core-test-utils/src/config/unitnet/plugins.js index be658bba73..9d8318c623 100644 --- a/packages/core-test-utils/src/config/unitnet/plugins.js +++ b/packages/core-test-utils/src/config/unitnet/plugins.js @@ -1,19 +1,6 @@ module.exports = { "@arkecosystem/core-event-emitter": {}, - "@arkecosystem/core-logger-winston": { - transports: { - console: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - dailyRotate: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - }, - }, + "@arkecosystem/core-logger-pino": {}, "@arkecosystem/core-database-postgres": { connection: { host: process.env.CORE_DB_HOST || "localhost", @@ -59,11 +46,6 @@ module.exports = { whitelist: ["127.0.0.1", "::ffff:127.0.0.1"], }, }, - "@arkecosystem/core-graphql": { - enabled: process.env.CORE_GRAPHQL_ENABLED, - host: process.env.CORE_GRAPHQL_HOST || "0.0.0.0", - port: process.env.CORE_GRAPHQL_PORT || 4005, - }, "@arkecosystem/core-forger": { hosts: [`http://127.0.0.1:${process.env.CORE_P2P_PORT || 4000}`], }, diff --git a/packages/core-tester-cli/package.json b/packages/core-tester-cli/package.json index eebb8a23b9..c0f2ec601f 100644 --- a/packages/core-tester-cli/package.json +++ b/packages/core-tester-cli/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-tester-cli", "description": "Tester CLI for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust ", "Alex Barnsley " @@ -40,8 +40,8 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-utils": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-utils": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@oclif/command": "^1.5.10", "@oclif/config": "^1.12.6", "@oclif/plugin-help": "^2.1.6", diff --git a/packages/core-transaction-pool/package.json b/packages/core-transaction-pool/package.json index a77e6855ed..01a38994a0 100644 --- a/packages/core-transaction-pool/package.json +++ b/packages/core-transaction-pool/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-transaction-pool", "description": "Transaction Pool Manager for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Kristjan Košič ", "Brian Faust ", @@ -36,10 +36,10 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-database": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-database": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/better-sqlite3": "^5.2.2", "@types/fs-extra": "^5.0.5", "@types/pluralize": "^0.0.29", @@ -51,8 +51,8 @@ "pluralize": "^7.0.0" }, "devDependencies": { - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", - "@arkecosystem/core-utils": "^2.2.0-beta.4", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", + "@arkecosystem/core-utils": "^2.2.0-beta.7", "@types/bip39": "^2.4.1", "@types/random-seed": "^0.3.3", "@types/lodash.clonedeep": "^4.5.3", diff --git a/packages/core-transaction-pool/src/connection.ts b/packages/core-transaction-pool/src/connection.ts index 63a82d553a..662781aa56 100644 --- a/packages/core-transaction-pool/src/connection.ts +++ b/packages/core-transaction-pool/src/connection.ts @@ -368,7 +368,7 @@ export class TransactionPool implements transactionPool.ITransactionPool { this.blockedByPublicKey[senderPublicKey] = blockReleaseTime; - logger.warn(`Sender ${senderPublicKey} blocked until ${this.blockedByPublicKey[senderPublicKey]} :stopwatch:`); + logger.warn(`Sender ${senderPublicKey} blocked until ${this.blockedByPublicKey[senderPublicKey]}`); return blockReleaseTime; } @@ -409,7 +409,7 @@ export class TransactionPool implements transactionPool.ITransactionPool { logger.error( `CanApply transaction test failed on acceptChainedBlock() in transaction pool for transaction id:${ data.id - } due to ${error.message}. Possible double spending attack :bomb:`, + } due to ${error.message}. Possible double spending attack`, ); return; } diff --git a/packages/core-utils/package.json b/packages/core-utils/package.json index 2b5d00064d..b327ed1e76 100644 --- a/packages/core-utils/package.json +++ b/packages/core-utils/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-utils", "description": "Utilities for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -31,8 +31,8 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "cli-table3": "^0.5.1", "dayjs-ext": "^2.2.0" }, diff --git a/packages/core-vote-report/package.json b/packages/core-vote-report/package.json index c42ef39e50..9d885239d7 100644 --- a/packages/core-vote-report/package.json +++ b/packages/core-vote-report/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-vote-report", "description": "Vote Report for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -31,14 +31,18 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-utils": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-utils": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@types/handlebars": "^4.0.40", "@types/lodash.sumby": "^4.6.4", "handlebars": "^4.1.0", - "lodash.sumby": "^4.6.0" + "lodash.sumby": "^4.6.0", + "vision": "^5.4.4" + }, + "devDependencies": { + "@types/vision": "^5.3.5" }, "publishConfig": { "access": "public" diff --git a/packages/core-vote-report/src/server.ts b/packages/core-vote-report/src/server.ts index 468edcfa47..8b6b3beb9f 100644 --- a/packages/core-vote-report/src/server.ts +++ b/packages/core-vote-report/src/server.ts @@ -14,6 +14,7 @@ export async function startServer(config) { relativeTo: __dirname, path: "templates", }), + [require("vision")], ); // @ts-ignore diff --git a/packages/core-webhooks/__tests__/__support__/setup.ts b/packages/core-webhooks/__tests__/__support__/setup.ts index 7506e10d01..2806d9fe22 100644 --- a/packages/core-webhooks/__tests__/__support__/setup.ts +++ b/packages/core-webhooks/__tests__/__support__/setup.ts @@ -9,7 +9,7 @@ export async function setUp() { process.env.CORE_WEBHOOKS_ENABLED = "true"; await setUpContainer({ - exit: "@arkecosystem/core-logger-winston", + exit: "@arkecosystem/core-logger-pino", }); database.make(); diff --git a/packages/core-webhooks/__tests__/server.test.ts b/packages/core-webhooks/__tests__/server.test.ts index dc88d34fb5..5aea628ec7 100644 --- a/packages/core-webhooks/__tests__/server.test.ts +++ b/packages/core-webhooks/__tests__/server.test.ts @@ -33,82 +33,72 @@ function createWebhook(data = null) { } describe("API 2.0 - Webhooks", () => { - describe("GET /webhooks", () => { - it("should GET all the webhooks", async () => { - const response = await utils.request("GET", "webhooks"); - utils.expectSuccessful(response); - utils.expectCollection(response); - }); + it("should GET all the webhooks", async () => { + const response = await utils.request("GET", "webhooks"); + utils.expectSuccessful(response); + utils.expectCollection(response); }); - describe("POST /webhooks", () => { - it("should POST a new webhook with a simple condition", async () => { - const response = await createWebhook(); - utils.expectSuccessful(response, 201); - utils.expectResource(response); - }); + it("should POST a new webhook with a simple condition", async () => { + const response = await createWebhook(); + utils.expectSuccessful(response, 201); + utils.expectResource(response); + }); - it("should POST a new webhook with a complex condition", async () => { - const response = await createWebhook({ - event: "block.forged", - target: "https://httpbin.org/post", - enabled: true, - conditions: [ - { - key: "fee", - condition: "between", - value: { - min: 1, - max: 2, - }, + it("should POST a new webhook with a complex condition", async () => { + const response = await createWebhook({ + event: "block.forged", + target: "https://httpbin.org/post", + enabled: true, + conditions: [ + { + key: "fee", + condition: "between", + value: { + min: 1, + max: 2, }, - ], - }); - utils.expectSuccessful(response, 201); - utils.expectResource(response); + }, + ], }); + utils.expectSuccessful(response, 201); + utils.expectResource(response); + }); - it("should POST a new webhook with an empty array as condition", async () => { - const response = await createWebhook({ - event: "block.forged", - target: "https://httpbin.org/post", - enabled: true, - conditions: [], - }); - utils.expectSuccessful(response, 201); - utils.expectResource(response); + it("should POST a new webhook with an empty array as condition", async () => { + const response = await createWebhook({ + event: "block.forged", + target: "https://httpbin.org/post", + enabled: true, + conditions: [], }); + utils.expectSuccessful(response, 201); + utils.expectResource(response); }); - describe("GET /webhooks/{id}", () => { - it("should GET a webhook by the given id", async () => { - const webhook = await createWebhook(); + it("should GET a webhook by the given id", async () => { + const webhook = await createWebhook(); - const response = await utils.request("GET", `webhooks/${webhook.data.data.id}`); - utils.expectSuccessful(response); - utils.expectResource(response); + const response = await utils.request("GET", `webhooks/${webhook.data.data.id}`); + utils.expectSuccessful(response); + utils.expectResource(response); - delete webhook.data.data.token; + delete webhook.data.data.token; - expect(response.data.data).toEqual(webhook.data.data); - }); + expect(response.data.data).toEqual(webhook.data.data); }); - describe("PUT /webhooks/{id}", () => { - it("should PUT a webhook by the given id", async () => { - const webhook = await createWebhook(); + it("should PUT a webhook by the given id", async () => { + const webhook = await createWebhook(); - const response = await utils.request("PUT", `webhooks/${webhook.data.data.id}`, postData); - utils.expectStatus(response, 204); - }); + const response = await utils.request("PUT", `webhooks/${webhook.data.data.id}`, postData); + utils.expectStatus(response, 204); }); - describe("DELETE /webhooks/{id}", () => { - it("should DELETE a webhook by the given id", async () => { - const webhook = await createWebhook(); + it("should DELETE a webhook by the given id", async () => { + const webhook = await createWebhook(); - const response = await utils.request("DELETE", `webhooks/${webhook.data.data.id}`); - utils.expectStatus(response, 204); - }); + const response = await utils.request("DELETE", `webhooks/${webhook.data.data.id}`); + utils.expectStatus(response, 204); }); }); diff --git a/packages/core-webhooks/package.json b/packages/core-webhooks/package.json index 6b501b90e7..77a646386c 100644 --- a/packages/core-webhooks/package.json +++ b/packages/core-webhooks/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core-webhooks", "description": "Webhooks for Ark Core", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "Brian Faust " ], @@ -31,9 +31,9 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-http-utils": "^2.2.0-beta.4", - "@arkecosystem/core-interfaces": "^2.2.0-beta.4", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-http-utils": "^2.2.0-beta.7", + "@arkecosystem/core-interfaces": "^2.2.0-beta.7", "@types/fs-extra": "^5.0.5", "@types/joi": "^14.3.1", "axios": "^0.18.0", @@ -45,7 +45,7 @@ "uuid": "^3.3.2" }, "devDependencies": { - "@arkecosystem/core-test-utils": "^2.2.0-beta.4", + "@arkecosystem/core-test-utils": "^2.2.0-beta.7", "@types/boom": "^7.2.1", "@types/lowdb": "^1.0.6", "@types/uuid": "^3.4.4" diff --git a/packages/core-webhooks/src/index.ts b/packages/core-webhooks/src/index.ts index 709ebdbc81..059e0d4f52 100644 --- a/packages/core-webhooks/src/index.ts +++ b/packages/core-webhooks/src/index.ts @@ -10,7 +10,7 @@ export const plugin: Container.PluginDescriptor = { alias: "webhooks", async register(container: Container.IContainer, options) { if (!options.enabled) { - container.resolvePlugin("logger").info("Webhooks are disabled :grey_exclamation:"); + container.resolvePlugin("logger").info("Webhooks are disabled"); return; } diff --git a/packages/core/bin/config/devnet/plugins.js b/packages/core/bin/config/devnet/plugins.js index 5ca56075ee..5cece283af 100644 --- a/packages/core/bin/config/devnet/plugins.js +++ b/packages/core/bin/config/devnet/plugins.js @@ -1,19 +1,6 @@ module.exports = { "@arkecosystem/core-event-emitter": {}, - "@arkecosystem/core-logger-winston": { - transports: { - console: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - dailyRotate: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - }, - }, + "@arkecosystem/core-logger-pino": {}, "@arkecosystem/core-database-postgres": { connection: { host: process.env.CORE_DB_HOST || "localhost", diff --git a/packages/core/bin/config/mainnet/plugins.js b/packages/core/bin/config/mainnet/plugins.js index eb750cd1f2..62c78b69bb 100644 --- a/packages/core/bin/config/mainnet/plugins.js +++ b/packages/core/bin/config/mainnet/plugins.js @@ -1,19 +1,6 @@ module.exports = { "@arkecosystem/core-event-emitter": {}, - "@arkecosystem/core-logger-winston": { - transports: { - console: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - dailyRotate: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - }, - }, + "@arkecosystem/core-logger-pino": {}, "@arkecosystem/core-database-postgres": { connection: { host: process.env.CORE_DB_HOST || "localhost", diff --git a/packages/core/bin/config/testnet/plugins.js b/packages/core/bin/config/testnet/plugins.js index d6b921dc01..312e91a11d 100644 --- a/packages/core/bin/config/testnet/plugins.js +++ b/packages/core/bin/config/testnet/plugins.js @@ -1,19 +1,6 @@ module.exports = { "@arkecosystem/core-event-emitter": {}, - "@arkecosystem/core-logger-winston": { - transports: { - console: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - dailyRotate: { - options: { - level: process.env.CORE_LOG_LEVEL || "debug", - }, - }, - }, - }, + "@arkecosystem/core-logger-pino": {}, "@arkecosystem/core-database-postgres": { connection: { host: process.env.CORE_DB_HOST || "localhost", diff --git a/packages/core/package.json b/packages/core/package.json index e47a6e0509..a363d02dee 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/core", "description": "Core of the Ark Blockchain", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Kristjan Košič ", @@ -23,7 +23,7 @@ "publish:alpha": "npm publish --tag alpha", "publish:beta": "npm publish --tag beta", "publish:rc": "npm publish --tag rc", - "publish:stable": "npm publish --tag latest", + "publish:latest": "npm publish --tag latest", "prepublishOnly": "yarn build", "pretest": "yarn lint && yarn build", "prepack": "oclif-dev manifest && npm shrinkwrap", @@ -52,19 +52,19 @@ "updates": "../../node_modules/npm-check-updates/bin/npm-check-updates -a" }, "dependencies": { - "@arkecosystem/core-api": "^2.2.0-beta.4", - "@arkecosystem/core-blockchain": "^2.2.0-beta.4", - "@arkecosystem/core-container": "^2.2.0-beta.4", - "@arkecosystem/core-database-postgres": "^2.2.0-beta.4", - "@arkecosystem/core-event-emitter": "^2.2.0-beta.4", - "@arkecosystem/core-forger": "^2.2.0-beta.4", - "@arkecosystem/core-json-rpc": "^2.2.0-beta.4", - "@arkecosystem/core-logger-winston": "^2.2.0-beta.4", - "@arkecosystem/core-p2p": "^2.2.0-beta.4", - "@arkecosystem/core-snapshots": "^2.2.0-beta.4", - "@arkecosystem/core-transaction-pool": "^2.2.0-beta.4", - "@arkecosystem/core-webhooks": "^2.2.0-beta.4", - "@arkecosystem/crypto": "^2.2.0-beta.4", + "@arkecosystem/core-api": "^2.2.0-beta.7", + "@arkecosystem/core-blockchain": "^2.2.0-beta.7", + "@arkecosystem/core-container": "^2.2.0-beta.7", + "@arkecosystem/core-database-postgres": "^2.2.0-beta.7", + "@arkecosystem/core-event-emitter": "^2.2.0-beta.7", + "@arkecosystem/core-forger": "^2.2.0-beta.7", + "@arkecosystem/core-json-rpc": "^2.2.0-beta.7", + "@arkecosystem/core-logger-pino": "^2.2.0-beta.7", + "@arkecosystem/core-p2p": "^2.2.0-beta.7", + "@arkecosystem/core-snapshots": "^2.2.0-beta.7", + "@arkecosystem/core-transaction-pool": "^2.2.0-beta.7", + "@arkecosystem/core-webhooks": "^2.2.0-beta.7", + "@arkecosystem/crypto": "^2.2.0-beta.7", "@oclif/command": "^1.5.8", "@oclif/config": "^1.10.3", "@oclif/plugin-autocomplete": "^0.1.0", @@ -75,6 +75,7 @@ "bip38": "^2.0.2", "bip39": "^2.5.0", "chalk": "^2.4.2", + "cli-progress": "^2.1.1", "cli-table3": "^0.5.1", "cli-ux": "^5.1.0", "dayjs-ext": "^2.2.0", @@ -85,7 +86,6 @@ "latest-version": "^4.0.0", "listr": "^0.14.3", "ora": "^3.0.0", - "pm2": "^3.2.3", "pretty-bytes": "^5.1.0", "pretty-ms": "^4.0.0", "prompts": "^2.0.0", @@ -96,6 +96,8 @@ "devDependencies": { "@types/bip38": "^2.0.0", "@types/bip39": "^2.4.1", + "@types/cli-progress": "^1.8.0", + "@types/env-paths": "^1.0.2", "@types/execa": "^0.9.0", "@types/fs-extra": "^5.0.4", "@types/got": "^9.4.0", @@ -105,6 +107,7 @@ "@types/ora": "^3.0.0", "@types/pino": "^5.8.4", "@types/pretty-bytes": "^5.1.0", + "@types/pretty-ms": "^4.0.0", "@types/prompts": "^1.2.0", "@types/semver": "^5.5.0", "@types/tail": "^1.2.0", @@ -122,7 +125,10 @@ "oclif": { "commands": "./dist/commands", "hooks": { - "init": "./dist/hooks/init" + "init": [ + "./dist/hooks/init/config", + "./dist/hooks/init/update" + ] }, "bin": "ark", "topics": { @@ -140,6 +146,9 @@ }, "relay": { "description": "manage a relay instance" + }, + "snapshot": { + "description": "manage a relay snapshots" } }, "plugins": [ diff --git a/packages/core/src/commands/command.ts b/packages/core/src/commands/command.ts index 0611debf60..12031a275e 100644 --- a/packages/core/src/commands/command.ts +++ b/packages/core/src/commands/command.ts @@ -1,11 +1,15 @@ import { networks } from "@arkecosystem/crypto"; import Command, { flags } from "@oclif/command"; +import cli from "cli-ux"; import envPaths from "env-paths"; -import { readdirSync } from "fs"; +import { existsSync, readdirSync } from "fs"; import Listr from "listr"; -import { join } from "path"; -import pm2 from "pm2"; +import { join, resolve } from "path"; import prompts from "prompts"; +import { configManager } from "../helpers/config"; +import { confirm } from "../helpers/prompts"; +import { processManager } from "../process-manager"; +import { CommandFlags, Options } from "../types"; // tslint:disable-next-line:no-var-requires const { version } = require("../../package.json"); @@ -16,8 +20,6 @@ export abstract class BaseCommand extends Command { public static flagsNetwork: Record = { token: flags.string({ description: "the name of the token that should be used", - default: "ark", - required: true, }), network: flags.string({ description: "the name of the network that should be used", @@ -58,9 +60,19 @@ export abstract class BaseCommand extends Command { }), }; + public static flagsSnapshot: Record = { + ...BaseCommand.flagsNetwork, + skipCompression: flags.boolean({ + description: "skip gzip compression", + }), + trace: flags.boolean({ + description: "dumps generated queries and settings to console", + }), + }; + protected tasks: Array<{ title: string; task: any }> = []; - protected buildPeerOptions(flags: Record) { + protected buildPeerOptions(flags: CommandFlags) { const config = { networkStart: flags.networkStart, disableDiscovery: flags.disableDiscovery, @@ -76,7 +88,7 @@ export abstract class BaseCommand extends Command { return config; } - protected async buildApplication(app, flags: Record, config: Record) { + protected async buildApplication(app, flags: CommandFlags, config: Options) { await app.setUp(version, flags, { ...{ skipPlugins: flags.skipPlugins }, ...config, @@ -85,7 +97,7 @@ export abstract class BaseCommand extends Command { return app; } - protected flagsToStrings(flags: Record, ignoreKeys: string[] = []): string { + protected flagsToStrings(flags: CommandFlags, ignoreKeys: string[] = []): string { const mappedFlags = []; for (const [key, value] of Object.entries(flags)) { @@ -112,28 +124,48 @@ export abstract class BaseCommand extends Command { } } - protected async getPaths(flags: Record): Promise { - const paths: envPaths.Paths = this.getEnvPaths(flags); + protected async getPaths(flags: CommandFlags): Promise { + let paths: envPaths.Paths = this.getEnvPaths(flags); for (const [key, value] of Object.entries(paths)) { paths[key] = `${value}/${flags.network}`; } + if (process.env.CORE_PATH_CONFIG) { + paths = { ...paths, ...{ config: resolve(process.env.CORE_PATH_CONFIG) } }; + } + return paths; } protected async parseWithNetwork(command: any): Promise { const { args, flags } = this.parse(command); - if (process.env.CORE_PATH_CONFIG) { - const network: string = process.env.CORE_PATH_CONFIG.split("/").pop(); + if (!flags.token) { + flags.token = configManager.get("token"); + } + + if (process.env.CORE_PATH_CONFIG && !flags.network) { + let config: string = process.env.CORE_PATH_CONFIG; + + if (!existsSync(config)) { + this.error(`The given config "${config}" does not exist.`); + } + + if (config.endsWith("/")) { + config = config.slice(0, -1); + } + + const network: string = config.split("/").pop(); if (!this.isValidNetwork(network)) { this.error(`The given network "${flags.network}" is not valid.`); } flags.network = network; - } else { + } + + if (!flags.network) { const { config } = this.getEnvPaths(flags); try { @@ -186,29 +218,7 @@ export abstract class BaseCommand extends Command { this.error("Please enter valid data and try again!"); } - protected createPm2Connection(callback, noDaemonMode: boolean = false): void { - pm2.connect(noDaemonMode, error => { - if (error) { - this.error(error.message); - } - - callback(); - }); - } - - protected async describePm2Process(processName: string, callback): Promise { - pm2.describe(processName, (error, apps) => { - if (error) { - pm2.disconnect(); - - this.error(error.message); - } - - callback(apps[0]); - }); - } - - protected async buildBIP38(flags: Record): Promise> { + protected async buildBIP38(flags: CommandFlags): Promise> { // initial values let bip38 = flags.bip38 || process.env.CORE_FORGER_BIP38; let password = flags.password || process.env.CORE_FORGER_PASSWORD; @@ -219,7 +229,14 @@ export abstract class BaseCommand extends Command { // config const { config } = await this.getPaths(flags); - const delegates = require(join(config, "delegates.json")); + + const configDelegates = join(config, "delegates.json"); + + if (!existsSync(configDelegates)) { + this.error(`The ${configDelegates} file does not exist.`); + } + + const delegates = require(configDelegates); if (!bip38 && delegates.bip38) { bip38 = delegates.bip38; @@ -267,7 +284,23 @@ export abstract class BaseCommand extends Command { return this.getNetworks().map(network => ({ title: network, value: network })); } - private getEnvPaths(flags: Record): envPaths.Paths { + protected async restartProcess(processName: string) { + if (processManager.exists(processName)) { + await confirm(`Would you like to restart the ${processName} process?`, () => { + try { + cli.action.start(`Restarting ${processName}`); + + processManager.restart(processName); + } catch (error) { + this.error(error.message); + } finally { + cli.action.stop(); + } + }); + } + } + + private getEnvPaths(flags: CommandFlags): envPaths.Paths { return envPaths(flags.token, { suffix: "core" }); } } diff --git a/packages/core/src/commands/config/cli.ts b/packages/core/src/commands/config/cli.ts new file mode 100644 index 0000000000..6ede365dc5 --- /dev/null +++ b/packages/core/src/commands/config/cli.ts @@ -0,0 +1,74 @@ +import { flags } from "@oclif/command"; +import cli from "cli-ux"; +import { configManager } from "../../helpers/config"; +import { installFromChannel } from "../../helpers/update"; +import { CommandFlags } from "../../types"; +import { BaseCommand } from "../command"; + +export class CommandLineInterfaceCommand extends BaseCommand { + public static description: string = "Update the CLI configuration"; + + public static examples: string[] = [ + `Set the token that should be used for configuration +$ ark config:cli --token=mine +`, + `Switch the npm registry channel +$ ark config:cli --channel=mine +`, + ]; + + public static flags: CommandFlags = { + token: flags.string({ + description: "the name of the token that should be used", + }), + channel: flags.string({ + description: "the name of the channel that should be used", + options: ["alpha", "beta", "rc", "latest"], + }), + }; + + public async run(): Promise { + const { flags } = this.parse(CommandLineInterfaceCommand); + + if (flags.token) { + configManager.update({ token: flags.token }); + } + + if (flags.channel) { + this.changeChannel(flags.channel); + } + } + + private async changeChannel(newChannel): Promise { + const oldChannel = configManager.get("channel"); + + if (oldChannel === newChannel) { + this.warn(`You are already on the "${newChannel}" channel.`); + return; + } + + configManager.update({ channel: newChannel }); + + const pkg = `${this.config.name}@${newChannel}`; + + try { + cli.action.start(`Installing ${pkg}`); + + await installFromChannel(this.config.name, newChannel); + + this.warn(`${pkg} has been installed.`); + + cli.action.stop(); + + const { flags } = await this.parseWithNetwork(CommandLineInterfaceCommand); + + await this.restartProcess(`${flags.token}-core`); + await this.restartProcess(`${flags.token}-relay`); + await this.restartProcess(`${flags.token}-forger`); + } catch (err) { + this.error(err.message); + } finally { + cli.action.stop(); + } + } +} diff --git a/packages/core/src/commands/config/forger/bip38.ts b/packages/core/src/commands/config/forger/bip38.ts index d693571f51..3a0209ac6d 100644 --- a/packages/core/src/commands/config/forger/bip38.ts +++ b/packages/core/src/commands/config/forger/bip38.ts @@ -5,6 +5,7 @@ import bip39 from "bip39"; import fs from "fs-extra"; import prompts from "prompts"; import wif from "wif"; +import { CommandFlags } from "../../../types"; import { BaseCommand } from "../../command"; export class BIP38Command extends BaseCommand { @@ -16,7 +17,7 @@ $ ark config:forger:bip38 --bip39="..." --password="..." `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, bip39: flags.string({ description: "the plain text bip39 passphrase", diff --git a/packages/core/src/commands/config/forger/bip39.ts b/packages/core/src/commands/config/forger/bip39.ts index 5087e5f5bf..90cfe54ef7 100644 --- a/packages/core/src/commands/config/forger/bip39.ts +++ b/packages/core/src/commands/config/forger/bip39.ts @@ -2,6 +2,7 @@ import { flags } from "@oclif/command"; import bip39 from "bip39"; import fs from "fs-extra"; import prompts from "prompts"; +import { CommandFlags } from "../../../types"; import { BaseCommand } from "../../command"; export class BIP39Command extends BaseCommand { @@ -13,7 +14,7 @@ $ ark config:forger:bip39 --bip39="..." `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, bip39: flags.string({ description: "the plain text bip39 passphrase", diff --git a/packages/core/src/commands/config/forger/index.ts b/packages/core/src/commands/config/forger/index.ts index 8334988ee6..528abe454e 100644 --- a/packages/core/src/commands/config/forger/index.ts +++ b/packages/core/src/commands/config/forger/index.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import prompts from "prompts"; +import { CommandFlags } from "../../../types"; import { BaseCommand } from "../../command"; import { BIP38Command } from "./bip38"; import { BIP39Command } from "./bip39"; @@ -16,7 +17,7 @@ $ ark config:forger --method=bip39 `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsForger, method: flags.string({ diff --git a/packages/core/src/commands/config/publish.ts b/packages/core/src/commands/config/publish.ts index 9ace57a285..d6c8fc227c 100644 --- a/packages/core/src/commands/config/publish.ts +++ b/packages/core/src/commands/config/publish.ts @@ -2,6 +2,8 @@ import { flags } from "@oclif/command"; import fs from "fs-extra"; import { resolve } from "path"; import prompts from "prompts"; +import { configManager } from "../../helpers/config"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class PublishCommand extends BaseCommand { @@ -13,13 +15,17 @@ $ ark config:publish --network=mainnet `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; public async run(): Promise { const { flags } = this.parse(PublishCommand); + if (!flags.token) { + flags.token = configManager.get("token"); + } + if (flags.network) { return this.performPublishment(flags); } @@ -48,7 +54,7 @@ $ ark config:publish --network=mainnet } } - private async performPublishment(flags: Record): Promise { + private async performPublishment(flags: CommandFlags): Promise { const { config } = await this.getPaths(flags); if (!this.isValidNetwork(flags.network)) { diff --git a/packages/core/src/commands/config/reset.ts b/packages/core/src/commands/config/reset.ts index 7eccd7225f..a7d924dfaa 100644 --- a/packages/core/src/commands/config/reset.ts +++ b/packages/core/src/commands/config/reset.ts @@ -1,8 +1,7 @@ import { flags } from "@oclif/command"; -import expandHomeDir from "expand-home-dir"; import fs from "fs-extra"; -import { resolve } from "path"; import prompts from "prompts"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; import { PublishCommand } from "./publish"; @@ -15,7 +14,7 @@ $ ark config:reset --network=mainnet `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, force: flags.boolean({ description: "force the configuration to be reset", @@ -43,7 +42,7 @@ $ ark config:reset --network=mainnet } } - private async performReset(flags: Record): Promise { + private async performReset(flags: CommandFlags): Promise { const { config } = await this.getPaths(flags); this.addTask("Remove configuration", async () => { diff --git a/packages/core/src/commands/core/log.ts b/packages/core/src/commands/core/log.ts index c2d72e6eea..f18e1f655f 100644 --- a/packages/core/src/commands/core/log.ts +++ b/packages/core/src/commands/core/log.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractLogCommand } from "../../shared/log"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class LogCommand extends AbstractLogCommand { @@ -7,7 +8,7 @@ export class LogCommand extends AbstractLogCommand { public static examples: string[] = [`$ ark core:log`]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, error: flags.boolean({ description: "only show error output", diff --git a/packages/core/src/commands/core/restart.ts b/packages/core/src/commands/core/restart.ts index f94ce0deb7..9e38d0ef55 100644 --- a/packages/core/src/commands/core/restart.ts +++ b/packages/core/src/commands/core/restart.ts @@ -1,4 +1,5 @@ import { AbstractRestartCommand } from "../../shared/restart"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class RestartCommand extends AbstractRestartCommand { @@ -10,7 +11,7 @@ $ ark core:restart `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; @@ -19,6 +20,6 @@ $ ark core:restart } public getSuffix(): string { - return "core-forger"; + return "core"; } } diff --git a/packages/core/src/commands/core/run.ts b/packages/core/src/commands/core/run.ts index 77894b1bc7..d0715d687a 100644 --- a/packages/core/src/commands/core/run.ts +++ b/packages/core/src/commands/core/run.ts @@ -1,4 +1,5 @@ import { app } from "@arkecosystem/core-container"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class RunCommand extends BaseCommand { @@ -25,7 +26,7 @@ $ ark core:run --launchMode=seed `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsBehaviour, ...BaseCommand.flagsForger, diff --git a/packages/core/src/commands/core/start.ts b/packages/core/src/commands/core/start.ts index 0ead0ae849..fa36fb9923 100644 --- a/packages/core/src/commands/core/start.ts +++ b/packages/core/src/commands/core/start.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractStartCommand } from "../../shared/start"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StartCommand extends AbstractStartCommand { @@ -29,7 +30,7 @@ $ ark core:start --no-daemon `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsBehaviour, ...BaseCommand.flagsForger, @@ -44,35 +45,28 @@ $ ark core:start --no-daemon return StartCommand; } - protected async runProcess(flags: Record): Promise { - this.createPm2Connection(() => { - this.describePm2Process(`${flags.token}-forger`, forger => { - this.abortWhenRunning(`${flags.token}-forger`, forger); + protected async runProcess(flags: CommandFlags): Promise { + this.abortWhenRunning(`${flags.token}-forger`); + this.abortWhenRunning(`${flags.token}-relay`); - this.describePm2Process(`${flags.token}-relay`, async relay => { - this.abortWhenRunning(`${flags.token}-relay`, relay); + try { + const { bip38, password } = await this.buildBIP38(flags); - try { - const { bip38, password } = await this.buildBIP38(flags); - - this.runWithPm2( - { - name: `${flags.token}-core`, - // @ts-ignore - script: this.config.options.root, - args: `core:run ${this.flagsToStrings(flags, ["daemon"])}`, - env: { - CORE_FORGER_BIP38: bip38, - CORE_FORGER_PASSWORD: password, - }, - }, - flags, - ); - } catch (error) { - this.error(error.message); - } - }); - }); - }); + await this.runWithPm2( + { + name: `${flags.token}-core`, + // @ts-ignore + script: this.config.options.root, + args: `core:run ${this.flagsToStrings(flags, ["daemon"])}`, + env: { + CORE_FORGER_BIP38: bip38, + CORE_FORGER_PASSWORD: password, + }, + }, + flags, + ); + } catch (error) { + this.error(error.message); + } } } diff --git a/packages/core/src/commands/core/status.ts b/packages/core/src/commands/core/status.ts index 648b7579cf..f2779a3000 100644 --- a/packages/core/src/commands/core/status.ts +++ b/packages/core/src/commands/core/status.ts @@ -1,4 +1,5 @@ import { AbstractStatusCommand } from "../../shared/status"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StatusCommand extends AbstractStatusCommand { @@ -6,7 +7,7 @@ export class StatusCommand extends AbstractStatusCommand { public static examples: string[] = [`$ ark core:status`]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/core/stop.ts b/packages/core/src/commands/core/stop.ts index f6e3dae9ac..a99213a378 100644 --- a/packages/core/src/commands/core/stop.ts +++ b/packages/core/src/commands/core/stop.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractStopCommand } from "../../shared/stop"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StopCommand extends AbstractStopCommand { @@ -14,7 +15,7 @@ $ ark core:stop --daemon `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, daemon: flags.boolean({ description: "stop the process or daemon", diff --git a/packages/core/src/commands/env/get.ts b/packages/core/src/commands/env/get.ts index 4ec8bd6050..ec074e3dee 100644 --- a/packages/core/src/commands/env/get.ts +++ b/packages/core/src/commands/env/get.ts @@ -1,5 +1,6 @@ import envfile from "envfile"; import { existsSync } from "fs-extra"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class GetCommand extends BaseCommand { @@ -11,7 +12,7 @@ $ ark env:get CORE_LOG_LEVEL `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/env/list.ts b/packages/core/src/commands/env/list.ts index 9bf10af609..6628afba3e 100644 --- a/packages/core/src/commands/env/list.ts +++ b/packages/core/src/commands/env/list.ts @@ -1,6 +1,7 @@ import Table from "cli-table3"; import envfile from "envfile"; import { existsSync } from "fs-extra"; +import { CommandFlags } from "../../types"; import { renderTable } from "../../utils"; import { BaseCommand } from "../command"; @@ -13,7 +14,7 @@ $ ark env:list `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/env/paths.ts b/packages/core/src/commands/env/paths.ts index af0bad012b..fe3e686e77 100644 --- a/packages/core/src/commands/env/paths.ts +++ b/packages/core/src/commands/env/paths.ts @@ -1,4 +1,5 @@ import Table from "cli-table3"; +import { CommandFlags } from "../../types"; import { renderTable } from "../../utils"; import { BaseCommand } from "../command"; @@ -11,7 +12,7 @@ $ ark env:paths `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/env/set.ts b/packages/core/src/commands/env/set.ts index 1f53744b67..341f8fedee 100644 --- a/packages/core/src/commands/env/set.ts +++ b/packages/core/src/commands/env/set.ts @@ -1,7 +1,6 @@ -import { flags } from "@oclif/command"; import envfile from "envfile"; -import expandHomeDir from "expand-home-dir"; import { existsSync, writeFileSync } from "fs-extra"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class SetCommand extends BaseCommand { @@ -13,7 +12,7 @@ $ ark env:set CORE_LOG_LEVEL info `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/forger/log.ts b/packages/core/src/commands/forger/log.ts index 90301084d5..4acb824d0a 100644 --- a/packages/core/src/commands/forger/log.ts +++ b/packages/core/src/commands/forger/log.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractLogCommand } from "../../shared/log"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class LogCommand extends AbstractLogCommand { @@ -7,7 +8,7 @@ export class LogCommand extends AbstractLogCommand { public static examples: string[] = [`$ ark forger:log`]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, error: flags.boolean({ description: "only show error output", diff --git a/packages/core/src/commands/forger/restart.ts b/packages/core/src/commands/forger/restart.ts index 5f69a9e70c..19bf37a4dc 100644 --- a/packages/core/src/commands/forger/restart.ts +++ b/packages/core/src/commands/forger/restart.ts @@ -1,4 +1,5 @@ import { AbstractRestartCommand } from "../../shared/restart"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class RestartCommand extends AbstractRestartCommand { @@ -10,7 +11,7 @@ $ ark forger:restart `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/forger/run.ts b/packages/core/src/commands/forger/run.ts index e3dcf13876..de3395250a 100644 --- a/packages/core/src/commands/forger/run.ts +++ b/packages/core/src/commands/forger/run.ts @@ -1,4 +1,5 @@ import { app } from "@arkecosystem/core-container"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class RunCommand extends BaseCommand { @@ -13,7 +14,7 @@ $ ark forger:run --bip38="..." --password="..." `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsForger, }; @@ -26,7 +27,7 @@ $ ark forger:run --bip38="..." --password="..." "@arkecosystem/core-event-emitter", "@arkecosystem/core-config", "@arkecosystem/core-logger", - "@arkecosystem/core-logger-winston", + "@arkecosystem/core-logger-pino", "@arkecosystem/core-forger", ], options: { diff --git a/packages/core/src/commands/forger/start.ts b/packages/core/src/commands/forger/start.ts index 1b5d517d59..6598b5647f 100644 --- a/packages/core/src/commands/forger/start.ts +++ b/packages/core/src/commands/forger/start.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractStartCommand } from "../../shared/start"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StartCommand extends AbstractStartCommand { @@ -17,7 +18,7 @@ $ ark forger:start --no-daemon `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsForger, daemon: flags.boolean({ @@ -31,31 +32,27 @@ $ ark forger:start --no-daemon return StartCommand; } - protected async runProcess(flags: Record): Promise { - this.createPm2Connection(() => { - this.describePm2Process(`${flags.token}-core`, async core => { - this.abortWhenRunning(`${flags.token}-core`, core); + protected async runProcess(flags: CommandFlags): Promise { + this.abortWhenRunning(`${flags.token}-core`); - try { - const { bip38, password } = await this.buildBIP38(flags); + try { + const { bip38, password } = await this.buildBIP38(flags); - this.runWithPm2( - { - name: `${flags.token}-forger`, - // @ts-ignore - script: this.config.options.root, - args: `forger:run ${this.flagsToStrings(flags, ["daemon"])}`, - env: { - CORE_FORGER_BIP38: bip38, - CORE_FORGER_PASSWORD: password, - }, - }, - flags, - ); - } catch (error) { - this.error(error.message); - } - }); - }); + await this.runWithPm2( + { + name: `${flags.token}-forger`, + // @ts-ignore + script: this.config.options.root, + args: `forger:run ${this.flagsToStrings(flags, ["daemon"])}`, + env: { + CORE_FORGER_BIP38: bip38, + CORE_FORGER_PASSWORD: password, + }, + }, + flags, + ); + } catch (error) { + this.error(error.message); + } } } diff --git a/packages/core/src/commands/forger/status.ts b/packages/core/src/commands/forger/status.ts index c6c4590e4d..95ff9ecb27 100644 --- a/packages/core/src/commands/forger/status.ts +++ b/packages/core/src/commands/forger/status.ts @@ -1,4 +1,5 @@ import { AbstractStatusCommand } from "../../shared/status"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StatusCommand extends AbstractStatusCommand { @@ -6,7 +7,7 @@ export class StatusCommand extends AbstractStatusCommand { public static examples: string[] = [`$ ark forger:status`]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/forger/stop.ts b/packages/core/src/commands/forger/stop.ts index ad4e691266..5caed0edbd 100644 --- a/packages/core/src/commands/forger/stop.ts +++ b/packages/core/src/commands/forger/stop.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractStopCommand } from "../../shared/stop"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StopCommand extends AbstractStopCommand { @@ -14,7 +15,7 @@ $ ark forger:stop --daemon `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, daemon: flags.boolean({ description: "stop the process or daemon", diff --git a/packages/core/src/commands/relay/log.ts b/packages/core/src/commands/relay/log.ts index 25f1f3027c..99917ec16d 100644 --- a/packages/core/src/commands/relay/log.ts +++ b/packages/core/src/commands/relay/log.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractLogCommand } from "../../shared/log"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class LogCommand extends AbstractLogCommand { @@ -7,7 +8,7 @@ export class LogCommand extends AbstractLogCommand { public static examples: string[] = [`$ ark relay:log`]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, error: flags.boolean({ description: "only show error output", diff --git a/packages/core/src/commands/relay/restart.ts b/packages/core/src/commands/relay/restart.ts index 17de07457a..14195f7b3c 100644 --- a/packages/core/src/commands/relay/restart.ts +++ b/packages/core/src/commands/relay/restart.ts @@ -1,4 +1,5 @@ import { AbstractRestartCommand } from "../../shared/restart"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class RestartCommand extends AbstractRestartCommand { @@ -10,7 +11,7 @@ $ ark relay:restart `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/relay/run.ts b/packages/core/src/commands/relay/run.ts index 551a81fc7f..16d86d823c 100644 --- a/packages/core/src/commands/relay/run.ts +++ b/packages/core/src/commands/relay/run.ts @@ -1,4 +1,5 @@ import { app } from "@arkecosystem/core-container"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class RunCommand extends BaseCommand { @@ -25,7 +26,7 @@ $ ark relay:run --launchMode=seed `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsBehaviour, }; diff --git a/packages/core/src/commands/relay/start.ts b/packages/core/src/commands/relay/start.ts index 131753a756..9b83e5dacd 100644 --- a/packages/core/src/commands/relay/start.ts +++ b/packages/core/src/commands/relay/start.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractStartCommand } from "../../shared/start"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StartCommand extends AbstractStartCommand { @@ -29,7 +30,7 @@ $ ark relay:start --no-daemon `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, ...BaseCommand.flagsBehaviour, daemon: flags.boolean({ @@ -43,21 +44,17 @@ $ ark relay:start --no-daemon return StartCommand; } - protected async runProcess(flags: Record): Promise { - this.createPm2Connection(() => { - this.describePm2Process(`${flags.token}-core`, core => { - this.abortWhenRunning(`${flags.token}-core`, core); + protected async runProcess(flags: CommandFlags): Promise { + this.abortWhenRunning(`${flags.token}-core`); - this.runWithPm2( - { - name: `${flags.token}-relay`, - // @ts-ignore - script: this.config.options.root, - args: `relay:run ${this.flagsToStrings(flags, ["daemon"])}`, - }, - flags, - ); - }); - }); + await this.runWithPm2( + { + name: `${flags.token}-relay`, + // @ts-ignore + script: this.config.options.root, + args: `relay:run ${this.flagsToStrings(flags, ["daemon"])}`, + }, + flags, + ); } } diff --git a/packages/core/src/commands/relay/status.ts b/packages/core/src/commands/relay/status.ts index 961034ca00..9aa1af50c4 100644 --- a/packages/core/src/commands/relay/status.ts +++ b/packages/core/src/commands/relay/status.ts @@ -1,4 +1,5 @@ import { AbstractStatusCommand } from "../../shared/status"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StatusCommand extends AbstractStatusCommand { @@ -6,7 +7,7 @@ export class StatusCommand extends AbstractStatusCommand { public static examples: string[] = [`$ ark relay:status`]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; diff --git a/packages/core/src/commands/relay/stop.ts b/packages/core/src/commands/relay/stop.ts index eee7dc7527..f1c9ffd79e 100644 --- a/packages/core/src/commands/relay/stop.ts +++ b/packages/core/src/commands/relay/stop.ts @@ -1,5 +1,6 @@ import { flags } from "@oclif/command"; import { AbstractStopCommand } from "../../shared/stop"; +import { CommandFlags } from "../../types"; import { BaseCommand } from "../command"; export class StopCommand extends AbstractStopCommand { @@ -14,7 +15,7 @@ $ ark relay:stop --daemon `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, daemon: flags.boolean({ description: "stop the process or daemon", diff --git a/packages/core/src/commands/snapshot/dump.ts b/packages/core/src/commands/snapshot/dump.ts new file mode 100644 index 0000000000..4706d8d13b --- /dev/null +++ b/packages/core/src/commands/snapshot/dump.ts @@ -0,0 +1,36 @@ +import { app } from "@arkecosystem/core-container"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import { setUpLite } from "../../helpers/snapshot"; +import { CommandFlags } from "../../types"; +import { BaseCommand } from "../command"; + +export class DumpCommand extends BaseCommand { + public static description: string = "create a full snapshot of the database"; + + public static flags: CommandFlags = { + ...BaseCommand.flagsSnapshot, + blocks: flags.string({ + description: "blocks to append to, correlates to folder name", + }), + start: flags.integer({ + description: "start network height to export", + default: -1, + }), + end: flags.integer({ + description: "end network height to export", + default: -1, + }), + codec: flags.string({ + description: "codec name, default is msg-lite binary", + }), + }; + + public async run(): Promise { + const { flags } = await this.parseWithNetwork(DumpCommand); + + await setUpLite(flags); + + await app.resolvePlugin("snapshots").exportData(flags); + } +} diff --git a/packages/core/src/commands/snapshot/restore.ts b/packages/core/src/commands/snapshot/restore.ts new file mode 100644 index 0000000000..90d62c58a8 --- /dev/null +++ b/packages/core/src/commands/snapshot/restore.ts @@ -0,0 +1,62 @@ +import { app } from "@arkecosystem/core-container"; +import { EventEmitter } from "@arkecosystem/core-interfaces"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import cliProgress from "cli-progress"; +import { setUpLite } from "../../helpers/snapshot"; +import { CommandFlags } from "../../types"; +import { BaseCommand } from "../command"; + +export class RestoreCommand extends BaseCommand { + public static description: string = "import data from specified snapshot"; + + public static flags: CommandFlags = { + ...BaseCommand.flagsSnapshot, + blocks: flags.string({ + description: "blocks to import, corelates to folder name", + required: true, + }), + codec: flags.string({ + description: "codec name, default is msg-lite binary", + }), + truncate: flags.boolean({ + description: "empty all tables before running import", + }), + skipRestartRound: flags.boolean({ + description: "skip revert to current round", + }), + signatureVerify: flags.boolean({ + description: "signature verification", + }), + }; + + public async run(): Promise { + // tslint:disable-next-line:no-shadowed-variable + const { flags } = this.parse(RestoreCommand); + + await setUpLite(flags); + + const emitter = app.resolvePlugin("event-emitter"); + + const progressBar = new cliProgress.Bar( + { + format: "{bar} {percentage}% | ETA: {eta}s | {value}/{total} | Duration: {duration}s", + }, + cliProgress.Presets.shades_classic, + ); + + emitter.on("start", data => { + progressBar.start(data.count, 1); + }); + + emitter.on("progress", data => { + progressBar.update(data.value); + }); + + emitter.on("complete", data => { + progressBar.stop(); + }); + + await app.resolvePlugin("snapshots").importData(flags); + } +} diff --git a/packages/core/src/commands/snapshot/rollback.ts b/packages/core/src/commands/snapshot/rollback.ts new file mode 100644 index 0000000000..470daee5df --- /dev/null +++ b/packages/core/src/commands/snapshot/rollback.ts @@ -0,0 +1,35 @@ +import { app } from "@arkecosystem/core-container"; +import { Logger } from "@arkecosystem/core-interfaces"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import { setUpLite } from "../../helpers/snapshot"; +import { CommandFlags } from "../../types"; +import { BaseCommand } from "../command"; + +export class RollbackCommand extends BaseCommand { + public static description: string = "rollback chain to specified height"; + + public static flags: CommandFlags = { + ...BaseCommand.flagsSnapshot, + height: flags.integer({ + description: "block network height number to rollback", + default: -1, + }), + }; + + public async run(): Promise { + const { flags } = await this.parseWithNetwork(RollbackCommand); + + await setUpLite(flags); + + const logger = app.resolvePlugin("logger"); + + if (flags.height === -1) { + logger.warn("Rollback height is not specified. Rolling back to last completed round."); + } + + logger.info(`Starting the process of blockchain rollback to block height of ${flags.height.toLocaleString()}`); + + await app.resolvePlugin("snapshots").rollbackChain(flags.height); + } +} diff --git a/packages/core/src/commands/snapshot/truncate.ts b/packages/core/src/commands/snapshot/truncate.ts new file mode 100644 index 0000000000..3eb27f355f --- /dev/null +++ b/packages/core/src/commands/snapshot/truncate.ts @@ -0,0 +1,16 @@ +import { app } from "@arkecosystem/core-container"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { setUpLite } from "../../helpers/snapshot"; +import { BaseCommand } from "../command"; + +export class TruncateCommand extends BaseCommand { + public static description: string = "truncate blockchain database"; + + public async run(): Promise { + const { flags } = await this.parseWithNetwork(TruncateCommand); + + await setUpLite(flags); + + await app.resolvePlugin("snapshots").truncateChain(); + } +} diff --git a/packages/core/src/commands/snapshot/verify.ts b/packages/core/src/commands/snapshot/verify.ts new file mode 100644 index 0000000000..f60f60a2a0 --- /dev/null +++ b/packages/core/src/commands/snapshot/verify.ts @@ -0,0 +1,31 @@ +import { app } from "@arkecosystem/core-container"; +import { SnapshotManager } from "@arkecosystem/core-snapshots"; +import { flags } from "@oclif/command"; +import { setUpLite } from "../../helpers/snapshot"; +import { CommandFlags } from "../../types"; +import { BaseCommand } from "../command"; + +export class VerifyCommand extends BaseCommand { + public static description: string = "check validity of specified snapshot"; + + public static flags: CommandFlags = { + ...BaseCommand.flagsSnapshot, + blocks: flags.string({ + description: "blocks to verify, corelates to folder name", + }), + codec: flags.string({ + description: "codec name, default is msg-lite binary", + }), + signatureVerify: flags.boolean({ + description: "signature verification", + }), + }; + + public async run(): Promise { + const { flags } = await this.parseWithNetwork(VerifyCommand); + + await setUpLite(flags); + + await app.resolvePlugin("snapshots").verifyData(flags); + } +} diff --git a/packages/core/src/commands/top.ts b/packages/core/src/commands/top.ts index fde1e10f87..8e2977205e 100644 --- a/packages/core/src/commands/top.ts +++ b/packages/core/src/commands/top.ts @@ -1,8 +1,9 @@ import Table from "cli-table3"; import dayjs from "dayjs-ext"; -import pm2 from "pm2"; import prettyBytes from "pretty-bytes"; import prettyMs from "pretty-ms"; +import { processManager } from "../process-manager"; +import { CommandFlags } from "../types"; import { renderTable } from "../utils"; import { BaseCommand } from "./command"; @@ -15,45 +16,35 @@ $ ark top `, ]; - public static flags: Record = { + public static flags: CommandFlags = { ...BaseCommand.flagsNetwork, }; public async run(): Promise { const { flags } = await this.parseWithNetwork(TopCommand); - this.createPm2Connection(() => { - pm2.list((error, processList) => { - pm2.disconnect(); - - if (error) { - this.error(error.message); - } - - const processes = Object.values(processList).filter(p => p.name.startsWith(flags.token as string)); - - if (!Object.keys(processes).length) { - this.warn("No processes are running."); - return; - } - - renderTable(["ID", "Name", "Version", "Status", "Uptime", "CPU", "RAM"], (table: Table.Table) => { - for (const process of processes) { - // @ts-ignore - table.push([ - process.pid, - process.name, - // @ts-ignore - process.pm2_env.version, - process.pm2_env.status, - // @ts-ignore - prettyMs(dayjs().diff(process.pm2_env.pm_uptime)), - `${process.monit.cpu}%`, - prettyBytes(process.monit.memory), - ]); - } - }); - }); + const processes = processManager.list(flags.token); + + if (!Object.keys(processes).length) { + this.warn("No processes are running."); + return; + } + + renderTable(["ID", "Name", "Version", "Status", "Uptime", "CPU", "RAM"], (table: Table.Table) => { + for (const process of processes) { + // @ts-ignore + table.push([ + process.pid, + process.name, + // @ts-ignore + process.pm2_env.version, + process.pm2_env.status, + // @ts-ignore + prettyMs(dayjs().diff(process.pm2_env.pm_uptime)), + `${process.monit.cpu}%`, + prettyBytes(process.monit.memory), + ]); + } }); } } diff --git a/packages/core/src/commands/update.ts b/packages/core/src/commands/update.ts index fcd21ca438..92b7ae9efc 100644 --- a/packages/core/src/commands/update.ts +++ b/packages/core/src/commands/update.ts @@ -1,10 +1,57 @@ -import { checkForUpdates } from "../helpers/update"; +import Chalk from "chalk"; +import cli from "cli-ux"; +import { removeSync } from "fs-extra"; +import { confirm } from "../helpers/prompts"; +import { checkForUpdates, installFromChannel } from "../helpers/update"; import { BaseCommand } from "./command"; export class UpdateCommand extends BaseCommand { public static description: string = "Update the core installation"; public async run(): Promise { - await checkForUpdates(this); + const state = await checkForUpdates(this); + + if (!state.ready) { + this.log(`You already have the latest version (${state.currentVersion})`); + + return; + } + + try { + const currentVersion = state.currentVersion; + const newVersion = state.updateVersion; + + this.warn( + `${state.name} update available from ${Chalk.greenBright(currentVersion)} to ${Chalk.greenBright( + newVersion, + )}.`, + ); + + await confirm("Would you like to update?", async () => { + try { + cli.action.start(`Updating from ${currentVersion} to ${newVersion}`); + + await installFromChannel(state.name, state.channel); + + cli.action.stop(); + + removeSync(state.cache); + + this.warn(`Version ${newVersion} has been installed.`); + + const { flags } = await this.parseWithNetwork(UpdateCommand); + + await this.restartProcess(`${flags.token}-core`); + await this.restartProcess(`${flags.token}-relay`); + await this.restartProcess(`${flags.token}-forger`); + } catch (err) { + this.error(err.message); + } finally { + cli.action.stop(); + } + }); + } catch (err) { + this.error(err.message); + } } } diff --git a/packages/core/src/helpers/config.ts b/packages/core/src/helpers/config.ts new file mode 100644 index 0000000000..69e328e2ed --- /dev/null +++ b/packages/core/src/helpers/config.ts @@ -0,0 +1,49 @@ +import { ensureFileSync, readJsonSync, removeSync, writeJsonSync } from "fs-extra"; +import { getRegistryChannel } from "./update"; + +class ConfigManager { + private config; + private file: string; + + public setup(config) { + this.config = config; + this.file = `${config.configDir}/config.json`; + + this.ensureDefaults(); + } + + public get(key) { + return this.read()[key]; + } + + public update(data): void { + this.write({ ...this.read(), ...data }); + } + + private ensureDefaults(): void { + if (!this.read()) { + removeSync(this.file); + + ensureFileSync(this.file); + + this.write({ + token: this.config.bin, + channel: getRegistryChannel(this.config), + }); + } + } + + private read(): any { + try { + return readJsonSync(this.file); + } catch (error) { + return false; + } + } + + private write(data): void { + writeJsonSync(this.file, data); + } +} + +export const configManager = new ConfigManager(); diff --git a/packages/core/src/helpers/prompts.ts b/packages/core/src/helpers/prompts.ts new file mode 100644 index 0000000000..d7998d25f7 --- /dev/null +++ b/packages/core/src/helpers/prompts.ts @@ -0,0 +1,15 @@ +import prompts from "prompts"; + +export async function confirm(message: string, callback: any): Promise { + const { confirm } = await prompts([ + { + type: "confirm", + name: "confirm", + message, + }, + ]); + + if (confirm) { + await callback(); + } +} diff --git a/packages/core/src/helpers/snapshot.ts b/packages/core/src/helpers/snapshot.ts new file mode 100644 index 0000000000..39f4a1b2da --- /dev/null +++ b/packages/core/src/helpers/snapshot.ts @@ -0,0 +1,23 @@ +import { app } from "@arkecosystem/core-container"; + +// tslint:disable-next-line:no-var-requires +const { version } = require("../../package.json"); + +export async function setUpLite(options) { + process.env.CORE_SKIP_BLOCKCHAIN = "true"; + + await app.setUp(version, options, { + include: [ + "@arkecosystem/core-logger", + "@arkecosystem/core-logger-pino", + "@arkecosystem/core-event-emitter", + "@arkecosystem/core-snapshots", + ], + }); + + return app; +} + +export async function tearDown() { + return app.tearDown(); +} diff --git a/packages/core/src/helpers/update.ts b/packages/core/src/helpers/update.ts index ed6909f30a..4484d547f3 100644 --- a/packages/core/src/helpers/update.ts +++ b/packages/core/src/helpers/update.ts @@ -1,16 +1,15 @@ import { IConfig } from "@oclif/config"; -import Chalk from "chalk"; import cli from "cli-ux"; import { shell } from "execa"; import { closeSync, openSync, statSync } from "fs"; import { existsSync } from "fs-extra"; -import { ensureDirSync, removeSync } from "fs-extra"; +import { ensureDirSync } from "fs-extra"; import latestVersion from "latest-version"; import { join } from "path"; -import prompts from "prompts"; import semver from "semver"; +import { configManager } from "./config"; -async function getVersionFromNode(name: string, channel: string): Promise { +async function getLatestVersion(name: string, channel: string): Promise { try { const version = await latestVersion(name, { version: channel }); @@ -23,10 +22,24 @@ async function getVersionFromNode(name: string, channel: string): Promise { +export async function checkForUpdates({ config, error, warn }): Promise { + const state = { + ready: false, + name: config.name, + currentVersion: config.version, + channel: configManager.get("channel"), + }; + if (existsSync(join(__dirname, "../../../..", ".git"))) { if (!process.env.CORE_DEVELOPER_MODE) { warn(`You are using a git clone for developers. Please install core via yarn for auto-updates.`); } - return; + + return state; } try { - const channel = getUpdateChannel(config); const cacheFile = ensureCacheFile(config); cli.action.start(`Checking for updates`); - const remoteVersion = await getVersionFromNode(config.name, channel); + const latestVersion = await getLatestVersion(state.name, state.channel); cli.action.stop(); - closeSync(openSync(cacheFile, "w")); + if (latestVersion === undefined) { + error(`We were unable to find any releases for the "${state.channel}" channel.`); - if (remoteVersion === undefined) { - error(`We were unable to find any releases for the "${channel}" channel.`); + return state; } - if (semver.gt(remoteVersion, config.version)) { - warn( - `${config.name} update available from ${Chalk.greenBright(config.version)} to ${Chalk.greenBright( - remoteVersion, - )}.`, - ); - - const response = await prompts([ - { - type: "confirm", - name: "confirm", - message: `Would you like to update?`, + if (semver.gt(latestVersion, config.version)) { + return { + ...state, + ...{ + ready: true, + updateVersion: latestVersion, + cache: cacheFile, }, - ]); - - if (response.confirm) { - cli.action.start(`Updating from ${config.version} to ${remoteVersion}`); - - try { - const { stdout, stderr } = await shell(`yarn global add ${config.name}@${channel}`); - - if (stderr) { - console.error(stderr); - } - - console.log(stdout); - - removeSync(cacheFile); - - cli.action.stop(); - - warn(`Version ${remoteVersion} has been installed. Please restart your relay and forger.`); - - process.exit(); - } catch (err) { - error(err.message); - } - } - } else { - log(`You already have the latest version (${config.version})`); + }; } } catch (err) { error(err.message); + } finally { + cli.action.stop(); } + + return state; } diff --git a/packages/core/src/hooks/init.ts b/packages/core/src/hooks/init.ts deleted file mode 100644 index 7f36278345..0000000000 --- a/packages/core/src/hooks/init.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Hook } from "@oclif/config"; -import { checkForUpdates, needsRefresh } from "../helpers/update"; - -// tslint:disable-next-line:only-arrow-functions -export const init: Hook<"init"> = async function({ id, config }) { - if (id === "update") { - return; - } - - if (!needsRefresh(config)) { - return; - } - - await checkForUpdates(this); -}; diff --git a/packages/core/src/hooks/init/config.ts b/packages/core/src/hooks/init/config.ts new file mode 100644 index 0000000000..0999f41832 --- /dev/null +++ b/packages/core/src/hooks/init/config.ts @@ -0,0 +1,7 @@ +import { Hook } from "@oclif/config"; +import { configManager } from "../../helpers/config"; + +// tslint:disable-next-line:only-arrow-functions +export const init: Hook<"init"> = async function({ config }) { + configManager.setup(config); +}; diff --git a/packages/core/src/hooks/init/update.ts b/packages/core/src/hooks/init/update.ts new file mode 100644 index 0000000000..92d0a0ffed --- /dev/null +++ b/packages/core/src/hooks/init/update.ts @@ -0,0 +1,37 @@ +import { Hook } from "@oclif/config"; +import Chalk from "chalk"; +import cli from "cli-ux"; +import { checkForUpdates, needsRefresh } from "../../helpers/update"; + +// tslint:disable-next-line:only-arrow-functions +export const init: Hook<"init"> = async function({ id, config }) { + if (id === "update") { + return; + } + + if (!needsRefresh(config)) { + return; + } + + const state = await checkForUpdates(this); + + if (!state.ready) { + this.warn( + `${state.name} update available from ${Chalk.greenBright(state.currentVersion)} to ${Chalk.greenBright( + state.updateVersion, + )}. Review the latest release and run "ark update" once you wish to update.`, + ); + + const branch = { + alpha: "develop", + beta: "develop", + rc: "develop", + latest: "master", + }[state.channel]; + + await cli.url( + `Click here to read the changelog for ${state.currentVersion}.`, + `https://github.com/ArkEcosystem/core/blob/${branch}/CHANGELOG.md`, + ); + } +}; diff --git a/packages/core/src/process-manager.ts b/packages/core/src/process-manager.ts new file mode 100644 index 0000000000..19a471aae6 --- /dev/null +++ b/packages/core/src/process-manager.ts @@ -0,0 +1,72 @@ +import { ExecaReturns, shellSync } from "execa"; +import { ProcessDescription } from "./types"; + +class ProcessManager { + public start(opts: Record, noDaemonMode: boolean): any { + const flags = ["--max-restarts=5", "--kill-timeout=30000"]; + + if (noDaemonMode) { + flags.push("--no-daemon"); + } + + return this.exec(`pm2 --name ${opts.name} ${flags.join(" ")} start ${opts.script} -- ${opts.args}`); + } + + public stop(name: string): boolean { + return this.execWithHandler(`pm2 stop ${name}`); + } + + public restart(name: string): boolean { + return this.execWithHandler(`pm2 restart ${name}`); + } + + public delete(name: string): boolean { + return this.execWithHandler(`pm2 delete ${name}`); + } + + public describe(name: string): any { + try { + const { stdout } = this.exec("pm2 jlist"); + + return JSON.parse(stdout).find(p => p.name === name); + } catch (error) { + return false; + } + } + + public exists(name: string): boolean { + try { + const { stdout } = this.exec(`pm2 id ${name} | awk '{ print $2 }'`); + + return !!stdout; + } catch (error) { + return false; + } + } + + public list(token: string): any { + try { + const { stdout } = this.exec("pm2 jlist"); + + return Object.values(JSON.parse(stdout)).filter((p: ProcessDescription) => p.name.startsWith(token)); + } catch (error) { + return false; + } + } + + private exec(command: string): ExecaReturns { + return shellSync(command); + } + + private execWithHandler(command: string): boolean { + try { + this.exec(command); + + return true; + } catch (error) { + return false; + } + } +} + +export const processManager = new ProcessManager(); diff --git a/packages/core/src/shared/log.ts b/packages/core/src/shared/log.ts index 0e35d86512..bb1f263005 100644 --- a/packages/core/src/shared/log.ts +++ b/packages/core/src/shared/log.ts @@ -1,7 +1,7 @@ import cli from "cli-ux"; -import pm2, { ProcessDescription } from "pm2"; import { Tail } from "tail"; import { BaseCommand } from "../commands/command"; +import { processManager } from "../process-manager"; export abstract class AbstractLogCommand extends BaseCommand { public async run(): Promise { @@ -9,37 +9,28 @@ export abstract class AbstractLogCommand extends BaseCommand { const processName = `${flags.token}-${this.getSuffix()}`; - this.createPm2Connection(() => { - pm2.describe(processName, (error, apps) => { - pm2.disconnect(); + if (!processManager.exists(processName)) { + this.warn(`The "${processName}" process is not running.`); + return; + } - if (error) { - this.error(error.message); - } + const { pm2_env } = processManager.describe(processName); - if (!apps[0]) { - this.warn(`The "${processName}" process is not running.`); - return; - } + const file = flags.error ? pm2_env.pm_err_log_path : pm2_env.pm_out_log_path; - const { pm2_env } = apps[0]; - const file = flags.error ? pm2_env.pm_err_log_path : pm2_env.pm_out_log_path; + const log = new Tail(file); - const log = new Tail(file); + cli.action.start(`Waiting for ${file}`); - cli.action.start(`Waiting for ${file}`); + log.on("line", data => { + console.log(data); - log.on("line", data => { - console.log(data); - - if (cli.action.running) { - cli.action.stop(); - } - }); - - log.on("error", error => console.error("ERROR: ", error)); - }); + if (cli.action.running) { + cli.action.stop(); + } }); + + log.on("error", error => console.error("ERROR: ", error)); } public abstract getClass(); diff --git a/packages/core/src/shared/restart.ts b/packages/core/src/shared/restart.ts index 373941f4a9..dd663be209 100644 --- a/packages/core/src/shared/restart.ts +++ b/packages/core/src/shared/restart.ts @@ -1,6 +1,6 @@ import cli from "cli-ux"; -import pm2 from "pm2"; import { BaseCommand } from "../commands/command"; +import { processManager } from "../process-manager"; export abstract class AbstractRestartCommand extends BaseCommand { public async run(): Promise { @@ -8,26 +8,15 @@ export abstract class AbstractRestartCommand extends BaseCommand { const processName = `${flags.token}-${this.getSuffix()}`; - cli.action.start(`Restarting ${processName}`); + try { + cli.action.start(`Restarting ${processName}`); - this.createPm2Connection(() => { - pm2.reload(processName, error => { - pm2.disconnect(); - - cli.action.stop(); - - if (error) { - if (error.message === "process name not found") { - this.warn(`The "${processName}" process does not exist.`); - return; - } - - throw error; - } - - process.exit(); - }); - }); + processManager.restart(processName); + } catch (error) { + this.warn(`The "${processName}" process does not exist.`); + } finally { + cli.action.stop(); + } } public abstract getClass(); diff --git a/packages/core/src/shared/start.ts b/packages/core/src/shared/start.ts index 7fae3c0b06..9490138a73 100644 --- a/packages/core/src/shared/start.ts +++ b/packages/core/src/shared/start.ts @@ -1,7 +1,8 @@ import cli from "cli-ux"; -import pm2, { ProcessDescription } from "pm2"; import prompts from "prompts"; import { BaseCommand } from "../commands/command"; +import { processManager } from "../process-manager"; +import { CommandFlags, ProcessDescription } from "../types"; export abstract class AbstractStartCommand extends BaseCommand { public async run(): Promise { @@ -12,78 +13,44 @@ export abstract class AbstractStartCommand extends BaseCommand { public abstract getClass(); - protected abstract async runProcess(flags: Record): Promise; + protected abstract async runProcess(flags: CommandFlags): Promise; - protected runWithPm2(options: any, flags: Record) { + protected async runWithPm2(options: any, flags: CommandFlags) { const processName = options.name; - const noDaemonMode = flags.daemon === false; - this.createPm2Connection(() => { - pm2.describe(processName, async (error, apps) => { - if (error) { - this.error(error.message); - } - - if (apps[0]) { - if (apps[0].pm2_env.status === "online") { - const response = await prompts({ - type: "confirm", - name: "confirm", - message: "A process is already running, would you like to restart it?", - }); - - if (!response.confirm) { - this.warn(`The "${processName}" process has not been restarted.`); - - pm2.disconnect(); - - process.exit(); - } - } + try { + if (processManager.exists(processName)) { + const app: ProcessDescription = processManager.describe(processName); + if (app.pm2_env.status === "online") { cli.action.start(`Restarting ${processName}`); - pm2.reload(processName, error => { - pm2.disconnect(); - - if (error) { - this.error(error.message); - } - - cli.action.stop(); - - process.exit(); + const response = await prompts({ + type: "confirm", + name: "confirm", + message: "A process is already running, would you like to restart it?", }); - } else { - cli.action.start(`Starting ${processName}`); - - pm2.start( - { - ...{ - max_restarts: 5, - min_uptime: "5m", - kill_timeout: 30000, - }, - ...options, - }, - error => { - pm2.disconnect(); - - if (error) { - this.error(error.message); - } - - cli.action.stop(); - process.exit(); - }, - ); + if (!response.confirm) { + this.warn(`The "${processName}" process has not been restarted.`); + return; + } } - }); - }, noDaemonMode); + } else { + cli.action.start(`Starting ${processName}`); + + processManager.start(options, flags.daemon === false); + } + } catch (error) { + this.error(error.message); + } finally { + cli.action.stop(); + } } - protected abortWhenRunning(processName: string, app: ProcessDescription): void { + protected abortWhenRunning(processName: string): void { + const app: ProcessDescription = processManager.describe(processName); + if (app && app.pm2_env.status === "online") { this.warn(`The "${processName}" process is already running.`); process.exit(); diff --git a/packages/core/src/shared/status.ts b/packages/core/src/shared/status.ts index c3c69e0ea6..2b3d547db9 100644 --- a/packages/core/src/shared/status.ts +++ b/packages/core/src/shared/status.ts @@ -1,9 +1,10 @@ import Table from "cli-table3"; import dayjs from "dayjs-ext"; -import pm2, { ProcessDescription } from "pm2"; import prettyBytes from "pretty-bytes"; import prettyMs from "pretty-ms"; import { BaseCommand } from "../commands/command"; +import { processManager } from "../process-manager"; +import { ProcessDescription } from "../types"; import { renderTable } from "../utils"; export abstract class AbstractStatusCommand extends BaseCommand { @@ -12,36 +13,26 @@ export abstract class AbstractStatusCommand extends BaseCommand { const processName = `${flags.token}-${this.getSuffix()}`; - this.createPm2Connection(() => { - pm2.describe(processName, (error, apps) => { - pm2.disconnect(); - - if (error) { - this.error(error.message); - } - - if (!apps[0]) { - this.warn(`The "${processName}" process is not running.`); - return; - } - - renderTable(["ID", "Name", "Version", "Status", "Uptime", "CPU", "RAM"], (table: Table.Table) => { - const process: ProcessDescription = apps[0]; - - // @ts-ignore - table.push([ - process.pid, - process.name, - // @ts-ignore - process.pm2_env.version, - process.pm2_env.status, - // @ts-ignore - prettyMs(dayjs().diff(process.pm2_env.pm_uptime)), - `${process.monit.cpu}%`, - prettyBytes(process.monit.memory), - ]); - }); - }); + if (!processManager.exists(processName)) { + this.warn(`The "${processName}" process is not running.`); + return; + } + + renderTable(["ID", "Name", "Version", "Status", "Uptime", "CPU", "RAM"], (table: Table.Table) => { + const app: ProcessDescription = processManager.describe(processName); + + // @ts-ignore + table.push([ + app.pid, + app.name, + // @ts-ignore + app.pm2_env.version, + app.pm2_env.status, + // @ts-ignore + prettyMs(dayjs().diff(app.pm2_env.pm_uptime)), + `${app.monit.cpu}%`, + prettyBytes(app.monit.memory), + ]); }); } diff --git a/packages/core/src/shared/stop.ts b/packages/core/src/shared/stop.ts index da8e5e0645..75867b17c7 100644 --- a/packages/core/src/shared/stop.ts +++ b/packages/core/src/shared/stop.ts @@ -1,6 +1,6 @@ import cli from "cli-ux"; -import pm2 from "pm2"; import { BaseCommand } from "../commands/command"; +import { processManager } from "../process-manager"; export abstract class AbstractStopCommand extends BaseCommand { public async run(): Promise { @@ -8,28 +8,15 @@ export abstract class AbstractStopCommand extends BaseCommand { const processName = `${flags.token}-${this.getSuffix()}`; - this.createPm2Connection(() => { - const method = flags.daemon ? "delete" : "stop"; - + try { cli.action.start(`Stopping ${processName}`); - pm2[method](processName, error => { - pm2.disconnect(); - - cli.action.stop(); - - if (error) { - if (error.message === "process name not found") { - this.warn(`The "${processName}" process does not exist.`); - return; - } - - throw error; - } - - process.exit(); - }); - }); + processManager[flags.daemon ? "delete" : "stop"](processName); + } catch (error) { + this.warn(`The "${processName}" process does not exist.`); + } finally { + cli.action.stop(); + } } public abstract getClass(); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts new file mode 100644 index 0000000000..2ae74e9a6d --- /dev/null +++ b/packages/core/src/types.ts @@ -0,0 +1,5 @@ +export type ProcessDescription = Record; + +export type CommandFlags = Record; + +export type Options = Record; diff --git a/packages/crypto/__tests__/crypto/crypto.test.ts b/packages/crypto/__tests__/crypto/crypto.test.ts index 9a53e21b59..fef461e6e6 100644 --- a/packages/crypto/__tests__/crypto/crypto.test.ts +++ b/packages/crypto/__tests__/crypto/crypto.test.ts @@ -166,9 +166,7 @@ describe("crypto.ts", () => { it("should fail this.getHash for transaction version > 1", () => { const transactionV2 = Object.assign({}, transaction, { version: 2 }); - expect(() => crypto.verifySecondSignature(transactionV2, keys2.publicKey)).toThrow( - TransactionVersionError - ); + expect(() => crypto.verifySecondSignature(transactionV2, keys2.publicKey)).toThrow(TransactionVersionError); }); }); diff --git a/packages/crypto/__tests__/models/block.test.ts b/packages/crypto/__tests__/models/block.test.ts index 42f90ab567..bc5cb5a9e0 100644 --- a/packages/crypto/__tests__/models/block.test.ts +++ b/packages/crypto/__tests__/models/block.test.ts @@ -144,6 +144,7 @@ describe("Models - Block", () => { maxPayload: 0, }, reward: 200000000, + vendorFieldLength: 64, })); const block = new Block(dummyBlock); @@ -155,7 +156,7 @@ describe("Models - Block", () => { it("should fail to verify a block if error is thrown", () => { const errorMessage = "Very very, very bad error"; - jest.spyOn(configManager, "getMilestone").mockImplementation(height => { + jest.spyOn(slots, "getSlotNumber").mockImplementation(height => { throw errorMessage; }); const block = new Block(dummyBlock); diff --git a/packages/crypto/__tests__/models/delegate.test.ts b/packages/crypto/__tests__/models/delegate.test.ts index f7cc427e45..d81380548d 100644 --- a/packages/crypto/__tests__/models/delegate.test.ts +++ b/packages/crypto/__tests__/models/delegate.test.ts @@ -3,11 +3,8 @@ import "jest-extended"; import { generators } from "@arkecosystem/core-test-utils"; const { generateSecondSignature } = generators; -import { SATOSHI } from "../../src/constants"; -import { configManager } from "../../src/managers/config"; import { ITransactionData } from "../../src/models"; import { Delegate } from "../../src/models/delegate"; -import { Wallet } from "../../src/models/wallet"; import { INetwork, testnet } from "../../src/networks"; import { Bignum } from "../../src/utils"; import { sortTransactions } from "../../src/utils"; diff --git a/packages/crypto/__tests__/transactions/deserializers/transaction.test.ts b/packages/crypto/__tests__/transactions/deserializers/transaction.test.ts index 566035d80d..4f4c0d6660 100644 --- a/packages/crypto/__tests__/transactions/deserializers/transaction.test.ts +++ b/packages/crypto/__tests__/transactions/deserializers/transaction.test.ts @@ -2,7 +2,8 @@ import "jest-extended"; import ByteBuffer from "bytebuffer"; import { client } from "../../../src/client"; -import { TransactionVersionError, UnkownTransactionError } from "../../../src/errors"; +import { TransactionSchemaError, TransactionVersionError, UnkownTransactionError } from "../../../src/errors"; +import { configManager } from "../../../src/managers"; import { Transaction } from "../../../src/models"; import { TransactionDeserializer } from "../../../src/transactions/deserializers"; import { TransactionSerializer } from "../../../src/transactions/serializers"; @@ -52,6 +53,51 @@ describe("Transaction serializer / deserializer", () => { expect(deserialized.data.vendorField).toBe(vendorField); expect(deserialized.data.recipientId).toBe(transfer.recipientId); }); + + it("should ser/deserialize with long vendorfield when vendorFieldLength=255 milestone is active", () => { + configManager.getMilestone().vendorFieldLength = 255; + + const transferWithLongVendorfield = client + .getBuilder() + .transfer() + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(10000) + .fee(50000000) + .vendorField("y".repeat(255)) + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + const serialized = Transaction.toBytes(transferWithLongVendorfield); + const deserialized = Transaction.fromBytes(serialized); + + expect(deserialized.verified).toBeTrue(); + expect(deserialized.data.vendorField).toHaveLength(255); + expect(deserialized.data.vendorFieldHex).toHaveLength(510); + expect(deserialized.data.vendorField).toEqual("y".repeat(255)); + + configManager.getMilestone().vendorFieldLength = 64; + }); + + it("should not ser/deserialize long vendorfield when vendorFieldLength=255 milestone is not active", () => { + const transferWithLongVendorfield = client + .getBuilder() + .transfer() + .recipientId("D5q7YfEFDky1JJVQQEy4MGyiUhr5cGg47F") + .amount(10000) + .fee(50000000) + .version(1) + .network(30) + .sign("dummy passphrase") + .getStruct(); + + transferWithLongVendorfield.vendorField = "y".repeat(255); + expect(() => { + const serialized = Transaction.toBytes(transferWithLongVendorfield); + Transaction.fromBytes(serialized); + }).toThrow(TransactionSchemaError); + }); }); describe("ser/deserialize - second signature", () => { diff --git a/packages/crypto/__tests__/validation/formats.test.ts b/packages/crypto/__tests__/validation/formats.test.ts new file mode 100644 index 0000000000..c8f9d6bb9e --- /dev/null +++ b/packages/crypto/__tests__/validation/formats.test.ts @@ -0,0 +1,73 @@ +import "jest-extended"; + +import { configManager } from "../../src"; +import { AjvWrapper } from "../../src/validation"; + +const ajv = AjvWrapper.instance(); + +describe("format vendorField", () => { + it("should be ok with 64 bytes", () => { + const schema = { type: "string", format: "vendorField" }; + const validate = ajv.compile(schema); + + expect(validate("1234")).toBeTrue(); + expect(validate("a".repeat(64))).toBeTrue(); + expect(validate("a".repeat(65))).toBeFalse(); + expect(validate("⊁".repeat(21))).toBeTrue(); + expect(validate("⊁".repeat(22))).toBeFalse(); + expect(validate({})).toBeFalse(); + expect(validate(null)).toBeFalse(); + expect(validate(undefined)).toBeFalse(); + }); + + it("should not be ok with over 64 bytes without milestone ", () => { + const schema = { type: "string", format: "vendorField" }; + const validate = ajv.compile(schema); + expect(validate("a".repeat(65))).toBeFalse(); + }); + + it("should be ok with up to 255 bytes with milestone ", () => { + configManager.getMilestone().vendorFieldLength = 255; + const schema = { type: "string", format: "vendorField" }; + const validate = ajv.compile(schema); + expect(validate("a".repeat(65))).toBeTrue(); + expect(validate("⊁".repeat(85))).toBeTrue(); + expect(validate("a".repeat(256))).toBeFalse(); + expect(validate("⊁".repeat(86))).toBeFalse(); + + configManager.getMilestone().vendorFieldLength = 64; + }); +}); + +describe("format vendorFieldHex", () => { + it("should be ok with 128 hex", () => { + const schema = { type: "string", format: "vendorFieldHex" }; + const validate = ajv.compile(schema); + + expect(validate("affe".repeat(32))).toBeTrue(); + expect(validate("affe".repeat(33))).toBeFalse(); + expect(validate("⊁".repeat(22))).toBeFalse(); + }); + + it("should be ok with 510 hex when milestone vendorFieldLength=255 is active", () => { + configManager.getMilestone().vendorFieldLength = 255; + const schema = { type: "string", format: "vendorFieldHex" }; + const validate = ajv.compile(schema); + + expect(validate("affe".repeat(127))).toBeTrue(); + expect(validate("affe".repeat(128))).toBeFalse(); + + configManager.getMilestone().vendorFieldLength = 64; + }); + + it("should not be ok with non hex", () => { + const schema = { type: "string", format: "vendorFieldHex" }; + const validate = ajv.compile(schema); + expect(validate("Z")).toBeFalse(); + expect(validate("Zaffe")).toBeFalse(); + expect(validate("⊁")).toBeFalse(); + expect(validate({})).toBeFalse(); + expect(validate(null)).toBeFalse(); + expect(validate(undefined)).toBeFalse(); + }); +}); diff --git a/packages/crypto/package.json b/packages/crypto/package.json index ffe0601e11..d6bea565bd 100644 --- a/packages/crypto/package.json +++ b/packages/crypto/package.json @@ -1,7 +1,7 @@ { "name": "@arkecosystem/crypto", "description": "Crypto utilities for the Ark Blockchain", - "version": "2.2.0-beta.4", + "version": "2.2.0-beta.7", "contributors": [ "François-Xavier Thoorens ", "Brian Faust ", @@ -55,6 +55,7 @@ "@types/wif": "^2.0.1", "ajv": "^6.9.1", "ajv-keywords": "^3.4.0", + "bcrypto": "^3.0.2", "bignumber.js": "^8.0.2", "bip32": "^1.0.2", "bip39": "^2.5.0", diff --git a/packages/crypto/src/builder/transactions/transaction.ts b/packages/crypto/src/builder/transactions/transaction.ts index 347e3b14ba..03a2670f53 100644 --- a/packages/crypto/src/builder/transactions/transaction.ts +++ b/packages/crypto/src/builder/transactions/transaction.ts @@ -3,7 +3,7 @@ import { MissingTransactionSignatureError } from "../../errors"; import { configManager } from "../../managers"; import { INetwork } from "../../networks"; import { ITransactionData, Transaction } from "../../transactions"; -import { Bignum } from "../../utils"; +import { Bignum, maxVendorFieldLength } from "../../utils"; export abstract class TransactionBuilder> { public data: ITransactionData; @@ -80,7 +80,7 @@ export abstract class TransactionBuilder { + sha256 = sha256.update(element); + }); + + return sha256.final(); + } + + return SHA256.digest(this.bufferize(buffer)); } /** * Create a "hash160" buffer. */ public static hash160(buffer: Buffer | string): Buffer { - return this.ripemd160(this.sha256(buffer)); + return Hash160.digest(this.bufferize(buffer)); } /** * Create a "hash256" buffer. */ public static hash256(buffer: Buffer | string): Buffer { - return this.sha256(this.sha256(buffer)); + return Hash256.digest(this.bufferize(buffer)); } + + private static bufferize = (buffer: Buffer | string) => + (buffer = buffer instanceof Buffer ? buffer : Buffer.from(buffer)); } diff --git a/packages/crypto/src/models/block.ts b/packages/crypto/src/models/block.ts index 558cb3cb23..a11645588c 100644 --- a/packages/crypto/src/models/block.ts +++ b/packages/crypto/src/models/block.ts @@ -1,6 +1,5 @@ -import { createHash } from "crypto"; import pluralize from "pluralize"; -import { crypto, slots } from "../crypto"; +import { crypto, HashAlgorithms, slots } from "../crypto"; import { configManager } from "../managers/config"; import { ITransactionData, Transaction } from "../transactions"; import { BlockDeserializer } from "../transactions/deserializers"; @@ -75,9 +74,7 @@ export class Block implements IBlock { data.generatorPublicKey = keys.publicKey; const payloadHash: Buffer = Block.serialize(data, false); - const hash = createHash("sha256") - .update(payloadHash) - .digest(); + const hash = HashAlgorithms.sha256(payloadHash); data.blockSignature = crypto.signHash(hash, keys); data.id = Block.getId(data); @@ -110,9 +107,7 @@ export class Block implements IBlock { const constants = configManager.getMilestone(data.id); const payloadHash: any = Block.serialize(data); - const hash = createHash("sha256") - .update(payloadHash) - .digest(); + const hash = HashAlgorithms.sha256(payloadHash); if (constants.block.idFullSha256) { return hash.toString("hex"); @@ -215,9 +210,7 @@ export class Block implements IBlock { */ public verifySignature(): boolean { const bytes: any = Block.serialize(this.data, false); - const hash = createHash("sha256") - .update(bytes) - .digest(); + const hash = HashAlgorithms.sha256(bytes); return crypto.verifyHash(hash, this.data.blockSignature, this.data.generatorPublicKey); } @@ -278,7 +271,6 @@ export class Block implements IBlock { // } let size = 0; - const payloadHash = createHash("sha256"); const invalidTransactions = this.transactions.filter(tx => !tx.verified); if (invalidTransactions.length > 0) { result.errors.push("One or more transactions are not verified:"); @@ -299,6 +291,7 @@ export class Block implements IBlock { const appliedTransactions = {}; let totalAmount = Bignum.ZERO; let totalFee = Bignum.ZERO; + const payloadBuffers = []; this.transactions.forEach(transaction => { const bytes = Buffer.from(transaction.data.id, "hex"); @@ -312,7 +305,7 @@ export class Block implements IBlock { totalFee = totalFee.plus(transaction.data.fee); size += bytes.length; - payloadHash.update(bytes); + payloadBuffers.push(bytes); }); if (!totalAmount.isEqualTo(block.totalAmount)) { @@ -327,7 +320,7 @@ export class Block implements IBlock { result.errors.push("Payload is too large"); } - if (payloadHash.digest().toString("hex") !== block.payloadHash) { + if (HashAlgorithms.sha256(payloadBuffers).toString("hex") !== block.payloadHash) { result.errors.push("Invalid payload hash"); } } catch (error) { diff --git a/packages/crypto/src/models/delegate.ts b/packages/crypto/src/models/delegate.ts index 7213ed6eed..ccfbf68a9c 100644 --- a/packages/crypto/src/models/delegate.ts +++ b/packages/crypto/src/models/delegate.ts @@ -1,14 +1,14 @@ -import { createHash } from "crypto"; import forge from "node-forge"; import { authenticator } from "otplib"; import wif from "wif"; import * as bip38 from "../crypto/bip38"; import { Bignum } from "../utils"; +import { HashAlgorithms } from "../crypto"; import { crypto } from "../crypto/crypto"; import { KeyPair } from "../identities"; import { INetwork } from "../networks"; -import { ITransactionData, Transaction } from "../transactions"; +import { ITransactionData } from "../transactions"; import { sortTransactions } from "../utils"; import { Block, IBlockData } from "./block"; @@ -98,14 +98,14 @@ export class Delegate { const transactionData = { amount: Bignum.ZERO, fee: Bignum.ZERO, - sha256: createHash("sha256"), }; + const payloadBuffers = []; const sortedTransactions = sortTransactions(transactions); sortedTransactions.forEach(transaction => { transactionData.amount = transactionData.amount.plus(transaction.amount); transactionData.fee = transactionData.fee.plus(transaction.fee); - transactionData.sha256.update(Buffer.from(transaction.id, "hex")); + payloadBuffers.push(Buffer.from(transaction.id, "hex")); }); const data: IBlockData = { @@ -120,7 +120,7 @@ export class Delegate { totalFee: transactionData.fee, reward: options.reward, payloadLength: 32 * sortedTransactions.length, - payloadHash: transactionData.sha256.digest().toString("hex"), + payloadHash: HashAlgorithms.sha256(payloadBuffers).toString("hex"), transactions: sortedTransactions, }; diff --git a/packages/crypto/src/networks/devnet/milestones.json b/packages/crypto/src/networks/devnet/milestones.json index ca111b9371..ffd5a39a08 100644 --- a/packages/crypto/src/networks/devnet/milestones.json +++ b/packages/crypto/src/networks/devnet/milestones.json @@ -23,7 +23,8 @@ "delegateResignation": 0 } }, - "ignoreInvalidSecondSignatureField": true + "ignoreInvalidSecondSignatureField": true, + "vendorFieldLength": 64 }, { "height": 10800, @@ -48,7 +49,11 @@ "ignoreInvalidSecondSignatureField": false }, { - "height": 1610000, + "height": 1750000, + "vendorFieldLength": 255 + }, + { + "height": 1750001, "block": { "idFullSha256": true } diff --git a/packages/crypto/src/networks/mainnet/milestones.json b/packages/crypto/src/networks/mainnet/milestones.json index 10a47088cf..00ac119c37 100644 --- a/packages/crypto/src/networks/mainnet/milestones.json +++ b/packages/crypto/src/networks/mainnet/milestones.json @@ -22,7 +22,8 @@ "multiPayment": 0, "delegateResignation": 0 } - } + }, + "vendorFieldLength": 64 }, { "height": 75600, @@ -36,7 +37,11 @@ } }, { - "height": 7400000, + "height": 8000000, + "vendorFieldLength": 255 + }, + { + "height": 8000001, "block": { "idFullSha256": true } diff --git a/packages/crypto/src/networks/testnet/milestones.json b/packages/crypto/src/networks/testnet/milestones.json index 98c4716e5e..9b794122a0 100644 --- a/packages/crypto/src/networks/testnet/milestones.json +++ b/packages/crypto/src/networks/testnet/milestones.json @@ -22,14 +22,19 @@ "multiPayment": 0, "delegateResignation": 0 } - } + }, + "vendorFieldLength": 64 }, { "height": 75600, "reward": 200000000 }, { - "height": 76000, + "height": 100000, + "vendorFieldLength": 255 + }, + { + "height": 100001, "block": { "idFullSha256": true } diff --git a/packages/crypto/src/networks/unitnet/milestones.json b/packages/crypto/src/networks/unitnet/milestones.json index 98c4716e5e..9b794122a0 100644 --- a/packages/crypto/src/networks/unitnet/milestones.json +++ b/packages/crypto/src/networks/unitnet/milestones.json @@ -22,14 +22,19 @@ "multiPayment": 0, "delegateResignation": 0 } - } + }, + "vendorFieldLength": 64 }, { "height": 75600, "reward": 200000000 }, { - "height": 76000, + "height": 100000, + "vendorFieldLength": 255 + }, + { + "height": 100001, "block": { "idFullSha256": true } diff --git a/packages/crypto/src/transactions/index.ts b/packages/crypto/src/transactions/index.ts index 0633d36948..6f31f004e0 100644 --- a/packages/crypto/src/transactions/index.ts +++ b/packages/crypto/src/transactions/index.ts @@ -1,3 +1,5 @@ export * from "./interfaces"; export * from "./types"; +export * from "./deserializers"; +export * from "./serializers"; export { transactionRegistry as TransactionRegistry } from "./registry"; diff --git a/packages/crypto/src/transactions/serializers/block.ts b/packages/crypto/src/transactions/serializers/block.ts index ad17ad2df4..22ff08332b 100644 --- a/packages/crypto/src/transactions/serializers/block.ts +++ b/packages/crypto/src/transactions/serializers/block.ts @@ -15,7 +15,7 @@ class BlockSerializer { .skip(transactions.length * 4); for (let i = 0; i < transactions.length; i++) { - const { serialized } = Transaction.fromData(transactions[i]); + const serialized = Transaction.toBytes(transactions[i]); buffer.writeUint32(serialized.length, serializedHeader.length + i * 4); buffer.append(serialized); } diff --git a/packages/crypto/src/transactions/serializers/transaction.ts b/packages/crypto/src/transactions/serializers/transaction.ts index 327024fb26..0b087d0407 100644 --- a/packages/crypto/src/transactions/serializers/transaction.ts +++ b/packages/crypto/src/transactions/serializers/transaction.ts @@ -5,7 +5,7 @@ import ByteBuffer from "bytebuffer"; import { TransactionTypes } from "../../constants"; import { TransactionVersionError } from "../../errors"; import { configManager } from "../../managers"; -import { Bignum } from "../../utils"; +import { Bignum, maxVendorFieldLength } from "../../utils"; import { ITransactionData } from "../interfaces"; import { Transaction } from "../types"; @@ -146,11 +146,11 @@ export class TransactionSerializer { for (let i = 0; i < fillstart; i++) { bb.writeByte(vf[i]); } - for (let i = fillstart; i < 64; i++) { + for (let i = fillstart; i < maxVendorFieldLength(); i++) { bb.writeByte(0); } } else { - for (let i = 0; i < 64; i++) { + for (let i = 0; i < maxVendorFieldLength(); i++) { bb.writeByte(0); } } diff --git a/packages/crypto/src/transactions/types/schemas.ts b/packages/crypto/src/transactions/types/schemas.ts index ee4ef6f624..f1531801e7 100644 --- a/packages/crypto/src/transactions/types/schemas.ts +++ b/packages/crypto/src/transactions/types/schemas.ts @@ -51,8 +51,8 @@ export const transfer = extend(transactionBaseSchema, { required: ["recipientId", "amount"], properties: { type: { transactionType: TransactionTypes.Transfer }, - vendorField: { anyOf: [{ type: "null" }, { type: "string", maxBytes: 64 }] }, - vendorFieldHex: { anyOf: [{ type: "null" }, { $ref: "hex", maximumLength: 128 }] }, + vendorField: { anyOf: [{ type: "null" }, { type: "string", format: "vendorField" }] }, + vendorFieldHex: { anyOf: [{ type: "null" }, { type: "string", format: "vendorFieldHex" }] }, recipientId: { $ref: "address" }, }, }); diff --git a/packages/crypto/src/utils.ts b/packages/crypto/src/utils.ts index 1b58820e98..da289a7833 100644 --- a/packages/crypto/src/utils.ts +++ b/packages/crypto/src/utils.ts @@ -72,4 +72,6 @@ export const isGenesisTransaction = (id: string): boolean => { return genesisTransactions[id]; }; +export const maxVendorFieldLength = (height?: number): number => configManager.getMilestone(height).vendorFieldLength; + export { Bignum }; diff --git a/packages/crypto/src/validation/ajv-wrapper.ts b/packages/crypto/src/validation/ajv-wrapper.ts index f0c8c53b94..2532f2cf68 100644 --- a/packages/crypto/src/validation/ajv-wrapper.ts +++ b/packages/crypto/src/validation/ajv-wrapper.ts @@ -4,6 +4,7 @@ import ajvKeywords from "ajv-keywords"; import { TransactionSchemaAlreadyExistsError } from "../errors"; import { ISchemaValidationResult } from "../models"; import { signedSchema, strictSchema, TransactionSchema } from "../transactions/types/schemas"; +import { formats } from "./formats"; import { keywords } from "./keywords"; import { schemas } from "./schemas"; @@ -19,6 +20,10 @@ class AjvWrapper { addKeyword(ajv); }); + formats.forEach(addFormat => { + addFormat(ajv); + }); + this.ajv = ajv; } diff --git a/packages/crypto/src/validation/formats.ts b/packages/crypto/src/validation/formats.ts new file mode 100644 index 0000000000..b33acc56d4 --- /dev/null +++ b/packages/crypto/src/validation/formats.ts @@ -0,0 +1,28 @@ +import { Ajv } from "ajv"; +import { maxVendorFieldLength } from "../utils"; + +const vendorField = (ajv: Ajv) => { + ajv.addFormat("vendorField", data => { + try { + return Buffer.from(data, "utf8").length <= maxVendorFieldLength(); + } catch { + return false; + } + }); +}; + +const vendorFieldHex = (ajv: Ajv) => { + ajv.addFormat("vendorFieldHex", data => { + try { + if (/^[0123456789A-Fa-f]+$/.test(data)) { + return Buffer.from(data, "hex").length <= maxVendorFieldLength(); + } + } catch { + return false; + } + + return false; + }); +}; + +export const formats = [vendorField, vendorFieldHex]; diff --git a/yarn.lock b/yarn.lock index b23b7e8b5e..3fb5fcc697 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,19 +2,7 @@ # yarn lockfile v1 -"@apollographql/apollo-tools@^0.3.0": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.3.2.tgz#415c984955c4ae249550def698cbffab8d27bda4" - integrity sha512-IiuO1XaxxvbZa19gGHBOEgQKmMHB+ghXaNjSbY5dWqCEQgBDgJBA/2a1Oq9tMPhEPAmEY2FOuhaWRxxKxmVdlQ== - dependencies: - apollo-env "0.3.2" - -"@apollographql/graphql-playground-html@^1.6.6": - version "1.6.6" - resolved "https://registry.yarnpkg.com/@apollographql/graphql-playground-html/-/graphql-playground-html-1.6.6.tgz#022209e28a2b547dcde15b219f0c50f47aa5beb3" - integrity sha512-lqK94b+caNtmKFs5oUVXlSpN3sm5IXZ+KfhMxOtr0LR2SqErzkoJilitjDvJ1WbjHlxLI7WtCjRmOLdOGJqtMQ== - -"@arkecosystem/core-container@^2.1.0", "@arkecosystem/core-container@^2.1.1": +"@arkecosystem/core-container@^2.1.1": version "2.1.1" resolved "https://registry.yarnpkg.com/@arkecosystem/core-container/-/core-container-2.1.1.tgz#164f4ee3cbeb3c205b8cc1c05eabffcbf9fa1b05" integrity sha512-06O252p0entfFP77EHUUJdNkmhLKCX7yKxsmVIqNXsY52J4vfNba4nAH0gwNycExhXPx+KcjJ8ZOISSPP9Wjng== @@ -51,47 +39,6 @@ awilix "^4.0.1" eventemitter3 "^3.1.0" -"@arkecosystem/core-jest-matchers@^2.1.0": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@arkecosystem/core-jest-matchers/-/core-jest-matchers-2.1.1.tgz#e86cb3a11be9205060679eb20c6d45e8d1c83efd" - integrity sha512-uPtm5jH38Vp0AM3lyy0RDnpCwfokmEplYdCouXB4C25vfYhimjn+fCR+Tf+LHoTfTIizswVbNa61lgh4r0hO9A== - dependencies: - "@arkecosystem/core-container" "^2.1.0" - "@arkecosystem/crypto" "^2.1.0" - "@types/bip39" "^2.4.1" - "@types/lodash.get" "^4.4.4" - "@types/lodash.isequal" "^4.5.3" - "@types/lodash.sortby" "^4.7.4" - bip39 "^2.5.0" - lodash.get "^4.4.2" - lodash.isequal "^4.5.0" - lodash.sortby "^4.7.0" - superheroes "^2.0.0" - xstate "^4.2.2" - -"@arkecosystem/core-test-utils@^2.1.1": - version "2.1.1" - resolved "https://registry.yarnpkg.com/@arkecosystem/core-test-utils/-/core-test-utils-2.1.1.tgz#f162e54a039082ab4073f3e319b1271178e6847b" - integrity sha512-MXXimx/5r+VKhHygW6HFfwGuQlnB7AkYZS757/SkPMl4v1vcjiEcK9FQI3x79Q5mYmxJe/2BD837dwYRibg90Q== - dependencies: - "@arkecosystem/core-container" "^2.1.0" - "@arkecosystem/core-interfaces" "^2.1.0" - "@arkecosystem/core-jest-matchers" "^2.1.0" - "@arkecosystem/crypto" "^2.1.0" - "@types/bip39" "^2.4.1" - "@types/lodash.get" "^4.4.4" - "@types/lodash.isequal" "^4.5.3" - "@types/lodash.isstring" "^4.0.4" - "@types/lodash.sortby" "^4.7.4" - awilix "^4.0.1" - bip39 "^2.5.0" - lodash.get "^4.4.2" - lodash.isequal "^4.5.0" - lodash.isstring "^4.0.1" - lodash.sortby "^4.7.0" - superheroes "^2.0.0" - xstate "^4.2.2" - "@arkecosystem/crypto@^2.1.0": version "2.1.1" resolved "https://registry.yarnpkg.com/@arkecosystem/crypto/-/crypto-2.1.1.tgz#4cc9ed1a3a248af3c087623dd6ecbb66a94f4433" @@ -135,10 +82,10 @@ webpack-node-externals "^1.7.2" wif "^2.0.6" -"@arkecosystem/utils@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@arkecosystem/utils/-/utils-0.2.3.tgz#8e8cac978cd892b6e8d9cd956d60f2e6c4312fd0" - integrity sha512-7DrnQlrPWxIUQA/C1c1Grry0aAkxIwt4uN7aKd0afSkqpwg+rIyByT9N+rwqwYyhSxEZtN6og+iOHXnbNs0epQ== +"@arkecosystem/utils@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@arkecosystem/utils/-/utils-0.2.4.tgz#1458a1046141adb55e9b8af0a355b00920d35332" + integrity sha512-24+DQLJ42KOJywFiq6oWd7oUJwbUZMRrVUkec8HprwXXza5MjmcIYf62cHqSfikTCxWJdXXKujUaKNd9ZJpPoA== dependencies: "@flatten/array" "^1.1.7" dottie "^2.0.1" @@ -704,14 +651,6 @@ js-levenshtein "^1.1.3" semver "^5.3.0" -"@babel/runtime-corejs2@7.3.1": - version "7.3.1" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.3.1.tgz#0c113242e2328f9674d42703a89bee6ebebe9a82" - integrity sha512-YpO13776h3e6Wy8dl2J8T9Qwlvopr+b4trCEhHE+yek6yIqV8sx6g3KozdHMbXeBpjosbPi+Ii5Z7X9oXFHUKA== - dependencies: - core-js "^2.5.7" - regenerator-runtime "^0.12.0" - "@babel/runtime@7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c" @@ -1634,113 +1573,6 @@ universal-user-agent "^2.0.0" url-template "^2.0.8" -"@pm2/agent-node@^1.0.9": - version "1.1.5" - resolved "https://registry.yarnpkg.com/@pm2/agent-node/-/agent-node-1.1.5.tgz#b907e6605371063973f551595c43f36e2df95032" - integrity sha512-3mn0i2prHEYowc2mqSSxePFi/Oht+a0vtHDcrMUBeYJavt/8fyx6/B5jL3vrz1wKOZvUWe8xdknGVO8YLq94YA== - dependencies: - debug "^3.1.0" - eventemitter2 "^5.0.1" - proxy-agent "^3.0.3" - ws "^6.0.0" - -"@pm2/agent@^0.5.22": - version "0.5.23" - resolved "https://registry.yarnpkg.com/@pm2/agent/-/agent-0.5.23.tgz#1cb7b868e7f4d49c701506be1587bcbe791bccc0" - integrity sha512-mviwkRt51y3wY161uxiqXc0wyHTjgo+sIkJ/Mh6m400dYAnAGQ12LFlK56EbnQRwPfPog0q6txqncbFpn4L5zA== - dependencies: - async "^2.6.0" - chalk "^2.3.2" - eventemitter2 "^5.0.1" - fclone "^1.0.11" - moment "^2.21.0" - nssocket "^0.6.0" - pm2-axon "^3.2.0" - pm2-axon-rpc "^0.5.0" - semver "^5.5.0" - ws "^5.1.0" - -"@pm2/io@~2.4.2": - version "2.4.7" - resolved "https://registry.yarnpkg.com/@pm2/io/-/io-2.4.7.tgz#153ce2a3827a115c8437315d9da71aae36fc5558" - integrity sha512-01IQBBeIFUO6Gs3mVDfoDYcZ3cbaN66gPo6guVQTfhiTv1+ftQlSuZH64dO+41wKbUYgDrXnIvFHR99bnpqj8Q== - dependencies: - "@pm2/agent-node" "^1.0.9" - async "^2.6.1" - debug "3.1.0" - deep-metrics "0.0.2" - deepmerge "2.1.1" - event-loop-inspector "^1.2.0" - json-stringify-safe "5.0.1" - semver "5.5.0" - signal-exit "3.0.2" - tslib "1.9.3" - vxx "1.2.2" - -"@pm2/js-api@^0.5.43": - version "0.5.49" - resolved "https://registry.yarnpkg.com/@pm2/js-api/-/js-api-0.5.49.tgz#e6589a31f2cdf2c2e8dc45bd008674c80f9427b8" - integrity sha512-H5BP9aAYLadqqKzs8qWisb2Gt9rnWzYfocn42+UKFVSEsDrGpV18dNi8nexIJ3Lm+Z7vraxtQ42K7EQgslJwdA== - dependencies: - async "^2.4.1" - axios "^0.16.2" - debug "^2.6.8" - eventemitter2 "^4.1.0" - ws "^3.0.0" - -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha1-m4sMxmPWaafY9vXQiToU00jzD78= - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha1-NVy8mLr61ZePntCV85diHx0Ga3A= - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU= - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E= - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik= - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha1-bMKyDFya1q0NzP0hynZz2Nf79o0= - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q= - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA= - "@samverschueren/stream-to-observable@^0.3.0": version "0.3.0" resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f" @@ -1908,11 +1740,6 @@ resolved "https://registry.yarnpkg.com/@types/boom/-/boom-7.2.1.tgz#a21e21ba08cc49d17b26baef98e1a77ee4d6cdb0" integrity sha512-kOiap+kSa4DPoookJXQGQyKy1rjZ55tgfKAh9F0m1NUdukkcwVzpSnXPMH42a5L+U++ugdQlh/xFJu/WAdr1aw== -"@types/braces@*": - version "2.3.0" - resolved "https://registry.yarnpkg.com/@types/braces/-/braces-2.3.0.tgz#d00ec0a76562b2acb6f29330be33a093e33ed25c" - integrity sha512-A3MV5EsLHgShHoJ/XES/fQAnwNISKLrFuH9eNBZY5OkTQB7JPIwbRoExvRpDsNABvkMojnKqKWS8x0m2rLYi+A== - "@types/bytebuffer@^5.0.37", "@types/bytebuffer@^5.0.40": version "5.0.40" resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.40.tgz#d6faac40dcfb09cd856cdc4c01d3690ba536d3ee" @@ -1950,13 +1777,6 @@ resolved "https://registry.yarnpkg.com/@types/clipboardy/-/clipboardy-1.1.0.tgz#316fe1a3ed71b1a51becb925e7e0497986c6a85c" integrity sha512-KOxf4ah9diZWmREM5jCupx2+pZaBPwKk5d5jeNK2+TY6IgEO35uhG55NnDT4cdXeRX8irDSHQPtdRrr0JOTQIw== -"@types/commander@^2.12.2": - version "2.12.2" - resolved "https://registry.yarnpkg.com/@types/commander/-/commander-2.12.2.tgz#183041a23842d4281478fa5d23c5ca78e6fd08ae" - integrity sha512-0QEFiR8ljcHp9bAbWxecjVRuAMr16ivPiGOw6KFQBVrVd0RQIcM3xKdRisH2EDWgVWujiYtHwhSkSUoAAGzH7Q== - dependencies: - commander "*" - "@types/connect@*": version "3.4.32" resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.32.tgz#aa0e9616b9435ccad02bc52b5b454ffc2c70ba28" @@ -2060,7 +1880,21 @@ resolved "https://registry.yarnpkg.com/@types/handlebars/-/handlebars-4.0.40.tgz#b714e13d296a75bff3f199316d1311933ca79ffd" integrity sha512-sGWNtsjNrLOdKha2RV1UeF8+UbQnPSG7qbe5wwbni0mw4h2gHXyPFUMOC+xwGirIiiydM/HSqjDO4rk6NFB18w== -"@types/hapi@*", "@types/hapi@^18.0.0": +"@types/hapi@*": + version "18.0.1" + resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-18.0.1.tgz#4e1f76b89b6d939aaab2ecbce7576146f5ea2739" + integrity sha512-2YznFunreTu7j+qWp8HU/0qLQ/RVJveWvqRQeT8N5/xOKMe6lIIS6gikNpuCZP5Ugh7WM+kpv9Nh/enVP+4E6w== + dependencies: + "@types/boom" "*" + "@types/catbox" "*" + "@types/iron" "*" + "@types/joi" "*" + "@types/mimos" "*" + "@types/node" "*" + "@types/podium" "*" + "@types/shot" "*" + +"@types/hapi@^18.0.0": version "18.0.0" resolved "https://registry.yarnpkg.com/@types/hapi/-/hapi-18.0.0.tgz#48b5a894a59fe95895d80068cca3683638714bf5" integrity sha512-6OSXjpS1AmN90BrJ90ruXR3H+utR3ByZcRdQyJqqAGA+saD2s/E3Dz54V09xI3phDTu2F0VSUxpRdFJprTuw+w== @@ -2084,13 +1918,6 @@ resolved "https://registry.yarnpkg.com/@types/hoek/-/hoek-4.1.3.tgz#d1982d48fb0d2a0e5d7e9d91838264d8e428d337" integrity sha1-0ZgtSPsNKg5dfp2Rg4Jk2OQo0zc= -"@types/inert@^5.1.2": - version "5.1.2" - resolved "https://registry.yarnpkg.com/@types/inert/-/inert-5.1.2.tgz#2bb8bef3b2462f904c960654c9edfa39285a85c6" - integrity sha512-3IoSFLQWvhLfZ85kHas/F3iD/TyZPfeJbTsDjrwYljK1MgBGCB2OywAsyeA/YiJ62VbNXfXBwpD1/VbJPIZSGA== - dependencies: - "@types/hapi" "*" - "@types/integer@*": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/integer/-/integer-1.0.0.tgz#f5b313876012fad0afeb5318f03cb871064eb33e" @@ -2335,7 +2162,7 @@ resolved "https://registry.yarnpkg.com/@types/log-symbols/-/log-symbols-2.0.0.tgz#7919e2ec3c8d13879bfdcab310dd7a3f7fc9466d" integrity sha512-YJhbp0sz3egFFKl3BcCNPQKzuGFOP4PACcsifhK6ROGnJUW9ViYLuLybQ9GQZm7Zejy3tkGuiXYMq3GiyGkU4g== -"@types/long@*", "@types/long@^4.0.0": +"@types/long@*": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.0.tgz#719551d2352d301ac8b81db732acb6bdc28dbdef" integrity sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q== @@ -2352,13 +2179,6 @@ resolved "https://registry.yarnpkg.com/@types/marked/-/marked-0.4.2.tgz#64a89e53ea37f61cc0f3ee1732c555c2dbf6452f" integrity sha512-cDB930/7MbzaGF6U3IwSQp6XBru8xWajF5PV2YZZeV8DyiliTuld11afVztGI9+yJZ29il5E+NpGA6ooV/Cjkg== -"@types/micromatch@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@types/micromatch/-/micromatch-3.1.0.tgz#514c8a3d24b2680a9b838eeb80e6d7d724545433" - integrity sha512-06uA9V7v68RTOzA3ky1Oi0HmCPa+YJ050vM+sTECwkxnHUQnO17TAcNCGX400QT6bldUiPb7ux5oKy0j8ccEDw== - dependencies: - "@types/braces" "*" - "@types/mime-db@*": version "1.27.0" resolved "https://registry.yarnpkg.com/@types/mime-db/-/mime-db-1.27.0.tgz#9bc014a1fd1fdf47649c1a54c6dd7966b8284792" @@ -2388,11 +2208,6 @@ dependencies: "@types/node" "*" -"@types/node-emoji@^1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@types/node-emoji/-/node-emoji-1.8.1.tgz#689cb74fdf6e84309bcafce93a135dfecd01de3f" - integrity sha512-0fRfA90FWm6KJfw6P9QGyo0HDTCmthZ7cWaBQndITlaWLTZ6njRyKwrwpzpg+n6kBXBIGKeUHEQuBx7bphGJkA== - "@types/node-forge@^0.7.11": version "0.7.11" resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.7.11.tgz#6b25860d54a0e2e37a53b97e2554f99fbcc7e8bb" @@ -2405,7 +2220,7 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-11.9.0.tgz#35fea17653490dab82e1d5e69731abfdbf13160d" integrity sha512-ry4DOrC+xenhQbzk1iIPzCZGhhPGEFv7ia7Iu6XXSLVluiJIe9FfG7Iu3mObH9mpxEXCWLCMU4JWbCCR9Oy1Zg== -"@types/node@^10.1.0", "@types/node@^10.12.17": +"@types/node@^10.12.17": version "10.12.25" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.25.tgz#0d01a7dd6127de60d861ece4a650963042abb538" integrity sha512-IcvnGLGSQFDvC07Bz2I8SX+QKErDZbUdiQq7S2u3XyzTyJfUmT0sWJMbeQkMzpTAkO7/N7sZpW/arUM2jfKsbQ== @@ -2639,14 +2454,6 @@ dependencies: "@types/node" "*" -"@types/ws@^6.0.0": - version "6.0.1" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-6.0.1.tgz#ca7a3f3756aa12f62a0a62145ed14c6db25d5a28" - integrity sha512-EzH8k1gyZ4xih/MaZTXwT2xOkPiIMSrhQ9b8wrlX88L0T02eYsddatQlwVFlEPyEqV0ChpdpNnE51QPH6NVT4Q== - dependencies: - "@types/events" "*" - "@types/node" "*" - "@webassemblyjs/ast@1.7.11": version "1.7.11" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.7.11.tgz#b988582cafbb2b095e8b556526f30c90d057cace" @@ -2828,7 +2635,7 @@ abbrev@~1.0.9: resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" integrity sha1-kbR5JYinc4wl813W9jdSovh3YTU= -accept@3.x.x, accept@^3.0.2: +accept@3.x.x: version "3.1.3" resolved "https://registry.yarnpkg.com/accept/-/accept-3.1.3.tgz#29c3e2b3a8f4eedbc2b690e472b9ebbdc7385e87" integrity sha512-OgOEAidVEOKPup+Gv2+2wdH2AgVKI9LxsJ4hicdJ6cY0faUuZdZoi56kkXWlHp9qicN1nWQLmW5ZRGk+SBS5xg== @@ -2942,18 +2749,6 @@ ammo@3.x.x: dependencies: hoek "6.x.x" -amp-message@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/amp-message/-/amp-message-0.1.2.tgz#a78f1c98995087ad36192a41298e4db49e3dfc45" - integrity sha1-p48cmJlQh602GSpBKY5NtJ49/EU= - dependencies: - amp "0.3.1" - -amp@0.3.1, amp@~0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/amp/-/amp-0.3.1.tgz#6adf8d58a74f361e82c1fa8d389c079e139fc47d" - integrity sha1-at+NWKdPNh6CwfqNOJwHnhOfxH0= - ansi-align@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f" @@ -3016,142 +2811,6 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -apollo-cache-control@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.5.0.tgz#78cfefec57c5c0a9648137e3250b91172998061e" - integrity sha512-zu26CFj7CboxLB6cckZQEiSUGXIr8MViEGIC5Vesz2yd37sjtevMfRwQhxFuK0HinR0T/WC3dz2k5cj+33vQQQ== - dependencies: - apollo-server-env "2.2.0" - graphql-extensions "0.5.0" - -apollo-datasource@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.3.0.tgz#88c680edd63a2f7ea05fc25da290a430632d39f4" - integrity sha512-+jWs3ezhx4lcAAPIHtlj0Zoiv2tvwfzn7feHuhxub3xFwkJm39T8hPjb3aMQCsuS7TukBD+F5ndgVob5hL/5Nw== - dependencies: - apollo-server-caching "0.3.0" - apollo-server-env "2.2.0" - -apollo-engine-reporting-protobuf@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting-protobuf/-/apollo-engine-reporting-protobuf-0.2.0.tgz#2aaf4d2eddefe7924d469cf1135267bc0deadf73" - integrity sha512-qI+GJKN78UMJ9Aq/ORdiM2qymZ5yswem+/VDdVFocq+/e1QqxjnpKjQWISkswci5+WtpJl9SpHBNxG98uHDKkA== - dependencies: - protobufjs "^6.8.6" - -apollo-engine-reporting@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.0.0.tgz#af93b755482ad5a6c52f9f728f3e24e801aa2385" - integrity sha512-9gZSME9ggZwL1nBBvfgSehwc+PtcvZC1/3NYrBOFgidJbrEFita2w5A0jM8Brjo+N2FMKNYWGj8WQ1ywO21JPg== - dependencies: - apollo-engine-reporting-protobuf "0.2.0" - apollo-graphql "0.1.0" - apollo-server-core "2.4.0" - apollo-server-env "2.2.0" - async-retry "^1.2.1" - graphql-extensions "0.5.0" - -apollo-env@0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/apollo-env/-/apollo-env-0.3.2.tgz#7cd9abd7a65cb0e00c4cfc057a999864f6157b55" - integrity sha512-r6nrOw5Pyk6YLNKEtvBiTguJK+oPI1sthKogd7tp6jfkJ+q8SR/9sOoTxyV3vsmR/mMENuBHF89BGLLo9rxDiA== - dependencies: - core-js "3.0.0-beta.3" - node-fetch "^2.2.0" - -apollo-graphql@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/apollo-graphql/-/apollo-graphql-0.1.0.tgz#e453b133daa8b686644da05ec8e556cf31c57037" - integrity sha512-Mi5GqZJz1A/0i8SEm9EVHWe/LkGbYzV5wzobUY+1Q0SI1NdFtRgqHZUdHU0hz1jDnL+dpRqK1huVmtOO/DGa/A== - dependencies: - lodash.sortby "^4.7.0" - -apollo-link@^1.2.3: - version "1.2.8" - resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.8.tgz#0f252adefd5047ac1a9f35ba9439d216587dcd84" - integrity sha512-lfzGRxhK9RmiH3HPFi7TIEBhhDY9M5a2ZDnllcfy5QDk7cCQHQ1WQArcw1FK0g1B+mV4Kl72DSrlvZHZJEolrA== - dependencies: - zen-observable-ts "^0.8.15" - -apollo-server-caching@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/apollo-server-caching/-/apollo-server-caching-0.3.0.tgz#96669b5e3bf693e6a54683edfab4b9495deb17d8" - integrity sha512-dHwWUsRZu7I1yUfzTwPJgOigMsftgp8w3X96Zdch1ICWN7cM6aNxks9tTnLd+liUSEzdYLlTmEy5VUturF2IAw== - dependencies: - lru-cache "^5.0.0" - -apollo-server-core@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.4.0.tgz#928c563f68d9f69a4288e7a4bf14ca79f8599f2b" - integrity sha512-rTFJa12NzTWC9IJrXDr8AZMs1Slbes9YxbyaI+cMC5fs8O9wkCkb34C/1Tp7xKX0fgauXrKZpXv7yPTSm+4YFg== - dependencies: - "@apollographql/apollo-tools" "^0.3.0" - "@apollographql/graphql-playground-html" "^1.6.6" - "@types/ws" "^6.0.0" - apollo-cache-control "0.5.0" - apollo-datasource "0.3.0" - apollo-engine-reporting "1.0.0" - apollo-server-caching "0.3.0" - apollo-server-env "2.2.0" - apollo-server-errors "2.2.0" - apollo-server-plugin-base "0.3.0" - apollo-tracing "0.5.0" - fast-json-stable-stringify "^2.0.0" - graphql-extensions "0.5.0" - graphql-subscriptions "^1.0.0" - graphql-tag "^2.9.2" - graphql-tools "^4.0.0" - graphql-upload "^8.0.2" - lodash "^4.17.10" - subscriptions-transport-ws "^0.9.11" - ws "^6.0.0" - -apollo-server-env@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.2.0.tgz#5eec5dbf46581f663fd6692b2e05c7e8ae6d6034" - integrity sha512-wjJiI5nQWPBpNmpiLP389Ezpstp71szS6DHAeTgYLb/ulCw3CTuuA+0/E1bsThVWiQaDeHZE0sE3yI8q2zrYiA== - dependencies: - node-fetch "^2.1.2" - util.promisify "^1.0.0" - -apollo-server-errors@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/apollo-server-errors/-/apollo-server-errors-2.2.0.tgz#5b452a1d6ff76440eb0f127511dc58031a8f3cb5" - integrity sha512-gV9EZG2tovFtT1cLuCTavnJu2DaKxnXPRNGSTo+SDI6IAk6cdzyW0Gje5N2+3LybI0Wq5KAbW6VLei31S4MWmg== - -apollo-server-hapi@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/apollo-server-hapi/-/apollo-server-hapi-2.4.0.tgz#bc7b96e51c0f12801468874e1c4ec9d71211e54c" - integrity sha512-f/TMnqf/cXRDuWuWtlku2ykfn/flV6iNVQAg2npLKoy7OIpMYb0NSzd3R/00rTcGtX1hYugDU3tCb35Jp6AGOQ== - dependencies: - "@apollographql/graphql-playground-html" "^1.6.6" - accept "^3.0.2" - apollo-server-core "2.4.0" - boom "^7.1.0" - graphql-subscriptions "^1.0.0" - graphql-tools "^4.0.0" - -apollo-server-plugin-base@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.3.0.tgz#26c7f9c020b2d444de5bebd2e3c406bebd9c302e" - integrity sha512-SOwp4cpZwyklvP1MkMcY6+12c1hrb5gwC4vK4a23kL5rr9FC0sENcXo3uVVM4XlDGOXIkY+sCM8ngKFuef2flw== - -apollo-tracing@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.5.0.tgz#6eebaf1e840ece99299103def689d39fe49780a3" - integrity sha512-j0icEhLYf0xS6Q/iCXA2j9KfpYw0a/XvLSUio7fm5yUwtXP2Pp11x5BtK1dI8sLMiaOqUrREz2XjV4PKLzQPuA== - dependencies: - apollo-server-env "2.2.0" - graphql-extensions "0.5.0" - -apollo-utilities@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.1.2.tgz#aa5eca9d1f1eb721c381a22e0dde03559d856db3" - integrity sha512-EjDx8vToK+zkWIxc76ZQY/irRX52puNg04xf/w8R0kVTDAgHuVfnFVC01O5vE25kFnIaa5em0pFI0p9b6YMkhQ== - dependencies: - fast-json-stable-stringify "^2.0.0" - tslib "^1.9.3" - append-transform@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/append-transform/-/append-transform-0.4.0.tgz#d76ebf8ca94d276e247a36bad44a4b74ab611991" @@ -3372,34 +3031,19 @@ async-limiter@~1.0.0: resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" integrity sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg== -async-listener@^0.6.0: - version "0.6.10" - resolved "https://registry.yarnpkg.com/async-listener/-/async-listener-0.6.10.tgz#a7c97abe570ba602d782273c0de60a51e3e17cbc" - integrity sha512-gpuo6xOyF4D5DE5WvyqZdPA3NGhiT6Qf07l7DCB0wwDEsLvDIbCr6j9S5aj5Ch96dLace5tXVzWBZkxU/c5ohw== - dependencies: - semver "^5.3.0" - shimmer "^1.1.0" - -async-retry@^1.2.1: - version "1.2.3" - resolved "https://registry.yarnpkg.com/async-retry/-/async-retry-1.2.3.tgz#a6521f338358d322b1a0012b79030c6f411d1ce0" - integrity sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q== - dependencies: - retry "0.12.0" +async@^1.4.0, async@~1.5: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= -async@2.6.1, async@^2.1.4, async@^2.5.0, async@^2.6.1: +async@^2.1.4, async@^2.5.0: version "2.6.1" resolved "https://registry.yarnpkg.com/async/-/async-2.6.1.tgz#b245a23ca71930044ec53fa46aa00a3e87c6a610" integrity sha512-fNEiL2+AZt6AlAw/29Cr0UDe4sRAHCpEHh54WMz+Bb7QfNcFw4h3loofyJpLeQs4Yx7yuqu/2dLgM5hKOs6HlQ== dependencies: lodash "^4.17.10" -async@^1.4.0, async@~1.5: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - integrity sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo= - -async@^2.4.1, async@^2.6, async@^2.6.0, async@^2.6.2: +async@^2.6.2: version "2.6.2" resolved "https://registry.yarnpkg.com/async/-/async-2.6.2.tgz#18330ea7e6e313887f5d2f2a904bac6fe4dd5381" integrity sha512-H1qVYh1MYhEEFLsP97cVKqCGo7KfCyTt6uEWqsTBr9SO84oK9Uwbyd/yCW+6rKJLHksBNUVWZDAjfS+Ccx0Bbg== @@ -3446,14 +3090,6 @@ axios-mock-adapter@^1.16.0: dependencies: deep-equal "^1.0.1" -axios@^0.16.2: - version "0.16.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.16.2.tgz#ba4f92f17167dfbab40983785454b9ac149c3c6d" - integrity sha1-uk+S8XFn37q0CYN4VFS5rBScPG0= - dependencies: - follow-redirects "^1.2.3" - is-buffer "^1.1.5" - axios@^0.18.0: version "0.18.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.0.tgz#32d53e4851efdc0a11993b6cd000789d70c05102" @@ -3640,11 +3276,6 @@ babylon@^6.18.0: resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== -backo2@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= - balanced-match@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" @@ -3682,6 +3313,16 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bcrypto@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/bcrypto/-/bcrypto-3.0.2.tgz#3fd96da06447724cebdfeed14821ade86babec53" + integrity sha512-JGPVqeO+uvlo+5QTEO78jL4/5UVvBcqc8UPd7ctLMQTb2FR85aocdDDKk0vtyLpXY2/orRrD51h3/Q0GwuyZAg== + dependencies: + bindings "~1.3.1" + bsert "~0.0.8" + bufio "~1.0.4" + nan "~2.12.1" + before-after-hook@^1.2.0: version "1.3.2" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-1.3.2.tgz#7bfbf844ad670aa7a96b5a4e4e15bd74b08ed66b" @@ -3735,6 +3376,11 @@ bindings@^1.2.1, bindings@^1.3.0: dependencies: file-uri-to-path "1.0.0" +bindings@~1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.1.tgz#21fc7c6d67c18516ec5aaa2815b145ff77b26ea5" + integrity sha512-i47mqjF9UbjxJhxGf+pZ6kSxrnI3wBLlnGI2ArWJ4r0VrvDS7ZYXkprq/pLaBWYq4GM0r4zdHY+NNRqEMU7uew== + bip32@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bip32/-/bip32-1.0.2.tgz#982e2ad2cae6fc6a2f53dda3e6c3be9364674b28" @@ -3785,11 +3431,6 @@ bl@~1.1.2: dependencies: readable-stream "~2.0.5" -blessed@^0.1.81: - version "0.1.81" - resolved "https://registry.yarnpkg.com/blessed/-/blessed-0.1.81.tgz#f962d687ec2c369570ae71af843256e6d0ca1129" - integrity sha1-+WLWh+wsNpVwrnGvhDJW5tDKESk= - block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -3807,11 +3448,6 @@ bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.11.3, bn.js@^4.11.8, bn.js@^4 resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" integrity sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA== -bodec@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/bodec/-/bodec-0.1.0.tgz#bc851555430f23c9f7650a75ef64c6a94c3418cc" - integrity sha1-vIUVVUMPI8n3ZQp172TGqUw0GMw= - body-parser@1.18.3, body-parser@^1.18.3: version "1.18.3" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.3.tgz#5b292198ffdd553b3a0f20ded0592b956955c8b4" @@ -3835,7 +3471,7 @@ boom@2.x.x: dependencies: hoek "2.x.x" -boom@7.x.x, boom@^7.1.0, boom@^7.1.1, boom@^7.2.0, boom@^7.3.0: +boom@7.x.x, boom@^7.1.1, boom@^7.2.0, boom@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/boom/-/boom-7.3.0.tgz#733a6d956d33b0b1999da3fe6c12996950d017b9" integrity sha512-Swpoyi2t5+GhOEGw8rEsKvTxFLIDiiKoUc2gsoV6Lyr43LHBIzch3k2MvYUs8RTROrIkVJ3Al0TkaOGjnb+B6A== @@ -4016,6 +3652,11 @@ bser@^2.0.0: dependencies: node-int64 "^0.4.0" +bsert@~0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/bsert/-/bsert-0.0.8.tgz#1614b6d0faf6b5940d8ba2ab9520180e2b9a695d" + integrity sha512-MzSxGNGymvJ2wAJ0lrC2cT+Irq2q+EMz5ijt8qXIxt02VoO+ZFWPNrmzNHGq6F8RJnIzzRmKz8mv1/j2xU1TQg== + btoa-lite@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/btoa-lite/-/btoa-lite-1.0.0.tgz#337766da15801210fdd956c22e9c6891ab9d0337" @@ -4050,6 +3691,11 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" +bufio@~1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/bufio/-/bufio-1.0.4.tgz#f6e197928cf4d1196c19b9dba6c3e89bb879aca7" + integrity sha512-z5HOP06DHRlwB6C4Ol6RNA2HWam8Pluo5pasPniQVyC4dzwutzYHHHDjDSOgSyGeThE7kmgT7ynV1A+EuD8s3w== + builtin-modules@^1.0.0, builtin-modules@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" @@ -4070,13 +3716,6 @@ builtins@^1.0.3: resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= -busboy@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/busboy/-/busboy-0.3.0.tgz#6ee3cb1c844fc1f691d8f9d824f70128b3b5e485" - integrity sha512-e+kzZRAbbvJPLjQz2z+zAyr78BSi9IFeBTyLwF76g78Q2zRt/RZ1NtS3MS17v2yLqYfLz69zHdC+1L4ja8PwqQ== - dependencies: - dicer "0.3.0" - byline@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/byline/-/byline-5.0.0.tgz#741c5216468eadc457b03410118ad77de8c1ddb1" @@ -4341,12 +3980,7 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -charm@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/charm/-/charm-0.1.2.tgz#06c21eed1a1b06aeb67553cdc53e23274bac2296" - integrity sha1-BsIe7RobBq62dVPNxT4jJ0usIpY= - -chokidar@^2.0.2, chokidar@^2.0.4: +chokidar@^2.0.2: version "2.1.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.1.tgz#adc39ad55a2adf26548bd2afa048f611091f9184" integrity sha512-gfw3p2oQV2wEt+8VuMlNsPjCxDxvvgnm/kz+uATu805mWVF8IJN7uz9DN7iBz+RMJISmiVbCOBFs9qBGMjtPfQ== @@ -4450,13 +4084,6 @@ cli-spinners@^1.3.1: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-1.3.1.tgz#002c1990912d0d59580c93bd36c056de99e4259a" integrity sha512-1QL4544moEsDVH9T/l6Cemov/37iv1RtoKf7NJ04A60+4MREXNfx/QvavbH6QoGdsD4N4Mwy49cmaINR/o2mdg== -cli-table-redemption@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cli-table-redemption/-/cli-table-redemption-1.0.1.tgz#0359d8c34df74980029d76dff071a05a127c4fdd" - integrity sha512-SjVCciRyx01I4azo2K2rcc0NP/wOceXGzG1ZpYkEulbbIxDA/5YWv0oxG2HtQ4v8zPC6bgbRI7SbNaTZCxMNkg== - dependencies: - chalk "^1.1.3" - cli-table3@^0.5.0, cli-table3@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/cli-table3/-/cli-table3-0.5.1.tgz#0252372d94dfc40dbd8df06005f48f31f656f202" @@ -4629,7 +4256,7 @@ collection-visit@^1.0.0: map-visit "^1.0.0" object-visit "^1.0.0" -color-convert@^1.9.0, color-convert@^1.9.1: +color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== @@ -4641,50 +4268,16 @@ color-name@1.1.3: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= -color-name@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.2: - version "1.5.3" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.3.tgz#c9bbc5f01b58b5492f3d6857459cb6590ce204cc" - integrity sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@3.0.x: - version "3.0.0" - resolved "https://registry.yarnpkg.com/color/-/color-3.0.0.tgz#d920b4328d534a3ac8295d68f7bd4ba6c427be9a" - integrity sha512-jCpd5+s0s0t7p3pHQKpnJ0TpQKKdleP71LWcA0aqiljpiuAkOSUFN/dyH8ZwF0hRmFlrIuRhufds1QyEP9EB+w== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.2" - -colornames@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/colornames/-/colornames-1.1.1.tgz#f8889030685c7c4ff9e2a559f5077eb76a816f96" - integrity sha1-+IiQMGhcfE/54qVZ9Qd+t2qBb5Y= - colors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= -colors@^1.1.2, colors@^1.2.1, colors@^1.3.3: +colors@^1.1.2: version "1.3.3" resolved "https://registry.yarnpkg.com/colors/-/colors-1.3.3.tgz#39e005d546afe01e01f9c4ca8fa50f686a01205d" integrity sha512-mmGt/1pZqYRjMxB1axhTo16/snVZ5krrKkcmMeVKxzECMMXoCgnvTPp10QgHfcbQZw8Dq2jMNG6je4JlWU0gWg== -colorspace@1.1.x: - version "1.1.1" - resolved "https://registry.yarnpkg.com/colorspace/-/colorspace-1.1.1.tgz#9ac2491e1bc6f8fb690e2176814f8d091636d972" - integrity sha512-pI3btWyiuz7Ken0BWh9Elzsmv2bM9AhA7psXib4anUXy/orfZ/E0MbQwhSOG/9L8hLlalqrU0UhOuqxW1YjmVw== - dependencies: - color "3.0.x" - text-hex "1.0.x" - columnify@^1.5.4, columnify@~1.5.4: version "1.5.4" resolved "https://registry.yarnpkg.com/columnify/-/columnify-1.5.4.tgz#4737ddf1c7b69a8a7c340570782e947eec8e78bb" @@ -4700,16 +4293,11 @@ combined-stream@^1.0.5, combined-stream@^1.0.6, combined-stream@~1.0.5, combined dependencies: delayed-stream "~1.0.0" -commander@*, commander@2.19.0, commander@^2.12.1, commander@^2.14.1, commander@^2.9.0: +commander@2.19.0, commander@^2.12.1, commander@^2.14.1, commander@^2.9.0: version "2.19.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.19.0.tgz#f6198aa84e5b83c46054b94ddedbfed5ee9ff12a" integrity sha512-6tvAOO+D6OENvRAh524Dh9jcfKTYDQAqvqezbCW82xj5X0pSrcpxtvRKHLG0yBY6SD7PSDrJaj+0AiOcKVd1Xg== -commander@2.15.1: - version "2.15.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" - integrity sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag== - commander@~2.17.1: version "2.17.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.17.1.tgz#bd77ab7de6de94205ceacc72f1716d29f20a77bf" @@ -4802,14 +4390,6 @@ content@4.x.x: dependencies: boom "7.x.x" -continuation-local-storage@^3.1.4: - version "3.2.1" - resolved "https://registry.yarnpkg.com/continuation-local-storage/-/continuation-local-storage-3.2.1.tgz#11f613f74e914fe9b34c92ad2d28fe6ae1db7ffb" - integrity sha512-jx44cconVqkCEEyLSKWwkvUXwO561jXMa3LPjTPsm5QR22PA0/mhe33FT4Xb5y74JDvt/Cq+5lm8S8rskLv9ZA== - dependencies: - async-listener "^0.6.0" - emitter-listener "^1.1.1" - conventional-changelog-angular@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/conventional-changelog-angular/-/conventional-changelog-angular-5.0.2.tgz#39d945635e03b6d0c9d4078b1df74e06163dc66a" @@ -4927,12 +4507,7 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -core-js@3.0.0-beta.3: - version "3.0.0-beta.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.0.0-beta.3.tgz#b0f22009972b8c6c04550ebf38513ca4b3cc9559" - integrity sha512-kM/OfrnMThP5PwGAj5HhQLdjUqzjrllqN2EVnk/X9qrLsfYjR2hzZ+E/8CzH0xuosexZtqMTLQrk//BULrBj9w== - -core-js@^2.4.0, core-js@^2.5.0, core-js@^2.5.7: +core-js@^2.4.0, core-js@^2.5.0: version "2.6.4" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.4.tgz#b8897c062c4d769dd30a0ac5c73976c47f92ea0d" integrity sha512-05qQ5hXShcqGkPZpXEFLIpxayZscVD2kuMBZewxiIPPEagukO4mqgPA9CWhUvFBJfy3ODdK2p9xyHh7FTU9/7A== @@ -5024,13 +4599,6 @@ create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4, create-hmac@^1.1.7: safe-buffer "^5.0.1" sha.js "^2.4.8" -cron@^1.3: - version "1.6.0" - resolved "https://registry.yarnpkg.com/cron/-/cron-1.6.0.tgz#15f92c1b5a930c5cdfcd53fe940064fa8ca2e72d" - integrity sha512-8OkXbeK3qF42ndzkIkCo3zfCDg2TxGjQiCQqVE+ZFFHa4vAcw0PdBc5i/6aJ9FppLncyKZmDuZdJ9/uuXEYZaw== - dependencies: - moment-timezone "^0.5.x" - cross-env@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-5.2.0.tgz#6ecd4c015d5773e614039ee529076669b9d126f2" @@ -5107,11 +4675,6 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" -culvert@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/culvert/-/culvert-0.1.2.tgz#9502f5f0154a2d5a22a023e79f71cc936fa6ef6f" - integrity sha1-lQL18BVKLVoioCPnn3HMk2+m728= - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -5119,11 +4682,6 @@ currently-unhandled@^0.4.1: dependencies: array-find-index "^1.0.1" -cycle@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/cycle/-/cycle-1.0.3.tgz#21e80b2be8580f98b468f379430662b046c34ad2" - integrity sha1-IegLK+hYD5i0aPN5QwZisEbDStI= - cyclist@~0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-0.2.2.tgz#1b33792e11e914a2fd6d6ed6447464444e5fa640" @@ -5171,7 +4729,7 @@ data-urls@^1.0.0: whatwg-mimetype "^2.2.0" whatwg-url "^7.0.0" -date-fns@^1.27.2, date-fns@^1.29.0: +date-fns@^1.27.2: version "1.30.1" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw== @@ -5194,7 +4752,7 @@ dayjs-ext@^2.2.0: fast-plural-rules "^0.0.1" timezone-support "^1.8.0" -debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.3, debug@^2.6.8, debug@^2.6.9: +debug@2, debug@2.6.9, debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== @@ -5215,7 +4773,7 @@ debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1: dependencies: ms "^2.1.1" -debug@^3, debug@^3.0, debug@^3.1, debug@^3.1.0, debug@^3.2.5, debug@^3.2.6: +debug@^3, debug@^3.1.0, debug@^3.2.5: version "3.2.6" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b" integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ== @@ -5272,23 +4830,11 @@ deep-is@~0.1.3: resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ= -deep-metrics@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/deep-metrics/-/deep-metrics-0.0.2.tgz#180900dea82a2c4b976be2b7684914748f5a0931" - integrity sha512-2b4DO8YcPWSHrZ7XW9YjjJajmflw2EhKUMmeriZmGYsC8XvCWIyztsEjCQ3f5kIQO+ItzBK7BqVjSWlFZQtONQ== - dependencies: - semver "^5.3.0" - deepmerge@*, deepmerge@3.1.0, deepmerge@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.1.0.tgz#a612626ce4803da410d77554bfd80361599c034d" integrity sha512-/TnecbwXEdycfbsM2++O3eGiatEFHjjNciHEwJclM+T5Kd94qD1AP+2elP/Mq0L5b9VZJao5znR01Mz6eX8Seg== -deepmerge@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.1.1.tgz#e862b4e45ea0555072bf51e7fd0d9845170ae768" - integrity sha512-urQxA1smbLZ2cBbXbaYObM1dJ82aJ2H57A1C/Kklfh/ZN1bgH4G/n5KWhdNfOK11W98gqZfyYj7W4frJJRwA2w== - deepmerge@^3.0.0: version "3.2.0" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-3.2.0.tgz#58ef463a57c08d376547f8869fdc5bcee957f44e" @@ -5392,11 +4938,6 @@ depd@~1.1.2: resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= -deprecated-decorator@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz#00966317b7a12fe92f3cc831f7583af329b86c37" - integrity sha1-AJZjF7ehL+kvPMgx91g68ym4bDc= - des.js@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" @@ -5445,22 +4986,6 @@ dezalgo@^1.0.0, dezalgo@^1.0.1, dezalgo@~1.0.3: asap "^2.0.0" wrappy "1" -diagnostics@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/diagnostics/-/diagnostics-1.1.1.tgz#cab6ac33df70c9d9a727490ae43ac995a769b22a" - integrity sha512-8wn1PmdunLJ9Tqbx+Fx/ZEuHfJf4NKSN2ZBj7SJC/OWRWha843+WsTjqMe1B5E3p28jqBlp+mJ2fPVxPyNgYKQ== - dependencies: - colorspace "1.1.x" - enabled "1.0.x" - kuler "1.0.x" - -dicer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/dicer/-/dicer-0.3.0.tgz#eacd98b3bfbf92e8ab5c2fdb71aaac44bb06b872" - integrity sha512-MdceRRWqltEG2dZqO769g27N/3PXfcKl04VhYnBlo2YhH7zPi88VebsjTKclaOyiuMaGU72hTfw3VkUitGcVCA== - dependencies: - streamsearch "0.1.2" - diff@^3.2.0: version "3.5.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.5.0.tgz#800c0dd1e0a8bfbc95835c202ad220fe317e5a12" @@ -5535,11 +5060,6 @@ drbg.js@^1.0.1: create-hash "^1.1.2" create-hmac "^1.1.4" -ducky@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/ducky/-/ducky-2.7.0.tgz#a8c263cbbc839d41190442c68aa462d4fff3cf47" - integrity sha512-zeeSq/GXN6xRmUBxssViJRsvEsTEKUyoKbEabmFO46d8J0DG07JGD82F3F8qYSfLHSD2fWEvwSIwQWhp+LxXQw== - duplexer3@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" @@ -5644,13 +5164,6 @@ email-validator@^2.0.4: resolved "https://registry.yarnpkg.com/email-validator/-/email-validator-2.0.4.tgz#b8dfaa5d0dae28f1b03c95881d904d4e40bfe7ed" integrity sha512-gYCwo7kh5S3IDyZPLZf6hSS0MnZT8QmJFqYvbqlDZSbwdZlY6QZWxJ4i/6UhITOJ4XzyI647Bm2MXKCLqnJ4nQ== -emitter-listener@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/emitter-listener/-/emitter-listener-1.1.2.tgz#56b140e8f6992375b3d7cb2cab1cc7432d9632e8" - integrity sha512-Bt1sBAGFHY9DKY+4/2cV6izcKJUf5T7/gkdmkxzX/qv9CcGH8xSwVRW5mtX03SWJtRTWSOpzCuWN9rBFYZepZQ== - dependencies: - shimmer "^1.2.0" - emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5661,13 +5174,6 @@ emojis-list@^2.0.0: resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= -enabled@1.0.x: - version "1.0.2" - resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93" - integrity sha1-ll9lE9LC0cX0ZStkouM5ZGf8L5M= - dependencies: - env-variable "0.0.x" - encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -5701,11 +5207,6 @@ env-paths@^2.0.0: resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.0.0.tgz#5a71723f3df7ca98113541f6fa972184f2c9611d" integrity sha512-13VpSqOO91W3MskXxWJ8x+Y33RKaPT53/HviPp8QcMmEbAJaPFEm8BmMpxCHroJ5rGADqr34Zl6zosBt3F+xAA== -env-variable@0.0.x: - version "0.0.5" - resolved "https://registry.yarnpkg.com/env-variable/-/env-variable-0.0.5.tgz#913dd830bef11e96a039c038d4130604eba37f88" - integrity sha512-zoB603vQReOFvTg5xMl9I1P2PnHsHQQKTEowsKKD7nseUfJq6UWzK+4YtlWUO1nhiQUxe6XMkk+JleSZD1NZFA== - envfile@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/envfile/-/envfile-2.3.0.tgz#0ced8f8846e45e2868623c54ecfe3d1914b1e3f4" @@ -5828,11 +5329,6 @@ escape-html@~1.0.3: resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= -escape-regexp@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/escape-regexp/-/escape-regexp-0.0.1.tgz#f44bda12d45bbdf9cb7f862ee7e4827b3dd32254" - integrity sha1-9EvaEtRbvfnLf4Yu5+SCez3TIlQ= - escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.4, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -5895,26 +5391,6 @@ event-lite@^0.1.1: resolved "https://registry.yarnpkg.com/event-lite/-/event-lite-0.1.2.tgz#838a3e0fdddef8cc90f128006c8e55a4e4e4c11b" integrity sha512-HnSYx1BsJ87/p6swwzv+2v6B4X+uxUteoDfRxsAb1S1BePzQqOLevVmkdA15GHJVd9A9Ok6wygUR18Hu0YeV9g== -event-loop-inspector@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/event-loop-inspector/-/event-loop-inspector-1.2.2.tgz#e56ed73f50a8b0b9193cc36be877fea18641aceb" - integrity sha512-v7OqIPmO0jqpmSH4Uc6IrY/H6lOidYzrXHE8vPHLDDOfV1Pw+yu+KEIE/AWnoFheWYlunZbxzKpZBAezVlrU9g== - -eventemitter2@5.0.1, eventemitter2@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-5.0.1.tgz#6197a095d5fb6b57e8942f6fd7eaad63a09c9452" - integrity sha1-YZegldX7a1folC9v1+qtY6CclFI= - -eventemitter2@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-4.1.2.tgz#0e1a8477af821a6ef3995b311bf74c23a5247f15" - integrity sha1-DhqEd6+CGm7zmVsxG/dMI6UkfxU= - -eventemitter2@~0.4.14: - version "0.4.14" - resolved "https://registry.yarnpkg.com/eventemitter2/-/eventemitter2-0.4.14.tgz#8f61b75cde012b2e9eb284d4545583b5643b61ab" - integrity sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas= - eventemitter3@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163" @@ -6099,7 +5575,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.2: +extend@3, extend@~3.0.0, extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -6205,7 +5681,7 @@ fast-redact@^1.4.2: resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-1.4.3.tgz#a11db5191e659354f8a431cca512d48d5f7951c7" integrity sha512-x4qQsA2zOcVuUBHv80EURely8MiAOTR3Z6T1Od82LzFbthhq7DXVUdxwfxtvP9hNCvd+rdcY9qMipK0YDTwWCw== -fast-safe-stringify@2.0.x, fast-safe-stringify@^2.0.4, fast-safe-stringify@^2.0.6: +fast-safe-stringify@2.0.x, fast-safe-stringify@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.6.tgz#04b26106cc56681f51a044cfc0d76cf0008ac2c2" integrity sha512-q8BZ89jjc+mz08rSxROs8VsrBBcn1SIw1kq9NjolL509tkABRk9io01RAjSaEv1Xb2uFLt8VtRiZbGp5H8iDtg== @@ -6227,16 +5703,6 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fclone@1.0.11, fclone@^1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fclone/-/fclone-1.0.11.tgz#10e85da38bfea7fc599341c296ee1d77266ee640" - integrity sha1-EOhdo4v+p/xZk0HClu4ddyZu5kA= - -fecha@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/fecha/-/fecha-2.3.3.tgz#948e74157df1a32fd1b12c3a3c3cdcb6ec9d96cd" - integrity sha512-lUGBnIamTAwk4znq5BcqsDaxSmZ9nDVJaij6NvRt/Tg4R69gERA+otPKbS86ROw9nxVMw2/mp1fnaiWqbs6Sdg== - figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: version "3.5.1" resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.1.tgz#862470112901c727a0e495a80744bd5baa1d6790" @@ -6257,13 +5723,6 @@ figures@^2.0.0: dependencies: escape-string-regexp "^1.0.5" -file-stream-rotator@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/file-stream-rotator/-/file-stream-rotator-0.4.1.tgz#09f67b86d6ea589d20b7852c51c59de55d916d6d" - integrity sha512-W3aa3QJEc8BS2MmdVpQiYLKHj3ijpto1gMDlsgCRSKfIUe6MwkcpODGPQ3vZfb0XvCeCqlu9CBQTN7oQri2TZQ== - dependencies: - moment "^2.11.2" - file-uri-to-path@1, file-uri-to-path@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" @@ -6380,13 +5839,6 @@ fn-name@~2.0.1: resolved "https://registry.yarnpkg.com/fn-name/-/fn-name-2.0.1.tgz#5214d7537a4d06a4a301c0cc262feb84188002e7" integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc= -follow-redirects@^1.2.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.7.0.tgz#489ebc198dc0e7f64167bd23b03c4c19b5784c76" - integrity sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ== - dependencies: - debug "^3.2.6" - follow-redirects@^1.3.0: version "1.6.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.6.1.tgz#514973c44b5757368bad8bddfe52f81f015c94cb" @@ -6466,11 +5918,6 @@ from2@^2.1.0, from2@^2.1.1: inherits "^2.0.1" readable-stream "^2.0.0" -fs-capacitor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fs-capacitor/-/fs-capacitor-2.0.0.tgz#6cbafaa39313eebf9c49ecff8795aadc08337fc5" - integrity sha512-CIJZpxbVWhO+qyODeCR55Q+6vj0p2oL8DAWd/DZi3Ev+25PimUoScw07K0fPgluaH3lFoqNvwW13BDYfHWFQJw== - fs-extra@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-6.0.1.tgz#8abc128f7946e310135ddc93b98bddb410e7a34b" @@ -6714,11 +6161,6 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" -git-node-fs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/git-node-fs/-/git-node-fs-1.0.0.tgz#49b215e242ebe43aa4c7561bbba499521752080f" - integrity sha1-SbIV4kLr5Dqkx1Ybu6SZUhdSCA8= - git-raw-commits@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/git-raw-commits/-/git-raw-commits-2.0.0.tgz#d92addf74440c14bcc5c83ecce3fb7f8a79118b5" @@ -6746,11 +6188,6 @@ git-semver-tags@^2.0.2: meow "^4.0.0" semver "^5.5.0" -git-sha1@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/git-sha1/-/git-sha1-0.1.2.tgz#599ac192b71875825e13a445f3a6e05118c2f745" - integrity sha1-WZrBkrcYdYJeE6RF86bgURjC90U= - git-up@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/git-up/-/git-up-4.0.1.tgz#cb2ef086653640e721d2042fe3104857d89007c0" @@ -6773,10 +6210,6 @@ gitconfiglocal@^1.0.0: dependencies: ini "^1.3.2" -"gkt@https://tgz.pm2.io/gkt-1.0.0.tgz": - version "1.0.0" - resolved "https://tgz.pm2.io/gkt-1.0.0.tgz#405502b007f319c3f47175c4474527300f2ab5ad" - glob-base@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" @@ -6985,69 +6418,12 @@ graphlib@^2.1.1, graphlib@^2.1.5: dependencies: lodash "^4.17.5" -graphql-extensions@0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.5.0.tgz#117b9872e5cafbebef5254ad795b41fa97037c14" - integrity sha512-2i0rpe4/D8jZj6XmxXGLFDAsGLhkFrSdpS5WfvTAzoXOc52hUAxNdsbgRQGeKMFhmanqA6FDXxO/s+BtPHChVA== - dependencies: - "@apollographql/apollo-tools" "^0.3.0" - -graphql-subscriptions@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/graphql-subscriptions/-/graphql-subscriptions-1.0.0.tgz#475267694b3bd465af6477dbab4263a3f62702b8" - integrity sha512-+ytmryoHF1LVf58NKEaNPRUzYyXplm120ntxfPcgOBC7TnK7Tv/4VRHeh4FAR9iL+O1bqhZs4nkibxQ+OA5cDQ== - dependencies: - iterall "^1.2.1" - -graphql-tag@^2.9.2: - version "2.10.1" - resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.10.1.tgz#10aa41f1cd8fae5373eaf11f1f67260a3cad5e02" - integrity sha512-jApXqWBzNXQ8jYa/HLkZJaVw9jgwNqZkywa2zfFn16Iv1Zb7ELNHkJaXHR7Quvd5SIGsy6Ny7SUKATgnu05uEg== - -graphql-tools-types@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/graphql-tools-types/-/graphql-tools-types-1.2.1.tgz#b8c277545d8623ae4e8944e7a869010ef879ffe1" - integrity sha512-wQj+SrM3T/s5s+mOpSJjxCNaJCdGCZwi1XvDizUHdUfgVBL4TP26FioRjtjmdi/psdWDktIEEqObiChQ1lYyxw== - dependencies: - "@babel/runtime-corejs2" "7.3.1" - ducky "2.7.0" - graphql "14.1.1" - pure-uuid "1.5.6" - -graphql-tools@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/graphql-tools/-/graphql-tools-4.0.4.tgz#ca08a63454221fdde825fe45fbd315eb2a6d566b" - integrity sha512-chF12etTIGVVGy3fCTJ1ivJX2KB7OSG4c6UOJQuqOHCmBQwTyNgCDuejZKvpYxNZiEx7bwIjrodDgDe9RIkjlw== - dependencies: - apollo-link "^1.2.3" - apollo-utilities "^1.0.1" - deprecated-decorator "^0.1.6" - iterall "^1.1.3" - uuid "^3.1.0" - -graphql-upload@^8.0.2: - version "8.0.4" - resolved "https://registry.yarnpkg.com/graphql-upload/-/graphql-upload-8.0.4.tgz#ed7cbde883b5cca493de77e39f95cddf40dfd514" - integrity sha512-jsTfVYXJ5mU6BXiiJ20CUCAcf41ICCQJ2ltwQFUuaFKiY4JhlG99uZZp5S3hbpQ/oA1kS7hz4pRtsnxPCa7Yfg== - dependencies: - busboy "^0.3.0" - fs-capacitor "^2.0.0" - http-errors "^1.7.1" - object-path "^0.11.4" - -graphql@14.1.1: - version "14.1.1" - resolved "https://registry.yarnpkg.com/graphql/-/graphql-14.1.1.tgz#d5d77df4b19ef41538d7215d1e7a28834619fac0" - integrity sha512-C5zDzLqvfPAgTtP8AUPIt9keDabrdRAqSWjj2OPRKrKxI9Fb65I36s1uCs1UUBFnSWTdO7hyHi7z1ZbwKMKF6Q== - dependencies: - iterall "^1.2.2" - growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -handlebars@4.x.x, handlebars@^4.0.2, handlebars@^4.0.3, handlebars@^4.0.6, handlebars@^4.1.0: +handlebars@^4.0.2, handlebars@^4.0.3, handlebars@^4.0.6, handlebars@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.1.0.tgz#0d6a6f34ff1f63cecec8423aa4169827bf787c3a" integrity sha512-l2jRuU1NAWK6AW5qqcTATWQJvNPEwkM7NEKSiv/gqOsoSQbVoWyqVEY5GS+XPQ88zLNmqASRpzfdm8d79hJS+w== @@ -7390,17 +6766,6 @@ http-errors@1.6.3, http-errors@~1.6.2, http-errors@~1.6.3: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-errors@^1.7.1: - version "1.7.1" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.1.tgz#6a4ffe5d35188e1c39f872534690585852e1f027" - integrity sha512-jWEUgtZWGSMba9I1N3gc1HmvpBUaNC9vDdA46yScAdp+C5rdEuKWUBLWTQpW9FwSWSbYYs++b6SDCxf9UEJzfw== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -7573,18 +6938,6 @@ indexof@0.0.1: resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= -inert@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/inert/-/inert-5.1.2.tgz#0c26f15bc22aae7af9c1f1a164bf867c58c5f4a6" - integrity sha512-5jSCKrQ7ENfdECnzLatCejXSkJwVzKFXZW30fI6TNHFbDuigT8IilRfydI2H5j9ZTnH7vXKO4WUg2qph9bItow== - dependencies: - ammo "3.x.x" - boom "7.x.x" - bounce "1.x.x" - hoek "6.x.x" - joi "14.x.x" - lru-cache "4.1.x" - inflight@^1.0.4, inflight@~1.0.5: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -7764,11 +7117,6 @@ is-arrayish@^0.2.1: resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - is-binary-path@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" @@ -8137,11 +7485,6 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= -is@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/is/-/is-3.3.0.tgz#61cff6dd3c4193db94a3d62582072b44e5645d79" - integrity sha512-nW24QBoPcFGGHJGUwnfpI7Yc5CdqWNdsyHQszVE/z2pKHXzh7FZ5GWhJqSyaQ9wMkQnsTx+kAI8bHlCX4tKdbg== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -8269,11 +7612,6 @@ items@2.x.x: resolved "https://registry.yarnpkg.com/items/-/items-2.1.2.tgz#0849354595805d586dac98e7e6e85556ea838558" integrity sha512-kezcEqgB97BGeZZYtX/MA8AG410ptURstvnz5RAgyFZ8wQFPMxHY8GpTq+/ZHKT3frSlIthUq7EvLt9xn3TvXg== -iterall@^1.1.3, iterall@^1.2.1, iterall@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7" - integrity sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA== - jest-changed-files@^23.4.2: version "23.4.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-23.4.2.tgz#1eed688370cd5eebafe4ae93d34bb3b64968fe83" @@ -8644,16 +7982,6 @@ joi@^13.0.2: isemail "3.x.x" topo "3.x.x" -js-git@^0.7.8: - version "0.7.8" - resolved "https://registry.yarnpkg.com/js-git/-/js-git-0.7.8.tgz#52fa655ab61877d6f1079efc6534b554f31e5444" - integrity sha1-UvplWrYYd9bxB578ZTS1VPMeVEQ= - dependencies: - bodec "^0.1.0" - culvert "^0.1.2" - git-sha1 "^0.1.2" - pako "^0.2.5" - js-levenshtein@^1.1.3: version "1.1.6" resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" @@ -8756,7 +8084,7 @@ json-schema@0.2.3: resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= -json-stringify-safe@5.0.1, json-stringify-safe@5.x.x, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: +json-stringify-safe@5.x.x, json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= @@ -8873,13 +8201,6 @@ kleur@^3.0.2: resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.2.tgz#83c7ec858a41098b613d5998a7b653962b504f68" integrity sha512-3h7B2WRT5LNXOtQiAaWonilegHcPSf9nLVXlSTci8lu1dZUuui61+EsPEZqSVxY7rXYmB2DVKMQILxaO5WL61Q== -kuler@1.0.x: - version "1.0.1" - resolved "https://registry.yarnpkg.com/kuler/-/kuler-1.0.1.tgz#ef7c784f36c9fb6e16dd3150d152677b2b0228a6" - integrity sha512-J9nVUucG1p/skKul6DU3PUZrhs0LPulNaeUOox0IyXDi8S4CztTHs1gQphhuZmzXG7VOQSf6NJfKuzteQLv9gQ== - dependencies: - colornames "^1.1.1" - latest-version@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-3.1.0.tgz#a205383fea322b33b5ae3b18abee0dc2f356ee15" @@ -8899,11 +8220,6 @@ lazy-cache@^0.2.3: resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= -lazy@~1.0.11: - version "1.0.11" - resolved "https://registry.yarnpkg.com/lazy/-/lazy-1.0.11.tgz#daa068206282542c088288e975c297c1ae77b690" - integrity sha1-2qBoIGKCVCwIgojpdcKXwa53tpA= - lcid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" @@ -9197,21 +8513,11 @@ lodash.fill@^3.4.0: resolved "https://registry.yarnpkg.com/lodash.fill/-/lodash.fill-3.4.0.tgz#a3c74ae640d053adf0dc2079f8720788e8bfef85" integrity sha1-o8dK5kDQU63w3CB5+HIHiOi/74U= -lodash.findindex@^4.4.0, lodash.findindex@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.findindex/-/lodash.findindex-4.6.0.tgz#a3245dee61fb9b6e0624b535125624bb69c11106" - integrity sha1-oyRd7mH7m24GJLU1ElYku2nBEQY= - lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= -lodash.foreach@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - integrity sha1-Gmo16s5AEoDH8G3d7DUWWrJ+PlM= - lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -9232,7 +8538,7 @@ lodash.isempty@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e" integrity sha1-b4bL7di+TsmHvpqvM8loTbGzHn4= -lodash.isequal@^4.0.0, lodash.isequal@^4.5.0: +lodash.isequal@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0" integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA= @@ -9252,16 +8558,6 @@ lodash.isstring@^4.0.1: resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= -lodash.last@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/lodash.last/-/lodash.last-3.0.0.tgz#242f663112dd4c6e63728c60a3c909d1bdadbd4c" - integrity sha1-JC9mMRLdTG5jcoxgo8kJ0b2tvUw= - -lodash.merge@^4.6.0: - version "4.6.1" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.1.tgz#adc25d9cb99b9391c59624f379fbba60d7111d54" - integrity sha512-AOYza4+Hf5z1/0Hztxpm2/xiPZgi/cjMqdnKTUWTBSKchJlxXXuUSxCCl8rJlf4g6yww/j6mA8nC8Hw/EZWxKQ== - lodash.orderby@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.orderby/-/lodash.orderby-4.6.0.tgz#e697f04ce5d78522f54d9338b32b81a3393e4eb3" @@ -9327,11 +8623,6 @@ lodash.templatesettings@^4.0.0: dependencies: lodash._reinterpolate "~3.0.0" -lodash.toarray@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" - integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE= - lodash.union@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88" @@ -9380,33 +8671,6 @@ log-update@^2.3.0: cli-cursor "^2.0.0" wrap-ansi "^3.0.1" -logform@^1.6.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/logform/-/logform-1.10.0.tgz#c9d5598714c92b546e23f4e78147c40f1e02012e" - integrity sha512-em5ojIhU18fIMOw/333mD+ZLE2fis0EzXl1ZwHx4iQzmpQi6odNiY/t+ITNr33JZhT9/KEaH+UPIipr6a9EjWg== - dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" - fecha "^2.3.3" - ms "^2.1.1" - triple-beam "^1.2.0" - -logform@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/logform/-/logform-2.1.2.tgz#957155ebeb67a13164069825ce67ddb5bb2dd360" - integrity sha512-+lZh4OpERDBLqjiwDLpAWNQu6KMjnlXH2ByZwCuSqVPJletw0kTWJf5CgSNAUKn1KUkv3m2cUz/LK8zyEy7wzQ== - dependencies: - colors "^1.2.1" - fast-safe-stringify "^2.0.4" - fecha "^2.3.3" - ms "^2.1.1" - triple-beam "^1.3.0" - -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - long@~3: version "3.2.0" resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" @@ -9427,15 +8691,6 @@ loud-rejection@^1.0.0: currently-unhandled "^0.4.1" signal-exit "^3.0.0" -lout@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/lout/-/lout-11.1.0.tgz#4c48b6b068b42197acb6420d79b856095d174589" - integrity sha512-HAowXHOraHWEPrYPuNc7mR8v1Sn1Gnmy8uvpXYDidVHsTMlrkxqUZ1liBXGOf4eN22kVB+eG29NWymPhZG/wKg== - dependencies: - boom "7.x.x" - handlebars "4.x.x" - hoek "5.x.x" - lowdb@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowdb/-/lowdb-1.0.0.tgz#5243be6b22786ccce30e50c9a33eac36b20c8064" @@ -9470,7 +8725,7 @@ lru-cache@4.1.x, lru-cache@^4.0.0, lru-cache@^4.0.1, lru-cache@^4.1.2, lru-cache pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@^5.0.0, lru-cache@^5.1.1: +lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== @@ -9695,7 +8950,7 @@ merge@^1.2.0: resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== -methods@^1.1.1, methods@~1.1.2: +methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= @@ -9873,7 +9128,7 @@ mixin-object@^2.0.1: for-in "^0.1.3" is-extendable "^0.1.1" -mkdirp@0.5.1, mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: +mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= @@ -9885,14 +9140,7 @@ modify-values@^1.0.0: resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.1.tgz#b3939fa605546474e3e3e3c63d64bd43b4ee6022" integrity sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw== -moment-timezone@^0.5.x: - version "0.5.23" - resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.23.tgz#7cbb00db2c14c71b19303cb47b0fb0a6d8651463" - integrity sha512-WHFH85DkCfiNMDX5D3X7hpNH3/PUhjTGcD0U1SgfBGZxJ3qUmJh5FdvaFjcClxOvB3rzdfj4oRffbI38jEnC1w== - dependencies: - moment ">= 2.9.0" - -moment@2.x.x, "moment@>= 2.9.0", moment@>=2.14.0, moment@^2.11.2, moment@^2.21.0, moment@^2.22.1, moment@^2.22.2: +moment@2.x.x, moment@>=2.14.0, moment@^2.22.1: version "2.24.0" resolved "https://registry.yarnpkg.com/moment/-/moment-2.24.0.tgz#0d055d53f5052aa653c9f6eb68bb5d12bf5c2b5b" integrity sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg== @@ -9954,7 +9202,7 @@ mute-stream@~0.0.4: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.10.0, nan@^2.2.1, nan@^2.9.2: +nan@^2.10.0, nan@^2.2.1, nan@^2.9.2, nan@~2.12.1: version "2.12.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552" integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw== @@ -9964,7 +9212,7 @@ nan@~2.10.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" integrity sha512-bAdJv7fBLhWC+/Bls0Oza+mvTaNQtP+1RyhhhvD95pgUJz6XM5IzgmxOkItJ9tkoCiplvAnXI1tNmmUD/eScyA== -nanomatch@^1.2.9: +nanomatch@^1.2.13, nanomatch@^1.2.9: version "1.2.13" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== @@ -10063,13 +9311,6 @@ node-alias@^1.0.4: chalk "^1.1.1" lodash "^4.2.0" -node-emoji@^1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.8.1.tgz#6eec6bfb07421e2148c75c6bba72421f8530a826" - integrity sha512-+ktMAh1Jwas+TnGodfCfjUbJKoANqPaJFN0z0iqh41eqD8dvguNzcitVSBSVK1pidz0AqGbLKcoVuVLRVZ/aVg== - dependencies: - lodash.toarray "^4.4.0" - node-fetch-npm@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.2.tgz#7258c9046182dca345b4208eda918daf33697ff7" @@ -10079,7 +9320,7 @@ node-fetch-npm@^2.0.2: json-parse-better-errors "^1.0.0" safe-buffer "^5.1.1" -node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.3.0: +node-fetch@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5" integrity sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA== @@ -10543,14 +9784,6 @@ npmlog@~4.0.0: gauge "~2.7.1" set-blocking "~2.0.0" -nssocket@0.6.0, nssocket@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/nssocket/-/nssocket-0.6.0.tgz#59f96f6ff321566f33c70f7dbeeecdfdc07154fa" - integrity sha1-Wflvb/MhVm8zxw99vu7N/cBxVPo= - dependencies: - eventemitter2 "~0.4.14" - lazy "~1.0.11" - number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -10585,21 +9818,11 @@ object-copy@^0.1.0: define-property "^0.2.5" kind-of "^3.0.3" -object-hash@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.3.1.tgz#fde452098a951cb145f039bb7d455449ddc126df" - integrity sha512-OSuu/pU4ENM9kmREg0BdNrUDIl1heYa4mBZacJc+vVWz4GtAwu7jO8s4AIt2aGRUTqxykpWzI3Oqnsm13tTMDA== - object-keys@^1.0.12: version "1.1.0" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.0.tgz#11bd22348dd2e096a045ab06f6c85bcc340fa032" integrity sha512-6OO5X1+2tYkNyNEx6TsCxEqFfRWaqx6EtMiSbGrw8Ob8v9Ne+Hl8rBAgLBZn5wjEz3s/s6U1WXFUFOcxxAwUpg== -object-path@^0.11.4: - version "0.11.4" - resolved "https://registry.yarnpkg.com/object-path/-/object-path-0.11.4.tgz#370ae752fbf37de3ea70a861c23bba8915691949" - integrity sha1-NwrnUvvzfePqcKhhwju6iRVpGUk= - object-visit@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" @@ -10649,11 +9872,6 @@ once@^1.3.0, once@^1.3.1, once@^1.3.3, once@^1.4.0, once@~1.4.0: dependencies: wrappy "1" -one-time@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/one-time/-/one-time-0.0.4.tgz#f8cdf77884826fe4dff93e3a9cc37b1e4480742e" - integrity sha1-+M33eISCb+Tf+T46nMN7HkSAdC4= - onetime@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" @@ -10920,20 +10138,6 @@ pac-proxy-agent@^2.0.1: raw-body "^2.2.0" socks-proxy-agent "^3.0.0" -pac-proxy-agent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pac-proxy-agent/-/pac-proxy-agent-3.0.0.tgz#11d578b72a164ad74bf9d5bac9ff462a38282432" - integrity sha512-AOUX9jES/EkQX2zRz0AW7lSx9jD//hQS8wFXBvcnd/J2Py9KaMJMqV/LPqJssj1tgGufotb2mmopGPR15ODv1Q== - dependencies: - agent-base "^4.2.0" - debug "^3.1.0" - get-uri "^2.0.0" - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - pac-resolver "^3.0.0" - raw-body "^2.2.0" - socks-proxy-agent "^4.0.1" - pac-resolver@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pac-resolver/-/pac-resolver-3.0.0.tgz#6aea30787db0a891704deb7800a722a7615a6f26" @@ -11003,11 +10207,6 @@ pacote@^9.4.1: unique-filename "^1.1.1" which "^1.3.1" -pako@^0.2.5: - version "0.2.9" - resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" - integrity sha1-8/dSL073gjSNqBYbrZ7P1Rv4OnU= - pako@~1.0.2, pako@~1.0.5: version "1.0.8" resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.8.tgz#6844890aab9c635af868ad5fecc62e8acbba3ea4" @@ -11285,13 +10484,6 @@ pgpass@1.x: dependencies: split "^1.0.0" -pidusage@^2.0.14: - version "2.0.17" - resolved "https://registry.yarnpkg.com/pidusage/-/pidusage-2.0.17.tgz#6b4a2b4a09026f0e9828f7e5627837e4c0672581" - integrity sha512-N8X5v18rBmlBoArfS83vrnD0gIFyZkXEo7a5pAS2aT0i2OLVymFb2AzVg+v8l/QcXnE1JwZcaXR8daJcoJqtjw== - dependencies: - safe-buffer "^5.1.2" - pify@3.0.0, pify@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" @@ -11314,6 +10506,13 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= +pino-multi-stream@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/pino-multi-stream/-/pino-multi-stream-4.0.2.tgz#1996716dc5938e4de58f044469c549dbb3d7008b" + integrity sha512-QQajDnc0dv5p/iOgPiDoYwI5EKnnBNxK1NCsdD6IlM14ddmIt5kaGqk+73qjvCc2usSELSNa95D2qXGLehl6YQ== + dependencies: + pino "^5.11.1" + pino-pretty@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/pino-pretty/-/pino-pretty-2.5.0.tgz#fade5b6d2acbdbf2c7e77adf220e7b7d89d04437" @@ -11377,77 +10576,6 @@ pluralize@^7.0.0: resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== -pm2-axon-rpc@^0.5.0, pm2-axon-rpc@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/pm2-axon-rpc/-/pm2-axon-rpc-0.5.1.tgz#ad3c43c43811c71f13e5eee2821194d03ceb03fe" - integrity sha512-hT8gN3/j05895QLXpwg+Ws8PjO4AVID6Uf9StWpud9HB2homjc1KKCcI0vg9BNOt56FmrqKDT1NQgheIz35+sA== - dependencies: - debug "^3.0" - -pm2-axon@3.3.0, pm2-axon@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/pm2-axon/-/pm2-axon-3.3.0.tgz#a9badfdb8e083fbd5d7d24317b4a21eb708f0735" - integrity sha512-dAFlFYRuFbFjX7oAk41zT+dx86EuaFX/TgOp5QpUKRKwxb946IM6ydnoH5sSTkdI2pHSVZ+3Am8n/l0ocr7jdQ== - dependencies: - amp "~0.3.1" - amp-message "~0.1.1" - debug "^3.0" - escape-regexp "0.0.1" - -pm2-deploy@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/pm2-deploy/-/pm2-deploy-0.4.0.tgz#d543076919f7776c57eb75841a4320f547287661" - integrity sha512-3BdCghcGwMKwl3ffHZhc+j5JY5dldH9nq8m/I9W5wehJuSRZIyO96VOgKTMv3hYp7Yk5E+2lRGm8WFNlp65vOA== - dependencies: - async "^2.6" - tv4 "^1.3" - -pm2-multimeter@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/pm2-multimeter/-/pm2-multimeter-0.1.2.tgz#1a1e55153d41a05534cea23cfe860abaa0eb4ace" - integrity sha1-Gh5VFT1BoFU0zqI8/oYKuqDrSs4= - dependencies: - charm "~0.1.1" - -pm2@^3.2.3: - version "3.2.9" - resolved "https://registry.yarnpkg.com/pm2/-/pm2-3.2.9.tgz#6e0e009355fa089007689d97c6565f6b3e341476" - integrity sha512-dlNIwmbsRe+Ttculrjj776ILtP5rjDsykxCOhpZB+ioCsnOmRUGJHrWCdmoOjcyHgA5tvE/X0s9M1J/hYsRKGQ== - dependencies: - "@pm2/agent" "^0.5.22" - "@pm2/io" "~2.4.2" - "@pm2/js-api" "^0.5.43" - async "^2.6.1" - blessed "^0.1.81" - chalk "^2.4.1" - chokidar "^2.0.4" - cli-table-redemption "^1.0.0" - commander "2.15.1" - cron "^1.3" - date-fns "^1.29.0" - debug "^3.1" - eventemitter2 "5.0.1" - fclone "1.0.11" - mkdirp "0.5.1" - moment "^2.22.2" - needle "^2.2.1" - nssocket "0.6.0" - pidusage "^2.0.14" - pm2-axon "3.3.0" - pm2-axon-rpc "^0.5.1" - pm2-deploy "^0.4.0" - pm2-multimeter "^0.1.2" - promptly "^2" - semver "^5.5" - shelljs "~0.8.2" - source-map-support "^0.5.6" - sprintf-js "1.1.1" - v8-compile-cache "^2.0.0" - vizion "~2.0.2" - yamljs "^0.3.0" - optionalDependencies: - gkt "https://tgz.pm2.io/gkt-1.0.0.tgz" - pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -11591,13 +10719,6 @@ promise-retry@^1.1.1: dependencies: asap "~2.0.3" -promptly@^2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/promptly/-/promptly-2.2.0.tgz#2a13fa063688a2a5983b161fff0108a07d26fc74" - integrity sha1-KhP6BjaIoqWYOxYf/wEIoH0m/HQ= - dependencies: - read "^1.0.4" - prompts@^0.1.9: version "0.1.14" resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2" @@ -11631,25 +10752,6 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= -protobufjs@^6.8.6: - version "6.8.8" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.8.8.tgz#c8b4f1282fd7a90e6f5b109ed11c84af82908e7c" - integrity sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.0" - "@types/node" "^10.1.0" - long "^4.0.0" - protocols@^1.1.0, protocols@^1.4.0: version "1.4.7" resolved "https://registry.yarnpkg.com/protocols/-/protocols-1.4.7.tgz#95f788a4f0e979b291ffefcf5636ad113d037d32" @@ -11684,20 +10786,6 @@ proxy-agent@2.3.1: proxy-from-env "^1.0.0" socks-proxy-agent "^3.0.0" -proxy-agent@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/proxy-agent/-/proxy-agent-3.0.3.tgz#1c1a33db60ef5f2e9e35b876fd63c2bc681c611d" - integrity sha512-PXVVVuH9tiQuxQltFJVSnXWuDtNr+8aNBP6XVDDCDiUuDN8eRCm+ii4/mFWmXWEA0w8jjJSlePa4LXlM4jIzNA== - dependencies: - agent-base "^4.2.0" - debug "^3.1.0" - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.1" - lru-cache "^4.1.2" - pac-proxy-agent "^3.0.0" - proxy-from-env "^1.0.0" - socks-proxy-agent "^4.0.1" - proxy-from-env@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.0.0.tgz#33c50398f70ea7eb96d21f7b817630a55791c7ee" @@ -11779,11 +10867,6 @@ punycode@^1.2.4, punycode@^1.4.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= -pure-uuid@1.5.6: - version "1.5.6" - resolved "https://registry.yarnpkg.com/pure-uuid/-/pure-uuid-1.5.6.tgz#313dc69835b42c0e3468199538edcbb730ddfa09" - integrity sha512-yUXFK0Xt+twSu6efNkBWPEBryDQ/3uaeA5nMKaGP4RM6WJyap657lU9yS4NSPB7X485sAkmP8SNv0vCxkXhrNA== - q@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" @@ -12007,7 +11090,7 @@ read-pkg@^4.0.1: parse-json "^4.0.0" pify "^3.0.0" -read@1, read@^1.0.4, read@~1.0.1, read@~1.0.7: +read@1, read@~1.0.1, read@~1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/read/-/read-1.0.7.tgz#b3da19bd052431a97671d44a42634adf710b40c4" integrity sha1-s9oZvQUkMal2cdRKQmNK33ELQMQ= @@ -12037,7 +11120,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.6, readable-stream@^3.1.1: +readable-stream@3, readable-stream@^3.0.0, readable-stream@^3.0.6: version "3.1.1" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.1.1.tgz#ed6bbc6c5ba58b090039ff18ce670515795aeb06" integrity sha512-DkN66hPyqDhnIQ6Jcsvx9bFjhw214O4poMBcIMgPVpQvNy9a0e0Uhg5SqySyDKAmUlwt8LonTBz1ezOnM8pUdA== @@ -12422,11 +11505,6 @@ ret@~0.1.10: resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== -retry@0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - retry@^0.10.0, retry@~0.10.0: version "0.10.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" @@ -12454,6 +11532,11 @@ ripemd160@^2.0.0, ripemd160@^2.0.1: hash-base "^3.0.0" inherits "^2.0.1" +rotating-file-stream@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/rotating-file-stream/-/rotating-file-stream-1.4.0.tgz#7df5dcfe1c8ac32296cee4ab79430d189608c780" + integrity sha512-y1hYZ+JcgPSCchbfeZYuFed0GyWpdcs7TR3eagZmvFTv4nWZq86gxyMmtuV4z8/D8ozcfJ2uJw4QCzQdn5X+WQ== + router-ips@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/router-ips/-/router-ips-1.0.0.tgz#44e00858ebebc0133d58e40b2cd8a1fbb04203f5" @@ -12600,11 +11683,6 @@ semver@4.3.2: resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.2.tgz#c7a07158a80bedd052355b770d82d6640f803be7" integrity sha1-x6BxWKgL7dBSNVt3DYLWZA+AO+c= -semver@5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - integrity sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA== - semver@^4.1.0: version "4.3.6" resolved "https://registry.yarnpkg.com/semver/-/semver-4.3.6.tgz#300bc6e0e86374f7ba61068b5b1ecd57fc6532da" @@ -12722,7 +11800,7 @@ shebang-regex@^1.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= -shelljs@^0.8.2, shelljs@~0.8.2: +shelljs@^0.8.2: version "0.8.3" resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.3.tgz#a7f3319520ebf09ee81275b2368adb286659b097" integrity sha512-fc0BKlAWiLpwZljmOvAOTE/gXawtCoNrP5oaY7KIaQbbyHeQVg01pSEuEGvGh3HEdBU4baCD7wQBwADmM/7f7A== @@ -12736,11 +11814,6 @@ shellwords@^0.1.1: resolved "https://registry.yarnpkg.com/shellwords/-/shellwords-0.1.1.tgz#d6b9181c1a48d397324c84871efbcfc73fc0654b" integrity sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww== -shimmer@^1.0.0, shimmer@^1.1.0, shimmer@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/shimmer/-/shimmer-1.2.1.tgz#610859f7de327b587efebf501fb43117f9aff337" - integrity sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw== - shot@4.x.x: version "4.0.7" resolved "https://registry.yarnpkg.com/shot/-/shot-4.0.7.tgz#b05d2858634fedc18ece99e8f638fab7c9f9d4c4" @@ -12749,7 +11822,7 @@ shot@4.x.x: hoek "6.x.x" joi "14.x.x" -signal-exit@3.0.2, signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0= @@ -12761,13 +11834,6 @@ simple-git@^1.85.0: dependencies: debug "^4.0.1" -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - sisteransi@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-0.1.1.tgz#5431447d5f7d1675aac667ccd0b865a4994cb3ce" @@ -13065,7 +12131,7 @@ socks-proxy-agent@^3.0.0: agent-base "^4.1.0" socks "^1.1.10" -socks-proxy-agent@^4.0.0, socks-proxy-agent@^4.0.1: +socks-proxy-agent@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" integrity sha512-Kezx6/VBguXOsEe5oU3lXYyKMi4+gva72TwJ7pQY5JfqUx2nMk7NXA6z/mpNqIlfQjWYVfeuNvQjexiTaTn6Nw== @@ -13226,11 +12292,6 @@ split@^1.0.0: dependencies: through "2" -sprintf-js@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.1.tgz#36be78320afe5801f6cea3ee78b6e5aab940ea0c" - integrity sha1-Nr54Mgr+WAH2zqPueLblqrlA6gw= - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -13282,7 +12343,7 @@ stack-generator@^2.0.3: dependencies: stackframe "^1.0.4" -stack-trace@0.0.10, stack-trace@0.0.x: +stack-trace@0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= @@ -13323,7 +12384,7 @@ static-extend@^0.1.1: define-property "^0.2.5" object-copy "^0.1.0" -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2": +"statuses@>= 1.4.0 < 2": version "1.5.0" resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= @@ -13377,11 +12438,6 @@ stream-shift@^1.0.0: resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.0.tgz#d5c752825e5367e786f78e18e445ea223a155952" integrity sha1-1cdSgl5TZ+eG944Y5EXqIjoVWVI= -streamsearch@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" - integrity sha1-gIudDlb8Jz2Am6VzOOkpkZoanxo= - strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" @@ -13523,17 +12579,6 @@ strong-log-transformer@^2.0.0: minimist "^1.2.0" through "^2.3.4" -subscriptions-transport-ws@^0.9.11: - version "0.9.15" - resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.15.tgz#68a8b7ba0037d8c489fb2f5a102d1494db297d0d" - integrity sha512-f9eBfWdHsePQV67QIX+VRhf++dn1adyC/PZHP6XI5AfKnZ4n0FW+v5omxwdHVpd4xq2ZijaHEcmlQrhBY79ZWQ== - dependencies: - backo2 "^1.0.2" - eventemitter3 "^3.1.0" - iterall "^1.2.1" - symbol-observable "^1.0.4" - ws "^5.2.0" - subtext@6.x.x: version "6.0.12" resolved "https://registry.yarnpkg.com/subtext/-/subtext-6.0.12.tgz#ac09be3eac1eca3396933adeadd65fc781f64fc1" @@ -13580,7 +12625,7 @@ supports-hyperlinks@^1.0.1: has-flag "^2.0.0" supports-color "^5.0.0" -symbol-observable@^1.0.4, symbol-observable@^1.1.0: +symbol-observable@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -13703,11 +12748,6 @@ text-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== -text-hex@1.0.x: - version "1.0.0" - resolved "https://registry.yarnpkg.com/text-hex/-/text-hex-1.0.0.tgz#69dc9c1b17446ee79a92bf5b884bb4b9127506f5" - integrity sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg== - text-table@~0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -13843,11 +12883,6 @@ to-regex@^3.0.1, to-regex@^3.0.2: regex-not "^1.0.2" safe-regex "^1.1.0" -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - toml@^2.3.2: version "2.3.6" resolved "https://registry.yarnpkg.com/toml/-/toml-2.3.6.tgz#25b0866483a9722474895559088b436fd11f861b" @@ -13929,11 +12964,6 @@ trim-right@^1.0.1: resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" integrity sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM= -triple-beam@^1.2.0, triple-beam@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/triple-beam/-/triple-beam-1.3.0.tgz#a595214c7298db8339eeeee083e4d10bd8cb8dd9" - integrity sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw== - ts-jest@^23.10.5: version "23.10.5" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-23.10.5.tgz#cdb550df4466a30489bf70ba867615799f388dd5" @@ -13960,7 +12990,7 @@ ts-loader@^5.3.1, ts-loader@^5.3.3: micromatch "^3.1.4" semver "^5.0.1" -tslib@1.9.3, tslib@^1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: +tslib@^1, tslib@^1.8.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: version "1.9.3" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286" integrity sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ== @@ -14012,11 +13042,6 @@ tunnel-agent@~0.4.1: resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" integrity sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us= -tv4@^1.3: - version "1.3.0" - resolved "https://registry.yarnpkg.com/tv4/-/tv4-1.3.0.tgz#d020c846fadd50c855abb25ebaecc68fc10f7963" - integrity sha1-0CDIRvrdUMhVq7JeuuzGj8EPeWM= - tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" @@ -14105,11 +13130,6 @@ uid-number@0.0.6: resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" integrity sha1-DqEOgDXo61uOREnwbaHHMGY7qoE= -ultron@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c" - integrity sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og== - umask@^1.1.0, umask@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/umask/-/umask-1.1.0.tgz#f29cebf01df517912bb58ff9c4e50fde8e33320d" @@ -14361,12 +13381,12 @@ uuid-parse@^1.0.0: resolved "https://registry.yarnpkg.com/uuid-parse/-/uuid-parse-1.0.0.tgz#f4657717624b0e4b88af36f98d89589a5bbee569" integrity sha1-9GV3F2JLDkuIrzb5jYlYmlu+5Wk= -uuid@^3.0.1, uuid@^3.1.0, uuid@^3.2.1, uuid@^3.3.2: +uuid@^3.0.1, uuid@^3.2.1, uuid@^3.3.2: version "3.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131" integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA== -v8-compile-cache@^2.0.0, v8-compile-cache@^2.0.2: +v8-compile-cache@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.2.tgz#a428b28bb26790734c4fc8bc9fa106fccebf6a6c" integrity sha512-1wFuMUIM16MDJRCrpbpuEPTUGmM5QMUg0cr3KFwra2XgOgFcPGDQHDh3CszSCD2Zewc/dh/pamNEW8CbfDebUw== @@ -14424,20 +13444,6 @@ vision@^5.4.4: items "2.x.x" joi "14.x.x" -vizion@~2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/vizion/-/vizion-2.0.2.tgz#fcc263f41a4543b02b655c1b6c4ff1406726d2fa" - integrity sha512-UGDB/UdC1iyPkwyQaI9AFMwKcluQyD4FleEXObrlu254MEf16MV8l+AZdpFErY/iVKZVWlQ+OgJlVVJIdeMUYg== - dependencies: - async "2.6.1" - git-node-fs "^1.0.0" - ini "^1.3.4" - js-git "^0.7.8" - lodash.findindex "^4.6.0" - lodash.foreach "^4.5.0" - lodash.get "^4.4.2" - lodash.last "^3.0.0" - vm-browserify@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" @@ -14450,23 +13456,6 @@ vscode-languageserver-types@^3.5.0: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.14.0.tgz#d3b5952246d30e5241592b6dde8280e03942e743" integrity sha512-lTmS6AlAlMHOvPQemVwo3CezxBp0sNB95KNPkqp3Nxd5VFEnuG1ByM0zlRWos0zjO3ZWtkvhal0COgiV1xIA4A== -vxx@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/vxx/-/vxx-1.2.2.tgz#741fb51c6f11d3383da6f9b92018a5d7ba807611" - integrity sha1-dB+1HG8R0zg9pvm5IBil17qAdhE= - dependencies: - continuation-local-storage "^3.1.4" - debug "^2.6.3" - extend "^3.0.0" - is "^3.2.0" - lodash.findindex "^4.4.0" - lodash.isequal "^4.0.0" - lodash.merge "^4.6.0" - methods "^1.1.1" - semver "^5.0.1" - shimmer "^1.0.0" - uuid "^3.0.1" - w3c-hr-time@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz#82ac2bff63d950ea9e3189a58a65625fedf19045" @@ -14666,50 +13655,6 @@ windows-release@^3.1.0: dependencies: execa "^0.10.0" -winston-compat@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/winston-compat/-/winston-compat-0.1.4.tgz#599b4ce807ffe728713ecc25ede3f6b89425b739" - integrity sha512-mMEfFsSm6GmkFF+f4/0UJtG4N1vSaczGmXLVJYmS/+u2zUaIPcw2ZRuwUg2TvVBjswgiraN+vNnAG8z4fRUZ4w== - dependencies: - cycle "~1.0.3" - logform "^1.6.0" - triple-beam "^1.2.0" - -winston-daily-rotate-file@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/winston-daily-rotate-file/-/winston-daily-rotate-file-3.6.0.tgz#93b18c62ee778611494955ed0740f3a3dd67ba19" - integrity sha512-J02YBlG+NJkgpcz0xOlziaNUCEXB/Rer9L3h1b3IClzEH/XsGDpttmyxaXN4pECBxkOSTyk2YDFiqGlTIlxmbw== - dependencies: - file-stream-rotator "^0.4.1" - object-hash "^1.3.0" - semver "^5.6.0" - triple-beam "^1.3.0" - winston-compat "^0.1.4" - winston-transport "^4.2.0" - -winston-transport@^4.2.0, winston-transport@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/winston-transport/-/winston-transport-4.3.0.tgz#df68c0c202482c448d9b47313c07304c2d7c2c66" - integrity sha512-B2wPuwUi3vhzn/51Uukcao4dIduEiPOcOt9HJ3QeaXgkJ5Z7UwpBzxS4ZGNHtrxrUvTwemsQiSys0ihOf8Mp1A== - dependencies: - readable-stream "^2.3.6" - triple-beam "^1.2.0" - -winston@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/winston/-/winston-3.2.1.tgz#63061377976c73584028be2490a1846055f77f07" - integrity sha512-zU6vgnS9dAWCEKg/QYigd6cgMVVNwyTzKs81XZtTFuRwJOcDdBg7AU0mXVyNbs7O5RH2zdv+BdNZUlx7mXPuOw== - dependencies: - async "^2.6.1" - diagnostics "^1.1.1" - is-stream "^1.1.0" - logform "^2.1.1" - one-time "0.0.4" - readable-stream "^3.1.1" - stack-trace "0.0.x" - triple-beam "^1.3.0" - winston-transport "^4.3.0" - wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" @@ -14804,29 +13749,13 @@ write-pkg@^3.1.0: sort-keys "^2.0.0" write-json-file "^2.2.0" -ws@^3.0.0: - version "3.3.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-3.3.3.tgz#f1cf84fe2d5e901ebce94efaece785f187a228f2" - integrity sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA== - dependencies: - async-limiter "~1.0.0" - safe-buffer "~5.1.0" - ultron "~1.1.0" - -ws@^5.1.0, ws@^5.2.0: +ws@^5.2.0: version "5.2.2" resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f" integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA== dependencies: async-limiter "~1.0.0" -ws@^6.0.0: - version "6.1.3" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.1.3.tgz#d2d2e5f0e3c700ef2de89080ebc0ac6e1bf3a72d" - integrity sha512-tbSxiT+qJI223AP4iLfQbkbxkwdFcneYinM2+x46Gx2wgvbaOMO36czfdfVUBRTHvzAMRhDd98sA5d/BuWbQdg== - dependencies: - async-limiter "~1.0.0" - xcase@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/xcase/-/xcase-2.0.1.tgz#c7fa72caa0f440db78fd5673432038ac984450b9" @@ -14860,11 +13789,6 @@ xregexp@2.0.0: resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= -xstate@^4.2.2: - version "4.3.2" - resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.3.2.tgz#772a5f10b80e96f3dd872bedd45bc5bcfd0d2692" - integrity sha512-xm01SXhV4/LmxTLOOuhQfdWLFXzl/6fi3DEY6P1WxnuCDJZ/NsSvSggNmKddOTKgGXJgA/5aWxFturJsExV9cw== - xstate@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/xstate/-/xstate-4.3.1.tgz#54554b2116470fe630ca3b7e92ff1ce74b508ff9" @@ -14895,14 +13819,6 @@ yallist@^3.0.0, yallist@^3.0.2: resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.3.tgz#b4b049e314be545e3ce802236d6cd22cd91c3de9" integrity sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A== -yamljs@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/yamljs/-/yamljs-0.3.0.tgz#dc060bf267447b39f7304e9b2bfbe8b5a7ddb03b" - integrity sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ== - dependencies: - argparse "^1.0.7" - glob "^7.0.5" - yargs-parser@10.x, yargs-parser@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -14990,15 +13906,3 @@ yup@^0.26.10: property-expr "^1.5.0" synchronous-promise "^2.0.5" toposort "^2.0.2" - -zen-observable-ts@^0.8.15: - version "0.8.15" - resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-0.8.15.tgz#6cf7df6aa619076e4af2f707ccf8a6290d26699b" - integrity sha512-sXKPWiw6JszNEkRv5dQ+lQCttyjHM2Iks74QU5NP8mMPS/NrzTlHDr780gf/wOBqmHkPO6NCLMlsa+fAQ8VE8w== - dependencies: - zen-observable "^0.8.0" - -zen-observable@^0.8.0: - version "0.8.13" - resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.13.tgz#a9f1b9dbdfd2d60a08761ceac6a861427d44ae2e" - integrity sha512-fa+6aDUVvavYsefZw0zaZ/v3ckEtMgCFi30sn91SEZea4y/6jQp05E3omjkX91zV6RVdn15fqnFZ6RKjRGbp2g==